当前位置: 首页 > news >正文

【QT】Qt Plugin开发

目录

    • 插件是什么
      • QT插件是什么
    • 为什么要有插件开发
      • 插件开发优势
      • 插件和动态库区别
    • Qt Plugin
      • QT插件类型
      • QT插件开发流程
      • QT插件应用
      • QT插件JSON文件
    • 参考文章

插件是什么

  1. 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。
  2. 其只能运行在程序规定的系统平台下(可能同时支持多个平台),而不能脱离指定的平台单独运行
  3. 因为插件需要调用原纯净系统提供的函数库或者数据。

QT插件是什么

  1. QT插件是重载了虚函数的dll,跟抽象工厂类类似,qt的插件可以说是一种动态库。

为什么要有插件开发

插件开发存在的原因主要是为了提高软件的可扩展性和可维护性。通过插件化开发,开发者可以将功能模块以插件的形式动态加载到应用程序中,使得开发者能够更加方便地对软件进行扩展和定制

插件开发优势

  1. 在函数中,我们导入Interface接口文件,也就是插件接口文件,不需要依赖静态库生成代码,类似C/C++关键字extern。而在最后我们通过系统的API加载dll或者so(通过动态库加载的两种方式)

  2. 定义开发范式,面向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。

插件和动态库区别

两者都是用于封装部分功能的实现,并降低模块代码耦合度。但其实插件也是被部署为动态库的形式,但是和传统的动态库还是有一些差别的。

  1. 插件主要面向接口编程,**无需访问 .lib 文件,热插拔、利于团队开发。即使在程序运行时 .dll 不存在,也可以正常启动,**只是相应插件的功能无法正常使用而已。

  2. 动态库需要访问 .lib 文件,而且在程序运行时必须保证 .dll 存在,否则无法正常启动。

  3. 插件的应用场景,一个大型项目的开发离不开插件化,可以让整个框架结构更加清晰和容易理解,比如说一个该项目经常会针对不同客户做功能定制,或者对于软件使用的不同场景,功能有所区别,那这时候插件就变得非常有用了,主工程中包含所有功能模块的调用,但是如果某些功能如果不需要,那最终程序打包只要不把插件的dll打包进去就OK了,程序依然可以正常运行,只是该插件的功能无法使用而已。

这样对于多功能模块的情况下,如果不同版本仅需要其中几项功能,就可以不用像动态链接库那样,全部dll都包含进去,从而也节省了安装包的空间。

Qt Plugin

QT插件类型

  1. The High-Level API:用于扩展Qt本身的功能,需放在Qt安装目录下的指定目录里;

  2. The Lower-Level API:用于扩展Qt应用程序的功能;

Qt Plugin按照类型又可分为两种:动态插件(dll)和静态插件(lib);

以下说的均为The Lower-Level API的动态插件。

  1. Qt5不再使用Q_EXPORT_PLUGIN2宏,可以在代码中跳转过去看,会发现这个宏已经作废了,在Qt5中,导出plugin使用Q_PLUGIN_METADATA宏,在Qt助手中搜“How to Create Qt Plugins”可以看到相关说明,还有一个不完整但清晰的demo。

QT插件开发流程

主程序开发流程(接口)

  1. 定义插件接口(抽象类模拟接口)
  2. 使用 Q_DECLARE_INTERFACE() 宏来告诉 Qt 元对象系统有关接口的情况

插件开发流程

  1. 声明一个插件类,该插件类继承自QObject和上一步定义的接口类
  2. 使用Q_INTERFACES宏将该接口类告诉Qt元系统
  3. 使用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如是说1

The meta data file of a plugin is a JSON file that contains all information that is necessary for loading the plugin’s library, determining whether plugins are to be loaded and in which order (depending on e.g. dependencies). In addition, it contains textual descriptions of who created the plugin, what it is for, and where to find more information about it. The file must be located in one of the include search paths when compiling the plugin, and must have the .json extension. The JSON file is compiled into the plugin as meta data, which then is read by Qt Creator when loading plugins.

插件的元数据文件是一个 JSON 文件,它包含加载插件库所需的所有信息,决定是否加载插件以及加载顺序(取决于依赖关系)。此外,它还包含了关于谁创建了这个插件、它的用途以及在哪里可以找到更多关于它的信息的文本描述。在编译插件时,该文件必须位于包含搜索路径之一中,并且必须具有。Json 分机。JSON 文件作为元数据编译到插件中,然后在加载插件时由 Qt Creator 读取。

Plugin.json中存储着插件的元信息。
PS:使用metaData()加载Plugin.json时只能读取qt文档1中定义的字段。
e.g.

// QJsonObject json = pluginLoader.metaData().value("MetaData").toObject();
foreach(QString fileName, pluginsDir.entryList(QDir::Files)) {if (!fileName.contains(".dll")){continue;}QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));QJsonObject json = pluginLoader.metaData().value("MetaData").toObject();auto versin = json.value("Version").toString();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();}
}

参考文章

  1. Qt5笔记之Qt5插件的生成与加载及json文件的读取
  2. Qt 插件的json文件如何生成
  3. 【QT】QT中插件化开发及其简单使用
  4. Qt插件化(Plugins)开发扩展应用程序
  5. 实战详细讲解Qt插件plugin的编写与用法
  6. QT插件化系列(二) 插件管理器
  7. 深入理解QtCreator的插件设计架构
  8. Parsing the metaData of a plugin in Qt5

  1. Plugin Meta Data ↩︎ ↩︎

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 操作系统安全:Windows系统安全配置,Windows安全基线检查加固
  • 简单了解java中的异常
  • 0117__ANSI C、ISO C、Standard 是什么关系
  • 408计算机网络知识点——第一章 概述
  • TrustZone 详解
  • IO进程线程(十一)进程间通信 消息队列
  • 一些简单却精妙的算法
  • 拼多多销量清零吗?销量排行榜哪里看?
  • SLT简介【简单介绍SLT】
  • 关于Spring Cacheable注解的讨论
  • 【APP逆向】央视频播放量增加,逆向全过程解密
  • 【JAVA WEB实用技巧与优化方案】如何排查JVM线程死锁和内存溢出问题
  • docker_构建镜像成功但启动容器的执行程序总是报文件找不到(lesson)
  • C++面向对象程序设计 - 异常处理
  • C#上位机开发
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • 345-反转字符串中的元音字母
  • Android开源项目规范总结
  • CentOS 7 修改主机名
  • CSS盒模型深入
  • gulp 教程
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • laravel 用artisan创建自己的模板
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • SQLServer之创建显式事务
  • Vue2.0 实现互斥
  • 程序员该如何有效的找工作?
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 入门级的git使用指北
  • 我看到的前端
  • 我是如何设计 Upload 上传组件的
  • 学习JavaScript数据结构与算法 — 树
  • 仓管云——企业云erp功能有哪些?
  • ​14:00面试,14:06就出来了,问的问题有点变态。。。
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (6) 深入探索Python-Pandas库的核心数据结构:DataFrame全面解析
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (不用互三)AI绘画工具应该如何选择
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (离散数学)逻辑连接词
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (数据大屏)(Hadoop)基于SSM框架的学院校友管理系统的设计与实现+文档
  • (四)Android布局类型(线性布局LinearLayout)
  • (算法)求1到1亿间的质数或素数
  • (转)Scala的“=”符号简介
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • ***利用Ms05002溢出找“肉鸡
  • .Net Remoting常用部署结构
  • .Net--CLS,CTS,CLI,BCL,FCL
  • .NET精简框架的“无法找到资源程序集”异常释疑