Qt Plugin插件开发

news2024/11/15 4:46:06

一、Qt 插件机制

.1 Qt 插件简介

插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。插件与宿主程序之间通过接口联系,就像硬件插卡一样,可以被随时删除,插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护。Qt 提供了两种API用于创建插件:一种是高阶 API,用于扩展 Qt 本身的功能,如自定义数据库驱动,图像格式,文本编码,自定义样式等;一种是低阶 API,用于扩展 Qt 应用程序。本文主要是通过低阶 API 来创建 Qt 插件,并通过静态、动态两种方式来调用插件。(以下都是 Qt5 的插件开发方式)

1.2 Qt 插件开发的流程

  1. 定义一个接口集(只有纯虚函数的类)。
  2. 用宏Q_DECLARE_INTERFACE()将该接口告诉 Qt 元对象系统
  3. 声明插件类,插件类继承自 QObject 和插件实现的接口
  4. 用宏Q_INTERFACES()将插件接口告诉 Qt 元对象系统(在头文件中)。
  5. 用适当的 .pro 文件构建插件。

1.3 Qt 插件调用的流程

  1. 包含接口头文件(只有纯虚函数的类)。
  2. 应用程序中用QPluginLoader来加载插件。
  3. 用宏qobject_cast()来判断一个插件是否实现了接口。

二、插件开发实例

2.1 创建目录工程

创建目录工程,以放置 GUI 应用工程和插件工程,选择 “Other Project”->“Subdirs Project”,填写工程名称为 PluginApp,选择保存目录。
在这里插入图片描述

2.2 创建GUI应用工程

在 PluginApp 工程上右键选择 “New Subproject” 菜单项,选择创建一个 GUI 应用:
在这里插入图片描述
填写工程应用名称为 MainWindow:
在这里插入图片描述
填写主界面类的名称:
在这里插入图片描述

2.3 创建插件子工程

在 PluginApp 工程上右键选择 “New Subproject” 菜单项,选择创建一个空的 Qt 工程,名称为 EchoPlugin。
在这里插入图片描述

2.4 插件的实现

1. 定义一个接口集(只有纯虚函数的类)
在 MainWindow 应用增加一个接口 Echonterface.h。

#ifndef ECHOINTERFACE_H
#define ECHOINTERFACE_H

#include <QString>

// 1.定义一个接口集(只有纯虚函数的类)
class EchoInterface
{
public:
    virtual ~EchoInterface() {}
    virtual QString echo(const QString &message) = 0;
};

// 2.用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统
QT_BEGIN_NAMESPACE
#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface"
Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE
#endif

2. 声明插件类,插件类继承自 QObject 和插件实现的接口
EchoPlugin.pro工程文件内容如下:

TEMPLATE        = lib
CONFIG         += plugin
QT             += widgets
INCLUDEPATH    += ../MainWindow
HEADERS         = EchoPlugin.h
SOURCES         = EchoPlugin.cpp
TARGET          = $$qtLibraryTarget(echoplugin)
DESTDIR         = ../EchoPlugin

# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/echoplugin/plugins
INSTALLS += target

CONFIG += install_ok  # Do not cargo-cult this!

在插件子工程中添加一个插件类 EchoPlugin,实现如下:

EchoPlugin.h 文件:

#ifndef ECHOPLUGIN_H
#define ECHOPLUGIN_H

#include <QObject>
#include <QtPlugin>
#include "EchoInterface.h"

// 3.声明插件类,插件类继承自QObject和插件实现的接口
class EchoPlugin : public QObject, EchoInterface
{
    // 3.用宏Q_INTERFACES()将插件接口告诉Qt元对象系统(在头文件中)
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface") // 宏需要声明通过对象实现的接口的IID,并引用一个包含插件元数据的文件
    Q_INTERFACES(EchoInterface)

public:
    QString echo(const QString &message) override; // 实现的接口:返回字符串消息
};

#endif

EchoPlugin.cpp 文件:

#include "EchoPlugin.h"

// 实现的接口:返回字符串消息
QString EchoPlugin::echo(const QString &message)
{
    return message;
}

2.5 GUI应用的实现

实现 MainWindow 主界面。

MainWindow.pro 文件:

QT += widgets

HEADERS    = Widget.h \
             EchoInterface.h
SOURCES    = Widget.cpp \
             main.cpp

TARGET     = echoplugin
QMAKE_PROJECT_NAME = MainWindow
win32 {
    CONFIG(debug, release|debug):DESTDIR = ../debug/
    CONFIG(release, release|debug):DESTDIR = ../release/
} else {
    DESTDIR    = ../
}

# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/echoplugin
INSTALLS += target

CONFIG += install_ok  # Do not cargo-cult this!

Widget.h 文件:

#ifndef ECHODIALOG_H
#define ECHODIALOG_H

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QMessageBox>
#include <QDir>
#include <QPluginLoader>
#include "EchoInterface.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget();

private slots:
    void sendEcho();

private:
    void initUI(); // 初始化UI
    bool loadPlugin(); // 加载插件

    EchoInterface *m_pEchoInterface;
    QLineEdit *m_pLineEdit;
    QLabel *m_pLabel;
    QPushButton *m_pBtn;
};

#endif

Widget.cpp 文件:

#include "Widget.h"

Widget::Widget()
{
    // 初始化UI
    initUI();

    // 加载插件
    if (!loadPlugin()) {
        QMessageBox::information(this, "Error", "Could not load the plugin");
        m_pLineEdit->setEnabled(false);
        m_pBtn->setEnabled(false);
    }
}

void Widget::sendEcho()
{
    // 调用插件接口 - EchoPlugin::echo
    QString text = m_pEchoInterface->echo(m_pLineEdit->text());
    m_pLabel->setText(text);
}

// 初始化UI
void Widget::initUI()
{
    m_pLineEdit = new QLineEdit;
    m_pLabel = new QLabel;
    m_pLabel->setFrameStyle(QFrame::Box | QFrame::Plain);
    m_pBtn = new QPushButton(tr("Send Message"));

    connect(m_pLineEdit, &QLineEdit::editingFinished,
            this, &Widget::sendEcho);
    connect(m_pBtn, &QPushButton::clicked,
            this, &Widget::sendEcho);

    QGridLayout *m_pLayoutMain = new QGridLayout(this);
    m_pLayoutMain->addWidget(new QLabel(tr("Message:")), 0, 0);
    m_pLayoutMain->addWidget(m_pLineEdit, 0, 1);
    m_pLayoutMain->addWidget(new QLabel(tr("Answer:")), 1, 0);
    m_pLayoutMain->addWidget(m_pLabel, 1, 1);
    m_pLayoutMain->addWidget(m_pBtn, 2, 1, Qt::AlignRight);
    m_pLayoutMain->setSizeConstraint(QLayout::SetFixedSize);
}

// 加载插件
bool Widget::loadPlugin()
{
    bool ret = true;

    // 获取当前应用程序所在路径
    QDir pluginsDir(qApp->applicationDirPath());
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();

    // 切换到插件目录
    pluginsDir.cd("plugins");
    // 遍历plugins目录下所有文件
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        if (plugin)
        {
            // 获取插件名称
            QString pluginName = plugin->metaObject()->className();
            if(pluginName == "EchoPlugin")
            {
                // 对插件初始化
                m_pEchoInterface = qobject_cast<EchoInterface *>(plugin);
                if (m_pEchoInterface)
                    ret =  true;
                break;
            }
            else
            {
                ret = false;
            }
        }
    }
    return ret;
}

Main.cpp文件:

#include <QtWidgets>
#include "Widget.h"
#include "EchoInterface.h"

int main(int argv, char *args[])
{
    QApplication app(argv, args);

    Widget window;
    window.show();

    return app.exec();
}

2.6 程序运行结果

在这里插入图片描述

三、定位插件

Qt 应用程序将会自动感知可用的插件,因为插件都被存储在标准的子目录当中。因此应用程序不需要任何查找或者加载插件的代码。
在开发过程中,插件的目录是 QTDIR/plugins(QTDIR 是 Qt 的安装目录),每个类型的插件放在相应类型的目录下面。如果想要应用程序使用插件,但不想用标准的插件存放路径,可以在应用程序的安装过程中指定要使用的插件的路径,可以使用 QSettings,保存插件路径,在应用程序运行时读取配置文件。应用程序可以通过QCoreApplication::addLibraryPath()函数将指定的插件路径加载到应用程序中。
使插件可加载的一种方法是在应用程序所在目录创建一个子目录,用于存放插件。如果要发布和 Qt 一起发布的插件(存放在 plugins 目录)中的任何插件,必须拷贝 plugins 目录下的插件子目录到应用程序的根目录下。

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

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

相关文章

2.2.4 Linux安装模式下,磁盘分区的选择(重要)

目录树结构 &#xff08;directory tree&#xff09; 整个Linux系统最重要的地方就是在于目录树架构。 所谓的目录树架构&#xff08;directory tree&#xff09;就是以根目录为主&#xff0c;然后向下呈现分支状的目录结构的一种文件架构。 所以&#xff0c;整个目录树架构最重…

Midjourney-Discord入门+高手指引手册

上一篇我们说了如何注册和订阅&#xff0c;今天我们来讲一讲相关的细节&#xff0c;首先我们来一个概览图&#xff0c;先有个大致的印象。 概览图 生成的四张图片&#xff0c;类似于Demo&#xff0c;从左至右&#xff0c;从上至下&#xff0c;1,2,3,4放大按钮U1&#xff0c;U2…

【旋转编码器如何工作以及如何将其与Arduino一起使用】

在本教程中,我们将学习旋转编码器的工作原理以及如何将其与Arduino一起使用。您可以观看以下视频或阅读下面的书面教程。 1. 概述 旋转编码器是一种位置传感器,用于确定旋转轴的角度位置。它根据旋转运动产生模拟或数字电信号。 有许多不同类型的旋转编码器按输出信号或传感…

三个令人惊艳超有用的 ChatGPT 项目,开源了!

自 3 月初 Open AI 开放 ChatGPT API 以来&#xff0c;GitHub 上诞生的开源项目数量之多&#xff0c;着实多得让我眼花缭乱、应接不暇。 今天&#xff0c;我将着重挑选几个对日常工作、生活、学习帮助较大的 ChatGPT 开源项目&#xff0c;跟大家分享下&#xff0c;希望对你有所…

Illustrator如何使用填充与线条之实例演示?

文章目录 0.引言1.绘制星空小插画2.制作清新小碎花壁纸3.双重描边文字 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对Illustrator进行了学习&#xff0c;本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文对…

labelme标注数据集,并利用paddleseg完成标注数据的准备工作

1、安装labelme 1、创建labelme虚拟环境 先检查python的版本 python -V使用命令 conda create -n labelme python=3.9,创建虚拟环境 2、激活虚拟环境 conda activate labelme3、安装labelme pip install labelme2、labelme的使用 1、打开cmd输入命令 labelme2、进入label…

关于安装Node/Yarn/Electron过程中遇到的问题

目录 1、安装Node2、安装electron很慢3、PowerShell中无法使用yarn命令4、Yarn命令目录bin与其全局安装位置不在同一个文件夹 1、安装Node 【参考文章】Node.js下载安装及环境配置教程 2、安装electron很慢 npm config set electron_mirror https://npm.taobao.org/mirrors/…

Java日志详解

文章目录 1.日志的概述1.1 日志文件1.1.1 调试日志1.1.2 系统日志 1.2 JAVA日志框架1.2.1 为什么要用日志框架1.2.2 日志框架和日志门面 2.JUL2.1 JUL简介2.2 JUL组件介绍2.3 JUL的基本使用2.3.1 日志输出的级别2.3.2 日志的输出方式2.3.3 自定义日志的级别2.3.4 将日志输出到具…

20个最流行的3D打印机切片软件

3D 打印切片机&#xff08;Slicer&#xff09;通过生成 G 代码为你的 3D 打印机准备 3D 模型&#xff0c;G 代码是一种广泛使用的数控 (NC) 编程语言。 3D打印切片软件的选择范围很广。 因此&#xff0c;为了帮助你找到最合适的工具&#xff0c;本文列出了20个顶级 3D 打印切片…

2022年NOC大赛编程马拉松赛道决赛图形化高年级A卷-正式卷

2022年NOC大赛编程马拉松赛道决赛图形化高年级A卷-正式卷 2022NOC-图形化决赛高年级A卷正式卷 编程题&#xff1a;蓝色星球的绿色踪迹任务描述&#xff1a;有一颗蔚蓝色的星球&#xff0c;她就是我们人类赖以生存的家园——地球。地球是人类的母亲&#xff0c;她为我们提供了各…

第三十七章 Unity GUI系统(上)

Unity 提供了三个 UI 系统帮助我们创建游戏界面 (UI)&#xff1a; 第一&#xff0c;UI 工具包 第二&#xff0c;Unity UI 软件包 (UGUI) 第三&#xff0c;IMGUI UI 工具包是 Unity 中最新的 UI 系统。它旨在优化跨平台的性能&#xff0c;并基于标准 Web 技术。您可以使用 U…

并发编程04:LockSupport与线程中断

文章目录 4.1 线程中断机制4.1.1 从阿里蚂蚁金服面试题讲起4.1.2 什么是中断机制4.1.3 中断的相关API方法之三大方法说明4.1.4 大厂面试题中断机制考点4.1.5 总结 4.2 LockSupport是什么4.3 线程等待唤醒机制4.3.1 三种让线程等待和唤醒的方法4.3.2 Object类中的wait和notify方…

uboot命令体系 源码解读并从中添加命令

1、uboot命令体系基础 1.1、使用uboot命令 (1)uboot启动后进入命令行底下&#xff0c;在此输入命令并回车结束&#xff0c;uboot会接收这个命令并解析&#xff0c;然后执行。 1.2、uboot命令体系实现代码在哪里 (1)uboot命令体系的实现代码在uboot/common/下&#xff0c;其…

【博学谷学习记录】超强总结,用心分享 | 架构师 MongoDB学习总结

文章目录 MongoDB基本使用Nosql简介什么是NoSQL为什么使用NoSQLRDBMS vs NoSQLNoSQLNoSQL的优缺点缺点 MongoDB基础什么是MongoDB存储结构主要特点 基本概念和传统数据库对比集合命令规范 文档key的命令规范注意事项 MongoDB的数据类型BSON支持的数据类型 MongoDB基本使用 Nos…

DT MongoDB Plug -in description

目录 DT MongoDB | Client Create MongoDB Client Connect By Url Get Collection DT MongoDB | Collection Insert One Insert Many Delete One Delete Many Replace One Update One Update Many Find Find One DT MongoDB | Document Create MongoDB Documen…

【算法与数据结构】顺序表

顺序表 数据结构 结构定义结构操作 顺序表&#xff1a;结构定义 一个数组&#xff0c;添加额外的几个属性&#xff1a;size, count等 size: 数组有多大 count: 数组中当前存储了多少元素 顺序表三部分&#xff1a; 一段连续的存储区&#xff1a;顺序表存储元素的地方整型…

【Linux】Linux安装Java环境(OracleJDK)

文章目录 前言第一步&#xff0c;到官网下载jdk1.8第二步&#xff0c;下载下来上传到/opt目录下&#xff0c;并且解压第三步&#xff0c;解压之后配置环境变量&#xff1a;第四步&#xff0c;刷新配置文件第五步&#xff0c;查看版本 前言 linux环境为CentOS7.8 版本。 上期跟…

shell教程

面试题&#xff1a; 1.Shell中单引号和双引号区别 1)单引号不取变量值 2)双引号取变量值 3)反引号&#xff0c;执行引号中命令 4)双引号内部嵌套单引号&#xff0c;取出变量值 5)单引号内部嵌套双引号&#xff0c;不取出变量值 一、shell脚本 1.shell脚本概…

ChatGLM的搭建过程

本次搭建的是清华大学开源的ChatGLM。源码地址。模型地址。 1、开启BBR加速 如何开启BBR加速可以去看我的这篇文章&#xff0c;Linux开启内核BBR加速。 2、拉取ChatGLM源码和ChatGLM模型 点击这里跳转到源码处。 点击这里跳转到模型下载处。 我这里在下载之前创建了一个目…

与ChatGPT的一次技术对话

文章目录 前言 - 向引领前沿技术的伟大工作者致敬提问&#xff1a;请给我推荐几个最先进的目标检测AI模型提问&#xff1a;YOLOv4是什么原理&#xff0c;有什么创新提问&#xff1a;请问你知道yolov5吗提问&#xff1a; 那yolov5又有什么创新呢提问&#xff1a;你刚刚的回答正确…