利用插件扩展Qt应用程序

简介: 简述不仅 Qt 本身可以通过插件进行扩展,而且 Qt 应用程序也可以通过插件来扩展,这需要应用程序使用 QPluginLoader 检测和加载插件。

简述

不仅 Qt 本身可以通过插件进行扩展,而且 Qt 应用程序也可以通过插件来扩展,这需要应用程序使用 QPluginLoader 检测和加载插件。在这种情况下,插件可能提供任意功能,不限于数据库驱动程序、图像格式、文本编解码器、样式以及扩展 Qt 功能的其他类型的插件。

版权所有:一去丶二三里,转载请注明出处:http://blog.csdn.net/liang19890820

扩展 Qt 应用程序

编写扩展 Qt 应用程序的插件,涉及以下步骤:

  • 声明一个继承自 QObject 和插件想要提供的接口的插件类
  • 使用 Q_INTERFACES() 宏来告诉 Qt 元对象系统有关接口的情况
  • 使用 Q_PLUGIN_METADATA() 宏导出插件
  • 使用合适的 .pro 文件构建插件

通过插件使应用程序可扩展,涉及以下步骤:

  • 定义一组用于与插件通信的接口(只有纯虚函数的类)
  • 使用 Q_DECLARE_INTERFACE() 宏来告诉 Qt 元对象系统有关接口的情况
  • 在应用程序中使用 QPluginLoader 加载插件
  • 使用 qobject_cast() 来测试插件是否实现了指定的接口

一个简单的插件

为我们的职业感到自豪,程序员可以创造世界。。。以此为例吧!

这里写图片描述

编写插件

工程文件 - .pro

和前面一样,由于我们正在构建的是一个共享库,所以在 .pro 中,需要将 TEMPLATE 设置为 lib,同时还必须将 CONFIG 设置为 plugin。

plugin.pro 内容如下:

QT += core
QT -= gui

TEMPLATE = lib
CONFIG += plugin
TARGET = personPlugin

HEADERS += \
    person.h \
    programmer.h

SOURCES += programmer.cpp

OTHER_FILES += programmer.json

win32 {
    CONFIG(debug, release|debug):DESTDIR = ../debug/plugins/
    CONFIG(release, release|debug):DESTDIR = ../release/plugins/
} else {
    DESTDIR = ../plugins/
}

这里,将插件生成在 plugins 目录中,当然,目录名可以随便起。

接口类 - IPerson

声明了一个关于人的接口类 - IPerson,定义了插件将提供的功能。

注意: 接口是一个仅由纯虚函数组成的类,如果在类中存在非虚函数,那么在 moc 文件中会出现误导的编译错误。

person.h 内容如下:

#ifndef PERSON_H
#define PERSON_H

#include <QtPlugin>
#include <QString>

class IPerson
{
public:
    virtual ~IPerson() {}
    virtual QString name() = 0;  // 人有名字
    virtual void eat() = 0;  // 人需要吃东西
    virtual void sleep() = 0;  // 人需要睡觉
    virtual void doSomething() = 0;  // 人还需要干其他事
};

#define IPerson_iid "org.qt-project.Qt.Examples.IPerson"

Q_DECLARE_INTERFACE(IPerson, IPerson_iid)

#endif // PERSON_H

使用 Q_DECLARE_INTERFACE 宏,是为了让 Qt 元对象系统知道该接口,这样以来,在运行时便可以识别实现接口的插件。其中,第二个参数(IPerson_iid)是一个标识接口的字符串,必须唯一。

插件类 - Programmer

现在,是时候该程序员出场了。定义一个 Programmer 类,继承自 QObjectIPerson,使这个类成为一个插件。

programmer.h 内容如下:

#ifndef PROGRAMMER_H
#define PROGRAMMER_H

#include "person.h"
#include <QObject>

class Programmer : public QObject, IPerson
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID IPerson_iid FILE "programmer.json")
    Q_INTERFACES(IPerson)

public:
    virtual QString name() Q_DECL_OVERRIDE;
    virtual void eat() Q_DECL_OVERRIDE;
    virtual void sleep() Q_DECL_OVERRIDE;
    virtual void doSomething() Q_DECL_OVERRIDE;
};

#endif // PROGRAMMER_H

Q_INTERFACES 宏用于告诉 Qt 该类实现的接口。Q_PLUGIN_METADATA 宏包含插件的 IID,并指向一个包含插件元数据的 Json 文件。该 Json 文件会被编译到插件中,无需安装。

程序员日常:吃饭、睡觉、撸代码。。。(⊙o⊙)…

programmer.cpp 内容如下:

#include "programmer.h"
#include <QtDebug>

QString Programmer::name()
{
    return "Pony";
}

void Programmer::eat()
{
    qDebug() << "Pizza";
}

void Programmer::sleep()
{
    qDebug() << "6 hours";
}

void Programmer::doSomething()
{
    qDebug() << "Coding...";
}

插件的元数据 - Json 文件

插件已经基本完成,但貌似还缺些什么?没错,元数据!插件总需要提供一些相关的信息吧!

这时上面提到的 Json 文件就相当有用了!可以包含一些元数据,例如:作者、日期、插件名、版本号、依赖关系等。。。

programmer.json 内容如下:

{
    "author" : "Waleon",
    "date" : "2017/09/01",
    "name" : "personPlugin",
    "version" : "1.0.0",
    "dependencies" : []
}

由于现在只有一个插件,并没有对于其他插件的依赖,所以暂时没有列出依赖关系(要实现一个插件系统,依赖关系必不可少)。

注意: 如果不想为插件提供信息,当然不会有任何问题,只需保证 Json 文件为空就行。

加载插件

一切准备就绪,来实现一个简单的控制台应用程序,以动态加载插件。

工程文件 - .pro

由于这里需要的是一个简单的控制台应用程序,所以,除了将 TARGET 设置为 app 之外,还需要将 CONFIG 设置为 console。

app.pro 内容如下:

QT += core
QT -= gui

CONFIG += c++11

TARGET = app
CONFIG += console
TEMPLATE = app

INCLUDEPATH += $$PWD/..

SOURCES += main.cpp

win32 {
    debug:DESTDIR = ../debug/
    release:DESTDIR = ../release/
} else {
    DESTDIR = ../
}

为了能在程序中使用插件相关的接口,需要使用 INCLUDEPATH 将插件的相关路径包含进来。

main() 函数

注意: 对于使用插件的应用程序,Qt 插件只是一个 QObject,该 QObject 使用多重继承来实现插件接口。

main.cpp 内容如下:

#include <QCoreApplication>
#include <QPluginLoader>
#include <QDir>
#include <QtDebug>
#include <QJsonObject>
#include <QJsonArray>
#include <plugin/person.h>

void loadPlugin()
{
    // 进入插件目录
    QDir pluginsDir(qApp->applicationDirPath());
    pluginsDir.cd("plugins");
    // 查找目录中的所有插件
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        // 返回插件的根组件对象
        QObject *pPlugin = loader.instance();
        if (pPlugin != Q_NULLPTR) {

            // 获取元数据(名称、版本、依赖)
            QJsonObject json = loader.metaData().value("MetaData").toObject();
            qDebug() << "********** MetaData **********";
            qDebug() << json.value("author").toVariant();
            qDebug() << json.value("date").toVariant();
            qDebug() << json.value("name").toVariant();
            qDebug() << json.value("version").toVariant();
            qDebug() << json.value("dependencies").toArray().toVariantList();

            // 访问感兴趣的接口
            IPerson *pPerson = qobject_cast<IPerson *>(pPlugin);
            if (pPerson != Q_NULLPTR) {
                qDebug() << "********** IPerson **********";
                qDebug() << pPerson->name();
                pPerson->eat();
                pPerson->sleep();
                pPerson->doSomething();
            } else {
                qWarning() << "qobject_cast falied";
            }
        }
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    loadPlugin();

    return a.exec();
}

初始化 pluginsDir 成员变量,以引用 plugins 子目录。然后,使用 QDir::entryList() 获取该目录中所有文件列表,并通过 foreach 迭代结果,尝试使用 QPluginLoader 加载插件。

插件提供的 QObject 可以通过 QPluginLoader::instance() 访问。如果动态库不是 Qt 插件,或者是由不兼容版本的 Qt 库进行编译,QPluginLoader::instance() 将返回一个空指针。

对于每个插件,可以根据 QPluginLoader::metaData() 来获取其元数据(定义在 Json 文件中)。要检查插件实现的接口,需要使用 qobject_cast(),从而进一步访问接口中的定义。

源码地址

GitHub 源码地址:PersonPlugin

目录
相关文章
|
6月前
|
Web App开发 存储 Linux
Linux(33)Rockchip RK3568 Ubuntu22.04上通过SSH运行Qt程序和关闭Chrome的密钥提示
Linux(33)Rockchip RK3568 Ubuntu22.04上通过SSH运行Qt程序和关闭Chrome的密钥提示
452 0
|
4月前
|
Linux iOS开发 开发者
Qt问题(二):无法定位程序输入点于动态链接库
动态链接库(Dynamic Link Library,简称DLL)是一种可执行文件格式,常见于Windows操作系统中,而在Linux和macOS等其他操作系统中,相似的概念通常被称为共享库(Shared Library)。动态链接库允许程序在运行时加载所需的代码和数据,而不是在编译时静态链接到应用程序中。这种方式带来了几个重要的优点:
468 3
|
2月前
|
C语言 Android开发 C++
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
本文介绍了使用MTuner软件进行Qt MinGW编译程序的内存泄漏检测的方法,提供了MTuner的下载链接和测试代码示例,并通过将Debug程序拖入MTuner来定位内存泄漏问题。
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
|
2月前
|
计算机视觉
基于QT的opencv插件框架qtCvFrameLearn实战
这篇文章详细介绍了如何基于Qt框架开发一个名为qtCvFrameLearn的OpenCV插件,包括项目配置、插件加载、Qt与OpenCV图像转换,以及通过各个插件学习OpenCV函数的使用,如仿射变换、卡通效果、腐蚀、旋转和锐化等。
45 10
|
2月前
|
C语言 C++ Windows
QT多插件通信框架CTK编译记录
本文记录了编译QT多插件通信框架CTK的过程,包括编译结果截图、部署配置、Log4Qt编译配置、参考链接和拓展资料。文中提供了详细的编译步骤和配置文件示例,以及相关的资源链接。
QT多插件通信框架CTK编译记录
|
6月前
|
开发框架 自然语言处理 Linux
Qt:构建强大跨平台应用程序的框架
Qt:构建强大跨平台应用程序的框架
|
4月前
|
监控 Python
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
|
4月前
|
调度
【浅入浅出】Qt多线程机制解析:提升程序响应性与并发处理能力
在学习QT线程的时候我们首先要知道的是QT的主线程,也叫GUI线程,意如其名,也就是我们程序的最主要的一个线程,主要负责初始化界面并监听事件循环,并根据事件处理做出界面上的反馈。但是当我们只限于在一个主线程上书写逻辑时碰到了需要一直等待的事件该怎么办?它的加载必定会带着主界面的卡顿,这时候我们就要去使用多线程。
159 6
|
5月前
Qt MainWindow 程序主窗口
Qt MainWindow 程序主窗口

推荐镜像

更多
下一篇
无影云桌面