赞
踩
插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。
一种高级API,用于为Qt本身编写插件:自定义数据库驱动程序,图像格式,文本编解码器,自定义样式等。
一种用于扩展Qt应用程序的低级API。也就是说扩展我们自己使用Qt编写的程序。这要求应用程序使用 QPluginLoader 检测和加载插件。在这种情况下,插件可以提供任意功能,不限于数据库驱动程序、图像格式、文本编解码器、样式以及扩展Qt功能的其他类型的插件。本文主要通过示例来展示第二种方式创建插件。
原理:
重载了虚函数的dll,这跟抽象工厂类类似,这便是插件的原理。qt的插件可以说是一种动态库
核心技术:QPluginLoader
Qt插件存储在共享库(DLL)中(即以动态库的方式存在),与使用QLibrary访问的共享库相比,它具有以下优势:
面向Interface编程,内部封装,模块和整体流程开发分离,提高开发效率。
QPluginLoader 检查插件是否与应用程序相同版本的 Qt 链接。
QPluginLoader 提供对根组件对象 (instance()) 的直接访问,而不是强制您手动解析 C 函数
插件
插件主要面向接口编程,无需访问.lib文件,热插拔、利于团队开发。即使在程序运行时.dll不存在,也可以正常启动,只是相应插件功能无法正常使用而已;
动态库
动态库需要访问.lib文件,而且在程序运行时必须保证.lib存在,否则无法正常启动
开发测试环境:QT5.15.2 + MSVC 2019
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*到接口指针的转换
QPluginLoader 类
~QPluginLoader()
销毁 QPluginLoader 对象。
除非显式调用 unload(),否则插件会一直保留在内存中,直到应用程序终止。
Load
加载插件,如果插件加载成功则返回true; 否则返回false。
unload
卸载插件,如果插件可以卸载则返回true,否则返回false。
这在应用程序终止时自动发生,因此通常不需要调用此函数。
如果 QPluginLoader 的其他实例正在使用相同的插件,则调用将失败,并且只有在每个实例都调用了 unload() 时才会发生卸载。
不要尝试删除根组件。 而是依靠 unload() 会在需要时自动删除它
Instance
返回插件的根组件对象,组件对象是一个 QObject。 使用 qobject_cast() 可将其转成所需的对象。如果根组件对象被销毁,则调用此函数会创建一个新实例。
该函数返回的根组件在 QPluginLoader 销毁时不会被删除。 如果要确保删除根组件,则应在不再需要访问核心组件时立即调用 unload()。 当库最终卸载时,根组件将自动删除。
metaData
返回此插件的元数据。 元数据是在编译插件时使用 Q_PLUGIN_METADATA() 宏以 json 格式指定的数据。
首先创建一个Qt Subdirs Project工程PluginDemo,并添加一个app应用程序端。并在app.pro添加输出目录DESTDIR = $$PWD/../bin
- #ifndef APPINTERFACE_H
-
- #define APPINTERFACE_H
-
- #include <QList>
-
- #include <QAction>
-
- class AppInterface
-
- {
-
- public:
-
- virtual ~AppInterface() {}
-
- // 插件的名字
-
- virtual QString name() const = 0;
-
- // 插件返回的QAction列表
-
- virtual QList<QAction *> actions() const = 0;
-
- };
-
- QT_BEGIN_NAMESPACE
-
- Q_DECLARE_INTERFACE(AppInterface, "plugindemo_app_interface")
-
- QT_END_NAMESPACE
-
- #endif // APPINTERFACE_H
添加CONFIG+=plugin //目的就是为是该dll作为插件
target.path = $$PWD/../bin/plugins、INSTALL+=target //把插件dll拷贝到容器的bin目录下。需构建步骤添加 make install
- #ifndef SUBPLUGIN_H
-
- #define SUBPLUGIN_H
-
- #include "subPlugin_global.h"
-
- #include "../app/appinterface.h"
-
- #include <QObject>
-
- class SUBPLUGIN_EXPORT SubPlugin : public QObject, public AppInterface
-
- {
-
- public:
-
- SubPlugin();
-
- Q_OBJECT
- Q_INTERFACES(AppInterface) //声明这个插件实现是基于哪个插件接口的
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.SubPlugin" FILE "SubPlugin.json") //使用Q_PLUGIN_METADATA宏导出插件
- // AppInterface interface
- public:
- virtual QString name() const override;
-
- virtual QList<QAction *> actions() const override;
-
- };
-
-
-
- #endif // SUBPLUGIN_H
-
-
-
- #include "subplugin.h"
-
- SubPlugin::SubPlugin()
-
- {
-
- }
-
- QString SubPlugin::name() const
-
- {
-
- return "SubPlugin";
-
- }
-
- QList<QAction *> SubPlugin::actions() const
-
- {
-
- QAction *aboutQt = new QAction;
-
- aboutQt->setText(tr("About Qt"));
-
- QList<QAction *> result;
-
- result << aboutQt;
-
- return result;
-
- }
- #include "dialog.h"
-
- #include "ui_dialog.h"
-
- #include "appinterface.h"
-
-
-
-
-
- #include <QDebug>
-
- #include <QPluginLoader>
-
- #include <QFileInfo>
-
- #include <QDir>
-
- #include <QPushButton>
-
-
-
-
-
- Dialog::Dialog(QWidget *parent)
-
- : QDialog(parent)
-
- , ui(new Ui::Dialog)
-
- {
-
- loadPlugins();
-
- ui->setupUi(this);
-
- }
-
-
-
- Dialog::~Dialog()
-
- {
-
- delete ui;
-
- }
-
-
-
- void Dialog::loadPlugins()
-
- {
-
- QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins";
-
- QStringList filters;
-
- #if defined(Q_OS_LINUX)
-
- filters << "*.so";
-
- #elif defined(Q_OS_WIN)
-
- filters << "*.dll";
-
- #endif
-
- QFileInfoList plugins = QDir(pluginPath).entryInfoList(filters,
-
- QDir::Files | QDir::NoSymLinks);
-
- for (int i = 0; i < plugins.count(); ++i) {
-
- QFileInfo plugin = plugins.at(i);
-
-
-
- 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();
-
- bool result = loader->load();
-
- qDebug() << QString("load plguin %1 result: ").arg(plugin.absoluteFilePath()) << result;
-
- if (result) {
-
- QObject *pluginInstance = loader->instance();
-
- AppInterface *appInterface = qobject_cast<AppInterface *>(pluginInstance);
-
- if (appInterface != nullptr) {
-
- qDebug() << "add actions from plugin:" << appInterface->name();
-
- QList<QAction *> actions = appInterface->actions();
-
- for (int k = 0; k < actions.count(); ++k) {
-
- QAction *ac = actions.at(k);
-
- QString strtext = ac->text();
-
- }
-
- }
-
- } else {
-
- qDebug() << loader->errorString();
-
- }
-
- }
-
- }
自定义pluginmanager类,封装成dll
管理所有插件,包括加载、卸载等。并暴露方法给主程序。
插件管理器代码
插件接口
- #ifndef PLUGININTERFACE_H
-
- #define PLUGININTERFACE_H
-
- #include <QtWidgets/qwidget.h>
-
- #include <QString>
-
-
-
- class PluginInterface
-
- {
-
- public:
-
- virtual ~PluginInterface() {}
-
- // 插件返回的QAction列表
-
- virtual QString datawrite() = 0;
-
- };
-
-
-
- Q_DECLARE_INTERFACE(PluginInterface, "PluginManager.PluginInterface")
-
- #endif // PLUGININTERFACE_H
插件管理器
- #ifndef PLUGINMANAGER_H
-
- #define PLUGINMANAGER_H
-
-
-
- #include "PluginManager_global.h"
-
- #include <QObject>
-
- #include <QPluginLoader>
-
-
-
- class PLUGINMANAGER_EXPORT PluginManager:public QObject
-
- {
-
- Q_OBJECT
-
- private:
-
- PluginManager();
-
- public:
-
- static PluginManager *getinstance();
-
-
-
- //加载所有插件
-
- void loadAllPlugins();
-
-
-
- //卸载所有插件
-
- void unloadAllPlugins();
-
-
-
- QString datawrite();
-
-
-
- private:
-
- static PluginManager *m_instance;
-
-
-
- QHash<QString, QPluginLoader *>m_loaders; //插件路径--QPluginLoader实例
-
-
-
- };
-
-
-
- #endif // PLUGINMANAGER_H
-
-
-
-
-
- #include "pluginmanager.h"
-
- #include <QDebug>
-
- #include <QPluginLoader>
-
- #include <QFileInfo>
-
- #include <QDir>
-
- #include <QCoreApplication>
-
-
-
- #include"PluginInterface.h"
-
-
-
- PluginManager* PluginManager::m_instance=nullptr;
-
- PluginManager::PluginManager()
-
- {
-
- }
-
-
-
- PluginManager *PluginManager::getinstance()
-
- {
-
- if(m_instance==nullptr)
-
- m_instance= new PluginManager();
-
- return m_instance;
-
- }
-
-
-
- void PluginManager::loadAllPlugins()
-
- {
-
- QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins";
-
- QStringList filters;
-
- #if defined(Q_OS_LINUX)
-
- filters << "*.so";
-
- #elif defined(Q_OS_WIN)
-
- filters << "*.dll";
-
- #endif
-
- QFileInfoList plugins = QDir(pluginPath).entryInfoList(filters,
-
- QDir::Files | QDir::NoSymLinks);
-
- for (int i = 0; i < plugins.count(); ++i)
-
- {
-
- QFileInfo plugin = plugins.at(i);
-
- 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();
-
- bool result = loader->load();
-
- qDebug() << QString("load plguin %1 result: ").arg(plugin.absoluteFilePath()) << result;
-
- if (result)
-
- {
-
- m_loaders.insert(plugin.absoluteFilePath(), loader);
-
- QObject *pluginInstance = loader->instance();
-
- PluginInterface *appInterface = qobject_cast<PluginInterface *>(pluginInstance);
-
- if (appInterface != nullptr)
-
- {
-
-
-
- }
-
- }
-
- else
-
- {
-
- qDebug() << loader->errorString();
-
- }
-
- }
-
-
-
- }
-
-
-
- void PluginManager::unloadAllPlugins()
-
- {
-
- for(auto filepath : m_loaders.keys())
-
- {
-
- QPluginLoader *loader = m_loaders.value(filepath);
-
- //卸载插件,并从内部数据结构中移除
-
- if(loader->unload())
-
- {
-
- m_loaders.remove(filepath);
-
- delete loader;
-
- loader = nullptr;
-
- }
-
- }
-
-
-
- }
-
-
-
- QString PluginManager::datawrite()
-
- {
-
- QString str;
-
- for(auto filepath : m_loaders.keys())
-
- {
-
- QPluginLoader *loader = m_loaders.value(filepath);
-
- PluginInterface *Plugin = dynamic_cast<PluginInterface*>(loader->instance());
-
- if(Plugin)
-
- str = Plugin->datawrite();
-
- }
-
- return str;
-
- }
插件代码
- #ifndef LOGPLUGIN_H
-
- #define LOGPLUGIN_H
-
- #include "logPlugin_global.h"
-
- #include "../PluginManager/PluginInterface.h"
-
- #include <QObject>
-
- class LOGPLUGIN_EXPORT LogPlugin: public QObject,public PluginInterface
-
- {
-
- Q_OBJECT
- Q_INTERFACES(PluginInterface) //声明这个插件实现是基于哪个插件接口的
- Q_PLUGIN_METADATA(IID "org.qt-LogPlugin" FILE "LogPlugin.json") //使用Q_PLUGIN_METADATA宏导出插件
- public:
- LogPlugin();
-
- virtual QString datawrite() override;
-
- };
-
-
-
- #endif // LOGPLUGIN_H
-
-
-
-
-
- #ifndef DBPLUGIN_H
-
- #define DBPLUGIN_H
-
-
-
- #include "DbPlugin_global.h"
-
- #include "../PluginManager/PluginInterface.h"
-
- #include <QObject>
-
- class DBPLUGIN_EXPORT DbPlugin : public QObject,public PluginInterface
-
- {
-
- public:
-
- Q_OBJECT
- Q_INTERFACES(PluginInterface) //声明这个插件实现是基于哪个插件接口的
- Q_PLUGIN_METADATA(IID "org.qt-DbPlugin" FILE "DbPlugin.json") //使用Q_PLUGIN_METADATA宏导出插件
- public:
- DbPlugin();
-
- virtual QString datawrite() override;
-
-
-
- };
-
-
-
- #endif // DBPLUGIN_H
App代码
- #include "dialog.h"
-
- #include "ui_dialog.h"
-
- #include <QDebug>
-
- #include <QPluginLoader>
-
- #include <QFileInfo>
-
- #include <QDir>
-
- #include <QPushButton>
-
- #include "../PluginManager/pluginmanager.h"
-
- Dialog::Dialog(QWidget *parent)
-
- : QDialog(parent)
-
- , ui(new Ui::Dialog)
-
- {
-
- ui->setupUi(this);
-
- }
-
-
-
- Dialog::~Dialog()
-
- {
-
- delete ui;
-
- }
-
-
-
- void Dialog::on_pushButton_clicked()
-
- {
-
- PluginManager::getinstance()->loadAllPlugins();
-
- PluginManager::getinstance()->datawrite();
-
- PluginManager::getinstance()->unloadAllPlugins();
-
- }
插件的通信通过插件管理器来管理,插件管理器转发插件的消息
Q_DECLARE_METATYPE(Type)
作用:向Qt元系统注册一些非基本类型
这个宏能使Qt元系统的QMetaType知道Type类型,但前提是Type类型必须提供一个公有的默认构造函数、公有的默认拷贝构造函数和公有的默认析构函数。
同时让插件管理器的接收槽函数转发给对应的插件
完整代码参见:
链接:https://pan.baidu.com/s/1fWK_oSYQTRNip4FLnTOLwg
提取码:qc0t
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。