做蛋糕网站有哪些,英德市住房和城乡建设局网站,服务器有了怎么做网站,wordpress博客程序简介#xff1a;
插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。
Qt 提供了2种APIs来创建插件#xff1a;
一种高级API#xff0c;用于为Qt本身编写插件#xff1a;自定义数据库驱动程序#xff0c;图像格…简介
插件(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_METADATAQ_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 然后添加一个appinterface.h的头文件该类作为插件接口类。
#ifndef APPINTERFACE_H#define APPINTERFACE_H#include QList#include QActionclass AppInterface{public:virtual ~AppInterface() {}// 插件的名字virtual QString name() const 0;// 插件返回的QAction列表virtual QListQAction * actions() const 0;};QT_BEGIN_NAMESPACEQ_DECLARE_INTERFACE(AppInterface, plugindemo_app_interface)QT_END_NAMESPACE#endif // APPINTERFACE_H
在工程上右键选择“新子项目”选择Library-C Library类型选择“shared Library”,qt module 选择“widgets”.插件名称为subplugin.修改subplugin.pro
添加CONFIGplugin //目的就是为是该dll作为插件
target.path $$PWD/../bin/plugins、INSTALLtarget //把插件dll拷贝到容器的bin目录下。需构建步骤添加 make install 实现接口类SubPlugin.h注意添加Q_INTERFACES和Q_PLUGIN_METADATA宏
#ifndef SUBPLUGIN_H#define SUBPLUGIN_H#include subPlugin_global.h#include ../app/appinterface.h#include QObjectclass SUBPLUGIN_EXPORT SubPlugin : public QObject, public AppInterface{public:SubPlugin();Q_OBJECTQ_INTERFACES(AppInterface) //声明这个插件实现是基于哪个插件接口的Q_PLUGIN_METADATA(IID org.qt-project.Qt.Examples.SubPlugin FILE SubPlugin.json) //使用Q_PLUGIN_METADATA宏导出插件// AppInterface interfacepublic:virtual QString name() const override;virtual QListQAction * actions() const override;};#endif // SUBPLUGIN_H#include subplugin.hSubPlugin::SubPlugin(){}QString SubPlugin::name() const{return SubPlugin;}QListQAction * SubPlugin::actions() const{QAction *aboutQt new QAction;aboutQt-setText(tr(About Qt));QListQAction * result;result aboutQt;return result;}
在容器的Dialog.cpp里面添加加载插件的代码。
#include dialog.h#include ui_dialog.h#include appinterface.h#include QDebug#include QPluginLoader#include QFileInfo#include QDir#include QPushButtonDialog::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;#endifQFileInfoList 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_castAppInterface *(pluginInstance);if (appInterface ! nullptr) {qDebug() add actions from plugin: appInterface-name();QListQAction * 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 QStringclass 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 QPluginLoaderclass PLUGINMANAGER_EXPORT PluginManager:public QObject{Q_OBJECTprivate:PluginManager();public:static PluginManager *getinstance();//加载所有插件void loadAllPlugins();//卸载所有插件void unloadAllPlugins();QString datawrite();private:static PluginManager *m_instance;QHashQString, QPluginLoader *m_loaders; //插件路径--QPluginLoader实例};#endif // PLUGINMANAGER_H#include pluginmanager.h#include QDebug#include QPluginLoader#include QFileInfo#include QDir#include QCoreApplication#includePluginInterface.hPluginManager* PluginManager::m_instancenullptr;PluginManager::PluginManager(){}PluginManager *PluginManager::getinstance(){if(m_instancenullptr)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;#endifQFileInfoList 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_castPluginInterface *(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_castPluginInterface*(loader-instance());if(Plugin)str Plugin-datawrite();}return str;} 插件代码
#ifndef LOGPLUGIN_H#define LOGPLUGIN_H#include logPlugin_global.h#include ../PluginManager/PluginInterface.h#include QObjectclass LOGPLUGIN_EXPORT LogPlugin: public QObject,public PluginInterface{Q_OBJECTQ_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 QObjectclass DBPLUGIN_EXPORT DbPlugin : public QObject,public PluginInterface{public: Q_OBJECTQ_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.hDialog::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类型必须提供一个公有的默认构造函数、公有的默认拷贝构造函数和公有的默认析构函数。 在插件重写插件管理器声明的接收虚函数把插件管理器声明的发送虚函数声明为信号。 在插件管理器的加载插件处加上connect函数是信号槽关联起来。其中把插件发送信号关联插件管理器的接收槽函数。 同时让插件管理器的接收槽函数转发给对应的插件 在app程序中调用调用插件的信号从而发送消息 完整代码参见
链接https://pan.baidu.com/s/1fWK_oSYQTRNip4FLnTOLwg
提取码qc0t