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

Qt 编写插件plugin,支持接口定义信号

https://blog.csdn.net/u014213012/article/details/122434193?spm=1001.2014.3001.5506
本教程基于该链接的内容进行升级,在编写插件的基础上,支持接口类定义信号。

环境:Qt5.12.12 + MSVC2017

一、创建项目

  1. 新建一个子项目便于程序管理【文件->新建文件或项目->其它项目->子目录项目】
    在这里插入图片描述
    2.本次演示项目命名为【PluginProject】,然后指定目录
    在这里插入图片描述

3.指定构建套件
在这里插入图片描述

4.完成并添加应用程序项目
在这里插入图片描述

二、创建调试项目

1.做好上一个步骤后,会自动弹窗新建子项目【Application->Qt Widgets Application】
在这里插入图片描述

2.本次演示应用程序命名为【MainApp】默认mainwindow窗口类
在这里插入图片描述

在这里插入图片描述

3.创建完成的项目结构如下
在这里插入图片描述
4.【项目->关掉shadow build】免得后面找不到插件路径
在这里插入图片描述

三、创建插件项目

1.【PluginProject->右键->新子项目->Application->Qt Widgets Application】
在这里插入图片描述
在这里插入图片描述

  1. 项目名为【PluginApp】
    在这里插入图片描述

3.基类选择QWidget,基类改名为【PluginWidget】
在这里插入图片描述

4.完成后的项目结构
在这里插入图片描述

5.在PluginApp.pro中添加以下配置:

TARGET = PluginApp
TEMPLATE =lib                        #template改app为lib
CONFIG += plugin                     #增加plugin的配置
DESTDIR = ../MainApp/debug/plugin    #目标直接生成到MainApp的debug/plugin 

在这里插入图片描述

6.【PluginApp->右键->新增C++ Header File】命名为abstractinterface
在这里插入图片描述

在这里插入图片描述

7.abstractinterface是对外暴漏的接口类
类里的函数必须是纯虚的,使用Q_DECLARE_INTERFACE()宏向Qt的元对象系统声明该接口
为了能在接口暴漏信号,必须使用Q_OBJECT宏,因此该类需要继承QObject

#ifndef ABSTRACTINTERFACE_H
#define ABSTRACTINTERFACE_H#include <QtPlugin>
class QWidget;
class AbstractInterface:public QObject{Q_OBJECT
public:virtual ~AbstractInterface(){}  //必须定义虚析构函数virtual QWidget* createWidgetPlugin(QWidget *parent)  = 0;
signals:virtual void printMessage(QString text)  = 0;
};
#define AbstarctInterface_IID "qt.org.com.abstactinterface/1.0"   //iid随便命名当前项目独一无二即可
Q_DECLARE_INTERFACE(AbstractInterface,AbstarctInterface_IID)      //声明接口#endif // ABSTRACTINTERFACE_H

在这里插入图片描述

8.【PluginApp->右键->新增C++ Class】命名为InterfaceImplement
在这里插入图片描述

在这里插入图片描述

9.InterfaceImplement是接口实现类,继承AbstractInterface。
使用Q_INTERFACES()宏告诉Qt的元对象系统有关接口的信息
使用Q_PLUGIN_METADATA ()宏导出插件。

#ifndef INTERFACEIMPLEMENT_H
#define INTERFACEIMPLEMENT_H#include"abstractinterface.h"class InterfaceImplement:public AbstractInterface
{
public:Q_OBJECTQ_INTERFACES(AbstractInterface)                              //实现的接口Q_PLUGIN_METADATA(IID AbstarctInterface_IID)                 //导出接口后面的 FILE “jsonfile”可省略
public:explicit InterfaceImplement();~InterfaceImplement() override;QWidget *createWidgetPlugin(QWidget *parent) override;
signals:void printMessage(QString text)  override;
};
#endif // INTERFACEIMPLEMENT_H

在这里插入图片描述

10.双击PluginWidget.ui,拖个按钮
在这里插入图片描述
14.右键PushButton->转到槽->选择clicked()->OK
在这里插入图片描述
15. PluginWidget.h添加:

signals:void sendMessage(QString text);

在这里插入图片描述

  1. PluginWidget.cpp的on_pushButton_clicked槽函数内,发射”hello world字符:
emit sendMessage("hello world");

在这里插入图片描述

17.在InterfaceImplement.cpp实现createWidgetPlugin,返回PluginWidget的实例,转发信号

#include "interfaceimplement.h"
#include "pluginwidget.h"
InterfaceImplement::InterfaceImplement()
{}InterfaceImplement::~InterfaceImplement()
{}QWidget *InterfaceImplement::createWidgetPlugin(QWidget *parent) 
{PluginWidget * w = new PluginWidget(parent);     //返回PluginApp实现的界面类connect(w, &PluginWidget::sendMessage, this, [this](const QString &text) {emit printMessage(text);});return w;
}

在这里插入图片描述

四.调试插件

1.MainApp.pro添加抽象类所在的目录:

QT       += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11
INCLUDEPATH += ../PluginApp            #包含PluginApp创建的HandEyePositionInterface.hSOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \mainwindow.ui

在这里插入图片描述
2.右键MainApp->执行qmake
在这里插入图片描述

3.点开MainApp下的mainwindow.h,使用QPluginLoader加载插件.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
class AbstractInterface;QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();
private slots:void print(QString text);
private:Ui::MainWindow *ui;
private:void loadPlugin();              //应用程序装载插件
private:AbstractInterface *mainInterface;      //插件类
};
#endif // MAINWINDOW_H

在这里插入图片描述

4.在loadPlugin()中加载插件。注意连接插件信号的connect要用SIGNAL() SLOT()的方式连

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "abstractinterface.h"
#include <QDir>
#include <QPluginLoader>     //包含插件装载头文件
#include <QVBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);loadPlugin();
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::print(QString text)
{qDebug() << "Received message from plugin:" << text;
}void MainWindow::loadPlugin()
{//插件存放在应用程序目录下的plugin目录下,便于管理QDir pluginDir(qApp->applicationDirPath());  //定位应用程序目录if( pluginDir.cd("plugin") )                 //进入plugin,前提先创建plugin目录{for(auto fileName:pluginDir.entryList(QDir::Files))  //遍历目录下的文件{QFileInfo info(fileName);if(info.completeSuffix() == "dll" || info.completeSuffix() == "so")  //过滤后缀为dll或so的动态库文件{QPluginLoader load(pluginDir.absoluteFilePath(fileName));        //装载插件QObject *pluginObj = load.instance();                            //获取插件根对象if(pluginObj){mainInterface = qobject_cast<AbstractInterface *>(pluginObj);  //转换QWidget*w =mainInterface->createWidgetPlugin(this);connect(mainInterface, SIGNAL(printMessage(QString)), this, SLOT(print(QString)));//注意,使用字符串连接的方式,避免编译阶段进行类型检查if (!ui->centralwidget->layout()){ui->centralwidget->setLayout(new QVBoxLayout());}ui->centralwidget->layout()->addWidget(w);  // 将插件小部件添加到 centralwidget 的布局中}}}}}

5.运行后点按钮,看输出效果
在这里插入图片描述

五.其他说明

如果插件类和实现类用到了三方库,以本项目为例,PluginApp.pro添加了三方库3rdParty.pri。那么在调试类,也需要包含同样的三方库,也就是本项目的MainApp.pro也需要包含这个3rdParty.pri,才能正常使用。

相关文章:

Qt 编写插件plugin,支持接口定义信号

https://blog.csdn.net/u014213012/article/details/122434193?spm1001.2014.3001.5506 本教程基于该链接的内容进行升级&#xff0c;在编写插件的基础上&#xff0c;支持接口类定义信号。 环境&#xff1a;Qt5.12.12 MSVC2017 一、创建项目 新建一个子项目便于程序管理【…...

Qt中 QWidget 和 QMainWindow 区别

QWidget 用来构建简单窗口 QMainWindow 用来构建更复杂的窗口&#xff0c;QMainWindow 继承自QWidget&#xff0c;在QWidget 的基础上提供了菜单栏、工具栏、状态栏等功能 菜单栏&#xff08;QMenuBar&#xff09;工具栏&#xff08;QToolBar&#xff09;状态栏&#xff08;Q…...

Kafka集群中数据的存储是按照什么方式存储的?

1&#xff09;Topic 数据的存储机制 Topic是逻辑上的概念&#xff0c;而partition是物理上的概念&#xff0c;每个partition对应于一个log文件&#xff0c;该log文件中存储的就是Producer生产的数据。Producer生产的数据会被不断追加到该log文件末端&#xff0c;为防止log文件…...

中断的硬件框架

往期内容 本专栏往期内容&#xff0c;interrtupr子系统&#xff1a; 深入解析Linux内核中断管理&#xff1a;从IRQ描述符到irq domain的设计与实现Linux内核中IRQ Domain的结构、操作及映射机制详解中断描述符irq_desc成员详解Linux 内核中断描述符 (irq_desc) 的初始化与动态分…...

数据备份策略:企业防御的关键

数据备份是保护数据免受网络攻击的重要步骤。在从恶意软件或勒索软件攻击中恢复时&#xff0c;公司可以使用保存的备份将其恢复到之前的状态。但是&#xff0c;为了确保数据的完全安全&#xff0c;任何公司的备份策略都应该在其总体策略中包含多种解决方案。 根据关于创建、消…...

Baget 私有化nuget

Baget下载 1、下载运行 方法一&#xff1a;cmd运行 dotnet BaGet.dll --urls http://*:8002 http://localhost:8002 方法二&#xff1a;bat脚本运行Baget 创建Start.bat dotnet BaGet.dll --urls http://*:8002 运行Start.bat 方法三&#xff1a;部署成Window服务 NSSM部…...

前端函数的参数都有哪些?

在前端开发中&#xff0c;函数的分类可以根据不同的标准进行。以下是一些常见的函数分类方式&#xff0c;并附有相应的例子&#xff1a; 按传递方式分类&#xff1a; 按值传递&#xff1a;JavaScript 中的基本类型&#xff08;如数字、字符串、布尔值&#xff09;都是按值传递的…...

【CSS】什么是BFC?

块级格式化上下文&#xff08;Block Formatting Context&#xff0c;简称BFC&#xff09;是CSS布局中的一种重要概念&#xff0c;它决定了块级盒子如何在其容器内排列&#xff0c;以及浮动元素对其周围元素的影响。理解BFC可以帮助解决许多常见的网页布局问题&#xff0c;比如清…...

HCIP小型园区网拓扑实验

1.拓扑以及需求 2.需求分析 需要的核心技术 1、虚拟局域网&#xff08;VLAN&#xff09; 2、链路聚合&#xff08;E-trunk&#xff09; 3、多生成树协议&#xff08;MSTP&#xff09; 4、VLANIF三层逻辑接口 5、虚拟路由冗余协议&#xff08;VRRP&#xff09; 6、动态主…...

GRR测量系统的重复性和再现性

GRR&#xff08;GaugeRepeatabilityandReproducibility&#xff09;即测量系统的重复性和再现性&#xff0c;是用于评估测量系统性能的一个重要指标。以下是对GRR的详细解释&#xff1a; 一、定义 • 重复性&#xff08;Repeatability&#xff09;&#xff1a;在相同条件下&…...

133.鸿蒙基础01

鸿蒙基础 1.自定义构建函数1. 构建函数-[Builder ](/Builder )2. 构建函数-传参传递(单向)3. 构建函数-传递参数(双向)4. 构建函数-传递参数练习5. 构建函数-[BuilderParam ](/BuilderParam ) 传递UI 2.组件状态共享1. 状态共享-父子单向2. 状态共享-父子双向3. 状态共享-后代组…...

科技查新小知识

首先科技查新是什么&#xff1f; 科技查新是文献检索和情报调研相结合的情报研究工作&#xff0c;它以文献为基础&#xff0c;以文献检索和情报调研为手段&#xff0c;以检出结果为依据&#xff0c;通过综合分析&#xff0c;对查新项目的新颖性进行情报学审查&#xff0c;写出有…...

docker安装portainer

1、拉取镜像 docker pull portainer/portainer-ce:latest2、执行 docker run -d --restartalways --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /data/portainer/data:/data -v /data/portainer/public:/public portainer/portain…...

【Word2Vec】传统词嵌入矩阵训练方法

目录 1. Word2Vec 简介2. Word2Vec 的训练方法2.1 Skip-Gram模型2.2 CBOW&#xff08;Continuous Bag of Words&#xff09;模型 3. Word2Vec 中的词嵌入表示4. 训练过程中是否使用独热编码&#xff1f; 1. Word2Vec 简介 Word2Vec 是一种词嵌入模型&#xff0c;主要通过无监督…...

电脑不显示wifi列表怎么办?电脑不显示WiF列表的解决办法

有用户会遇到电脑总是不显示wifi列表的问题&#xff0c;但是不知道要怎么解决。随着无线网络的普及和使用&#xff0c;电脑无法显示WiFi列表的问题有时会让人感到困扰。电脑不显示WiFi列表是很常见的问题&#xff0c;但这并不意味着你无法连接到网络。不用担心&#xff0c;这个…...

详解 Dockerfile:从入门到实践

Docker 是一个开源的应用容器引擎&#xff0c;它允许开发者将应用及其依赖包打包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器或 Windows 机器上&#xff0c;也可以实现虚拟化。Dockerfile 是一个文本文件&#xff0c;其中包含了一系列命令&#xff0c;用…...

随机变量的概率分布

第 5 章——概率分布 5.2 随机变量的概率分布 【例5-1】 计算期望值、方差、标准差 【代码框5-1】 计算期望值、方差、标准差 import pandas as pd import numpy as np example5_1 = pd.read_csv(./pydata/example/chap05/example5_1.csv)# 计算期望值 mymean = sum...

Kafka生产者如何提高吞吐量?

批量发送&#xff1a;生产者可以配置 batch.size 参数&#xff0c;将多个消息打包成一个批次发送。这样可以减少网络通信的次数&#xff0c;提高吞吐量。inger.ms&#xff1a;设置 linger.ms 参数&#xff0c;可以让生产者在发送消息前等待一段时间&#xff0c;以便收集更多的消…...

mysql:解决windows启动失败无报错(或长时间未响应)

前言 遇到好多次在修改配置文件后&#xff0c;mysql无法启动的问题了&#xff0c;这里给出一个可能原因的解决方案。 由于mysql需要修改配置文件&#xff0c;所以我在winserver2012服务器上更改了配置文件my.ini mysql5.7配置文件默认地址&#xff1a;C:\ProgramData\MySQL\MyS…...

【山——回文判断】

题目 代码 #include <bits/stdc.h> using namespace std; bool check(int num) {string s to_string(num);int l 0, r s.size() - 1;while (l < r){if (l && s[l] - s[l - 1] < 0)return false;if (s[l] ! s[r--])return false;}if (l && l r…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...