C++ Qt插件开发样例

参考链接:
1、【QT】Qt Plugin开发
2、Qt 插件管理器机制
3、插件间通信
4、B站:Qt编程保姆级实战教程第七集插件

一些参考代码链接:
git地址:https://github.com/keiler2018/QtPluginManagerTest
基于Qt插件实现的项目:https://github.com/nitroshare/nitroshare-desktop
Qt 插件框架:https://gitee.com/penghongbin/QFrameWork


万事先问个为什么,为什么要学习QT插件开发?
QT插件开发很重要,可以实现代码解耦合,当一个界面程序功能模块很多的时候,如果都放在主程序编译会很耗时,代码量过多,维护困难;另外对于团队共同在一个工程里开发一个程序,会涉及到代码合并,每个开发者代码水平不一样,代码都写在一起,逐渐变成“屎山”等诸多问题。所以通过QT插件开发可以更好的分工和统一管理代码一致性,不同的人负责不同模块封装开发,本来这个博客在2024年12月份就开始写了,那时候各种测试也都通过了,后来一个事情打断了,所以导致这个事情一直搁置,现在捡起来是因为想对目前的程序重构,更换成界面+插件方式实现,至少目前还认为这个结构会更好,下面开干吧。

一个插件应该包含一个公共的接口头文件、插件DLL开发、主框架App开发

1、公共接口头文件

接口头文件会在主程序框架和插件中共同引用,其主要就是一个*.h的头文件

样例为:PluginInterface.h

#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QWidget>
//接口里面都是纯虚函数,在插件动态库中要一个个具体实现,这里可以暴露自己需要的接口,这里样例只是调用出QWidget的插件窗口

#define PluginInterfaceIID "com.xxx.JX/1.0.0"//定义接口iid: 组织机构域名 + 产品名 + 版本号
class PluginInterface
{
public:
    virtual ~PluginInterface()=default;
    virtual QWidget * genWidget()=0;
};

//声明接口,与iid进行关联
Q_DECLARE_INTERFACE(PluginInterface,PluginInterfaceIID)

#endif // PLUGININTERFACE_H

2、插件dll开发

2.1 新建插件dll工程(PluginDll_2工程)

选择Library
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2、plugindll_2.h 代码部分调整

第一步将PluginInterface.h公共头文件拷贝到项目同级目录
第二步修改plugindll_2.h,主要修改:
1、include公共头文件PluginInterface.h,(重点)
2、修改继承,继承父类 QObject ,PluginInterface(重点)
3、新增类声明、接口声明、插件声明(重点)
4、重构所有接口文件中的方法(重点)
5、开发插件的QWidget,这里只是简单实现一下样例(插件功能部分)

原始代码

#ifndef PLUGINDLL_2_H
#define PLUGINDLL_2_H

#include "PluginDll_2_global.h"

class PLUGINDLL_2_EXPORT PluginDll_2
{
public:
    PluginDll_2();
};

#endif // PLUGINDLL_2_H

修改后的代码

#ifndef PLUGINDLL_2_H
#define PLUGINDLL_2_H

#include "PluginDll_2_global.h"
#include "PluginInterface.h"//1、引入插件公共头文件


class PLUGINDLL_2_EXPORT PluginDll_2:public QObject,public PluginInterface//2、修改继承
{
    //3、类声明、接口声明、插件声明
    Q_OBJECT
    Q_PLUGIN_METADATA(IID PluginInterfaceIID)
    Q_INTERFACES(PluginInterface)

public:
    PluginDll_2();
    ~PluginDll_2();
    QWidget * genWidget() override;//4、重写接口文件中的方法,返回成员变量

private:
    QWidget *m_MyWidget;//要弹出来的实际窗口

};

#endif // PLUGINDLL_2_H

2.4、插入一个自定义的窗口对象

这部分主要是为成员变量QWidget *m_MyWidget的实例化来做准备,也是插件开发的实际业务部分
插入一个QT设计师界面类,自定义类名:MyJX_Widget
在这里插入图片描述
在这里插入图片描述
为了简化区分,在Widget窗口中拖入一个按钮,做一个标识,这个插件就开发完了
在这里插入图片描述

2.5、plugindll_2.cpp 代码部分调整

在cpp中主要是new一个自定义窗口,用MyJX_Widget类实例化成员变量m_MyWidget,在调用接口方法的时候将m_MyWidget返回出去即可。

#include "plugindll_2.h"
#include "myjx_widget.h"


PluginDll_2::PluginDll_2()
{
    this->m_MyWidget = Q_NULLPTR;//初始化
}

PluginDll_2::~PluginDll_2()
{
    if(this->m_MyWidget){//释放
        this->m_MyWidget->deleteLater();
    }
}

QWidget *PluginDll_2::genWidget()
{
    if(!this->m_MyWidget)
    {
        this->m_MyWidget = new MyJX_Widget();//新建窗口对象,这里是实际业务窗口,继承了QWidget为父类
    }
    return this->m_MyWidget;
}

3、主框架app开发

主框架app程序包含加载插件和卸载插件两部分,当然如果有很多插件可以写一个界面进行管控插件加载位置,并对所有插件进行使用txt文件存储,需要的时候可以手工更改txt对应信息,可让插件加载到软件不同位置。
下面我这里主要讲解如何加载一个插件和卸载一个插件,有了这个思路,后面可以借助AI完成多个插件的集成开发;
项目中包含公共接口头文件(PluginInterface.h,前面列出了)、一个设计师类(widget.cpp、widget.h、widget.ui)、一个主程序入口(main.cpp)

main.cpp文件

主程序入口比较常规

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget设计师类

ui文件如下,主要为加载插件按钮、卸载按钮、一个QTabWidget窗口显示位置
在这里插入图片描述
widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPluginLoader>//插件引进类


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();


    void LoadSigPlugin(QPluginLoader& PluginLoad,QString PluginPath,QString TitleName);//加载一个指定插件,对插件加载位置进行控制

private slots:
    void on_btn_LoadPlugin_clicked();//加载插件
    void on_btn_UnLoadPlugin_clicked();//卸载插件

private:
    Ui::Widget *ui;
    QPluginLoader m_loader ;//插件对象,为了便于加载和卸载、写进成员变量
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
#include "ui_widget.h"
#include "PluginInterface.h"
#include <QDir>
#include <QMessageBox>
#include <QDebug>



Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
    if(m_loader.isLoaded())
    {
        m_loader.unload();//卸载插件
    }
}


void Widget::LoadSigPlugin(QPluginLoader& PluginLoad,QString PluginPath,QString TitleName)
{//加载一个插件到ui->tabWidget对象中去

    if(PluginLoad.isLoaded())
    {//如果加载了,先卸载掉,否则多次点击加载插件按钮,则插件卸载会有异常
        PluginLoad.unload();//卸载插件
    }

    PluginLoad.setFileName(PluginPath);
    if(!PluginLoad.load())
    {//如果插件加载失败
        QMessageBox::critical(this,"",PluginLoad.errorString());//弹出不能加载的原因
        return;
    }

    //获取插件元数据
    QJsonObject metaData = PluginLoad.metaData();//获取元数据
    qDebug() << "Plugin metadata:" << metaData;//输出元数据信息



    PluginInterface * pi = qobject_cast<PluginInterface*>(PluginLoad.instance());//实例化插件
    if(pi)
    {
        QWidget* w = pi->genWidget();
        if(w){
            this->ui->tabWidget->addTab(w,TitleName);
        }
    }

}

void Widget::on_btn_LoadPlugin_clicked()
{
    QDir dir(qApp->applicationDirPath());//程序执行文件夹
    QString PluginPath = dir.filePath("PluginDll_2.dll");
//    m_loader.setFileName(dir.filePath("PluginDll_2.dll"));


    LoadSigPlugin(m_loader,PluginPath,"TestDll2");//加载一个插件到软件中

 /*
    if(m_loader.isLoaded())
    {//如果加载了,先卸载掉
        m_loader.unload();//卸载插件
    }

    m_loader.setFileName(PluginPath);
    if(!m_loader.load())
    {//如果插件加载失败
        QMessageBox::critical(this,"",m_loader.errorString());//弹出不能加载的原因
        return;
    }

    //获取插件元数据
    QJsonObject metaData = m_loader.metaData();//获取元数据
    qDebug() << "Plugin metadata:" << metaData;//输出元数据信息



    PluginInterface * pi = qobject_cast<PluginInterface*>(m_loader.instance());//实例化插件
    if(pi)
    {
        QWidget* w = pi->genWidget();
        if(w){
            ui->tabWidget->addTab(w,"hello plugin");
        }
    }
*/

}

void Widget::on_btn_UnLoadPlugin_clicked()
{
    if(m_loader.isLoaded())
    {
        m_loader.unload();//卸载插件
    }
}

4、效果展示

原始主程序

在这里插入图片描述

点击加载插件按钮

在这里插入图片描述

切换插件

在这里插入图片描述

卸载插件

在这里插入图片描述

### 关于Visual Studio 2022中的Qt项目 在开发环境中集成Qt框架并创建示项目是一个常见的需求。以下是关于如何在Visual Studio 2022中查找和配置Qt项目的详细说明。 #### 配置Qt与Visual Studio 2022 为了在Visual Studio 2022中使用Qt,通常需要安装Qt Visual Studio Tools插件。该工具允许开发者轻松地将Qt库集成到Visual Studio项目中[^1]。完成插件安装后,可以通过以下方式访问示代码: 1. **通过Qt Creator导入示** Qt官方提供了大量的示项目,这些项目可以在Qt Creator中找到。虽然这是另一种IDE,但其示可以被导出并在Visual Studio中打开[^2]。 2. **手动下载示代码** 访问[Qt官方网站](https://doc.qt.io/qt-6/examples.html),这里列出了各种功能模块的示代码。可以选择适合的功能模块(如GUI、网络编程等),并将源码复制到Visual Studio项目中进行编译和运行[^3]。 #### 设置环境变量 如果计划直接在命令行或脚本文件中构建Qt应用程序,则需设置相应的环境变量。如,在Linux环境下可能涉及`VULKAN_SDK`路径或其他依赖项的定义[^4]。对于Windows平台上的Visual Studio,类似的步骤如下所示: ```batch set QTDIR=C:\Qt\5.x.x\msvc2019_64 set PATH=%QTDIR%\bin;%PATH% ``` 上述代码片段展示了如何指定Qt安装目录以及更新系统的可执行文件搜索路径。这一步骤确保了编译器能够定位必要的头文件和链接库。 #### 创建简单的Makefile示 尽管Visual Studio主要依靠解决方案(.sln)和项目文件(.vcxproj),了解基本的Makefile结构仍然有助于理解跨平台构建过程。下面提供了一个简化版的Makefile用于演示目的: ```makefile hello: main.o age.o g++ main.o age.o -o hello main.o: main.cpp g++ -c main.cpp age.o: age.cpp g++ -c age.cpp clean: rm *.o hello ``` 此Makefile描述了一种两步法来生成最终的目标程序——先分别编译各个源文件为目标文件,再把这些目标文件连接成完整的二进制文件。 --- ### 提供的相关代码 假设要实现一个基础窗口界面应用,可以参考以下C++代码作为起点: ```cpp #include <QApplication> #include <QWidget> int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; window.resize(250, 150); window.setWindowTitle("Simple Example"); window.show(); return app.exec(); } ``` 以上代码展示的是最基本的Qt GUI应用程序模板。它包含了启动事件循环所需的必要组件,并设置了初始窗口大小及其标题栏文字。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值