【QT】Qt Plugin开发
目录
- 插件是什么
- 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的编写与用法
相关文章:

【QT】Qt Plugin开发
目录 插件是什么QT插件是什么 为什么要有插件开发插件开发优势插件和动态库区别 Qt PluginQT插件类型QT插件开发流程QT插件应用QT插件JSON文件 参考文章 插件是什么 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。…...

快速了解GPU分布通信技术:PCIe、NVLink与NVSwitch
在现代高性能计算和深度学习领域,GPU的强大计算能力使其成为不可或缺的工具。然而,随着模型复杂度的增加,单个GPU已经无法满足需求,需要多个GPU甚至多台服务器协同工作。这就要求高效的GPU互联通信技术,以确保数据传输…...

Python对获取数据的举例说明
当使用Python来获取数据时,有许多不同的方法和库可以根据你的需求来选择。以下是一些常见的示例,说明如何使用Python来从各种来源获取数据。 1. 从网站或API获取JSON数据 你可以使用requests库从网站或API获取JSON格式的数据。例如,从某个API…...

JVMの垃圾回收
在上一篇中,介绍了JVM组件中的运行时数据区域,这一篇主要介绍垃圾回收器 JVM架构图: 1、垃圾回收概述 在第一篇中介绍JVM特点时,有提到过内存管理,即Java语言相对于C,C进行的优化,可以在适当的…...

人工智能就业方向有哪些?
人工智能就业方向有哪些? 随着人工智能技术的不断发展,其应用领域也越来越广泛。对于想要进入人工智能领域的年轻人来说,选择一个合适的职业方向是至关重要的。今天给大家介绍六个热门的人工智能就业方向,分别是机器学习工程师、自然语言处理…...

自定义类型:枚举和联合体
在之前我们已经深入学习了自定义类型中的结构体类型 ,了解了结构体当中的内存对齐,位段等知识,接下来在本篇中将继续学习剩下的两个自定义类型:枚举类型与联合体类型,一起加油!! 1.枚举类型 …...

负载均衡加权轮询算法
随机数加权轮询算法 public int select() {int[] weights {10, 20, 50};int totalWeight weights[0] weights[1] weights[2];// 取随机数int offset ThreadLocalRandom.current().nextInt(totalWeight);for (int i 0; i < weights.length; i) {offset - weights[i];i…...

PyTorch 相关知识介绍
一、PyTorch和TensorFlow 1、PyTorch PyTorch是由Facebook开发的开源深度学习框架,它在动态图和易用性方面表现出色。它以Python为基础,并提供了丰富的工具和接口,使得构建和训练神经网络变得简单快捷。 发展历史和背景 PyTorch 是由 Fac…...

1千2初中英语语法题库ACCESS\EXCEL数据库
英语语法是针对英语语言进行研究后,系统地总结归纳出来的一系列语言规则。英语语法的精髓在于掌握语言的使用。比如词类有名词、代词、数词、感叹词等,时态有一般状态、进行状态、完成状态和完成进行状态四种,语态有主动语态、被动语态等。 …...

高德面试:为什么Map不能插入null?
在 Java 中,Map 是属于 java.util 包下的一个接口(interface),所以说“为什么 Map 不能插入 null?”这个问题本身问的不严谨。Map 部分类关系图如下: 所以,这里面试官其实想问的是:为…...

MySQL数据库主从配置
MySQL主从配置 1. 修改数据库my.cnf文件 修改数据库my.cnf文件,在文件中添加如下内容,其中主数据库的server-id必须要比从库的更小。 # 注册集群id server-id101 # 开启二进制日志文件 log-binmysql-bin # 设置日志格式 binlog-formatrow # 开启中继日…...

测试工程师经常使用的Python中的库,以及对应常用的函数
os (操作系统接口) 该库提供了许多与操作系统交互的函数,如文件处理、目录操作、进程管理等。 常用功能包括: os.name: 获取操作系统的名称。 os.path: 用于操作文件路径的模块,如os.path.join拼接路径。 os.mkdir: 创建目录。 os.remove: 删…...

【frp】服务端配置与systemd启动
ini配置的方式已经废弃。官方文档是toml 。阿里云ecs 部署服务端参考大神的文章 使用Frp配置内网访问(穿透) 0.54 版本 我现在用最新的0.58版本。systemd apt install systemdfrp服务端配置 /root/frp目录 vim frps.toml#服务绑定的IP与端口 bindAddr = "0.0.0.0" …...

计算机网络学习实践:模拟RIP动态路由
计算机网络学习实践:模拟RIP动态路由 模拟动态路由RIP协议 1.实验准备 实验环境:华为模拟器ENSP 实验设备: 3个路由器,3个二层交换机(不是三层的),3个PC机 5个网段 192.168.1.0 255.255.…...

详解 Flink 的常见部署方式
一、常见部署模式分类 1. 按是否依赖外部资源调度 1.1 Standalone 模式 独立模式 (Standalone) 是独立运行的,不依赖任何外部的资源管理平台,只需要运行所有 Flink 组件服务 1.2 Yarn 模式 Yarn 模式是指客户端把 Flink 应用提交给 Yarn 的 ResourceMa…...

【UE5.1 角色练习】11-坐骑——Part1(控制大象移动)
前言 在上一篇(【UE5.1 角色练习】10-物体抬升、抛出技能 - part2)基础上创建一个新的大象坐骑角色,并实现控制该角色行走的功能。 效果 步骤 1. 在商城中下载“African Animal Pack”资产和“ANIMAL VARIETY PACK”资产导入工程中 2. 复…...

数据结构严蔚敏版精简版-线性表以及c语言代码实现
线性表、栈、队列、串和数组都属于线性结构。线性结构的基本特点是除第一个元素无直接前驱,最后一个元素无直接后继之外,其他每个数据元素都有一个前驱和后继。 1 线性表的定义和特点 如此类由n(n大于等于0)个数据特性相同的元素…...

【react】react项目支持鼠标拖拽的边框改变元素宽度的组件
目录 安装使用方法示例Props 属性方法示例代码调整兄弟div的宽度 re-resizable github地址 安装 $ npm install --save re-resizable这将安装re-resizable库并将其保存为项目的依赖项。 使用方法 re-resizable 提供了一个 <Resizable> 组件,它可以包裹任何…...

QT 创建文件 Ui 不允许使用不完整类型,可以尝试添加一下任何头文件
#include "debug.h" #include "qmessagebox.h" #pragma execution_character_set("utf-8") //QT 创建文件 Ui 不允许使用不完整类型,尝试添加一下任何头文件,或者添加ui_xx.h头文件 debug::debug(QWidget *parent) : QDialog(p…...

Python:深入探索其生态系统与应用领域
Python:深入探索其生态系统与应用领域 Python,作为一种广泛应用的编程语言,其生态系统之丰富、应用领域之广泛,常常令人叹为观止。那么,Python究竟涉及哪些系统?本文将从四个方面、五个方面、六个方面和七…...

EXCEL从图片链接获取图片
step1: 选中图片地址列 step2:开发工具→Visual Basic 文件→导入 导入我制作的脚本(代码见文章末尾) 点击excel的小图标回到表格界面。 点击【宏】 选中刚才导入的脚本,点执行,等待完成。 代码本体: Sub InsertPict…...

Docker迁移默认存储目录(GPT-4o)
Docker在Ubuntu的默认存储目录是/var/lib/docker,要将 Docker 的默认存储目录迁移到指定目录(譬如大存储磁盘),可以通过修改 Docker 守护进程的配置文件来实现。 1.创建新的存储目录: 选择你想要存储 Docker 分层存储…...

植物大战僵尸杂交版2.0.88最新版安装包
游戏简介 游戏中独特的杂交植物更是为游戏增添了不少亮点。这些杂交植物不仅外观独特,而且拥有更强大的能力,能够帮助玩家更好地应对游戏中的挑战。玩家可以通过一定的条件和方式,解锁并培养这些杂交植物,从而不断提升自己的战斗…...

MQ基础(RabbitMQ)
通信 同步通信:就相当于打电话,双方交互是实时的。同一时刻,只能与一人交互。 异步通信:就相当于发短信,双方交互不是实时的。不需要立刻回应对方,可以多线程操作,跟不同人同时聊天。 RabbitM…...

eclipse添加maven插件
打开eclipse菜单 Help/Install New SoftwareWork with下拉菜单选择 2022-03 - https://download.eclipse.org/releases/2022-03‘type filter text’搜索框中输入 maven选择 M2E - Maven Integration for Eclipse一路next安装,重启eclipseImport项目时,就…...

知识库系统:从认识到搭建
在这个信息过载的时代,企业越来越需要一个集中的知识库系统来促进员工协作和解决问题。本文跟着LookLook同学一起来探讨搭建高效知识库系统的所有注意事项和知识库系统的最佳推荐。 | 什么是知识库系统 知识库系统是一种软件或工具,旨在填补组织内的知识…...

JVM双亲委派模型
在之前的JVM类加载器篇中说过,各个类加载器都有自己加载的范围,比如引导类加载器只加载Java核心库中的class如String,那如果用户自己建一个包名和类名与String相同的类,会不会被引导类加载器加载。可以通过如下代码测试࿰…...

Python语言与算法:深度探索与实战应用
Python语言与算法:深度探索与实战应用 在数字化浪潮汹涌的时代,Python语言以其简洁、易读和强大的功能库成为了编程界的翘楚。而算法,作为计算机科学的核心,是解决问题、优化性能的关键。本文将围绕Python语言与算法的结合&#…...

Python实现连连看7
3.3 根据地图显示图片 在获取了图片地图之后,就可以根据该图片地图显示图片了。显示图片的功能在自定义函数drawMap()中实现。 3.3.1 清除画布中的内容 在画布上显示图片之前,需要将画布中图1的启动界面内容清除,代码如下所示。 canvas.delete(all) 其中,delete()方法…...

C#中的as和is
在 C# 中,as 和 is 是用于类型转换和类型检查的操作符。 as 操作符: as 操作符用于尝试将一个对象转换为指定的引用类型或可空类型,如果转换失败,将返回 null。语法:expression as type示例: object obj &…...