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_OBJECT
    Q_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 gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
INCLUDEPATH += ../PluginApp            #包含PluginApp创建的HandEyePositionInterface.h


SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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,才能正常使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915670.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python】python使用虚拟环境

使用虚拟环境的好处是创建一个独立干净的环境 首先cd到新项目的目录下 创建虚拟环境 使用日期命名方便自己找到版本 python -m venv venv20241114激活虚拟环境 .\venv20241114\Scripts\activate会创建一个文件夹 点进去可以看到是python的脚本所存文件结构 纯净环境 p…

CSS 技巧:如何让 div 完美填充 td 高度

引言 一天哈比比突然冒出一个毫无理头的一个问题: 本文就该问题进行展开… 原文链接: 昆仑虚F2E 一、需求说明 大致需求如下, 当然这里做了些简化 有如下初始代码: 一个自适应的表格每个单元格的宽度固定 200px每个单元格高度则是自适应每个单元格内是一个 div 标签, div 标签…

跟上AI的浪潮

现在AI技术已广泛应用至语音助手、写作、绘图、视频&#xff0c;甚至是各种语言的代码编写。平常我们都是应用别人开发好的模型&#xff0c;或者说智能体&#xff0c;那么我们自己能否做那个开发AI智能体的人&#xff0c;近期加了一个AI学习的大社区&#xff0c;几万在AI道路上…

专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结

目录 动态规划 动态规范五步走&#xff1a; 1. 第 N 个泰波那契数&#xff08;easy&#xff09; 解析&#xff1a; 1.状态表达式&#xff1a; 2.状态转移方程&#xff1a; 3.初始化&#xff1a; 4.填表顺序&#xff1a; 5.返回值 编写代码&#xff1a; 总结&#xff…

MySQL技巧之跨服务器数据查询:基础篇-更新语句如何写

MySQL技巧之跨服务器数据查询&#xff1a;基础篇-更新语句如何写 上一篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的连接名: MY_ODBC_MYSQL 以及用同样的…

C/C++语言 多项式加法和乘法

多项式加法和乘法 多项式的加法题目描述输入输出样例步骤代码段全局变量设定新建结点合并链表 完整代码 多项式乘法题目描述输入输出样例代码段计算两多项式结果输入 完整代码 多项式的加法 题目描述 输入输出 样例 步骤 总体思想是用链表来做 ① 我们发现输入样例中&#xf…

ArkTs面向对象编程

ArkTs面向对象编程 1.1 面向对象编程概述 1.1.1 什么是面向对象编程 面向对象编程是一种编程范式&#xff0c;它使用“对象”来设计软件和创建可重用的程序设计 对象是包含数据和方法的实体&#xff0c;可以与其他对象进行交互 面相对象编程鼓励使用已有的对象来组合或修改以…

乳腺癌诊断分析——基于聚类分析实现

一、研究背景 乳腺癌属于恶性肿瘤&#xff0c;在早期发现后需要及早将病变组织切除&#xff0c;而且术后还要化疗和放射等辅助治疗&#xff0c;能够抑制癌细胞的扩散和增长。 二、研究目的 研究乳腺癌病人的患病特征通过聚类分析方法对特征进行分类通过上述聚类结果对乳腺诊…

丹摩征文活动|FLUX.1 和 ComfyUI:从部署到上手,轻松驾驭!

FLUX.1 和 ComfyUI&#xff1a;从部署到上手&#xff0c;轻松驾驭&#xff01; FLUX.1历史曲线 黑森林实验室推出了一款名为FLUX.1的先进图像生成模型&#xff0c;根据不同用户需求&#xff0c;提供了三种独特的版本。 FLUX.1-pro&#xff1a;作为专为企业打造的强大闭源版本…

数据分析:16s差异分析DESeq2 | Corncob | MaAsLin2 | ALDEx2

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍DESeq2原理计算步骤结果Corncob原理计算步骤结果MaAsLin2原理计算步骤结果ALDEx2原理计算步骤结果加载R包数据链接数据预处理微生物数据样本信息提取物种名称过滤零值保留结果读取…

OCR识别铁路电子客票

随着中国铁路客运领域进入全面数字化时代&#xff0c;国家税务总局、财政部和国铁集团于2024年10月18日联合发布公告&#xff0c;自2024年11月1日起&#xff0c;推广使用“电子发票&#xff08;铁路电子客票&#xff09;”。这一举措不仅为旅客出行提供了极大的便利&#xff0c…

【MySQL基础刷题】总结题型(三)

十题左右&#xff0c;便于复习 1.查询结果的质量和占比2.每月交易I3.销售分析III4.只出现一次的最大数字5.买下所有产品的客户6.员工的直属部门7.指定日期的产品价格 1.查询结果的质量和占比 avg大神啊… SELECT query_name, ROUND(avg(rating / position), 2) as quality, …

python 同时控制多部手机

在这个智能时代,我们的手机早已成为生活和工作中不可或缺的工具。无论是管理多个社交媒体账号,还是处理多台设备上的事务,如何更高效地控制多个手机成为了每个人的痛点。 今天带来的这个的软件为你提供了一键控制多部手机的强大功能。无论是办公、娱乐,还是社交,你都能通过…

外星人入侵

学习于Python编程从入门到实践&#xff08;Eric Matthes 著&#xff09; 整体目录&#xff1a;外星人入侵文件夹是打包后的不必在意 图片和音效都是网上下载的 音效下载网站&#xff1a;Free 游戏爆击中 Sound Effects Download - Pixabay 运行效果&#xff1a;可以上下左右移…

前端监控与埋点 全总结

一、概念 前端埋点是指在网页或者应用程序中插入特定的代码&#xff0c;用于收集用户的行为数据并发送给服务器进行分析。这些数据可以包括用户的点击、浏览、输入等操作&#xff0c;帮助开发者了解用户的在其网站中的行为&#xff0c;从而进行针对性的优化和改进。 前端埋点…

Python简单文件操作day9

1、文件操作的重要性和场景 重要性&#xff1a; 数据持久化、跨平台兼容性、数据备份与恢复、数据共享、配置管理、日志记录 应用场景&#xff1a; 数据分析、web开发、文本处理 2、文件的概念 文件是一个存储在某种持久性存储介质【硬盘、光盘、磁盘等】上的数据的结合。 …

指令存储和指令流水线

要求存储器的编址单位&#xff0c;首先观察到计算机采用的是32位定长指令字&#xff0c;因此一条指令就是32位&#xff0c;即4B&#xff0c;根据表中可知一条指令所占地址空间为08048104H-08048100H4H&#xff0c;因此所用的编制单位为字节&#xff08;B&#xff09; 将所有指令…

kafka管理工具

文章目录 前言一、Kafka Assistan1.1 描述1.2、配置安装 二、Conduktor2.1、描述2.2、配置安装 三、kafka-maneger3.1、描述3.2、配置安装3.3、命令启动3.4、[refer to](https://www.ctyun.cn/document/10000120/10033218#section-39755766f4910e4b) 前言 提示&#xff1a;这里…

JavaWeb常见注解

1.Controller 在 JavaWeb 开发中&#xff0c;Controller是 Spring 框架中的一个注解&#xff0c;主要用于定义控制器类&#xff08;Controller&#xff09;&#xff0c;是 Spring MVC 模式的核心组件之一。它表示该类是一个 Spring MVC 控制器&#xff0c;用来处理 HTTP 请求并…

axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)

fetch 是啥&#xff1f; fetch 函数是 JavaScript 中用于发送网络请求的内置 API&#xff0c;可以替代传统的 XMLHttpRequest。它可以发送 HTTP 请求&#xff08;如 GET、POST 等&#xff09;&#xff0c;并返回一个 Promise&#xff0c;从而简化异步操作 基本用法 /* 下面是…