Qt插件开发

使用插件开发都是为了扩展应用程序的功能。

Qt如何开发插件,可见帮助文档。

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

Qt扩展应用程序

要想使用插件来扩展应用程序,那么首先在主程序中的步骤如下:

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

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

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

示例:

环境:Qt5.9.7+MSVC20015 32

步骤:

1.新建一个子目录项目

创建好后会提示创建一个子项目,这里直接创建一个mainwindow主工程项目,并命名为frmMain。

接着,我们再创建一个插件的工程,右击主工程,添加子项目:

选择一个空项目添加:Empty qmake Project.

主工程和插件工程已创建完成。

2.定义通信接口

接下来在主工程中新建一个头文件,用于定义与插件通信的接口(只有纯虚函数的类)

#ifndef ARITH_H
#define ARITH_H
//定义接口
class arithInterface
{
public:
    virtual ~arithInterface(){}
    virtual int add(int a,int b)=0;
    virtual int div(int a,int b)=0;
    virtual int multiply(int a,int b)=0;
    virtual int sub(int a,int b)=0;


};

#define arithInterface_iid "Examples.Pulgin.arithInterface"
//arithInterface_iid 宏定义字符串一定要是唯一的,然后使用宏Q_DECLARE_INTERFACE来声明该接口。

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(arithInterface,arithInterface_iid)

QT_END_NAMESPACE

#endif // ARITH_H

为方面演示,这里只做了非常简单的加法运算,所以创建了一个纯虚函数接口add,这里的CalInterface_iid 宏定义字符串一定要是唯一的,然后使用宏Q_DECLARE_INTERFACE来声明该接口。

3.编写插件

插件通信接口写完了,然后开始来编写插件,在插件工程下创建一个新的类,同时继承于QObject和通信接口类CalInterface,如下:

#ifndef ARITHPLUGIN_H
#define ARITHPLUGIN_H

#include <QObject>
#include<QtPlugin>
#include"../frmMain/arith.h"
class arithPlugin : public QObject,public arithInterface
{
    Q_OBJECT
    Q_INTERFACES(arithInterface)
    Q_PLUGIN_METADATA(IID arithInterface_iid FILE "arithplugin.json")
public:
    explicit arithPlugin(QObject *parent = nullptr);
    int add(int a,int b);
    int div(int a,int b);
    int multiply(int a,int b);
    int sub(int a,int b);

};

#endif // ARITHPLUGIN_H
#include "arithplugin.h"

arithPlugin::arithPlugin(QObject *parent) : QObject(parent)
{

}

int arithPlugin::add(int a, int b)
{
    return a+b;

}

int arithPlugin::div(int a, int b)
{
    if(b!=0)
        return a/b;
    else
        return -1;
}

int arithPlugin::multiply(int a, int b)
{
    return a*b;

}

int arithPlugin::sub(int a, int b)
{
    return a-b;
}

注意,在头文件中一定要用Q_INTERFACES宏来声明接口类。
这里的calplugin.json是为插件提供插件信息的,并使用Q_PLUGIN_METADATA声明(实例化该对象的)插件的元数据,元数据是插件的一部分。

Q_PLUGIN_METADATA这个宏所在的类必须是默认可构造的。

FILE 是可选的,并指向一个 Json 文件。

Json 文件必须位于构建系统指定的包含目录之一中。当无法找到指定的文件时,moc 会出现错误。

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

4.修改插件类pro文件

由于插件编译出来也是dll的形式,所以我们需要在工程文件中将类型改成lib,以及其他的一些配置,如下:

TEMPLATE        = lib
CONFIG         += plugin
QT             += widgets
INCLUDEPATH    += ../frmMain

TARGET          = $$qtLibraryTarget(arithplugin)
DESTDIR         = ../plugins


EXAMPLE_FILES = arithplugin.json



HEADERS += \
    arithplugin.h

SOURCES += \
    arithplugin.cpp
CONFIG += install_ok

修改frmMain.pro文件

#-------------------------------------------------
#
# Project created by QtCreator 2021-03-03T11:45:38
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = frmMain
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp

HEADERS += \
        mainwindow.h \
    arith.h

FORMS += \
        mainwindow.ui


win32 {
    CONFIG(debug, release|debug):DESTDIR = ../debug/
    CONFIG(release, release|debug):DESTDIR = ../release/
} else {
    DESTDIR    = ../
}
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target


CONFIG += install_ok

 

5.主工程中调用插件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include"arith.h"
#include<QDebug>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void on_calBtn_clicked();

private:
    bool loadPlugin();
private:
    Ui::MainWindow *ui;
    arithInterface *m_pInterface = nullptr;
};

#endif // MAINWINDOW_H
#pragma execution_character_set("utf-8")
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtWidgets>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    if(!loadPlugin())
        QMessageBox::information(this,"info","plugin is not exist!");
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::loadPlugin()
{

    QDir pluginsDir(qApp->applicationDirPath());
    qDebug()<<qApp->applicationDirPath();
#if defined(Q_OS_WIN)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    if (pluginsDir.dirName() == "MacOS") {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif
    pluginsDir.cd("plugins");
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        qDebug() << "--->>>Lynn<<<---" << __FUNCTION__ << pluginLoader.errorString();
        if (plugin) {
            m_pInterface = qobject_cast<arithInterface *>(plugin);
            if (m_pInterface)
                return true;
        }
    }

    return false;


}

void MainWindow::on_calBtn_clicked()
{
    int a = ui->lineEditA->text().toInt();
    int b = ui->lineEditB->text().toInt();
    int r = -1;
    if(m_pInterface)
    {
        if(ui->comboBox->currentText() == "+")
        {
            r = m_pInterface->add(a,b);
        }
        else if(ui->comboBox->currentText() == "-")
        {
            r = m_pInterface->sub(a,b);
        }
        else if(ui->comboBox->currentText() == "*")
        {
            r = m_pInterface->multiply(a,b);
        }
        else if(ui->comboBox->currentText() == "/")
        {
            r = m_pInterface->div(a,b);
        }
        else{}
        ui->lineEditC->setText(QString::number(r));
    }

}

其中loadPlugin()函数就是用于加载本地的插件文件,以上是动态加载的方式实现。

运行截图

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fyzy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值