QtCreator的核心就是一个插件加载器,其全部功能都是经过插件实现的。
QtCreator架构以下:
QtCreator的核心功能由Core Plugin (Core::ICore)实现。
插件管理器(ExtensionSystem::PluginManager)对插件协做提供了简单方式,容许插件为其余插件扩展提供钩子。
PluginManager负责插件的加载,管理,销毁等工做。Core插件是QtCreator最基础的插件,提供了向界面增长菜单等功能。
QtCreator的核心系统是由PluginManager和Core插件构成。PluginManager负责插件的管理工做,Core负责提供QtCreator的最小功能集合。PluginManager将Core当作普通插件进行加载。对于自定义插件,Core是一个基础功能库,使用Core能够扩展QtCreator的功能。
QtCreator的全部功能,全是由插件实现的,使用插件机制的优势是简化了顶层业务,即插件管理工做的逻辑,缺点是增长了加载插件的复杂度,因为Core插件须要被其余插件依赖,因此qtcreator在插件加载时就必需要考虑插件之间的依赖性。git
最基本的插件是一个共享库,从开发者的角度,插件是一个模块。
插件模块的实现须要知足如下功能:
A、在一个类中实现ExtensionSystem::IPlugin接口。
B、使用Q_EXPORT_PLUGIN宏导出插件类。
C、提供一个pluginspec插件描述文件,用于描述插件的元信息。
D、向其它插件暴露一个或多个对象。
E、查找其它插件暴露出来的可用的一个或多个对象。
插件都须要继承IPlugin的接口,插件是由描述文件和继承IPlugin的类库组成。
描述文件内容以下:json
<plugin name="DoNothing" version="1.0.0" compatVersion="2.8.1"> <vendor>Scorpio.org</vendor> <copyright>(C) 2010-2011 Scorpio.org</copyright> <license>Do anything you want.</license> <description>A plugin that does nothing.</description> <url>http://www.scorpio.net</url> <dependencyList> <dependency name="Core" version="2.8.1"/> </dependencyList> </plugin>
插件描述文件描述了插件的基本信息,用于被插件管理器加载。最后一行描述了插件所依赖的其它插件,PluginManager会根据插件之间的依赖关系决定加载顺序。
IPlugin是插件的基类接口,主要接口以下:架构
//初始化函数,在插件被加载时会调用 bool IPlugin::initialize(const QStringList &arguments, QString *errorString) //在全部插件的initialize函数被调用后,调用该函数,此时该插件依赖的插件已经初始化完成 void IPlugin::extensionsInitialized() //在全部插件的extensionsInitialized函数调用完成之后进行调用 bool IPlugin::delayedInitialize()
暴露对象是存在于插件管理器对象池中的对象。
插件暴露出的对象会加入到PluginManager的对象池。PluginManager的allObjects()函数用于获取对象池中全部QObject对象的指针列表。下面的代码演示了如何在QListWidget组件中列出对象池中全部的对象:ide
#include <extensionsystem/pluginmanager.h> ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); QList<QObject*> objects = pm->allObjects(); QListWidget* listWidget = new QListWidget; Q_FOREACH(QObject* obj, objects) { QString objInfo = QString("%1 (%2)") .arg(obj->objectName()) .arg(obj->metaObject()->className()); listWidget->addItem(objInfo); }
将DoNothing插件中doNothing函数修改以下:函数
#include <extensionsystem/pluginmanager.h> void DoNothingPlugin::doNothing() { ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); QList<QObject*> objects = pm->allObjects(); QListWidget* listWidget = new QListWidget(); Q_FOREACH(QObject* obj, objects) { QString objInfo = QString(QString::fromUtf8("%1 (%2)")) .arg(obj->objectName()) .arg(QString::fromUtf8(obj->metaObject()->className())); listWidget->addItem(objInfo); } listWidget->resize(300,600); listWidget->show(); }
一个对外暴露的对象是由一个插件对外暴露的QObject(或其子类)的实例,暴露对象存在于对象池中,而且可供其它插件使用。ui
有三种方法从插件中暴露一个对象:
A、IPlugin::addAutoReleasedObject(QObject)
B、IPlugin::addObject(QObject)
C、PluginManager::addObject(QObject)
IPlugin::addObject()和IPlugin::addAutoReleasedObject()其实都是调用的PluginManager::addObject()函数。建议使用IPlugin的函数添加对象。addAutoReleasedObject()和addObject()的惟一区别是,前者添加的对象会在插件销毁的时候自动按照注册顺序的反向顺序从对象池中移除并delete。
在任意时刻,均可以使用IPlugin::removeObject(QObject)函数将对象从对象池中移除。this
插件能够暴露任何对象。一般,被其它插件使用了某些功能的对象会被暴露。QtCreator中,功能经过接口的方式定义。
下面是其中一些接口:
Core::INavigationWidgetFactory
Core::IEditor
Core::IOptionsPage
Core::IOutputPane
Core::IWizard
若是一个插件包含实现了接口的对象,那么这个对象就应该被暴露出来。例如,一个插件中的某个类实现了INavigationWidgetFactory接口,而且暴露出来,那么Core就会自动把这个类提供的组件当作导航组件显示出来。建立一个导航栏插件TableNav,经过实现 Core::INavigationWidgetFactory接口,将一个简单的QTableWidget当作导航组件。
Core::INavigationWidgetFactory接口实现以下:
NavWidgetFactory.h文件:url
#ifndef NAVWIDGETFACTORY_H #define NAVWIDGETFACTORY_H #include <coreplugin/inavigationwidgetfactory.h> #include <coreplugin/id.h> using namespace Core; class NavWidgetFactory : public Core::INavigationWidgetFactory { public: NavWidgetFactory(); ~NavWidgetFactory(); Core::NavigationView createWidget(); QString displayName() const; int priority() const; Id id() const; }; #endif // NAVWIDGETFACTORY_H
NavWidgetFactory.cpp文件:spa
#include "NavWidgetFactory.h" #include <QtGui> NavWidgetFactory::NavWidgetFactory() { } NavWidgetFactory::~NavWidgetFactory() { } Core::NavigationView NavWidgetFactory::createWidget() { Core::NavigationView view; view.widget = new QTableWidget(50, 3); return view; } QString NavWidgetFactory::displayName() const { return QString::fromUtf8("TableNav"); } int NavWidgetFactory::priority() const { return 0; } Id NavWidgetFactory::id() const { return Id::fromName("TableNav"); }
TableNav插件实现以下:
TableNavPlugin .h文件:.net
#ifndef TABLENAVPLUGIN_H #define TABLENAVPLUGIN_H #include <extensionsystem/iplugin.h> #include "NavWidgetFactory.h" #include <QtPlugin> #include <QtGui> class TableNavPlugin : public ExtensionSystem::IPlugin { public: TableNavPlugin(); ~TableNavPlugin(); void extensionsInitialized(); bool initialize(const QStringList & arguments, QString * errorString); void shutdown(); }; #endif // TABLENAVPLUGIN_H
TableNavPlugin .cpp文件:
#include "TableNavPlugin.h" #include "NavWidgetFactory.h" #include <QtPlugin> #include <QtGui> TableNavPlugin::TableNavPlugin() { // Do nothing } TableNavPlugin::~TableNavPlugin() { // Do notning } bool TableNavPlugin::initialize(const QStringList& args, QString *errMsg) { Q_UNUSED(args); Q_UNUSED(errMsg); // Provide a navigation widget factory. // Qt Creator’s navigation widget will automatically // hook to our INavigationWidgetFactory implementation, which // is the NavWidgetFactory class, and show the QTableWidget // created by it in the navigation panel. //暴露对象 addAutoReleasedObject(new NavWidgetFactory); return true; } void TableNavPlugin::extensionsInitialized() { // Do nothing } void TableNavPlugin::shutdown() { // Do nothing } Q_EXPORT_PLUGIN(TableNavPlugin)
TableNav插件描述文件以下:
<plugin name="TableNav" version="0.0.1" compatVersion="2.8.1"> <vendor>Scorpio</vendor> <copyright>(C) 2010-2011 Scorpio.org</copyright> <license>MIT</license> <description>Table widget as navigation.</description> <url>http://www.scorpio.net</url> <dependencyList> <dependency name="Core" version="2.8.1"/> </dependencyList> </plugin>
TableNav插件依赖文件以下:
QTC_PLUGIN_NAME = TableNav QTC_PLUGIN_DEPENDS += \ coreplugin
TableNav插件工程文件以下:
EMPLATE = lib TARGET = TableNav include(../../qtcreatorplugin.pri) PROVIDER = Scorpio include(../../plugins/coreplugin/coreplugin.pri) HEADERS += TableNavPlugin.h \ NavWidgetFactory.h SOURCES += TableNavPlugin.cpp \ NavWidgetFactory.cpp OTHER_FILES += TableNav.pluginspec \ TableNav_dependencies.pri
结果以下:
当使用PluginManager::addObject()添加对象时,PluginManager就会发出objectAdded(QObject)信号。应用程序可使用objectAdded(QObject)信号来弄清楚被添加的对象。
只有插件被初始化后,插件管理器才会发出objectAdded(QObject*)信号。只有被初始化后添加到插件管理器对象池的插件对象,才能收到objectAdded()信号。
一般,链接到objectAdded()信号的slot会寻找一个或多个已知接口。假设插件要找的是INavigationWidgetFactory接口,那么链接objectAdded()信号的槽函数以下:
void xxxPlugin::slotObjectAdded(QObject * obj) { INavigationWidgetFactory *factory = Aggregation::query(obj); if(factory) { // use it here... } }
有时,插件须要在应用程序中查找提供了某些功能的对象。目前,已知查找对象的方法有两种:
A、PluginManager::allObjects()函数返回一个QList<QObject*>形式的对象池。
B、经过链接PluginManager::objectAdded()信号,能够知道被暴露的对象。
假设须要查找一个实现了INavigationWidgetFactory接口的对象,而后把它添加到一个QListWidget中显示出来。那么,可使用PluginManager::getObjects<T>()函数。下面是代码片断:
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); QList<Core::INavigationWidgetFactory*> objects = pm->getObjects<Core::INavigationWidgetFactory>(); QListWidget* listWidget = new QListWidget(); Q_FOREACH(Core::INavigationWidgetFactory* obj, objects) { QString objInfo = QString("%1 (%2)") .arg(obj->displayName()) .arg(obj->metaObject()->className()); listWidget->addItem(objInfo); }
QtCreator的核心系统由PluginManager和Core插件构成。PluginManager负责插件的管理工做,将Core插件当作普通插件进行加载;Core插件负责提供QtCreator的最小功能集合,为其它插件提供基础功能。
QtCreator全部功能由插件实现,优势是简化了顶层业务,即插件管理工做的逻辑,只有PlunginManager和Plugin;缺点是增长了加载插件的复杂度,由于Core基础库插件须要被其余插件依赖,因此QtCreator在插件加载时就必需要考虑插件之间的依赖性。
只包括core、Find、Locator、TextEditor四个必须插件的QtCreator界面以下:
C++ 开发者一般会将只包含 public纯虚函数的类当作接口。在QtCreator中,接口则是拥有一个或多个纯虚函数的QObject子类。若是一个插件实现了IXXX接口的对象,那么这个对象就应该被暴露出来。例如,一个插件中的某个类实现了INavigationWidgetFactory接口,而且暴露出来,那么 Core 就会自动把这个类提供的组件当作导航组件显示出来。
QtCreator中,功能经过接口的方式定义。Core插件模块定义了QtCreator的经常使用功能接口集合,以下:
Core::IOptionsPage
Core::IWizard
Core::IEditor
Core::IEditorFactory
Core::IDocumentFactory
Core::IExternalEditor
Core::IContext
Core::ICore
Core::ICoreListener
Core::IDocument
Core::IFileWizardExtension
Core::IMode
Core::INavigationWidgetFactory
Core::IOutputPane
Core::IVersionControl
功能接口会在其它插件或Core插件实现,如git插件在GitVersionControl类对Core::IVersionControl接口进行了实现,Core插件在TextDocument类中对IDocument接口进行了实现。
coreplugin.h文件:
#ifndef COREPLUGIN_H #define COREPLUGIN_H #include <extensionsystem/iplugin.h> namespace Core { class DesignMode; namespace Internal { class EditMode; class MainWindow; class CorePlugin : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json") public: CorePlugin(); ~CorePlugin(); //必须实现接口initialize bool initialize(const QStringList &arguments, QString *errorMessage = 0); //必须实现接口extensionsInitialized void extensionsInitialized(); bool delayedInitialize(); ShutdownFlag aboutToShutdown(); QObject *remoteCommand(const QStringList & /* options */, const QStringList &args); public slots: void fileOpenRequest(const QString&); private: void parseArguments(const QStringList & arguments); MainWindow *m_mainWindow;//主窗口 EditMode *m_editMode;//编辑模式 DesignMode *m_designMode;//设计器模式 }; } // namespace Internal } // namespace Core #endif // COREPLUGIN_H
coreplugin.cpp文件:
#include "coreplugin.h" #include "actionmanager.h" #include "designmode.h" #include "editmode.h" #include "editormanager.h" #include "fileiconprovider.h" #include "helpmanager.h" #include "mainwindow.h" #include "mimedatabase.h" #include "modemanager.h" #include "infobar.h" #include <utils/savefile.h> #include <QtPlugin> #include <QDebug> #include <QDateTime> using namespace Core; using namespace Core::Internal; CorePlugin::CorePlugin() : m_mainWindow(new MainWindow), m_editMode(0), m_designMode(0) { } CorePlugin::~CorePlugin() { if (m_editMode) { removeObject(m_editMode); delete m_editMode; } if (m_designMode) { if (m_designMode->designModeIsRequired()) removeObject(m_designMode); delete m_designMode; } // delete FileIconProvider singleton delete FileIconProvider::instance(); delete m_mainWindow; } void CorePlugin::parseArguments(const QStringList &arguments) { for (int i = 0; i < arguments.size(); ++i) { if (arguments.at(i) == QLatin1String("-color")) { const QString colorcode(arguments.at(i + 1)); m_mainWindow->setOverrideColor(QColor(colorcode)); i++; // skip the argument } if (arguments.at(i) == QLatin1String("-presentationMode")) ActionManager::setPresentationModeEnabled(true); } } bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) { qsrand(QDateTime::currentDateTime().toTime_t()); parseArguments(arguments); const bool success = m_mainWindow->init(errorMessage); if (success) { m_editMode = new EditMode; addObject(m_editMode); //切换到编辑模式 ModeManager::activateMode(m_editMode->id()); m_designMode = new DesignMode; InfoBar::initializeGloballySuppressed(); } // Make sure we respect the process's umask when creating new files Utils::SaveFile::initializeUmask(); return success; } void CorePlugin::extensionsInitialized() { m_mainWindow->mimeDatabase()->syncUserModifiedMimeTypes(); if (m_designMode->designModeIsRequired()) addObject(m_designMode); m_mainWindow->extensionsInitialized(); } bool CorePlugin::delayedInitialize() { HelpManager::instance()->setupHelpManager(); return true; } QObject *CorePlugin::remoteCommand(const QStringList & /* options */, const QStringList &args) { IDocument *res = m_mainWindow->openFiles( args, ICore::OpenFilesFlags(ICore::SwitchMode | ICore::CanContainLineNumbers)); m_mainWindow->raiseWindow(); return res; } void CorePlugin::fileOpenRequest(const QString &f) { remoteCommand(QStringList(), QStringList(f)); } ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown() { m_mainWindow->aboutToShutdown(); return SynchronousShutdown; } Q_EXPORT_PLUGIN(CorePlugin)
Core插件对Core::IMode进行了不一样实现,如EditMode、DesignMode,并在initialize函数加载了相应功能。
在main函数中由ExtensionSystem::PluginManager插件管理器加载。
pluginManager.loadPlugins();
void PluginManager::loadPlugins()函数调用了void PluginManagerPrivate::loadPlugins()函数。
void PluginManagerPrivate::loadPlugins() { //获取待加载的插件,loadQueue根据插件批次依赖关系进行排序 QList<PluginSpec *> queue = loadQueue(); //加载插件 foreach (PluginSpec *spec, queue) { loadPlugin(spec, PluginSpec::Loaded); } //初始化插件 foreach (PluginSpec *spec, queue) { loadPlugin(spec, PluginSpec::Initialized); } QListIterator<PluginSpec *> it(queue); it.toBack(); while (it.hasPrevious()) { loadPlugin(it.previous(), PluginSpec::Running); } emit q->pluginsChanged(); }
自定义插件使用Core插件提供的功能向界面添加菜单代码以下:
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg) { Q_UNUSED(args); Q_UNUSED(errMsg); Core::ActionManager* am = Core::ICore::instance()->actionManager(); // Create a DoNothing menu Core::ActionContainer* ac = am->createMenu("DoNothingPlugin.DoNothingMenu"); ac->menu()->setTitle(QString::fromUtf8("DoNothing")); // Create a command for "About DoNothing". Core::Command* cmd = am->registerAction( new QAction(this), "DoNothingPlugin.AboutDoNothing", Core::Context(Core::Constants::C_GLOBAL)); cmd->action()->setText(QString::fromUtf8("About DoNothing")); connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(doNothing())); // Insert the "DoNothing" menu between "Window" and "Help". QMenu* helpMenu = am->actionContainer(Core::Constants::M_HELP)->menu(); QMenuBar* menuBar = am->actionContainer(Core::Constants::MENU_BAR)->menuBar(); menuBar->insertMenu(helpMenu->menuAction(), ac->menu()); // Add the "About DoNothing" action to the DoNothing menu ac->addAction(cmd); return true; }
DoNothing插件在initialize函数中使用Core插件的Core::ActionManager、Core::ActionContainer、Core::Command功能向主界面菜单栏添加菜单。
Git插件使用Core插件提供的功能向主界面菜单栏的git菜单提供菜单和菜单项,代码以下:
//register actions Core::ActionContainer *toolsContainer = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); Core::ActionContainer *gitContainer = Core::ActionManager::createMenu("Git"); gitContainer->menu()->setTitle(tr("&Git")); toolsContainer->addMenu(gitContainer); m_menuAction = gitContainer->menu()->menuAction(); /* "Current File" menu */ Core::ActionContainer *currentFileMenu = Core::ActionManager::createMenu(Core::Id("Git.CurrentFileMenu")); currentFileMenu->menu()->setTitle(tr("Current &File")); gitContainer->addMenu(currentFileMenu);
聚合由Aggregation命名空间提供,提供了一种将不一样类型的QObject粘合在一块儿的能力,所以能够将不一样类型对象相互转换。使用Aggregation命名空间中的类和函数,就能够绑定相关对象到一个单独实体(聚合)。被绑定到聚合中的对象可以从聚合转换为不一样的对象类类型。
若是想要一个对象提供两个接口的实现,实现代码以下:
class Interface1 { .... }; Q_DECLARE_INTERFACE("Interface1", "Interface1"); class Interface2 { .... }; Q_DECLARE_INTERFACE("Interface2", "Interface2"); class Bundle : public QObject, public Interface1, public Interface2 { Q_OBJECT Q_INTERFACES(Interface1 Interface2) .... }; Bundle bundle;
对象bundle同时实现了Interface1和Interface2。可使用类型转换运算符,将bundle转换成Interface1或者Interface2:
Interface1* iface1Ptr = qobject_cast<Interface1*>(&bundle); Interface2* iface2Ptr = qobject_cast<Interface2*>(&bundle);
QtCreator的Aggregation库提供了一种更加简洁的方式,来定义接口,而后将其打包成一个对象。建立Aggregation::Aggregate实例,而后将对象添加进该对象。加入聚合的每个对象均可以实现一个接口。下面的代码显示了如何建立聚合。
#include <aggregation/aggregate.h> class Interface1 : public QObject { Q_OBJECT public: Interface1() { } ~Interface1() { } }; class Interface2 : public QObject { Q_OBJECT public: Interface2() { } ~Interface2() { } }; Aggregation::Aggregate bundle; bundle.add(new Interface1); bundle.add(new Interface2);
聚合实例bundle如今有两个接口的实现。若是须要转换成相应接口,可使用以下代码:
Interface1* iface1Ptr = Aggregation::query<Interface1>(&bundle); Interface2* iface2Ptr = Aggregation::query<Interface2>(&bundle);
利用聚合,能够屡次添加具备相同接口的多个对象。例如:
Aggregation::Aggregate bundle; bundle.add(new Interface1); bundle.add(new Interface2); bundle.add(new Interface1); bundle.add(new Interface1); QList<Interface1*>gt; iface1Ptrs = Aggregation::query_all<Interface1>(&bundle);
使用Aggregation的另外一优势是,delete聚合中的任一对象,均可以将整个聚合delete掉。例如:
Aggregation::Aggregate* bundle = new Aggregation::Aggregate; bundle->add(new Interface1); bundle->add(new Interface2); Interface1* iface1Ptr = Aggregation::query(bundle); delete iface1Ptr; // 同时会 delete 这个 bundle 及其中全部对象 // 等价于 delete bundle