在开展新内容前,先简单回顾下上篇文章的内容。
上次我们是直接在Qt 自带的例子基础上做的修改,直接运行。我们的插件需要继承Qt 的Style插件,之后重新实现自己想要实现的部分。在主程序中直接通过QApplication::setStyle进行调用。
下面开展我们本次的内容,官方文档说明
通过插件不仅可以扩展Qt本身,而且可以扩展Qt应用程序。 这要求应用程序使用QPluginLoader检测和加载插件。 在这种情况下,插件可以提供任意功能,并且不仅限于数据库驱动程序,图像格式,文本编解码器,样式以及扩展Qt功能的其他类型的插件。
1. 通过插件使应用程序可扩展涉及以下步骤:
①定义一组用于与插件对话的接口(仅具有纯虚函数的类)。
②使用Q_DECLARE_INTERFACE()宏向Qt的元对象系统声明该接口。
③在应用程序中使用QPluginLoader加载插件。
④使用qobject_cast()测试插件是否实现了给定的接口。
2. 编写一个插件的步骤:
①声明一个插件类,该类继承自QObject和该插件要提供的接口。
②使用Q_INTERFACES()宏告诉Qt的元对象系统有关接口的信息。
③使用Q_PLUGIN_METADATA()宏导出插件。
④使用合适的.pro文件构建插件。
上面的步骤看不大懂?没关系,下面我们通过程序来逐步分解上面的步骤
1. 创建子工程Qt应用程序
在Qt新建工程时,选择创建子工程,如下图。
按照提示完成子工程的创建,我的工程名称是MyFirstPlugin
创建完成后工程是空的,选中工程后鼠标右键,【New SubProject...】,如图。之后添加的子工程就像平时创建带有UI的工程一样,我选择的是继承QWidget。
此时编译运行的话会显示一个为空的QWidget窗体。创建成功后大概向下面的样子
2. 通过插件使应用程序可以被扩展
【应用程序扩展插件步骤】
①编写仅具有纯虚函数的类
选中文件夹Headers后右键,选择【Add New...】,选择【C++ Header File】,我的名称是 abstractinterface.h。由于我想创建的插件是带有UI的,所以类型是QWidget 。
#include <QObject>
class QWidget;
class AbstractInterface
{
public:
virtual ~AbstractInterface() {}
virtual QWidget *createPluginWidget(QWidget *parent) = 0;
};
【应用程序扩展插件步骤】
②使用Q_DECLARE_INTERFACE()宏向Qt的元对象系统声明该接口。
里面字符串内容是声明一个独一无二的属于你的iid,我的是“欢迎关注公众号”。
#define AbstractInterface_iid "Welcome to pay attention to the public number"
Q_DECLARE_INTERFACE(AbstractInterface, AbstractInterface_iid)
此时通过插件使应用程序可以被扩展的前两步就完成了,后面两步之后在宿主程序中加载插件时再介绍。此时你的工程看起来是这个样子:
3. 再添加一个子工程用于编写插件
①再次添加一个子工程
选中工程【MyFirstPlugin】后鼠标右键,【New SubProject...】。之后添加的子工程就像平时创建带有UI的工程一样,我选择的是继承QWidget。我的名称是PluginWidget
②修改PluginWidget
新创建的PluginWidget包含有main.cpp,将它删除。
修改PluginWidget.peo,将 TEMPLATE = app 改为TEMPLATE = lib;添加CONFIG += plugin
【插件编写步骤】
③声明一个插件类,该类继承自QObject和该插件要提供的接口
之后添加一个继承QObject的类,我的名称是MyFirstPlugin。该类就是插件类。
此时你的工程看起来是这个样子:
这步还没结束,还要再继承AbstractInterface类,但是在myfirstplugin.h中无法直接包含abstractinterface.h,这时需要修改PluginWidget.pro。根据创建的目录添加INCLUDEPATH += ../MainWidget,这时就可以include abstractinterface.h了。
顺便说下,通过拆分不同的.pro也可以解耦程序,以后根据具体情况再和大家分享。
【插件编写步骤】
④使用Q_INTERFACES()宏告诉Qt的元对象系统有关接口的信息
Q_INTERFACES(AbstractInterface)
【插件编写步骤】
⑤使用Q_PLUGIN_METADATA ()宏导出插件
Q_PLUGIN_METADATA(IID "Welcome to pay attention to the public number." FILE "myfirstplugin.json")
注意下myfirstplugin.json,这是我们echoplugin中直接改名复制的,这个需要有。echoplugin是Qt自带的插件例程。
插件编写最后一步是实现createPluginWidget(QWidget *parent)
QWidget *MyFirstPlugin::createPluginWidget(QWidget *parent)
{
PluginWidget *pluginWidget = new PluginWidget(parent);
return pluginWidget;
}
PluginWidget是插件逻辑实现的子工程,该工程中我仅在UI中添加了两个label。
至此,插件编写完成。
此时运行工程会生成一个插件,如图:
4. 宿主程序加载插件
①在应用程序中使用QPluginLoader()加载插件
宿主程序中有一个AbstractInterface对象
遍历PluginWidget目录下的文件,如果实例化成功则使用qobject_cast()测试插件是否实现了给定的接口。【应用程序扩展插件步骤的③和④】
foreach(QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if(plugin)
{
m_pluginInterface = qobject_cast<AbstractInterface *>(plugin);
if(m_pluginInterface)
{
m_pluginInterface->createPluginWidget(ui->pluginWidget);
ok = true;
}
}
}
在主UI中添加了一个Widget(ui->pluginWidget)和一个测试按钮。最后效果如下:
总结:
插件创建完以及加载后,整个流程就像官网描述的一样。对没有基础的同志来讲还是有一定难度,所以我就又自己搭建了一遍。
过程中涉及到了Qt的子工程、qmake的使用等。
Qt的插件从C++的角度来讲就是C++纯虚函数的应用,需要规定接口,由插件去实现,宿主程序只负责调用。