目录
- 插件是什么
- QT插件是什么
- 为什么要有插件开发
- 插件开发优势
- 插件和动态库区别
- Qt Plugin
- QT插件类型
- QT插件开发流程
- QT插件应用
- QT插件JSON文件
- 参考文章
插件是什么
- 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。
- 其只能运行在程序规定的系统平台下(可能同时支持多个平台),而不能脱离指定的平台单独运行。
- 因为插件需要调用原纯净系统提供的函数库或者数据。
QT插件是什么
- QT插件是重载了虚函数的dll,跟抽象工厂类类似,qt的插件可以说是一种动态库。
为什么要有插件开发
插件开发存在的原因主要是为了提高软件的可扩展性和可维护性。通过插件化开发,开发者可以将功能模块以插件的形式动态加载到应用程序中,使得开发者能够更加方便地对软件进行扩展和定制
插件开发优势
-
在函数中,我们导入Interface接口文件,也就是插件接口文件,不需要依赖静态库生成代码,类似C/C++关键字extern。而在最后我们通过系统的API加载dll或者so(通过动态库加载的两种方式)
-
定义开发范式,面向Interface编程,内部封装,模块和整体流程开发分离,提高开发效率。应用场景QtCreator-IDE、WPS、visual studio、Nodepad++等等,都是采用这种开发方式。
动态库的加载主要有两种方式(源自GPT)
隐式加载:也叫载入时加载,是在编译的时候,指明所依赖的动态链接库,这样在程序启动的时候,loader会将所有的动态链接库映射到内存中。这种方式需要.h文件,.dll文件,.lib文件。在vs的项目属性->链接器的附加库目录设置为存放.lib文件的路径,附加依赖项加入用到的.lib文件名字。将.dll文件和项目生成的.exe文件放在一起就可以使用.dll文件中的函数了。
显式加载:也叫运行时加载,是在程序运行过程中,通过调用系统函数,把动态库加载到程序中,然后执行动态库中的代码。这种方式需要.h文件,.dll文件。这种方式是将较大的程序分开加载的,程序运行时只需要将主程序载入内存,软件打开速度快,用户体验好。显式加载使用libdl.so库的API接口在运行中加载和卸载动态库,主要的API有dlopen、dlclose、dlsym、dlerror。
插件和动态库区别
两者都是用于封装部分功能的实现,并降低模块代码耦合度。但其实插件也是被部署为动态库的形式,但是和传统的动态库还是有一些差别的。
-
插件主要面向接口编程,**无需访问 .lib 文件,热插拔、利于团队开发。即使在程序运行时 .dll 不存在,也可以正常启动,**只是相应插件的功能无法正常使用而已。
-
动态库需要访问 .lib 文件,而且在程序运行时必须保证 .dll 存在,否则无法正常启动。
-
插件的应用场景,一个大型项目的开发离不开插件化,可以让整个框架结构更加清晰和容易理解,比如说一个该项目经常会针对不同客户做功能定制,或者对于软件使用的不同场景,功能有所区别,那这时候插件就变得非常有用了,主工程中包含所有功能模块的调用,但是如果某些功能如果不需要,那最终程序打包只要不把插件的dll打包进去就OK了,程序依然可以正常运行,只是该插件的功能无法使用而已。
这样对于多功能模块的情况下,如果不同版本仅需要其中几项功能,就可以不用像动态链接库那样,全部dll都包含进去,从而也节省了安装包的空间。
Qt Plugin
QT插件类型
-
The High-Level API:用于扩展Qt本身的功能,需放在Qt安装目录下的指定目录里;
-
The Lower-Level API:用于扩展Qt应用程序的功能;
Qt Plugin按照类型又可分为两种:动态插件(dll)和静态插件(lib);
以下说的均为The Lower-Level API的动态插件。
- Qt5不再使用Q_EXPORT_PLUGIN2宏,可以在代码中跳转过去看,会发现这个宏已经作废了,在Qt5中,导出plugin使用Q_PLUGIN_METADATA宏,在Qt助手中搜“How to Create Qt Plugins”可以看到相关说明,还有一个不完整但清晰的demo。
QT插件开发流程
主程序开发流程(接口)
- 定义插件接口(抽象类模拟接口)
- 使用 Q_DECLARE_INTERFACE() 宏来告诉 Qt 元对象系统有关接口的情况
插件开发流程
- 声明一个插件类,该插件类继承自QObject和上一步定义的接口类
- 使用Q_INTERFACES宏将该接口类告诉Qt元系统
- 使用Q_PLUGIN_METADATA宏导出该插件类(Qt5不再使用Q_EXPORT_PLUGIN2宏)
// 接口声明
#include <QtCore/QtPlugin>#define Interface_iid "Plugin.Interface"class Interface
{
public:virtual ~Interface() {};/// @brief 获取插件名virtual const QString getPluginName() = 0;
};Q_DECLARE_INTERFACE(Interface, Interface_iid);
// 插件主体
#pragma once#include <QtCore/QObject>
#include <QtCore/QtPlugin>#include "../QtPluginTest/Interface.h"class Plugin_1 : public QObject, public Interface
{Q_OBJECTQ_INTERFACES(Interface)Q_PLUGIN_METADATA(IID Interface_iid FILE "xx.json")// FILE是可选参数,他指向一个json文件。// Q_PLUGIN_METADATA(IID Interface_iid) // 这样也可以public:explicit Plugin_1(QObject* parent = nullptr);const QString getPluginName() override;
};#include "Plugin_1.h"Plugin_1::Plugin_1(QObject* parent):QObject(parent)
{
}const QString Plugin_1::getPluginName()
{return QString("Plugin_1");
}
QT插件应用
完成插件代码后,编译插件工程生成dll,拷贝到应用工程中,然后要包含Interface的头文件(如果有Interface.cpp,那么还要加上附加库目录和附加依赖项);
然后通过QPluginLoader类来动态加载插件,也就是xxxx.dll文件,加载比较简单,只要xxxx.dll是一个插件(可以理解为一个有Q_DECLARE_INTERFACE、Q_PLUGIN_METADATA、Q_INTERFACES这三个宏的工程生成的dll就是插件),把它丢到exe所在目录下就可以加载了。
QDir pluginsDir(qApp->applicationDirPath());
auto path = pluginsDir.dirName();
pluginsDir.cd("Plugins");
path = pluginsDir.dirName();foreach(QString fileName, pluginsDir.entryList(QDir::Files)) {if (!fileName.contains(".dll")){continue;}QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));QObject* plugin = pluginLoader.instance();if (plugin) {auto name = plugin->metaObject()->className();Interface* m_pInterface = qobject_cast<Interface*>(plugin);if (m_pInterface)qDebug() << m_pInterface->getPluginName();}
}// or
QDir path(qApp->applicationDirPath());
QPluginLoader loader(path.absoluteFilePath("Plugin.dll"));//Plugin.dll是一个插件
if (!loader.load()) {qDebug() << "It is not a plugin";return;
}
QObject *obj = loader.instance();
if (obj) {Interface *plugin = qobject_cast<Interface *>(obj);
}
// TODO
QCoreApplication::addLibraryPath(“/path/to/your/plugins”) 加载插件
PS:参考文档Qt QCoreApplication addLibraryPath use
参考
QT插件JSON文件
Qt plugin工程在创建时就会自带一个json文件,用于存储元信息和一些配置信息,读取json文件如下。
我个人认为该json就是存储插件的一些信息,方便标识
参考文章
- Qt5笔记之Qt5插件的生成与加载及json文件的读取
- Qt 插件的json文件如何生成
- 【QT】QT中插件化开发及其简单使用
- Qt插件化(Plugins)开发扩展应用程序
- 实战详细讲解Qt插件plugin的编写与用法