目录
1.Qt的插件是什么?优点是什么?
2.实战项目需求
3. 程序设计分析
4.Qt插件的编写
Q_DECLARE_INTERFACE(类名,标识符)
Q_INTERFACES
5.插件的调用
总结感言:
1.Qt的插件是什么?优点是什么?
所谓的插件,只不过是重载了虚函数的dll,这跟抽象工厂类类似,这便是插件的原理。qt的插件可以说是一种动态库
在函数中,我们导入Interface接口文件,也就是插件接口文件,不需要依赖静态库生成代码,类似C/C++关键字extern。而在最后我们通过系统的API加载dll或者so,这个可以自行百度查阅 “动态库加载的两种方式”。
这样做的好处:定义开发范式,面向Interface编程,内部封装,模块和整体流程开发分离,提高开发效率。应用场景QtCreator-IDE、WPS、visual studio、Nodepad++等等,都是采用这种开发方式。
2.实战项目需求
有一个嵌入式的显示屏,需要编写软件来写入数据。显示屏的规约已经有一个文档了,现在需要阅读文档,然后根据显示屏规约,封装按页刷显示屏整体数据接口、封装单独修改指定页名称/型号/数量接口,使用插件方式封装。 封装规约文档部分截图如下
显示屏如下
3. 程序设计分析
具体就不讲那么多了,我只需要根据显示屏规约将特定的字符转化为16进制等方式,通过串口通信发送给显示屏,从而实现控制显示屏的显示
我实现的软件如下
点击写入后将指定数据写入到显示屏中:
软件的界面我写好了,现在需要写一个插件将接口都封装好,给我的软件调用,然后将接口返回的值,通过串口通信写入显示屏中
接下来讲解插件的编写
4.Qt插件的编写
创建完成后,看到如下界面
Qt模板中,默认继承了QGenericPlugin的类,这里我们不需要用到,选择自己写一下抽象类,来继承,然后子类来实现。
我们新建一个头文件,抽象类是不需要实现的,所以新建一个头文件即可
这里我新建了一个PackDataApi的头文件,然后根据需求加入文档的接口,有四个接口,这些接口是给我的软件调用的。比如说我要修改显示屏的用户名和密码,调用后只需要输入要修改的用户名和密码,接口就会返回特定的字符串给你,你只要在把这些字符串发送显示屏就可以修改显示屏的用户名和密码了。
还有类外的这两行是干嘛的呢?
#define PackDataApi_iid "PackDataPlugin.packdataapi"
Q_DECLARE_INTERFACE(PackDataApi, PackDataApi_iid)
Q_DECLARE_INTERFACE(类名,标识符)
此宏用于把标识符与类名接口关联起来。这个标识符是唯一的,这个宏通常在被放到一个类被定后的位置。
这个是Qt实现插件必须要有的一个宏, 把标识符与类名接口关联起来。标识符PackDataApi_iid我一般用"工程名.接口类名"来标识,看个人习惯啦,不重复就行
抽象类接口都写好后,回到要实现的子类中,把默认继承的类改为自己实现的抽象类,前面在继承一个QObjecet,如下图所示
发现还用到了两个宏
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID PackDataApi_iid FILE "PackDataPlugin.json")
#endif // QT_VERSION >= 0x050000
Q_INTERFACES(PackDataApi)
第一个:Q_PLUGIN_METADATA
这个宏被用于声明元数据,这个元数据是被实例化插件的一部分。
这个宏需要通过对象声明被实例化接口的IID,并且要引用包含元数据内容的文件。
在Qt插件源码里面,应该宏应该只能出现异常。
注意,这个宏只能出现在可以被实例化的类中(不能放在抽象类中)。
FILE是可选参数,他指向一个json文件。
这个json文件要包含在构建目录中(为资源文件),创建插件模板时已经自动生成,并且已经包含在里面了,不然moc会出错。
Q_INTERFACES
此宏告诉Qt哪些接口被类实例了。这个宏通常用于插件的实例
这几个宏是必须要写的,Qt规定好的
然后实现接口即可,加入自定义的函数辅助完成接口的开发,如下所示
具体实现我就不展示了,主要是理解插件开发的过程
实现后,然后编译,会发现在生成目录下有一个动态库生成,这个就是我们要给程序动态加载调用的库,加载后转化为接口类,调用即可
5.插件的调用
我们编译好插件程序,生成动态库了,接下来怎么调用呢?
调用时只需要动态库和实现接口的头文件即可,拿我的软件程序举例,将生成的动态库复制到程序目录下或者指定目录下。
在把接口头文件加入到程序工程目录当中,然后如下所示
然后在要加载动态库的文件中包含接口头文件
然后加载插件
加载插件代码如下,通用的,注意下加载插件的目录即可
void GlobalHelper::loadPlugins()
{
QDir pluginsDir(QCoreApplication::applicationDirPath());
qDebug()<< (QString("正在加载BasePageApi插件,路径[%1]").arg(pluginsDir.path()));
for(QString fileName: pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (plugin) {
m_dataApi = qobject_cast<PackDataApi *>(plugin);
if (m_dataApi) {
qDebug()<<"BasePageApi插件加载成功!";
return;
}
}
}
if(QMessageBox::critical(nullptr, "警告", "BasePageApi插件加载失败!", QMessageBox::Ok)==QMessageBox::Ok) {
qDebug()<<("BasePageApi插件加载失败!!");
}
}
这样子就完成调用啦
简单叙述调用过程:1.添加接口头文件 2.加载动态库 3.调用
如果要给别人调用的话,我们只需要写好接口文档,怎么调用即可,接口是干嘛的,参数是什么意思等等。
总结感言:
qt插件的开发,可以说是动态库的开发,因为调用方式类似,多熟悉一下,利用Qt框架开发很简单的。
在想想这种调用过程有的熟悉,比如公司买了一堆摄像头,公司要对摄像头进行二次开发,实现自己想要的功能,集成到一套设备里去,然后在卖给用户。
此时厂商肯定会提供开发文档,并给你SDK包(软件开发工具包), 包里你会发现只有头文件和一堆库文件,没有源文件,因为实现的过程是产商公司保密的,给你看还得了。所以都封装成了库的形式给你调用,如静态库或者动态库。这也是为什么会有库这种东西产生,为了自己写的东西不给别人知道我怎么写的。封装起来保密,这可是自己的知识产权呀!
还有Qt插件的开发,或者说库的开发是独立的一个模块。一般来说,一个大工程是有很多模块组成的,每个模块开发的人不一样,每个模块不同的人各司其职,模块都开发完后,在整合到起立调用,优化,最终完成一个工程。这样子的优点是开发效率高,各司其职,使软件开发更加独立,程序模块直接独立.......
就讲解那么多啦,写了几个小时啦,肝不下啦,觉得对你有帮助的点个赞再走⑧