QtCreator 插件框架探索与分析

Qt环境:Qt 5.11.3 QtCreator4.8.2
源码包 Qt 4.8.2

插件动态拔插

需同时满足Base与Extension需求(基本插件与其他扩展性插件)。而在QtCreator插件探索的过程中,已发现源码中对基本插件与外部插件相关使用。如下:

  • QtProject: app/main.cpp, line 541~607。
    const PluginSpecSet plugins = PluginManager::plugins();
        PluginSpec *coreplugin = 0;
        foreach (PluginSpec *spec, plugins) {
            if (spec->name() == QLatin1String(corePluginNameC)) {
                coreplugin = spec;
                break;
            }
        }
        if (!coreplugin) {
            QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1Char(',')));
            const QString reason = QCoreApplication::translate("Application", "Could not find Core plugin in %1").arg(nativePaths);
            displayError(msgCoreLoadFailure(reason));
            return 1;
        }
        if (!coreplugin->isEffectivelyEnabled()) {
            const QString reason = QCoreApplication::translate("Application", "Core plugin is disabled.");
            displayError(msgCoreLoadFailure(reason));
            return 1;
        }
        if (coreplugin->hasError()) {
            displayError(msgCoreLoadFailure(coreplugin->errorString()));
            return 1;
        }
        if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
            printVersion(coreplugin);
            return 0;
        }
        if (foundAppOptions.contains(QLatin1String(HELP_OPTION1))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION2))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION3))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION4))) {
            printHelp(QFileInfo(app.applicationFilePath()).baseName());
            return 0;
        }
    

插件认证机制

插件认证机制分为插件基本信息和身份校验。主要作用防止其他无关插件或者非当前应用插件加载。PluginSpec Class(Plugin外壳)包含插件基本信息。包含插件名称、主版本、兼容版本、次版本、版权、协议、描述、路径、类别。

  • QtProject:src/libs/extensionsystem/pluginspec, line 78~144
    class EXTENSIONSYSTEM_EXPORT PluginSpec
    {
    public:
        enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
    
        ~PluginSpec();
    
        // information from the xml file, valid after 'Read' state is reached
        QString name() const;
        QString version() const;
        QString compatVersion() const;
        QString vendor() const;
        QString copyright() const;
        QString license() const;
        QString description() const;
        QString url() const;
        QString category() const;
    ...
    };
    

QtPlugin 支持的身份校验

  • Q_PLUGIN_METADATA(官网可查阅用法)
  • Json文件
     {
        \"Name\" : \"HelloWorld\",
        \"Version\" : \"$$QTCREATOR_VERSION\",
        \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
        \"DisabledByDefault\" : true,
        \"Vendor\" : \"The Qt Company Ltd\",
        \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
        \"License\" : [ \"Commercial Usage\",
                      \"\",
                      \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
                      \"\",
                      \"GNU General Public License Usage\",
                      \"\",
                      \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
        ],
        \"Description\" : \"Hello World sample plugin.\",
        \"Url\" : \"http://www.qt.io\",
        $$dependencyList
    }
    

插件加载行为模式

本章节针对QtCreator插件行为模式进行相关分析,以明确插件程序初始化流程。

  • 阶段性加载
    QtCreator 插件框架探索与分析

QtCreator拥有插件阶段加载,如上图所示,其中CorePlugin也是众多插件的一个,和阶段3中的任意子模块存储格式一致,均为动态库。在程序main中(此处应该是闭包PluginManager的程序)启动后,先对CorePlugin进行相关的插件加载,其中CorePlugin包含的模块将在下一小节CorePlugin插件内部中进行相关解释。在启动CorePlugin之后,将会针对每个每个业务进行相关的插件加载。
此处略过load和Plugin壳加载,插件内部将代码实现的三段分层(包含先后顺序):

  1. virtual bool initialize = 0;
  2. virtual void extensionsInitialized = 0;
  3. virtual bool delayedInitialize;

CorePlugin插件内部实现

  • 定义与构造私有资源
    QtCreator 插件框架探索与分析
    CorePlugin中存在三个需要动态new的对象, MainWindow 继承于QWidget,主要涉及界面框架草、EditMode继承于QObject,涉及界面的编辑模式、Locator继承QObject,涉及本地服务检索。此处主要关心点为MainWindow的初始化。

  • 全局服务定义
    QtCreator 插件框架探索与分析
    每一项注册到Action中将会提供相关的标识ID,这里实现主要针对界面的注册序号映射。

  • 分类管理器
    QtCreator 插件框架探索与分析

    • 在阶段2CorePlugin的加载中,主要包含MainWindow与实例反映射器,对外提供统一的抽象接口namespace Core::Container,这将关乎闭包问题,假设我们CorePlugin,把自己已经注册的插件对象中new出来的任何Object直接拿给其他想要注册的插件进行操作,我们将无法确定开发者会怎么使用,本身C++程序的脆弱性(二次释放造成的空指针指向,与野指针访问),将会出现很大的问题,所以CorePlugin在内部做了程序闭环。
    • 当CorePlugin被启动时,会初始化相关动态注册数据结构(QList QAction有序二级表、QList<EditorArea *>界面编辑View的有序表、QList<FutureProgress *>后台Task有序表)。
    • 而具体操作这些数据结构定义了专有类进行管理,actionManager,EditManger,ProgressManager。

在CorePlugin的MainWindow.cpp中方法void MainWindow::registerDefaultActions()存在详细的使用,源码如下:

  • qt-creator-opensource-src-4.8.2/src/plugins/coreplugin/mainwindow.cpp
    // Return to editor shortcut: Note this requires Qt to fix up   
    // handling of shortcut overrides in menus, item views, combos....    
    m_focusToEditor = new QAction(tr("Return to Editor"), this);    
    Command *cmd = ActionManager::registerAction(m_focusToEditor, Constants::S_RETURNTOEDITOR);    
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Escape));    
    connect(m_focusToEditor, &QAction::triggered, this, &MainWindow::setFocusToEditor);
    在默认插件的初始化后整个mainwindow就构造出来,而此时CorePlugin才算是真正的初始化完成。
    

插件通信

  • QProcess程序劫持与输出重定向
    实例Creator 工程编译执行文件与QtCreator打印窗口通信。这是关于与外部通信的QProcess调用方法。实例插件:libProjectExplorer.so,含有的相关插件依赖项如下:
    libCore.so  => ./libCore.so (0x00007f46c0f99000)
    libTextEditor.so  =>  ./libTextEditor.so (0x00007f46c0c7d000)
    libQtcSsh.so.4 => ./../libQtcSsh.so.4 (0x00007f46c08e0000)
    libAggregation.so.4 => ./../libAggregation.so.4 (0x00007f46c08d6000)
    libExtensionSystem.so.4 =>  ./../libExtensionSystem.so.4 (0x00007f46c085e000)
    libUtils.so.4 => ./../libUtils.so.4 (0x00007f46c058e000)  
      libCore:核心服务插件。
      libTextEdit:编辑器插件。
      libQtcSsh:通信库。
      libAggregation:
      libExtensionSystem:插件扩展系统基本库。
      libUtils:实例库,内置算法和静态资源等。
    
  • (信号槽)实例化对象的直接获取
    实例插件:libProjectExplorer.so与libClangTools.so。
    clangtools/clangtidyclazytool.cpp,line 293~294:
    connect(ProjectExplorerPlugin::instance(),&ProjectExplorerPlugin::updateRunActions,
    this,&ClangTidyClazyTool::updateRunActions);
    
  • projectexplorer/projectexplorer.cpp,line 263~266
    ProjectExplorerPlugin *ProjectExplorerPlugin::instance(){    return m_instance;}
    主要作用于RunAction的样式更改,采用信号槽的插件消息传递,但是此部分造成了插件间的依赖。
    除此之外还有debug插件等,也存在相关问题。
    
    正确的方式应该是通过corePlugin或者基本扩展系统Lib进行转发(此处也需要反映射机制),而不是直接获取插件中的指针,因为框架中,别的模块获取相关的instance指针并使用,这会存在连带性的指针问题(取决于使用者),这是无法预料的。
  • QlocalSocket模块通信
    关联模块:libCorePlugin.so与main.cpp。
    主要功能:参数传递重定向到插件。
    • /src/shared/qtsingleapplication/qtlocalpeer.cpp,line139~179
      	void QtLocalPeer::receiveConnection()
      	{
      	    QLocalSocket* socket = server->nextPendingConnection();
      	    ...
      	        socket->waitForReadyRead(1000);
      	    ...
      	    // ### async this
      	    QString message = QString::fromUtf8(uMsg.constData(), uMsg.size());
      	    socket->write(ack, qstrlen(ack));
      	    socket->waitForBytesWritten(1000);
      	    emit messageReceived(message, socket); // ##(might take a long time to return)
      	}
      
    监听并读取的server(/tmp/qtsigleapplication-xxxx-xxx-lockfile)本地文件通信,而在如下代码中转发了来自本地文件通信的参数。
    • /src/shared/qtsingleapplication/qtsingleapplication.cpp,line 94
      connect(pidPeer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived);
      
    • /src/app/main.cpp,line 617~619
      QObject::connect(&app,&SharedTools::QtSingleApplication::messageReceived,
      &pluginManager, &PluginManager::remoteArguments);
      转发来自本地文件传输的参数到PluginManager中
      
    • /src/libs/extensionsystem/pluginmanager.cpp,line 613~634
      if (ps->state() == PluginSpec::Running) {
                  const QStringList pluginOptions = subList(serializedArguments, QLatin1Char(':') + ps->name());
                  QObject *socketParent = ps->plugin()->remoteCommand(pluginOptions, workingDirectory,
                                                                      arguments);
                  if (socketParent && socket) {
                      socket->setParent(socketParent);
                      socket = nullptr;
                  }
      

QtCreator插件实例

此处生成了标准QtCreator插件模板,QtCreator插件MyPlugin,如下图所示:
QtCreator 插件框架探索与分析
其中mypluginconstants中标记了当前插件的QAction 反映射ID,如下图:
QtCreator 插件框架探索与分析
结合之前的CorePlugin的数据结构项QList绑定的Group,不难看出这里将产生新的QMenu。在mypluginplugin中,继承了来自通用插件接口IPlugin,其中重载实现了initialize函数:

bool MyPluginPlugin::initialize(const QStringList &arguments, QString *errorString)
{
...
    auto action = new QAction(tr("MyPlugin Action"), this);
    Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID,
                                                             Core::Context(Core::Constants::C_GLOBAL));
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    connect(action, &QAction::triggered, this, &MyPluginPlugin::triggerAction);

    Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::MENU_ID);
    menu->menu()->setTitle(tr("MyPlugin"));
    menu->addAction(cmd);
    Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
    return true;
}

其中已经指明插件逻辑:Core::ActionManager::createMenu 其中包含当前QAction的反映射ID项。
Core::ActionManager::createMenu创建了一个新的QMenu并把QAction设置到QMenu,在工具栏中(Core::Constants::M_TOOLS)添加当前QMenu。最后QAction拥有自己的处理逻辑,由信号QAction::triggered进行触发。
QtCreator 插件框架探索与分析

插件界面服务注册总结
在界面组件的反映射上,QtCreator与文件管理器拥有不同的UI,在详细设计中需要对对MainWindow和相关的组件UI进行反映射系统的构造,以达到界面组件动态加载的目的。而为了方便开发,可提供一套供Plugin生成的QtCreator入口或者工具。

本文总结

  1. 在执行的流程上,QtCreator核心的插件框架部分可取,插件初始化进行了阶段性加载涉及的方面,程序启动优化比较符合现有框架(三段式的插件程序加载思维可取)。

  2. 而在Plugin的初始化流程中,缺乏相关流程阶段性监控( virtual bool initialize = 0,返回值为bool,主要用作相关的错误上报和日志留文,此处框架不太符合),需要对Plugin插件接口需要改造。在程序Runtime结构上存在一些比较大的差异,需要针对CorePlugin进行比较大的改造,在执行的CorePlugin中发调用delayedInitialize并没有针对相关做容错项,无论内部delayedInitialize初始化与否,都会略过相关的错误逻辑。

  3. 需要阶段性代码执行判断,以防止直接调用函数形成的效率底下。例如可将插件接口更改为如下:

    virtual int extensionsInitialized ; //非纯虚函数
    virtual int delayedInitialize ; //非纯虚函数
    

    可新增函数如下:

      virtual  bool initializeFlags(){return false;}  = 0
      virtual  bool extensionsInitializedFlags(){return false;}  = 0
      virtual  bool delayedInitializeFlags(){return false;}  = 0
    

    在init函数执行之前,针对函数进行标志位赋值,用以标识当前插件是否存在初始化函数,在执行init函数中将直接针对内部执行结果进行return,此部分可直接上报错误和日志打印。

  4. 为了符合文件管理器多个实例的的内部管理,需要插件启动与对象衍生函数接口(插件使能一般包含对象的指针实例化和执行逻辑,而在文件管理器的架构需求中,存在以个插件内多次实例化对象的指针的情况,需要插件的接口预留拷贝构造的函数)。

  5. QtCreator在插件通信上分离做得不是很彻底,主要为了直接使用Qt信号槽系统,如果对插件不想对外裸露指针,可通过CoreService通过加载的每一个插件,读取到身份标识后,与实际的Plugin实例化指针关联。对外暴露查询接口,以弱化对指针外留的插件通信需求。

  6. 外部消息传递上,QtCreator消息传递采用了QLocalSocket,而文件管理器架构中对程序拥有localsocket,DBus等更多的兼容性接口,与前期框架结果一致。可以通过不同插件内的参数重定向进行插件参数的分发。

main.cpp 源码注释

  • QtCreator工程中src/app/source下main.cpp
    int main(int argc, char **argv)
    {
        //系统环境变量加载
        Utils::Environment::systemEnvironment(); // cache system environment before we do any changes 
    
    
        // Manually determine various command line options
        // We can't use the regular way of the plugin manager,
        // because settings can change the way plugin manager behaves
        //命令行参数解析
        Options options = parseCommandLine(argc, argv);
        //此举有任何作用
        applicationDirPath(argv[0]);
    
    	//从用户参数获取用户库路径
        if (options.userLibraryPath) {
        	//当前用户库路径为空
            if ((*options.userLibraryPath).isEmpty()) {
            	//当前环境变量程序实例环境变量设置为空
                Utils::Environment::modifySystemEnvironment(
                    {{"LD_LIBRARY_PATH", "", Utils::EnvironmentItem::Unset}});
            } else {
            	//进行参数设置
                Utils::Environment::modifySystemEnvironment(
                    {{"LD_LIBRARY_PATH", *options.userLibraryPath, Utils::EnvironmentItem::Set}});
            }
        }
    
    #ifdef Q_OS_WIN //WIN兼容
        if (!qEnvironmentVariableIsSet("QT_OPENGL"))
            QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
    #endif
    
    	//设置当前MenuBar是否与本地兼容,AA_DontUseNativeMenuBar参数
        if (qEnvironmentVariableIsSet("QTCREATOR_DISABLE_NATIVE_MENUBAR")
                || qgetenv("XDG_CURRENT_DESKTOP").startsWith("Unity")) {
            QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
        }
    
    	//设置临时文件目录
        Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX");
    
    #ifdef Q_OS_MAC //MAC兼容
        // increase the number of file that can be opened in Qt Creator.
        struct rlimit rl;
        getrlimit(RLIMIT_NOFILE, &rl);
    
        rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max);
        setrlimit(RLIMIT_NOFILE, &rl);
    #endif
    
    	//生成智能指针临时清除设置目录类
        QScopedPointer<Utils::TemporaryDirectory> temporaryCleanSettingsDir;
       	//命令行设置路径为空 且 当前存在测试选项
        if (options.settingsPath.isEmpty() && options.hasTestOption) {
        	//初始化Setting类
            temporaryCleanSettingsDir.reset(new Utils::TemporaryDirectory("qtc-test-settings")); 
            if (!temporaryCleanSettingsDir->isValid()) //当前class类为空 直接返回
                return 1;
            options.settingsPath = temporaryCleanSettingsDir->path(); //当前设置目录指向tmp/qtc-test-settings
        }
        if (!options.settingsPath.isEmpty()) //当前设置不等于空
            QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, options.settingsPath); // Windows::IniFormat::UserScope::FOLDERID_RoamingAppData格式
    
        // Must be done before any QSettings class is created 需要在Class QSetting 创建之后才能进行相关操作
        QSettings::setDefaultFormat(QSettings::IniFormat); //设置当前设置文件为默认为初始化程序文件格式
        setupInstallSettings(options.installSettingsPath); //安装目录设置
        // plugin manager takes control of this settings object Setting对象设置插件管理器控制口令
    
        setHighDpiEnvironmentVariable(); //高清显示设置 设置环境变量依赖例如OpenGL
    
        //设置当前Application为GL上下文
        SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts); 
    	
    	
        int numberofArguments = static_cast<int>(options.appArguments.size());//获取当前options的个数
    
    	//const char IDE_DISPLAY_NAME[] = "Qt Creator";
        SharedTools::QtSingleApplication app((QLatin1String(Core::Constants::IDE_DISPLAY_NAME)),
                                             numberofArguments,
                                             options.appArguments.data());
        const QStringList pluginArguments = app.arguments(); //options参数转换
    
        /*Initialize global settings and resetup install settings with QApplication::applicationDirPath */
        //初始化全局设置与重新安装设置到当前qtcreator运行目录
        setupInstallSettings(options.installSettingsPath); 
        QSettings *settings = userSettings(); //获取用户设置
        QSettings *globalSettings = new QSettings(QSettings::IniFormat, QSettings::SystemScope,
                                                  QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                                                  QLatin1String(Core::Constants::IDE_CASED_ID));
        loadFonts(); //字体加载
    
    	//当前为windows系统,则设置application style 为windows
        if (Utils::HostOsInfo().isWindowsHost()
                && !qFuzzyCompare(qApp->devicePixelRatio(), 1.0)
                && QApplication::style()->objectName().startsWith(
                    QLatin1String("windows"), Qt::CaseInsensitive)) {
            QApplication::setStyle(QLatin1String("fusion"));
        }
        const int threadCount = QThreadPool::globalInstance()->maxThreadCount();//线程数量的最大统计
        //当前线程设置 如果当前线程数统计小于4,则初始化线程池参数4,大于4则按照实际统计设置
        QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount)); 
        
    	//获取当前执行文件文件目录
        const QString libexecPath = QCoreApplication::applicationDirPath()
                + '/' + RELATIVE_LIBEXEC_PATH;
    #ifdef ENABLE_QT_BREAKPAD
        QtSystemExceptionHandler systemExceptionHandler(libexecPath);
    #else
        // Display a backtrace once a serious signal is delivered (Linux only).
        // 异常退出处理设置
        CrashHandlerSetup setupCrashHandler(Core::Constants::IDE_DISPLAY_NAME,
                                            CrashHandlerSetup::EnableRestart, libexecPath);
    #endif
    
        app.setAttribute(Qt::AA_UseHighDpiPixmaps);  //设置当前Application高清显示
    
    	//重点来了
        PluginManager pluginManager; //初始化Plugin框架 
        PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin")); //设置注册插件身份
        PluginManager::setGlobalSettings(globalSettings); //设置Plugin的全局设置
        PluginManager::setSettings(settings);	//插件设置
    
    	//UI界面国际化
        QTranslator translator;
        QTranslator qtTranslator;
        QStringList uiLanguages;
        uiLanguages = QLocale::system().uiLanguages(); //获取本地UI语言
        QString overrideLanguage = settings->value(QLatin1String("General/OverrideLanguage")).toString();
        if (!overrideLanguage.isEmpty())
            uiLanguages.prepend(overrideLanguage);
        const QString &creatorTrPath = resourcePath() + "/translations";
        foreach (QString locale, uiLanguages) {
            locale = QLocale(locale).name();
            if (translator.load("qtcreator_" + locale, creatorTrPath)) {
                const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
                const QString &qtTrFile = QLatin1String("qt_") + locale;
                // Binary installer puts Qt tr files into creatorTrPath
                if (qtTranslator.load(qtTrFile, qtTrPath) || qtTranslator.load(qtTrFile, creatorTrPath)) {
                    app.installTranslator(&translator);
                    app.installTranslator(&qtTranslator);
                    app.setProperty("qtc_locale", locale);
                    break;
                }
                translator.load(QString()); // unload()
            } else if (locale == QLatin1String("C") /* overrideLanguage == "English" */) {
                // use built-in
                break;
            } else if (locale.startsWith(QLatin1String("en")) /* "English" is built-in */) {
                // use built-in
                break;
            }
        }
    
        // Make sure we honor the system's proxy settings
        // 网络代理
        QNetworkProxyFactory::setUseSystemConfiguration(true); 
    
        // Load
       	// 插件加载、外部传递插件参数解析
        const QStringList pluginPaths = getPluginPaths() + options.customPluginPaths; //插件目录
        PluginManager::setPluginPaths(pluginPaths); //设置当前插件管理器加载插件目录
        QMap<QString, QString> foundAppOptions; //
        if (pluginArguments.size() > 1) { //插件需要传递参数大小大于1
            QMap<QString, bool> appOptions;
            appOptions.insert(QLatin1String(HELP_OPTION1), false);
            appOptions.insert(QLatin1String(HELP_OPTION2), false);
            appOptions.insert(QLatin1String(HELP_OPTION3), false);
            appOptions.insert(QLatin1String(HELP_OPTION4), false);
            appOptions.insert(QLatin1String(VERSION_OPTION), false);
            appOptions.insert(QLatin1String(CLIENT_OPTION), false);
            appOptions.insert(QLatin1String(PID_OPTION), true);
            appOptions.insert(QLatin1String(BLOCK_OPTION), false);
            QString errorMessage;
            if (!PluginManager::parseOptions(pluginArguments, appOptions, &foundAppOptions, &errorMessage)) {
                displayError(errorMessage);
                printHelp(QFileInfo(app.applicationFilePath()).baseName());
                return -1;
            }
        }
    
    	//核心插件加载 错误参数兼容
        const PluginSpecSet plugins = PluginManager::plugins(); //PluginMagerPrivate,插件名称与路径映射表
        PluginSpec *coreplugin = 0;
        foreach (PluginSpec *spec, plugins) {
            if (spec->name() == QLatin1String(corePluginNameC)) {
                coreplugin = spec;
                break;
            }
        }
        if (!coreplugin) {
            QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1Char(',')));
            const QString reason = QCoreApplication::translate("Application", "Could not find Core plugin in %1").arg(nativePaths);
            displayError(msgCoreLoadFailure(reason));
            return 1;
        }
        if (!coreplugin->isEffectivelyEnabled()) {
            const QString reason = QCoreApplication::translate("Application", "Core plugin is disabled.");
            displayError(msgCoreLoadFailure(reason));
            return 1;
        }
        if (coreplugin->hasError()) {
            displayError(msgCoreLoadFailure(coreplugin->errorString()));
            return 1;
        }
        if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
            printVersion(coreplugin);
            return 0;
        }	
        //help 参数打印
        if (foundAppOptions.contains(QLatin1String(HELP_OPTION1))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION2))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION3))
                || foundAppOptions.contains(QLatin1String(HELP_OPTION4))) {
            printHelp(QFileInfo(app.applicationFilePath()).baseName());
            return 0;
        }
    
        qint64 pid = -1;
        if (foundAppOptions.contains(QLatin1String(PID_OPTION))) {
            QString pidString = foundAppOptions.value(QLatin1String(PID_OPTION));
            bool pidOk;
            qint64 tmpPid = pidString.toInt(&pidOk);
            if (pidOk)
                pid = tmpPid;
        }
    
        bool isBlock = foundAppOptions.contains(QLatin1String(BLOCK_OPTION));
        if (app.isRunning() && (pid != -1 || isBlock
                                || foundAppOptions.contains(QLatin1String(CLIENT_OPTION)))) {
            app.setBlock(isBlock);
            if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
                return 0;
    
            // Message could not be send, maybe it was in the process of quitting
            if (app.isRunning(pid)) {
                // Nah app is still running, ask the user
                int button = askMsgSendFailed();
                while (button == QMessageBox::Retry) {
                    if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
                        return 0;
                    if (!app.isRunning(pid)) // App quit while we were trying so start a new creator
                        button = QMessageBox::Yes;
                    else
                        button = askMsgSendFailed();
                }
                if (button == QMessageBox::No)
                    return -1;
            }
        }
    
        PluginManager::loadPlugins(); //插件加载
        if (coreplugin->hasError()) {
            displayError(msgCoreLoadFailure(coreplugin->errorString()));
            return 1;
        }
    
        // Set up remote arguments. //参数转发
        QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived,
                         &pluginManager, &PluginManager::remoteArguments);
    
        QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(),
                         SLOT(fileOpenRequest(QString))); //
    
        // shutdown plugin manager on the exit
        QObject::connect(&app, &QCoreApplication::aboutToQuit, &pluginManager, &PluginManager::shutdown);
    
        return app.exec();
    }
    
上一篇:qt自定义软件安装包


下一篇:Centos&Qt——ChatRoom for Neuedu