简介:
插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。
Qt 提供了2种APIs来创建插件:
一种高级API,用于为Qt本身编写插件:自定义数据库驱动程序,图像格式,文本编解码器,自定义样式等。
一种用于扩展Qt应用程序的低级API。也就是说扩展我们自己使用Qt编写的程序。这要求应用程序使用 QPluginLoader 检测和加载插件。在这种情况下,插件可以提供任意功能,不限于数据库驱动程序、图像格式、文本编解码器、样式以及扩展Qt功能的其他类型的插件。本文主要通过示例来展示第二种方式创建插件。
原理:
重载了虚函数的dll,这跟抽象工厂类类似,这便是插件的原理。qt的插件可以说是一种动态库
Qt插件特点:
核心技术:QPluginLoader
Qt插件存储在共享库(DLL)中(即以动态库的方式存在),与使用QLibrary访问的共享库相比,它具有以下优势:
面向Interface编程,内部封装,模块和整体流程开发分离,提高开发效率。
QPluginLoader 检查插件是否与应用程序相同版本的 Qt 链接。
QPluginLoader 提供对根组件对象 (instance()) 的直接访问,而不是强制您手动解析 C 函数
插件
插件主要面向接口编程,无需访问.lib文件,热插拔、利于团队开发。即使在程序运行时.dll不存在,也可以正常启动,只是相应插件功能无法正常使用而已;
动态库
动态库需要访问.lib文件,而且在程序运行时必须保证.lib存在,否则无法正常启动
开发测试环境:QT5.15.2 + MSVC 2019
在容器端:
- 定义一组接口(只有纯虚函数的类)。
- 在QT_BEGIN_NAMESPACE和QT_END_NAMESPACE之间使用Q_DECLARE_INTERFACE()宏告诉Qt的元对象系统有关接口的信息。
- 在程序中使用 QPluginLoader加载插件。
- 使用qobject_cast()测试插件是否实现给定的接口。
Q_DECLARE_INTERFACE
Q_DECLARE_INTERFACE(类名,标识符)
此宏用于把标识符与类名接口关联起来。这个标识符是唯一的,这个宏通常在被放到一个类被定后的位置。
这个是Qt实现插件必须要有的一个宏, 把标识符与类名接口关联起来。
在插件端:
声明一个插件类,该类继承自QObject和插件要提供的接口类。
使用Q_INTERFACES()宏告诉Qt的元对象系统有关接口的信息。
使用Q_PLUGIN_METADATA()宏导出插件。
Q_PLUGIN_METADATA
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.SubPlugin" FILE "SubPlugin.json")
这个宏第一次参数定义了一个uuid,保证唯一即可,第二个json是必须要有的,当无法找到指定的文件时,moc 会出现错误,即使是空的文件也行
SubPlugin.json文件存放在源代码目录下,可以记录一下插件的信息(如插件名称、插件版本、插件依赖库),如下
{
"name": "testSubPlugin",
"version": "1.0",
"dependencies": []
}
这个宏被用于声明元数据,这个元数据是被实例化插件的一部分。
这个宏需要通过对象声明被实例化接口的IID,并且要引用包含元数据内容的文件。
需要获取这些元数据(即上面的json数据),可在在通过下面的办法。
QPluginLoader *loader = new QPluginLoader(this);
loader->setFileName(plugin.absoluteFilePath());
qDebug()<<loader->metaData().keys();
QJsonObject json = loader->metaData().value("MetaData").toObject();
QVariant var = json.value("name").toVariant();
Q_INTERFACES
Q_INTERFACES(AppInterface) //声明这个插件实现是基于哪个插件接口的,使qobject_cast()能正确进行QObject*到接口指针的转换