Qt创建插件及使用

news2024/12/24 1:02:01

        本文使用“Qt Creator 6.0.1”和“Qt 6.2.2”完成插件创建及使用,主要有如下步骤:(1)创建子目录项目MyProject;(2)在子目录项目中创建应用程序项目MyApp;(3)在子目录项目中创建插件项目MyPlugin;(4)在项目MyApp中添加接口文件“MyInterfaces.h”;(5)在接口文件“MyInterfaces.h”中添加接口AnimalInterface;(6)在项目MyPlugin中实现接口;(7)在项目MyApp中使用插件。

1. 创建子目录项目MyProject

        在Qt Creator的主界面点击“文件”--“新建文件或项目”,在弹出的对话框中选择“其他项目”--“子目录项目”,如下图所示:

点击“Choose...”,指定项目名称(指定为MyProject)、项目创建路径(D:\QtTests),如下图所示:

点击“下一步”,在弹出的对话框中指定Kits(构建套件)(我选择的是“Desktop (x86_windows_msvc2019_pe_64bit) ”)

点击“下一步”

点击“完成& 添加子项目”按钮,完成子目录项目MyProject的创建。

紧接着,Qt Creator会自动弹出“新建子项目”对话框,这就进入了下一个步骤“在子目录项目中创建应用程序MyApp”

2. 在子目录项目中创建应用程序项目MyApp

在“新建子项目”对话框中选择“Application”--“Qt Widgets Application”

点击“Choose...”,指定项目名称(指定为MyApp)、项目创建路径(D:\QtTests),如下图所示:

点击“下一步”,指定构建系统(Build System)(我指定为qmake),

点击“下一步”,

无需修改,点击“下一步”

无需修改,点击“下一步”

我选择的是“Desktop (x86_windows_msvc2019_pe_64bit) ”,点击“下一步”

点击“完成”按钮,完成应用程序项目MyApp的创建。

3. 在子目录项目中创建插件项目MyPlugin

右键单击子目录项目MyProject,在弹出菜单中点击“新子项目...”

在弹出的对话框中选择“Library”--“C++ Library”

点击“Choose...”按钮,在弹出的对话框中指定名称(MyPlugin)和创建路径(D:\QtTests\MyProject),

点击“下一步”,指定构建系统(Build System)(我指定为qmake),

点击“下一步”,指定类型为“Qt Plugin”

点击“下一步”

点击“下一步”

点击“下一步”

点击“完成”按钮,完成插件项目MyPlugin的创建。

为了使得项目能够正常编译 ,可将函数GenericPlugin::create中的代码“static_assert(false, "You need to implement this function");”暂时修改为“return nullptr;”。

4. 在项目MyApp中添加接口文件“MyInterfaces.h”

右键单击项目MyProject,在弹出菜单中点击“Add New...”

在弹出的对话框中选择“C/C++”--“C/C++ Header File”

点击按钮“Choose...”,在弹出的对话框中指定文件名(“MyInterfaces.h”)、路径(“D:\QtTests\MyProject\MyApp”)

点击“下一步”

点击“完成”按钮,完成接口文件“MyInterfaces.h”的添加。Qt Creator会自动修改文件“MyApp.pro”,将HEADERS修改为:

HEADERS += \
    MyInterfaces.h \
    mainwindow.h

5. 在接口文件“MyInterfaces.h”中添加接口AnimalInterface

AnimalInterface即动物接口,该接口中就一个方法cry,这里cry意思是叫,大多数动物都会叫。

添加接口AnimalInterface的定义后,文件“MyInterfaces.h”的内容如下:

#ifndef MYINTERFACES_H
#define MYINTERFACES_H

#include <QtPlugin>

class AnimalInterface{
public:
    virtual ~AnimalInterface() = default;
    virtual int cry() = 0;
};

QT_BEGIN_NAMESPACE
#define AnimalInterface_iid "MyPlugin.AnimalInterface/1.0"
Q_DECLARE_INTERFACE(AnimalInterface, AnimalInterface_iid)

QT_END_NAMESPACE

#endif // MYINTERFACES_H

类AnimalInterface声明了一个纯虚函数cry(当然也可以声明更多的纯虚函数)。

该类还有一个虚拟析构函数。接口类通常不需要这样的析构函数(因为通过指向接口的指针删除实现接口的对象没有什么意义),但一些编译器会对声明虚拟函数但没有虚拟析构函数的类发出警告。我们提供析构函数来让这些编译器满意(即不发出警告)。(这段话来自参考资料[6],原文是: The class also has a virtual destructor. Interface classes usually don't need such a destructor (because it would make little sense to delete the object that implements the interface through a pointer to the interface), but some compilers emit a warning for classes that declare virtual functions but no virtual destructor. We provide the destructor to keep these compilers happy.)

‌QT_BEGIN_NAMESPACE的主要作用是定义一个命名空间,以避免不同库中的标识符冲突。通过使用QT_BEGIN_NAMESPACE和QT_END_NAMESPACE宏,Qt框架将所有的类和函数封装在一个特定的作用域内,从而防止它们与全局标识符或其他库中的标识符发生冲突。

为了在运行时可以查询某插件是否实现了给定的接口,我们必须使用Q_DECLARE_INTERFACE()宏。该宏的第一个参数是接口的名称;第二个参数是一个以唯一方式标识接口的字符串。按照惯例,我们使用“Java包名”语法来标识接口。若在后期更改接口,则必须使用不同的字符串来标识新接口;否则,应用程序可能会崩溃。因此,如上述代码所示,在字符串中加入版本号是一个好主意。(这段话来自参考资料[6],原文是:To make it possible to query at run-time whether a plugin implements a given interface, we must use the Q_DECLARE_INTERFACE() macro. The first argument is the name of the interface. The second argument is a string identifying the interface in a unique way. By convention, we use a "Java package name" syntax to identify interfaces. If we later change the interfaces, we must use a different string to identify the new interface; otherwise, the application might crash. It is therefore a good idea to include a version number in the string, as we did above.)

6. 在项目MyPlugin中实现接口

为了便于在项目MyPlugin的代码中找到接口定义,在文件“MyPlugin.pro”添加如下代码:

INCLUDEPATH += ../MyApp
DESTDIR = ../MyApp/Plugins

‌INCLUDEPATH指定项目要用到的头文件路径,从而避免在#include时需要提供完整的文件路径;DESTDIR宏用于指定生成的可执行文件(或库文件)的输出目录。

添加接口的实现类。这里,我们创建两个类:一个类是狗Dog,另一个类是猫Cat。这两个类都是接口AnimalInterface的实现类,在实现方法cry(叫)时,发出的声音不同,狗“汪汪”叫,猫“喵喵”叫。

(1)添加Dog类

右键单击项目“MyPlugin”,在弹出的菜单中点击“Add New...”

在弹出的对话框中,选择“C/C++”--“C++ Class”

点击按钮“Choose...”,在弹出的对话框中:指定类名(Class name)为Dog,基类(Base class)指定为“<custom>”--“AnimalInterface”,勾选“Include QObject”、“Add Q_OBJECT”。

点击“下一步”,

点击“完成”按钮,完成Dog类的添加。

“Dog.h”的初始代码如下:

#ifndef DOG_H
#define DOG_H

#include <QObject>

class Dog : public AnimalInterface
{
    Q_OBJECT
public:
    Dog();
};

#endif // DOG_H

向其中添加继承QObject及接口AnimalInterface的实现方法后,“Dog.h”的代码如下:

#ifndef DOG_H
#define DOG_H

#include <QObject>
#include "MyInterfaces.h"   //  添加

class Dog : public QObject,  //  添加
        public AnimalInterface
{
    Q_OBJECT
    Q_INTERFACES(AnimalInterface)   //  添加
public:
    Dog();
    virtual int cry();   //  添加
};

#endif // DOG_H
在“Dog.cpp”中,添加cry的实现代码,如下:
#include <QMessageBox>
int Dog::cry()
{
    QMessageBox::information(nullptr, "Dog", "WangWang!");
    return 1;
}

为了使用QMessageBox,需要将文件“MyPlugin.pro”中的“QT += gui”修改为“QT += gui widgets”。

(2)添加Cat类

添加Cat类的步骤与添加Dog类的步骤基本相同。

“Cat.h”的代码如下:

#ifndef CAT_H
#define CAT_H

#include <QObject>
#include "MyInterfaces.h"   //  添加

class Cat :  public QObject,  //  添加
        public AnimalInterface
{
    Q_OBJECT
    Q_INTERFACES(AnimalInterface)   //  添加
public:
    explicit Cat(QObject *parent = nullptr);
    virtual int cry();   //  添加

};

#endif // CAT_H

在“Cat.cpp”中,添加cry的实现代码,如下:

#include <QMessageBox>
int Cat::cry()
{
    QMessageBox::information(nullptr, "Cat", "MiaoMiao!");
    return 1;
}

(3)修改函数GenericPlugin::create的实现代码

代码修改为:

#include "dog.h"
#include "cat.h"
QObject *GenericPlugin::create(const QString &name, const QString &spec)
{
    //static_assert(false, "You need to implement this function");
    if(AnimalInterface_iid != name)
        return nullptr;
    if(spec == "Dog")
        return new Dog;
    else if(spec == "Cat")
        return new Cat;

    return nullptr;
}

7. 在项目MyApp中使用插件

为了简化处理,直接在main函数中使用插件。

对“main.cpp”进行修改,最终代码如下:

#include "mainwindow.h"

#include <QApplication>
#include "MyInterfaces.h"   //  添加
#include <QPluginLoader>   //  添加
#include <QDir>     //  添加
#include <QList>    //  添加
#include <QGenericPlugin>   //  添加
#include <QMessageBox>   //  添加

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ///  添加,开始
    QDir pluginDir; //  插件所在的目录
    pluginDir = QDir(QCoreApplication::applicationDirPath());
    QString strDir = pluginDir.absolutePath();
    pluginDir.cd("../Plugins");
    QList<QString> pluginFileNames;    //  插件文件名
    auto entryList = pluginDir.entryList(QDir::Files);
    for (const QString &fileName : entryList) {
        //使用QPluginLoader加载插件
        QPluginLoader loader(pluginDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (plugin) {
            pluginFileNames.append(pluginDir.absoluteFilePath(fileName));
        }
    }
    //将插件实例pluginLoader.instance()转换为QGenericPlugin类型
    QGenericPlugin *genericPlugin = nullptr;
    QPluginLoader loader(pluginFileNames[0]);
    QObject *plugin = loader.instance();
    if (plugin) {
        genericPlugin = qobject_cast<QGenericPlugin *>(plugin);
    }
    if (genericPlugin) {    // 插件加载成功,可以使用插件
        // 使用 QGenericPlugin 的 create 函数来创建对象
        //  创建一个狗的实例,并调用函数cry
        AnimalInterface *pDog = qobject_cast<AnimalInterface *>
                (genericPlugin->create(AnimalInterface_iid, "Dog"));
        if(pDog)
            pDog->cry();
        //  创建一个猫的实例,并调用函数cry
        AnimalInterface *pCat = qobject_cast<AnimalInterface *>
                (genericPlugin->create(AnimalInterface_iid, "Cat"));
        if(pCat)
            pCat->cry();
    }else{
        //  插件加载失败
        QMessageBox::information(nullptr, "main", "插件加载失败!");
    }
    ///  添加,结束

    MainWindow w;
    w.show();
    return a.exec();
}

该程序首先使用QCoreApplication::applicationDirPath()获取可执行文件(“MyApp.exe”)所在的目录,然后进入该目录的上级目录的Plugins目录(因为文件“MyPlugin.pro”中有代码“DESTDIR = ../MyApp/Plugins”),然后获取Plugins目录中的所有文件名并保存在entryList中;然后使用QPluginLoader逐个文件加载,若成功,则将文件名的绝对路径保存到字符串列表pluginFileNames中;这里为了简化,只有一个插件,所以,随后再使用QPluginLoader加载第一个文件;将插件实例pluginLoader.instance()转换为QGenericPlugin类型genericPlugin;最后调用genericPlugin的create函数创建了一个狗的实例、一个猫的实例,并调用了每个实例的cry函数。

该程序运行的结果是先弹出一个狗叫的对话框,再弹出一个猫叫的对话框。

如下图所示:

   

8. 值得一提的参考资料

参考资料[6]是一个优秀的创建和使用Qt插件的例子,值得大家学习,参考资料[7]是这个例子的源代码网址。

9. 参考资料:

[1]使用QGenericPlugin和QPluginLoader创建和加载插件. 使用QGenericPlugin和QPluginLoader创建和加载插件-CSDN博客 .

[2] QGenericPlugin Class. QGenericPlugin Class | Qt GUI 6.2.13 .

[3] QPluginLoader Class. QPluginLoader Class | Qt Core 6.2.13 .

[4] How to Create Qt Plugins. How to Create Qt Plugins | Qt 6.2 .

[5] Deploying Plugins (部署插件). Deploying Plugins | Qt 6.2 .

[6] Plug & Paint Example. Plug & Paint Example | Qt Widgets 6.2.13 .

[7] plugandpaint源代码. tools « widgets « examples - qt/qtbase.git - Qt Base (Core, Gui, Widgets, Network, ...) .   

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python实现RC4加解密算法

RC4算法 一、算法介绍1.1 背景1.2 密钥调度算法(KSA)1.3 伪随机生成算法(PRGA) 二、代码实现三、演示效果 一、算法介绍 1.1 背景 RC4算法是由Ron Rivest在1987年为RSA数据安全公司设计的一种流密码算法&#xff0c;其安全性主要依赖于其密钥流的随机性和不可预测性。该算法因…

Spring Cloud 3.x 集成admin快速入门Demo

1.什么是Spring Boot Admin&#xff1f; Spring Boot Admin(SBA)是一个社区开源项目&#xff0c;用于管理和监视Spring Boot 应用程序&#xff0c;它提供详细的健康(Health)信息、内存信息、JVM 系统和环境属性、垃圾回收信息、日志设置和查看、定时任务查看、Spring Boot 缓存…

Steam Deck掌机可装“黑苹果” 开发者成功安装macOS 15 Sequoia

在Steam Deck掌机上运行Windows 11相对轻松&#xff0c;但要让其成功搭载“黑苹果”系统则颇具挑战性。近日&#xff0c;有博主勇于尝试&#xff0c;将macOS 15 Sequoia安装到了Steam Deck上。 开发者kaitlyn在X平台上分享道&#xff1a;“在朋友们的鼎力相助下&#xff0c;我…

【机器学习】KNN算法及鸢尾花案例练习

KNN 算法 knn算法思想 : K-近邻算法&#xff08;K Nearest Neighbor&#xff0c;简称KNN&#xff09;。比如&#xff1a;根据你的“邻居”来推断出你的类别 如果一个样本在特征空间中的 k 个最相似的样本中的大多数属于某一个类别&#xff0c;则该样本也属于这个类别 常见距…

Mybatis测试案例

1.创建springboot工程 创建实体类user和接口 user类 注意&#xff1a;java和mysql的对象的属性数据类型要一致 mapper接口 2.配置mybatis(连接数据库信息) # spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver #地址url spring.datasource.urljdbc:mysql://localho…

虚拟仿真产品图册生成器,上传PDF即可实现

随着科技的飞速发展&#xff0c;我国各行各业对虚拟仿真的需求越来越大&#xff0c;尤其在产品设计、制造、销售等领域&#xff0c;虚拟仿真技术已经成为了企业提高竞争力的重要手段。为了让企业能够更方便、快捷地展示产品&#xff0c;给大家推荐一款创新性的工具——FLBOOK在…

说说BPMN概念及应用

BPMN&#xff08;Business Process Modeling and Notation&#xff09;即业务流程建模与标注&#xff0c;是一种由OMG&#xff08;Object Management Group&#xff0c;对象管理组织&#xff09;制定的业务流程建模语言。以下是对BPMN标准的详细解释&#xff1a; 一、BPMN的起…

Linux操作系统垃圾清理

Linux操作系统虽然是一个占用资源少、结构简洁的计算机系统软件&#xff0c;但长时间频繁使用、安装软件较多后也是会产生不少系统垃圾的。使用Debian系Linux操作系统的用户可以使用麒麟管家中的垃圾清理工具清理&#xff0c;也可以下载安装BleachBit软件进行清理操作。 一、麒…

Studying-多线程学习Part3 - condition_variable与其使用场景、C++11实现跨平台线程池

来源&#xff1a;多线程学习 目录 condition_variable与其使用场景 生产者与消费者模型 C11实现跨平台线程池 condition_variable与其使用场景 生产者与消费者模型 生产者-消费者模式是一种经典的多线程设计模式&#xff0c;用于解决多个线程之间的数据共享和协作问题。…

『网络游戏』动态界面制作创建角色UI【02】

将上一章的登录界面隐藏 创建空物体重命名为CreateWnd 自适应铺满父物体 创建image重命名为bg并铺满 将以下资源图片放进Art文件夹 设置为精灵模式 填充背景 创建介绍Image面板与角色按钮 制作将3D模型动态防止UI界面上 首先创建RawImage 创建RenderTextures文件夹 创建Render…

输电线路缺陷图像检测数据集,导线散股,塔材锈蚀两类,分别为581张和1407张,标注为xml和txt格式 1988张

输电线路缺陷图像检测数据集&#xff0c;分为导线散股&#xff0c;塔材锈蚀两类&#xff0c;分别为581张和1407张&#xff0c;标注为xml和txt格式 数据集名称 输电线路缺陷图像检测数据集 (Transmission Line Defect Detection Dataset) 数据集概述 该数据集是一个专门用于训…

红队apt--文本宏病毒攻击思路整理

免责声明:本文仅用于了解攻击方手法&#xff0c;用于提高防范意识。禁止用于非法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文主要整理文本类病毒攻击思路 宏简介 这个东西可以直接当做编程理解。用于创建模版(简历模版),定制化需求&#xff0c;自…

【数据管理】DAMA-元数据专题

导读&#xff1a;元数据是关于数据的组织、数据域及其关系的信息&#xff0c;是描述数据的数据。在数据治理中&#xff0c;元数据扮演着至关重要的角色&#xff0c;是数据治理的基础和支撑。以下是对数据治理中元数据专题方案的详细介绍&#xff1a; 目录 一、元数据的重要性 …

基于STM32的智能门锁控制系统设计

引言 本项目基于STM32微控制器设计了一个智能门锁控制系统&#xff0c;用户可以通过密码输入或指纹识别来控制门锁的开关。该系统集成了键盘、指纹传感器、舵机等外设&#xff0c;实现了门锁的安全、便捷控制&#xff0c;同时也具备了较强的扩展性。该项目展示了STM32在安防领…

基于STM32的智能水族箱控制系统设计

引言 本项目基于STM32微控制器设计一个智能水族箱控制系统。该系统能够通过传感器监测水温、照明和水位&#xff0c;并自动控制加热器、LED灯和水泵&#xff0c;确保水族箱内的环境适宜鱼类生长。该项目展示了STM32在环境监测、设备控制和智能反馈系统中的应用。 环境准备 1…

Java:数据结构-初始结合框架 时间复杂度和空间复杂度 初识泛型

一 初始结合框架 1.什么是Java的集合框架 Java 的集合框架&#xff08;Java Collection Framework&#xff0c;JCF&#xff09;是 Java 标准库中的一部分&#xff0c;用于存储和操作一组数据。它提供了一些常用的数据结构和接口&#xff0c;用来高效管理和操作数据。Java 的…

全面图解Docker架构设计:掌握Docker全链路思维与优化(命令篇)

Docker 是一个革命性的开放平台&#xff0c;用于开发、交付和运行应用程序。通过使用 Docker&#xff0c;开发者可以打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何支持 Docker 的环境中&#xff0c;在不同环境中实现一致的运行。无论是在虚…

ctf.bugku - POST

题目来源 &#xff1a;POST - Bugku CTF 访问请求&#xff0c;返回如下信息&#xff1b; 从这里可以得到信息&#xff1b;想要得到flag &#xff0c;需要发送post请求&#xff0c;并且请求带有what参数&#xff0c;参数值为flag 构造消息体&#xff1b; burpsuite中&#x…

运用MinIO技术服务器实现文件上传——利用程序上传图片(二 )

在上一篇文章中&#xff0c;我们已经在云服务器中安装并开启了minio服务&#xff0c;本章我们将为大家讲解如何利用程序将文件上传到minio桶中 下面介绍MinIO中的几个核心概念&#xff0c;这些概念在所有的对象存储服务中也都是通用的。 - **对象&#xff08;Object&#xff0…

C++笔记之原子操作

C++笔记之原子操作 code review! 文章目录 C++笔记之原子操作1.初始化2.赋值3.取值4.赋给另一个原子类型5.`exchange`6.`compare_exchange_weak` 和 `compare_exchange_strong`使用场景7.注意事项在 C++ 中,原子类型提供了对共享变量的无锁操作,确保多线程环境下的安全。以下…