前言
插件是一种遵循一定规范的应用程序接口编写出来的程序,本教程说的插件是用于扩展Qt应用程序的插件,笔者做对创建和使用方法,做下简单的记录
一、Qt插件创建和使用流程
1.定义一个接口集(只有纯虚函数的类),用来与插件交流。
2.用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
3.应用程序中用QPluginLoader来加载插件。
4.用宏qobject_cast()来判断一个插件是否实现了接口。
二、定义接口文件
在QtCreator中,新建头文件
![](https://img-blog.csdnimg.cn/80cab0c01c50493eabc328dce95c5327.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/b92713f98ebd4d94b944d5d21086f5fa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/8a58c7c5a8454968807da5593ddc50c6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
头文件的内容修改如下
#ifndef DEVICEPLUGININTERFACE_H
#define DEVICEPLUGININTERFACE_H
#include <QObject>
#include <QJsonObject>
class DevicePluginInterface
{
public:
//析构函数
virtual ~DevicePluginInterface() {}
//测试设备
virtual bool testDevice(const QJsonObject &requestJsonObject, QString &errorString) = 0;
//返回设备信息
virtual const QJsonObject deviceInfo() = 0;
};
#define DEVICEPLUGININTERFACE_IID "com.xdqd.deviceplugin"
Q_DECLARE_INTERFACE(DevicePluginInterface, DEVICEPLUGININTERFACE_IID)
#endif // DEVICEPLUGININTERFACE_H
读者请自行修改,
注:
1.虚析构函数一定要定义,这有助于确保利用基类的指针访问子类时,可以调用子类的析构函数,以避免内存泄漏
遗憾的是,大多数C++编译器并没有警示信息
2.Q_DECLARE_INTERFACE宏,将类定义为接口
3.DEVICEPLUGININTERFACE_IID 应该是与包名格式类似的唯一字符串,可以根据自己的喜好更改
三、创建插件
使用QtCreator创建共享库工程
![](https://img-blog.csdnimg.cn/5a4151174d7043c58e01d3285b3459eb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/7b77514375c14edb93d3c5746130ac8e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/2ea8697a055147d0b539872ecbb602ed.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/a499401246da4b4a9b720ec768d4736a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
工程文件中添加如下代码
CONFIG += plugin
将已写好的接口文件,加入工程中
![](https://img-blog.csdnimg.cn/334f34d6b21e4240be935c343071bf23.png)
修改导出类如下
#ifndef TEST_DEVICE_PLUGIN_H
#define TEST_DEVICE_PLUGIN_H
#include "test_device_plugin_global.h"
#include "deviceplugininterface.h"
class TEST_DEVICE_PLUGIN_EXPORT Test_Device_Plugin: public QObject, public DevicePluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.xdqd.deviceplugin")
Q_INTERFACES(DevicePluginInterface)
public:
Test_Device_Plugin();
//析构函数
~Test_Device_Plugin();
//测试设备
bool testDevice(const QJsonObject &requestJsonObject, QString &errorString);
//返回设备信息
const QJsonObject deviceInfo();
};
#endif // TEST_DEVICE_PLUGIN_H
1.添加接口类的头文件
2.确保导出类Test_Device_Plugin继承了QObject和DevicePluginInterface
3.添加必要的宏,以便将库识别为插件
Q_OBJECT Qt宏,以便使用Qt特定的功能
Q_PLUGIN_METADATA(IID "com.xdqd.deviceplugin") 用于添加关于插件的元数据
Q_INTERFACES(DevicePluginInterface) 用于声明插件中实现的接口
实现对应函数的功能后,可以直接编译,生成所需的库文件
![](https://img-blog.csdnimg.cn/f61e60ff5cb74c599ce23ac284ca0e27.png)
四、使用插件
创建测试工程
![](https://img-blog.csdnimg.cn/b3e3e14959d2413cb4393e3134359f6b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/fdd02a61d17c4f67aab8202f33c63b88.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/3d95ee21d9a34daf93ef5718fec80581.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYWdnczE5OTA=,size_20,color_FFFFFF,t_70,g_se,x_16)
在工程文件中,添加如下内容
CONFIG += plugin
添加接口文件到工程中
![](https://img-blog.csdnimg.cn/00ce2ed7006e45028a8e9f49ab8faea1.png)
把插件放入指定的目录
main函数修改如下
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<< QLibrary::isLibrary("test_device_plugin.dll");
if(!QLibrary::isLibrary("test_device_plugin.dll"))
{
qDebug() << "QLibrary::isLibrary not a library";
return -1;
}
QPluginLoader pluginLoader("E:/workplace/plugin/test_device_plugin.dll");
qDebug() << "QPluginLoader::isLoaded" << pluginLoader.isLoaded();
DevicePluginInterface *devicePluginInterface = qobject_cast<DevicePluginInterface*>(pluginLoader.instance());
if(devicePluginInterface)
{
QJsonObject requestJsonObject;
QString errorString;
qDebug() << devicePluginInterface->testDevice(requestJsonObject, errorString) << errorString;
qDebug() << devicePluginInterface->testDevice(requestJsonObject, errorString) << errorString;
qDebug() << devicePluginInterface->deviceInfo();
pluginLoader.unload(); // we can unload for now
}
else
{
qDebug() << "plugin error";
}
return a.exec();
}
1.先使用QLibrary::isLibrary()检查下当前的文件是否是一个库文件
2.使用QPluginLoader载入插件文件
3.使用qobject_cast<>()将插件内的实例,转换成接口类
4.调用接口类的成员函数
注:笔者测试时发现,QPluginLoader需要使用绝对路径,使用相对路径时报错。
示例源码下载
后记
操作系统不同,插件的文件扩展名也可能不同。如window系统下是.dll,macOS是.dylib,linux系统下是.so,android系统下是so
使用插件时,要确保是使用同一编译器和Qt版本,否则可能出现错误