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

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


http://www.kler.cn/a/390369.html

相关文章:

  • Springboot集成syslog+logstash收集日志到ES
  • 【Java SE】接口类型
  • 常用中间件介绍
  • CLion配置QT开发环境
  • Java-Redisson分布式锁+自定义注解+AOP的方式来实现后台防止重复请求扩展
  • STM32学习笔记------GPIO介绍
  • 【日志】力扣167.两数之和2 - 输入有序数组 // Unity——Roll A Ball(一)
  • diboot低代码中使用junit测试controller,入参不生效问题解决
  • Java学习教程,从入门到精通,Java修饰符语法知识点及案例代码(23)
  • openlayers实现图层裁剪,只展示关心区域,抹掉无关区域,“抠”地图
  • ARM64环境使用docker-compose进行ElasticSearch8集群部署
  • Scala中的可变Map操作:简单易懂指南 #Scala Map #Scala
  • CTF 入门指南:从零开始学习网络安全竞赛
  • 数据结构 栈和队列
  • kafka面试题解答(四)
  • 软件测试学习记录 Day1
  • Mysql中数据添加,修改,删除
  • python实战(七)——基于LangChain的RAG实践
  • Simulink对仿真数据进行FFT频谱分析
  • Unity中IK动画与布偶死亡动画切换的实现
  • 【学习记录丨UVM】2.1uvm_component 与uvm_object
  • 人到一定年纪,要学会远离多巴胺
  • 群控系统服务端开发模式-应用开发-前端框架
  • 必应 Bing 国内广告开户及代运营服务的优势有哪些?
  • UE5.3 CineCameraRigRail组件实测
  • 实现3D热力图