Qt Plugin插件开发

一、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 目录下的插件子目录到应用程序的根目录下。

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

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

相关文章

docker学习笔记(二)

目录 启动Docker ​编辑 建立 Docker 用户 ​编辑 测试 Docker 是否正常工作 卸载Docker Docker镜像加速器配置 配置镜像 检查加速器是否生效 如何在Linux中的.json文件下保存并退出 如果我是使用vi操作进来的&#xff0c;我该如何保存并退出呢&#xff1f; 如何在Li…

基于粒子群优化算法的最佳方式优化无线传感器节点的位置(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 此代码优化了由于电池耗尽而产生覆盖空洞后 WSN 节点的位置。如果活动通信中的任何节点死亡&#xff0c;则通过PSO优化再次定位…

Java Socket和ServerSocket 使用

在Java中&#xff0c;Socket和ServerSocket是用于创建网络连接的重要类。Socket类用于创建客户端套接字&#xff0c;而ServerSocket类用于创建服务器套接字。在本文中&#xff0c;我们将讨论Socket和ServerSocket的作用、使用方法以及相关代码示例。 Socket的作用 Socket是Jav…

【2023 · CANN训练营第一季】应用开发深入讲解——第四章 DVPP初级

学习资源 1.JPEG图片解码 文档 2.图片缩放 文档 媒体数据处理 数据预处理的典型使用场景 数据预处理的多种方式 分为两种&#xff0c;AIPP和DVPP DVPP数据预处理功能 了解两个重要概念 宽stride 和 高stride 理解&#xff1a;这里假设一张图片的宽为500&#xff0c;高为3…

匹配算法之 匈牙利算法详解

参考&#xff1a; 算法学习笔记(5)&#xff1a;匈牙利算法漫谈匈牙利算法匈牙利算法、KM算法匈牙利算法&#xff08;二分图&#xff09;通俗易懂小白入门&#xff09;二分图最大匹配——匈牙利算法多目标跟踪之数据关联&#xff08;匈牙利匹配算法和KM算法&#xff09;【小白学…

187页9万字企业大数据治理与云平台实施方案(word)

1 项目背景概述 1.1 项目背景理解 1.2 项目需求范围 2 项目技术方案 2.1 咨询研究服务方案 2.1.1 咨询研究服务内容 2.1.2 咨询服务方案 2.2 第三方独立评估 2.2.1 概述 2.2.2 管理办法 2.2.3 考核机制 2.3 安全咨询研究服务方案 2.3.1 安全咨询服务内…

【k8s】【ELK】日志环境部署【待写】

1、日志收集基本概念 k8s中pod的路径&#xff1a; containers log: /var/log/containers/*.log Pod log&#xff1a; /var/log/pods docker log: /var/lib/docker/containers/*/*.log如何收集日志 使用 EFKLogstashKafka 1、filebeat读取容器中的日志&#xff0c;然后写入K…

ChatGPT在小红书文案实践

今天聊一聊ChatGPT在小红书这个实际应用场景的案例。ChatGPT 以较低的门槛提高了使用者创作水平&#xff0c;有较高的下限&#xff0c;但如何创造更高质量的内容就要依靠使用者在领域的能力和AI使用技巧&#xff0c;作者无任何小红书推广和文案写作经验&#xff0c;文章内容来自…

快速排序、希尔排序、归并排序、堆排序、插入排序、冒泡排序、选择排序(递归、非递归)C语言详解

1.排序的概念及其运用 1.1排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&a…

python中使用opencv LED屏数字识别(可用做车牌识别,一样的原理)

应项目要求需要基于cpu的LED数字识别&#xff0c;为了满足需求&#xff0c;使用传统方法进行实验。识别传感器中显示的数字。因此使用opencv的函数做一些处理&#xff0c;实现功能需求。 首先读取图像&#xff0c;因为我没想大致得到LED屏幕的区域&#xff0c;因此将RGB转换为H…

postman处理各种请求数据

1、后台request接收postman参数 2、后台单个参数接收postman 3、后台RequestParam参数接收postman 注意事项&#xff1a;情况一&#xff1a;全部都是单个字符串的 情况二&#xff1a;有可能是一个json对象序列化成字符串过来的&#xff0c;那么需要在form-data中设置 …

鸿蒙Hi3861学习十-Huawei LiteOS-M(消息队列)

一、简介 消息队列&#xff0c;是一种常用于任务间通信的数据结构&#xff0c;实现了接收来自任务或中断的不固定长度的消息&#xff0c;并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息&#xff0c;当队列中的消息是空时&#xff0c;挂起读取任务…

国内免费cdn汇总2023最新

内容分发网络简称CDN&#xff0c;其原理大概是将网站内容分发至加速节点&#xff0c;让用户从就近的服务器节点上获取内容&#xff0c;从而提高网站的访问加载速度。大部分服务商&#xff08;如阿里云&#xff0c;腾讯云&#xff0c;京东云等&#xff09;的CDN服务是按使用量收…

【iOS】-- GET和POST(NSURLSession)

文章目录 NSURLSessionGET和POST区别 GET方法GET请求步骤 POSTPOST请求步骤 NSURLSessionDataDelegate代理方法AFNetWorking添加头文件GETPOST第一种第二种 NSURLSession 使用NSURLSession&#xff0c;一般有两步操作&#xff1a;通过NSURLSession的实例创建task&#xff1b;执…

STL配接器(容器适配器)—— stack 的介绍使用以及模拟实现。

注意 &#xff1a; 以下所有文档都来源此网站 &#xff1a; http://cplusplus.com/ 一、stack 的介绍和使用 stack 文档的介绍&#xff1a;https://cplusplus.com/reference/stack/stack/ 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&…

预训练模型之BERT、Transformer-XL、XL-Net等

文章目录 预训练模型&#xff08;Pre-trained Models, PTMs&#xff09;前置知识BERTTransformer-XLXLNetTransformer-XL类似工作&#xff08;Scalable Transformer&#xff09;1. 《Scaling Transformer to 1M tokens and beyond with RMT》2. 《》 预训练模型&#xff08;Pre…

【Linux常见指令以及权限理解】基本指令(3)

写在前面 上一篇文章&#xff0c;我们学习了Linux的一些常用指令&#xff0c; 学习了如何理解Linux系统&#xff0c;介绍了对Linux系统的理解&#xff1a;Linux下一切皆文件 介绍了重定向还有管道相关的知识。这里是上一篇博客的链接&#xff1a;http://t.csdn.cn/2d6fc 接…

Vue组件化编程

2.1. 模块与组件、模块化与组件化 模块&#xff1a; 理解&#xff1a;向外提供特定功能的 js 程序&#xff0c;一般就是一个 js 文件为什么&#xff1a;js 文件很多很复杂作用&#xff1a;复用、简化 js 的编写&#xff0c;提高 js 运行效率 组件&#xff1a; 定义&#xff…

QT界面开发杂记(五)

QString转char* QString("name").toStdString().c_str() c_str()没有‘\0’结尾可能导致一些错误可以使用以下方法解决&#xff1a; QString xmlPath "path"; const char cXmlName[1024] {0}&#xff1b; memcpy((void*)cXmlName,xmlPath.toStdStri…

Prompt learning 教学[案例篇]:文生文案例设定汇总,你可以扮演任意角色进行专业分析

Prompt learning 教学[案例篇]&#xff1a;文生文案例设定汇总&#xff0c;你可以扮演任意角色进行专业分析 1.角色扮演 行为Prompt写法“牙医”““我想让你扮演一名牙医。我会向你提供有关寻找牙科服务&#xff08;例如 X 光、清洁和其他治疗&#xff09;的个人的详细信息。…