阅览本文大概需求 8 分钟

插件听上去很巨大上,实践上便是一个个动态库,动态库在不同平台下后缀名不相同,比方在 Windows下以.dll完毕,Linux 下以.so完毕

开发插件其实便是开发一个动态库,该动态库能够很好的加载进主程序、拜访主程序资源、和主程序之间进行通讯

本篇文章一同学习下 Qt Creator傍边是怎样完成插件的开发、加载、解析、办理以及相互通讯的,便利咱们开发自界说插件打下基础

简介

Qt Creator 插件了解起来其实很简单,界说一个接口类作为基类,其他插件需求承继该类完成对应的虚办法,每个插件作为独立子工程编译后生成对应的动态库

Qt Creator 源码学习笔记04,多插件实现原理分析

主函数加载每个插件目标,然后转化为对应插件实例

QPluginLoader loader(pluginName);
loader.load();
IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance());
// 比方转为中心插件实例
CorePlugin *pCorePluginObj = qobject_cast<CorePlugin*>(loader.instance());

然后每个插件各自依据对应事务逻辑调用接口就行了

当然了,Qt Creator 在完成进程傍边必定不止这么简单,插件的加载、解析、卸载等办理还是比较复杂的,十分值得咱们去学习

插件组成

整个插件体系由插件办理器、中心插件、其它插件组成,其间中心插件是体系中不行缺少的,其它插件都要依靠中心插件来进行开发通讯

咱们先翻开 Qt Creator 插件菜单看看都包括那些插件

Qt Creator 源码学习笔记04,多插件实现原理分析

能够看到一切的插件依据类型进行了分组,同一个类型插件同属一个树节点,每个插件后边有个复选框能够控制加载/卸载该插件

每个插件还包括了版别信息以及归属作者信息,这些信息都能够经过目标元数据来装备,插件的版别也很有用,咱们编写的插件能够限定在某个版别之间兼容,这个时分版别号就起效果了,具体完成后边会讲解到

咱们能够加载、卸载某个插件,可是无论怎样挑选,中心Core插件是不能卸载的,why? 因为整个插件体系是建立在 Core 中心插件基础之上的,离开中心插件其它插件无法存活

所以咱们学习的要点应该放在中心插件上,学会后其它插件很容易上手了

插件办理

插件的中心其实便是对插件的办理,这个是本篇的要点,是咱们阅览源码时需求要点重视的部分,为什么这么说呢,我举个栗子咱们就清楚了

咱们日常写代码的时分,比方界说一个变量,需求重视的有这么几点:

  • 变量的名
  • 变量的值
  • 变量的类型
  • 变量的效果域
  • 变量的生命周期

对每个界说的变量都十分清楚它的一些要素,那么必定不会犯错的

插件也相同,每个插件到实践开发傍边也是一个个目标,咱们界说的目标是什么类型?姓名叫什么?它的值是多少?它的效果域范围是什么?生命周期呢?什么时分创立和释放?

搞清楚上述这些,关于了解插件办理工作就更进一步了,下面要点来看看插件的生命周期

插件办理器

插件办理器完成首要在PluginManager 类傍边完成,该类办理了一切的插件加载、卸载以及释放

目标办理池

class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject
{
    Q_OBJECT
public:
    static PluginManager *instance();
    static void addObject(QObject *obj);
    static void removeObject(QObject *obj);
    ......
    friend class Internal::PluginManagerPrivate;
}

这个类是一个单例类,首要办理插件目标,能够了解为目标池,具体完成都封装在了 d指针类里边,

咱们继续进去看看

pluginmanager_p.h

class EXTENSIONSYSTEM_EXPORT PluginManagerPrivate : public QObject
{
    Q_OBJECT
public:
    ......
    QHash<QString, QList<PluginSpec *>> pluginCategories;
    QList<PluginSpec *> pluginSpecs;
    QList<QObject *> allObjects; // ### make this a QList<QPointer<QObject> > > ?
    ......
}

能够看到底层存储每个目标用的容器QList,从Qt Creator 4.10版别开端换成了 QVector来存储,说起来这两个容器的差异让我想到了,现在最新版别的 Qt傍边,现已把两者合二为一了

template<typename T> using QVector = QList<T>;

所以运用哪个无所谓了,不过咱们还是要搞清楚这两个容器的差异,什么时分用Vector,什么时分用 List

增加目标

void PluginManagerPrivate::addObject(QObject *obj)
{
    {
        QWriteLocker lock(&m_lock);
        if (obj == 0) {
            qWarning() << "PluginManagerPrivate::addObject(): trying to add null object";
            return;
        }
        if (allObjects.contains(obj)) {
            qWarning() << "PluginManagerPrivate::addObject(): trying to add duplicate object";
            return;
        }
        allObjects.append(obj);
    }
    emit q->objectAdded(obj);
}

这块中心代码其实很好了解,每次增加目标前先加锁,由于运用的是读写锁,不用忧虑函数回来死锁问题,判别目标是否合法以及是否现已存在,不存在则追加到 list 傍边,最终抛出一个信号,这个信号在外部需求运用的当地能够绑定,比方形式切换里边就运用到了

void ModeManager::init()
{
    QObject::connect(ExtensionSystem::PluginManager::instance(), &ExtensionSystem::PluginManager::objectAdded,
                     m_instance, &ModeManager::objectAdded);
}

增加就对应的删除,原理和增加相同

– 删除目标

void PluginManagerPrivate::removeObject(QObject *obj)
{
    if (obj == 0) {
        qWarning() << "PluginManagerPrivate::removeObject(): trying to remove null object";
        return;
    }
    if (!allObjects.contains(obj)) {
        qWarning() << "PluginManagerPrivate::removeObject(): object not in list:"
            << obj << obj->objectName();
        return;
    }
    emit q->aboutToRemoveObject(obj);
    QWriteLocker lock(&m_lock);
    allObjects.removeAll(obj);
}

相同的把目标从list 傍边进行了删除,在删除之前也向外抛出了信号,用法和增加信号配对运用

这儿有个疑问,为啥锁不在函数最最初加呢?

插件办理

每个插件目标对应到底层是由 PluginSpec 来实例化的,每个插件运用 list容器存储,如下所示

QList<PluginSpec *> pluginSpecs;

插件中心类完成

class EXTENSIONSYSTEM_EXPORT PluginSpec
{
public:
    QString name() const;
    QString version() const;
    QString compatVersion() const;
    QString vendor() const;
    QString copyright() const;
    ......
    bool isRequir   ed() const;
    ......
    QVector<PluginDependency> dependencies() const;
private:
    PluginSpec();
}

阅览代码就能够发现,这个类首要是记载了每个插件的一些根本信息,那么这些信息是怎样赋值的呢?经过插件描述文件来进行主动加载的,后边学习中心插件会看到

有个中心部分代码,插件依靠项dependencies,这个首要解决插件之间依靠联系运用,这个类也很简单很好了解

/*
 * 插件依靠相关信息
*/
struct EXTENSIONSYSTEM_EXPORT PluginDependency
{
    enum Type {
        Required,       // 有必要有此依靠
        Optional,       // 此依靠不是有必要的
        Test
    };
    PluginDependency() : type(Required) {}
    QString name;           //被依靠的插件姓名
    QString version;        //对应的版别号
    Type type;              //依靠类型
    bool operator==(const PluginDependency &other) const;
    QString toString() const;
};

比方插件A依靠插件BC,那么在插件A加载的时分对应的list傍边就包括了B,C插件信息,有必要等到这两个插件加载完成后才能加载插件A,这一点很重要

插件加载流程

前面学习了插件办理器傍边的一些根本数据结构,现在来看看这些插件是怎样加载进去的,加载顺序和流程是怎样样的

插件加载流程比较复杂一些,一同也是最重要的部分,首要分为下面几个进程

Qt Creator 源码学习笔记04,多插件实现原理分析

下面咱们来具体看看每个进程都干了那些工作,源码面前了无隐秘

设置插件 IID

setPluginIID(const QString &iid)

这个id 是大局仅有,加载插件时会首先判别插件 ID 合法性,用于确定是你自己编写的插件,这样能够避免其它插件歹意注册加载

咱们能够想想一下,假如他人也写了一个相似的插件,那么假如没有 ID 差异是不是就能加载进插件体系傍边,从而损坏软件结构?

Qt Creator 默认的 ID 为 org.qt-project.Qt.QtCreatorPlugin,每个插件加载时经过宏进行设置

class CorePlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
}
宏展开后:
#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)
#define QT_ANNOTATE_CLASS(type, ...)

这个宏是为了配合moc处理器生成插件导出函数,终究在调用插件接口回来实例时能够精确回来自己。咱们写个 demo 来验证下

新建一个插件叫做 PluginDemo

class PluginDemo : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "PluginDemo.json")
};

qmake 编译一下看下中心成果内容:

static const qt_meta_stringdata_PluginDemo_t qt_meta_stringdata_PluginDemo = {
    {
QT_MOC_LITERAL(0, 0, 10) // "PluginDemo"
    },
    "PluginDemo"
};
void *PluginDemo::qt_metacast(const char *_clname)
{
    if (!_clname) return Q_NULLPTR;
    if (!strcmp(_clname, qt_meta_stringdata_PluginDemo.stringdata0))
        return static_cast<void*>(const_cast< PluginDemo*>(this));
    return ExtensionSystem::IPlugin::qt_metacast(_clname);
}

设置大局装备类

setGlobalSettings(QSettings *settings)

大局装备,一般寄存的是默认值,用于恢复设置运用

设置部分装备类

setSettings(QSettings *settings)

寄存程序当时装备参数类。比方咱们设置某个参数装备保存后会存在某个装备文件中,程序加载时会从该文件加载到QSettings目标傍边供咱们调用

设置插件途径

setPluginPaths(const QStringList &paths)

插件途径一般是咱们 exe 程序相邻途径下的,比方plugins/xxx.dll,当然也能够为任意途径下的动态库,只需途径正确合法都能够加载的,能够设置多条插件途径

比方正常 Qt Creator启动时会给两个途径分别为:

 ("D:/CloundCode/QTC/bin/Win32/Debug/QTC/lib/qtc/plugins",
 "C:/Users/devstone/AppData/Local/QtProject/qtc/plugins/4.4.1")

关于途径的获取能够看后边主程序加载部分能够看到

读取插件信息

用于读取插件原目标信息,首要包括三个进程

readMetaData()
resolveDependencies()
pluginsChanged()
  • 读元数据:这儿会挨个读取每个插件,初始化 QPluginLoader,设置姓名,为后边加载做准备,能够叫预加载,创立插件实例目标 PluginSpec,存储到 List 结构傍边
  • 检测依靠联系::用于从头加载剖析每个插件依靠联系,是一个双重循环,每个插件会和其它插件比较一次,最终依照插件姓名进行排序
  • 插件改变:向外抛出信号,插件办理窗口用来刷新 view 列表信息

加载插件

到了这儿才开端真实加载插件了,首要包括下面几个流程

loadQueue()
loadPlugins()
(PluginSpec::Loaded)
(PluginSpec::Initialized)
(PluginSpec::Running)
  • 依靠初始化
  • 加载插件:这儿里边才会真真去加载初始化每个插件,计算获取插件加载行列
  • 加载(PluginSpec::Loaded):
loadPlugin(PluginSpec *spec, PluginSpec::State destState)

调用 QPluginLoader.load(),真实加载插件,加载成功才能够获取每个插件办法,存储插件实例:

IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance());
  • 初始化(PluginSpec::Initialized)
loadPlugin(PluginSpec *spec, PluginSpec::State destState)

这儿会调用每个插件的初始化函数:initialize(),该函数是纯虚函数,每个插件有必要从头完成

  • 运转(PluginSpec::Running)
loadPlugin(PluginSpec *spec, PluginSpec::State destState)

调用每个插件扩展初始化函数:extensionsInitialized(),此刻会挨个判别买个插件状态是否在运转,是的话加入到推迟行列

  • 推迟初始化
nextDelayedInitialize()

从推迟行列傍边取出买个插件,调用各自推迟初始化函数:delayedInitialize()

插件加载完毕

到此整个插件加载完毕了,能够看出来,整个插件的加载进程说白了便是动态库加载解析然后调用每个动态库里边的虚函数来完成的,一切的插件都承继自一同的基类(接口),原理很简单,可是要办理这些插件尤其是多种依靠联系并存情况下是十分不容易的

看到这儿咱们是不是很猎奇,为啥不引证头文件直接能够调用动态库里边的办法了?这个首要运用 QPluginLoader 来完成动态加载动态库,这个类很好了解,具体运用能够看我之前写的SDK调用相关文章

包括了运用示例以及对应解析

template <typename T>
T getFunction(QLibrary *lib, const char *symbol)
{
    T f = (T)lib->resolve(func);
    if (f == nullptr)
    {
        return nullptr;
    }
    return f;
}

怎样运用 QLibrary 加载动态库

中心插件

学习了解清楚了插件怎样办理,怎样加载,下面来看看中心插件怎样完成,以及怎样完成自己的插件

插件描述文件

插件描述文件一般用于记载每个插件的根本信息,有必要有,并且字段和用法都是固定的。姓名一般取插件姓名,完毕一般都是.json.in

看到这儿是不是猎奇,我记得自己第一次看届时也很猎奇,为啥是.in完毕,这个其实是一个模板文件,经过qmake构建后终究在暂时目录下会生成终究的去掉.in的文件

Core.json.in

插件代码中包括该文件

class CorePlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
};

文件内容大概如下所示:

{
    \"Name\" : \"Core\",
    \"Version\" : \"$$QTCREATOR_VERSION\",
    \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
    \"Required\" : true,
    \"HiddenByDefault\" : 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.\"
    ],
    \"Category\" : \"Qt Creator\",
    \"Description\" : \"The core plugin for the Qt IDE.\",
    \"Url\" : \"http://www.qt.io\",
    \"Arguments\" : [
        {
            \"Name\" : \"-color\",
            \"Parameter\" : \"color\",
            \"Description\" : \"Override selected UI color\"
        },
        {
            \"Name\" : \"-theme\",
            \"Parameter\" : \"default|dark\",
            \"Description\" : \"Choose a built-in theme or pass a .creatortheme file\"
        },
        {
            \"Name\" : \"-presentationMode\",
            \"Description\" : \"Enable presentation mode with pop-ups for key combos\"
        }
    ],
    $$dependencyList
}

其实便是一个规范的json装备文件,每个字段都很清楚,或许有些变量值不清楚,这儿一同学习下。比方版别号字段:

\"Version\" : \"$$QTCREATOR_VERSION\",

很明显后边是一个变量值,也能够说是宏界说,咱们一般界说json装备都是固定值,这儿选用动态装备办法,其间QTCREATOR_VERSION 变量是在pro工程中界说的

这样做有什么好处呢?想一想

是不是咱们下次变更版别号的时分,直接在pro文件中更改一次,其它引证到该变量的当地都主动同步了,是不是很便利并且能够削减许多犯错(这便是软件开发傍边的封装思维)

其实,除过在装备文件中能够引证变量以外,在代码中也能够直接引证,关于怎样运用,能够看我之前写的一篇文章,具体介绍了原理以及完成办法

qmake奇淫技巧之字符串宏界说

中心插件初始化

中心插件首要初始化根本界面结构,包括一个QMainWindow、菜单栏、状态栏、形式工具栏、多窗口面板等等

正如第一篇笔记傍边写到,假如只编译中心插件,那么运转后整个界面张这个样子

Qt Creator 源码学习笔记04,多插件实现原理分析

能够看到仅仅包括根本菜单,插件查看,状态栏等内容

每个插件都需求完成自己的初始化函数

bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
{
    ......
}

初始化函数傍边首先要注册一切插件的mime type类型,这个是从插件元数据傍边读取的,会跳过现已封闭的插件

接着初始化体系主题,主题其实和咱们经常用的 qss 款式表相似,大概张这个样子

[General]
ThemeName=Flat Dark
PreferredStyles=Fusion
DefaultTextEditorColorScheme=creator-dark.xml
[Palette]
shadowBackground=ff404244
text=ffd0d0d0
textDisabled=60a4a6a8
toolBarItem=b6fbfdff

其实便是一个.ini文件格局的内容,界说了许多界面款式相关变量字段,这些字段会一一映射到对应主题办理类傍边,这样相关界面设置款式就能够直接调用了

接着也是一个很重要的模块,初始化菜单办理类,这个类办理了菜单栏一切的菜单/Action,以供其它插件模块拜访

    new ActionManager(this);
    ActionManager::setPresentationModeEnabled(args.presentationMode);

ActionManager 这个类是一个特别的单例类,单例目标初始化只能在中心插件傍边,虽然供给了单例回来接口,可是初度假如没有初始化目标回来的是空指针

class CORE_EXPORT ActionManager : public QObject
{
    Q_OBJECT
public:
    static ActionManager *instance();
private:
    ActionManager(QObject *parent = 0);
    ~ActionManager();
    friend class Core::Internal::CorePlugin; // initialization
    friend class Core::Internal::MainWindow; // saving settings and setting context
};
static ActionManager *m_instance = 0;
ActionManager *ActionManager::instance()
{
    return m_instance;
}

一切才有了后边两个友元类的声明了,这样能够直接拜访并且初始化目标实例了,中心插件初始化完成后,其它当地能够直接调用单例函数了

接着便是主界面初始化,初始化 mainWindow实例

    m_mainWindow = new MainWindow;
    if (args.overrideColor.isValid())
        m_mainWindow->setOverrideColor(args.overrideColor);
    qsrand(QDateTime::currentDateTime().toTime_t());
    const bool success = m_mainWindow->init(errorMessage);

主界面实例初始化后,接着会调用主界面的初始化函数,主界面真实初始化了多插件界面完成,假如想要学习多插件界面是怎样完成的,能够要点重视下这个初始化函数

最终是修正形式、查找相关功用初始化,这些功用不是本次要点,后边有需求再具体看完成思维

主界面初始化

主界面和咱们平常创立项目运用的QMainWindow没有两样,最大的差异便是Qt Creator把界面上一切的操作都进行了封装办理,这样其它插件也能够进行拜访,更好的对界面体系进行了扩展

主界面咱们要点来学习了菜单栏的运用,看看是怎样封装办理的

首要涉及到下面几个类

  • ActionContainer
  • ActionContainerPrivate
  • MenuActionContainer
  • MenuBarActionContainer
  • ActionManager

这些类的联系如下所示

Qt Creator 源码学习笔记04,多插件实现原理分析

其间 ActionContainer目标是基类,向外部一切插件暴露,后边拜访某个菜单大部分场景是回来该类指针的

MenuActionContainer 是菜单栏傍边的菜单目标,能够包括 n 个菜单

MenuBarActionContainer 是咱们的菜单栏,整个 MainWindows仅此一份实例

最终便是 ActionManager类了,咱们一切的操作均是经过该类来进行,很显然它是一个单例类,并且整个体系都是能够拜访的

  • 创立菜单栏
ActionContainer *menubar = ActionManager::createMenuBar(Constants::MENU_BAR);
if (!HostOsInfo::isMacHost()) // System menu bar on Mac
    setMenuBar(menubar->menuBar());
  • 创立文件菜单
    ActionContainer *filemenu = ActionManager::createMenu(Constants::M_FILE);
    menubar->addMenu(filemenu, Constants::G_FILE);
    filemenu->menu()->setTitle(tr("&File"));

能够看到运用是来是十分便利的,并且这种经过传入字符串创立菜单的方式也简单了解,外部运用的人员彻底不用了解函数内部是怎样完成的,只需求依据自己需求传入规则格局的字符串即可

每个菜单都有仅有的字符串 ID来进行差异,字符串命名严厉依照菜单格局,比方

const char M_FILE[]                = "QtCreator.Menu.File";
const char M_FILE_RECENTFILES[]    = "QtCreator.Menu.File.RecentFiles";
const char M_EDIT[]                = "QtCreator.Menu.Edit";

这样的格局也很好了解,Menu相当于是大菜单,后边一级是每个子菜单,假如该菜单还有子菜单,那么继续扩展下去

其它界面菜单栏菜单创立和上面的进程是相似的,能够照猫画虎写出来

  • 创立每个 Action

上面创立了界面的菜单栏,可是每个菜单下面还是空的,需求创立对应的 Action 才行,下面来看看是怎样创立的

    ActionContainer *mfile = ActionManager::actionContainer(Constants::M_FILE);
    mfile->addSeparator(Constants::G_FILE_SAVE);
    // Open Action
    QIcon icon = QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon());
    QAction *m_openAction = new QAction(icon, tr("&Open File or Project..."), this);
    Command *cmd = ActionManager::registerAction(m_openAction, Constants::OPEN);
    cmd->setDefaultKeySequence(QKeySequence::Open);
    mfile->addAction(cmd, Constants::G_FILE_OPEN);
    connect(m_openAction, &QAction::triggered, this, &MainWindow::openFile);

第一行代码经过菜单办理器回来上面创立的「文件」菜单指针,第二行增加了一个分隔符,后边创立了一个Command目标,这个类是对每个QAction进行了封装,一同支持设置快捷键等操作,这样咱们后续的操作就相当于是一个command

这样咱们的菜单栏就创立初始化完成了,剩余的便是左侧形式工具条以及中央内容区域的创立了

限于篇幅原因,这些内容咱们后边在看

App 程序初始化

前面花费了大量篇幅来介绍插件的办理以及主界面的完成,下面咱们来看看主程序是怎样初始化的

主函数 main.cpp 里边的内容挺多的,咱们看首要加载流程就行了

  • 设置体系装备目标指针
    QSettings *settings = userSettings();
    QSettings *globalSettings = new QSettings(QSettings::IniFormat, QSettings::SystemScope,
                                              QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                                              QLatin1String("QTC"));
    PluginManager pluginManager;
    PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin"));
    PluginManager::setGlobalSettings(globalSettings);
    PluginManager::setSettings(settings);

首要是体系傍边的一些装备,插件办理器需求记载那些插件被禁用了,这样在后边插件初始化时能够跳过了

其间很重要的设置插件ID,这个作为插件仅有标识符,用来差异歹意插件,假如他人不知道你的软件插件IID,那么他编写的插件放入你程序目录下是能够直接经过这个IID过滤掉的

  • 设置插件途径
    const QStringList pluginPaths = getPluginPaths() + customPluginPaths;
    PluginManager::setPluginPaths(pluginPaths);

这儿的插件途径包括了两部分,一部分是咱们程序目录下的插件目录,另一个是公共目录,比方下面这个

("D:/CloundCode/QTC/bin/Win32/Debug/QTC/lib/qtc/plugins", "C:/Users/devstone/AppData/Local/QtProject/qtc/plugins/4.4.1")

这一步走完后,假如没有错误整个插件都加载完成了

  • 异常判别

主意是夸姣的,可是事实总不如愿,插件在加载进程中或许会遇到一些问题导致加载异常,这样程序就无法正常运转了,需求抛犯错误给用户

    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;
    }

这段代码对核插件加载情况进行了判别,假如有错误没有加载完成或者被禁用了,那么就直接回来了。理论上来说中心插件是无法被禁用的,可是假如有人歹意修正装备文件禁用了中心插件,那么此刻程序会无法正常启动的

  • 加载插件

这一步其实是最重要的,上面设置插件途径后仅仅是读取每个插件目标,此刻这些对应都是静态的,到了这一步才真实动起来

PluginManager::loadPlugins();
if (coreplugin->hasError()) {
    displayError(msgCoreLoadFailure(coreplugin->errorString()));
    return 1;
}

关于插件加载这个流程最前面插件办理器傍边介绍清楚了,这儿咱们直接略过就行了

好了关于插件加载学习就到这儿了

总结

插件部分内容还是挺长,初度学习源码的朋友或许会感觉到无从下手、一脸茫然,不用忧虑,我第一次也是这种感觉,遇到不明白不了解的小标记下,先了解把握全体设计思维和流程,再渐渐逐一模块攻破

软件开发也是这个道理,一开端你不行能考虑到一切模块细节,把握全体结构没有问题,再挨个完成细节部分

Qt Creator十分值得咱们多看、多写的,所谓一波三折么,看的多了也就明白了一些道理

咱们日常开发进程中遇到的一些问题,或许Qt Creator傍边早就完成好了,能够直接拿来运用,比方像奔溃dump办理、日志办理、网络通讯、特别控件完成等都能够拿来直接用的

希望本次共享的笔记对你有协助,假如觉得有用不妨重视下,有任何问题能够相互交流学习

引荐阅览

Qt Creator 源码学习笔记01,初识QTC\

Qt Creator 源码学习笔记02,知道框架结构结构\

Qt Creator 源码学习笔记03,大型项目怎样办理工程