1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Qt插件开发总结2--插件管理器

Qt插件开发总结2--插件管理器

时间:2021-03-14 08:17:43

相关推荐

Qt插件开发总结2--插件管理器

文章目录

一、前言二、项目结构三、创建插件管理器文件四、插件管理器的使用

一、前言

插件开发总结–插件的创建及使用一文中,展示了在Qt中如何使用Qt Low-API插件实例。但是这却满足不了大型应用程序的实际场景,没有扩展性。而插件间的通信、加载卸载(释放内存)、插件元数据、插件生命周期、插件依赖等问题,便是我们要做的。在QT内部,高级 API 有 PluginManager 负责做这些事,但是低级 API 就需要自己写插件管理器来帮助我们解决这些问题。

想象一台 windows 系统的电脑,包含了主机、显示屏、键鼠等部件。假如我们拔掉键盘,电脑不会出错,只是缺失了键盘的功能,因此键盘就可以看做是一个插件。与此同时一台完整的电脑不仅包含了键鼠,还有耳机、音响、光驱、显卡等部件,这些部件其实都可以看成插件。对 windows 来说,这些“插件”都有一个管理者,即为设备管理器。设备管理器负责添加和删除电脑所有的硬件和驱动,因此可以将设备管理器理解为插件管理器。最后一点电脑系统都有自己的内核,一个 windows 系统从启动到关机都是内核在响应,而内核就可以看做加载插件的主程序,仿佛:“一旦你插上,我就能用你来打游戏”。

二、项目结构

上文中的程序,主程序和插件程序是分开的,不便管理,本文中采取父子工程结构,如上图,创建流程如下:

1、创建子目录项目

2、创建子项目

Main、pluginA、pluginB都是子项目

PluginInterface.h

#ifndef PLUGININTERFACE_H#define PLUGININTERFACE_H#include <QObject>#include <QJsonObject>struct PluginMetaData{QString from; //消息来源QString dest; //消息去向QString msg; //消息QObject* object = nullptr;QJsonObject info = QJsonObject();};Q_DECLARE_METATYPE(PluginMetaData); //确保类型可以通过信号槽传递//定义接口class PluginInterface{public:virtual ~PluginInterface(){}virtual void recMsgFromManager(PluginMetaData) = 0; //接收来自插件管理器的消息virtual void sendMsgToManager(PluginMetaData) = 0; //发生消息到插件管理器};//一定是唯一的标识符#define PluginInterface_iid "Examples.Plugin.PluginInterface"QT_BEGIN_NAMESPACEQ_DECLARE_INTERFACE(PluginInterface,PluginInterface_iid)QT_END_NAMESPACE#endif // PLUGININTERFACE_H

widget.h

#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include <QMessageBox>#include <QDebug>#include "PluginInterface.h"#include "PluginManager.h"QT_BEGIN_NAMESPACEnamespace Ui {class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;};#endif // WIDGET_H

widget.cpp

#include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);PluginManager::instance()->loadAllPlugins();//qDebug()<<"allPluginsName: "<<PluginManager::instance()->allPluginsName();QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");if(loader1) {PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());if(pluginA) {PluginMetaData m;m.dest = "pluginB";m.from = "pluginA";m.msg = "插件A发给插件B的消息";pluginA->sendMsgToManager(m);}}QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");if(loader2) {PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());if(pluginB) {PluginMetaData m;m.dest = "pluginA";m.from = "pluginB";m.msg = "插件B发给插件A的消息";pluginB->sendMsgToManager(m);}}}Widget::~Widget(){delete ui;}

main.cpp

#include "widget.h"#include <QApplication>int main(int argc, char *argv[]){QApplication a(argc, argv);Widget w;w.show();return a.exec();}

pluginA.pro

QT += widgetsTEMPLATE = lib#表明这个makefile是一个lib的makefileCONFIG += plugin #应用程序是一个插件TARGET = pluginA #插件名称DESTDIR = ../plugins # 输出目录HEADERS += \pluginA.hSOURCES += \pluginA.cppDISTFILES += \pluginA.json

pluginA.h

#ifndef PLUGINA_H#define PLUGINA_H#include <QObject>#include <QtPlugin>#include <QDebug>#include "../Main/PluginInterface.h"class PluginA : public QObject,public PluginInterface{Q_OBJECTQ_INTERFACES(PluginInterface)Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginA.json")public:explicit PluginA(QObject *parent = nullptr);void show_pluginA();void recMsgFromManager(PluginMetaData metaData);signals:void sendMsgToManager(PluginMetaData);};#endif // PLUGINA_H

pluginA.cpp

#include "pluginA.h"PluginA::PluginA(QObject *parent) : QObject(parent){}void PluginA::show_pluginA(){qDebug()<<"这是插件A";}void PluginA::recMsgFromManager(PluginMetaData metaData){qDebug()<<"插件A接收到消息:"<<metaData.msg;}

pluginB.pro

QT += widgetsTEMPLATE = lib#表明这个makefile是一个lib的makefileCONFIG += plugin #应用程序是一个插件TARGET = pluginB #插件名称DESTDIR = ../plugins # 输出目录HEADERS += \pluginB.hSOURCES += \pluginB.cppDISTFILES += \pluginB.json

pluginB.h

#ifndef PLUGINB_H#define PLUGINB_H#include <QObject>#include <QtPlugin>#include <QDebug>#include "../Main/PluginInterface.h"class PluginB : public QObject,public PluginInterface{Q_OBJECTQ_INTERFACES(PluginInterface)Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginB.json")public:explicit PluginB(QObject *parent = nullptr);void show_pluginB();void recMsgFromManager(PluginMetaData metaData);signals:void sendMsgToManager(PluginMetaData);};#endif // PLUGINB_H

pluginB.cpp

#include "pluginB.h"PluginB::PluginB(QObject *parent) : QObject(parent){}void PluginB::show_pluginB(){qDebug()<<"这是插件B";}void PluginB::recMsgFromManager(PluginMetaData metaData){qDebug()<<"插件B接收到消息:"<<metaData.msg;}

三、创建插件管理器文件

在主程序Main项目中创建PluginManager类

#ifndef PLUGINMANAGER_H#define PLUGINMANAGER_H#include "PluginInterface.h"#include <QObject>#include <QPluginLoader>#include <QVariant>class PluginManager : public QObject{Q_OBJECTpublic:explicit PluginManager(QObject *parent = nullptr);~PluginManager();static PluginManager *instance(){if(m_instance==nullptr)m_instance=new PluginManager();return m_instance;}//扫描JSON文件中的插件元数据void scanMetaData(const QString &filepath);//加载所有插件void loadAllPlugins();//加载其中某个插件void loadPlugin(const QString &filepath);//卸载所有插件void unloadAllPlugins();//卸载某个插件void unloadPlugin(const QString &filepath);//获取所有插件名称QList<QVariant> allPluginsName();//获取所有插件QList<QPluginLoader *> allPlugins();//获取某个插件名称QVariant getPluginName(QPluginLoader *loader);//根据名称获得插件QPluginLoader* getPlugin(const QString &name);public slots:void recMsgFromPlugin(PluginMetaData);private:static PluginManager *m_instance;class PluginsManagerPrivate;PluginsManagerPrivate *managerPrivate;};#endif // PLUGINMANAGER_H

#include "PluginManager.h"#include <QDir>#include <QCoreApplication>#include <QJsonArray>#include <QDebug>PluginManager* PluginManager::m_instance=nullptr;class PluginManager::PluginsManagerPrivate{public:PluginsManagerPrivate(){m_names.clear();m_versions.clear();m_dependencies.clear();m_loaders.clear();}~PluginsManagerPrivate(){}QHash<QString, QVariant> m_names;//插件路径--插件名称QHash<QString, QVariant> m_versions; //插件路径--插件版本QHash<QString, QVariantList> m_dependencies; //插件路径--插件额外依赖的其他插件QHash<QString, QPluginLoader *> m_loaders;//插件路径--QPluginLoader实例bool check(const QString &filepath) //插件依赖检测{bool status = true;foreach (QVariant item, m_dependencies.value(filepath)) {QVariantMap map = item.toMap();// 依赖的插件名称、版本、路径QVariant name = map.value("name");QVariant version = map.value("version");QString path = m_names.key(name);/********** 检测插件是否依赖于其他插件 **********/// 先检测插件名称if (!m_names.values().contains(name)) {qDebug() << Q_FUNC_INFO << " Missing dependency:" << name.toString() << "for plugin" << path;status = false;continue;}// 再检测插件版本if (m_versions.value(path) != version) {qDebug() << Q_FUNC_INFO << " Version mismatch:" << name.toString() << "version"<< m_versions.value(m_names.key(name)).toString() << "but" << version.toString() << "required for plugin" << path;status = false;continue;}// 然后,检测被依赖的插件是否还依赖于另外的插件if (!check(path)) {qDebug() << Q_FUNC_INFO << "Corrupted dependency:" << name.toString() << "for plugin" << path;status = false;continue;}}return status;}};PluginManager::PluginManager(QObject *parent) : QObject(parent){managerPrivate = new PluginsManagerPrivate;}PluginManager::~PluginManager(){delete managerPrivate;}void PluginManager::loadAllPlugins(){QDir pluginsDir(qApp->applicationDirPath()); //pluginsDir: "../build-xxx-debug/debug"if(pluginsDir.dirName().toLower() == "debug" ||pluginsDir.dirName().toLower() == "release") {pluginsDir.cdUp(); //pluginsDir: "../build-xxx-debug"pluginsDir.cdUp(); //pluginsDir: "../"}pluginsDir.cd("plugins");QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);//初始化插件中的元数据for(QFileInfo fileinfo : pluginsInfo){//qDebug()<<"loadAllPlugins:"<<fileinfo.absoluteFilePath();scanMetaData(fileinfo.absoluteFilePath());}//加载插件for(QFileInfo fileinfo : pluginsInfo)loadPlugin(fileinfo.absoluteFilePath());}void PluginManager::scanMetaData(const QString &filepath){//判断是否为库(后缀有效性)if(!QLibrary::isLibrary(filepath))return ;//获取元数据QPluginLoader *loader = new QPluginLoader(filepath);//qDebug()<<loader->metaData().keys();QJsonObject json = loader->metaData().value("MetaData").toObject();// for(int i=0; i<json.keys().size(); ++i) {// qDebug()<<json.keys().at(i)<< " : "<<json.value(json.keys().at(i));// }managerPrivate->m_names.insert(filepath, json.value("name").toVariant());managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());delete loader;loader = nullptr;}void PluginManager::loadPlugin(const QString &filepath){if(!QLibrary::isLibrary(filepath))return;//检测依赖if(!managerPrivate->check(filepath))return;//加载插件QPluginLoader *loader = new QPluginLoader(filepath);if(loader->load()) {PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());if(plugin) {managerPrivate->m_loaders.insert(filepath, loader);connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),this,SLOT(recMsgFromPlugin(PluginMetaData)));}else {delete loader;loader = nullptr;}}else{qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();}}void PluginManager::unloadAllPlugins(){for(QString filepath : managerPrivate->m_loaders.keys())unloadPlugin(filepath);}void PluginManager::unloadPlugin(const QString &filepath){QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);//卸载插件,并从内部数据结构中移除if(loader->unload()) {managerPrivate->m_loaders.remove(filepath);delete loader;loader = nullptr;}}QList<QPluginLoader *> PluginManager::allPlugins(){return managerPrivate->m_loaders.values();}QList<QVariant> PluginManager::allPluginsName(){return managerPrivate->m_names.values();}QVariant PluginManager::getPluginName(QPluginLoader *loader){if(loader)return managerPrivate->m_names.value(managerPrivate->m_loaders.key(loader));elsereturn "";}QPluginLoader *PluginManager::getPlugin(const QString &name){return managerPrivate->m_loaders.value(managerPrivate->m_names.key(name));}void PluginManager::recMsgFromPlugin(PluginMetaData metaData){auto loader = getPlugin(metaData.dest); //目标插件if(loader) {auto interface = qobject_cast<PluginInterface*>(loader->instance());if(interface) {interface->recMsgFromManager(metaData); //转发给对应的插件}}}

四、插件管理器的使用

直接编译运行,就会生成插件在plugins文件夹中,无须移动,程序加载插件会直接去此文件夹中加载插件

#include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);PluginManager::instance()->loadAllPlugins();//加载所有插件//qDebug()<<"allPluginsName: "<<PluginManager::instance()->allPluginsName();QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");if(loader1) {PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());if(pluginA) {PluginMetaData m;m.dest = "pluginB";m.from = "pluginA";m.msg = "插件A发给插件B的消息";pluginA->sendMsgToManager(m);}}QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");if(loader2) {PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());if(pluginB) {PluginMetaData m;m.dest = "pluginA";m.from = "pluginB";m.msg = "插件B发给插件A的消息";pluginB->sendMsgToManager(m);}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
tmux插件管理器

tmux插件管理器

2022-02-02

jmeter插件管理器

jmeter插件管理器

2020-11-07

设计器插件管理

设计器插件管理

2023-04-02

Chrome插件管理器

Chrome插件管理器

2019-03-21