Qt Creator 最主要的功能是在名为 Core 的插件中实现的(Core::ICore)。我们已经在前面的章节中见到过这个插件。在后面的内容中,我们将把 “core plugin” 简称为 Core。
插件管理器(ExtensionSystem::PluginManager)提供插件合作机制,这样就能让插件相互依赖,共同工作。
到底什么是“插件”?从最底层看,插件可以看成一个动态链接库(Windows 上的 DLL 文件,Linux 上的 SO 文件,Mac 上的 DYLIB 文件)。从开发者的角度来说,插件是一个模块,这个模块应该:

我们在前面的章节中已经或多或少接触到上面所说的前三点,但是还没有接触到后两点。
什么是暴露出的对象?暴露出的对象会加入到PluginManager的对象池。PluginManager的allObjects()函数用于获得这个对象池中所有QObject对象的指针列表。下面的代码演示了如何将这个对象列表导出到QListWidget组件:
#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);}
如果我们使用上面的代码,将所有对象在一个QListWidget中显示出来时,我们会得到如下效果(大家可以尝试修改上一章中我们编写的 DoNothing 插件。具体方案是使用上面代码替换about()函数的代码):
从上图中显示的类名可以看出,这些对象来自不同的插件。现在,我们可以更好地定义所谓的“暴露出的对象”了:
一个对外暴露的对象是由一个插件暴露的QObject(或其子类)的实例,这个对象存在于对象池中,并且可供其它插件使用。
如何让插件“暴露”对象?我们有三种方法可以让插件暴露其对象:
IPlugin::addAutoReleasedObject(QObject)IPlugin::addObject(QObject)PluginManager::addObject(QObject)IPlugin::addObject()和IPlugin::addAutoReleasedObject()其实都是调用的PluginManager::addObject()函数。所以,这些IPlugin的函数仅仅为了使用方便。我们的建议是,使用IPlugin的函数添加对象。addAutoReleasedObject()和addObject()的唯一区别是,前者添加的对象会在插件销毁的时候自动按照注册顺序从对象池中移除并 delete。
在任意时刻,我们都可以使用IPlugin::removeObject(QObject)函数将对象从对象池中移除。什么对象可以暴露出来?
插件可以暴露任何对象。一般地,我们会把有可能被其它插件使用到的一些提供某些功能的对象暴露出来。在 Qt Creator 中,这种功能的定义通常使用接口。下面是其中一些接口:
Core::INavigationWidgetFactoryCore::IEditorCore::IOptionsPageCore::IOutputPaneCore::IWizardC++ 开发者通常会将只包含 public 纯虚函数的类当做接口。在 Qt Creator 中,接口则是拥有一个或多个纯虚函数的 QObject 子类。
【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】
点击这里:「链接」
如果一个插件有实现了这样的接口的对象,那么这个对象就应该被暴露出来。例如,一个插件中的某个类实现了INavigationWidgetFactory接口,并且暴露出来,那么 Core 就会自动把这个类提供的组件当做导航组件显示出来。来看一下下面的代码:我们通过实现 Core::INavigationWidgetFactory将一个简单的QTableWidget当做导航组件。
NavWidgetFactory.h
#ifndef NAVWIDGETFACTORY_H#define NAVWIDGETFACTORY_H#include <coreplugin/inavigationwidgetfactory.h>class NavWidgetFactory : public Core::INavigationWidgetFactory{public: NavWidgetFactory(); ~NavWidgetFactory(); Core::NavigationView createWidget(); QString displayName() const; int priority() const; QString id() const;}; #endif // NAVWIDGETFACTORY_H
NavWidgetFactory.cpp
#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 "Spreadsheet";}int NavWidgetFactory::priority() const{ return 0;}QString NavWidgetFactory::id() const{ return "Spreedsheet";}
TableNav.h
#ifndef TABLENAVPLUGIN_H#define TABLENAVPLUGIN_H#include <extensionsystem/iplugin.h>class TableNavPlugin : public ExtensionSystem::IPlugin{public: TableNavPlugin(); ~TableNavPlugin(); void extensionsInitialized(); bool initialize(const QStringList & arguments, QString errorString); void shutdown();};#endif // TABLENAVPLUGIN_H
TableNav.cpp
#include "TableNav.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)
我们修改下 .pro 和 .pluginspec 文件,编译之后运行一下 Qt Creator。注意,在左侧的编译面板中,我们可以在下拉框中找到 Spreadsheet 一项,点击这一项,就可以看到我们创建的新的 table 导航:
监控暴露对象
当使用PluginManager::addObject()添加对象的时候,PluginManager就会发出objectAdded(QObject)信号。我们的应用程序可以使用这个信号来处理被添加的对象。
显然,只有当插件建立了连接之后,它才能接收信号。这需要在插件初始化之后,也就是说,只有插件初始化之后被添加的对象发出的objectAdded()信号,才能够被插件接收到。
通常,连接到objectAdded()信号的 slot 会寻找一个或多个已知接口。假设你的插件要找的是INavigationWidgetFactory接口,那么就应该使用类似下面的代码:
void Plugin::slotObjectAdded(QObject obj){ INavigationWidgetFactory factory = Aggregation::query(obj); if(factory) { // use it here... }}
查找对象有时候,插件需要查找提供其他功能的对象。现在我们已经知道:
PluginManager::allObjects()以QList<QObject>的形式返回一个对象池;通过连接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);}
我们可以发现,在我们的 list 中显示出来的顺序,和导航下拉框中的显示顺序不一定一致:
聚合
聚合由Aggregation命名空间提供。它为我们提供一种将不同类型的QObject“粘合”在一起的能力,这样,你就可以将它们“相互转换”。使用这个命名空间中的类和函数,你就可以方便地管理相关对象。在聚合中管理的对象可以由该聚合“转换成”对象的类型。
传统实现方式假设有一个对象实现了两个接口。那么,我们可以编写如下代码:
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);
Qt Creator 实现方式Qt Creator 的Aggregation库提供了一种更加简洁的方式,来定义接口,然后将其打包成一个对象。创建Aggregation::Aggregate实例,然后将对象添加进该对象。加入聚合的每一个对象都可以实现一个接口。下面的代码显示了如何创建聚合。
#include <aggregation/aggregate.h>class Interface1 : public QObject{ Q_OBJECTpublic: Interface1() { } ~Interface1() { }};class Interface2 : public QObject{ Q_OBJECTpublic: 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
这里也许看不到使用聚合的好处,但是在后面我们涉及到真正的插件实现的时候,我们就会看到这样做的优势了。
【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】
点击这里:Qt资料领取(视频教程+文档+代码+项目实战)
原文链接:https://www.devbean.net/2011/09/qtcreator-plugin-develop-5/