Qt - 插件化编程 & 插件框架

Qt插件机制(插件化编程)

1、通过插件扩展应用程序功能
A、定义一个接口集(只有纯虚函数的类),用来与插件交流。
B、用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
C、应用程序中用QPluginLoader来加载插件。
D、用宏qobject_cast()来判断一个插件是否实现了接口。

2、创建插件
A、声明插件类,插件类继承自QObject和插件实现的接口。
B、用宏Q_INTERFACES()将插件接口告诉Qt元对象系统。(注意Qt版本)
C、用宏Q_EXPORT_PLUGIN2()导出插件类。(注意Qt版本)
D、用适当的.pro文件构建插件。
注:在加载插件前,QCoreApplication对象必须被初始化。

Qt插件类创建(插件化编程)

////--------------------------------------------------------------------------------------step 1 定义接口类
class RegExpInterface
{
public:
    virtual ~RegExpInterface() {}
    virtual QString regexp(const QString &message) = 0;
};
////--------------------------------------------------------------------------------------step 2 声明是接口类(接口类中)
// 使用 Q_DECLARE_INTERFACE 宏,是为了让Qt元对象系统知道该接口,这样以来,在运行时便可以识别实现接口的插件。
// Q_DECLARE_INTERFACE(接口类名, 接口标识),第二个参数(RegExpInterface_iid)是一个标识接口的字符串,必须唯一。
#define RegExpInterface_iid "org.qter.Examples.myplugin.RegExpInterface"
Q_DECLARE_INTERFACE(RegExpInterface,RegExpInterface_iid )
#endif // REGEXPINTERFACE_H
////--------------------------------------------------------------------------------------step 3 实现接口
class REGEXPLUGINSHARED_EXPORT RegexPlugin : public QObject,RegExpInterface
{
    Q_OBJECT
    //Q_PLUGIN_METADATA宏用于描述插件元数据,第一个参数为插件的 IID,与接口定义的IID相同
    //第二个参数FILE是可选的,指定一个本地json文件,该文件中描述插件的相关数据信息
    Q_PLUGIN_METADATA(IID RegExpInterface_iid FILE "regexplugindata.json")
    //Q_INTERFACES 宏用于告诉 Qt 该类实现的接口。
    Q_INTERFACES(RegExpInterface)
public:
    RegexPlugin();
    // RegExpInterface interface
public:
    QString regexp(const QString &message);
};
////---------------------------------------------------------------------------------------step 4 使用接口
// 进入插件目录
QDir pluginsDir(qApp->applicationDirPath());
pluginsDir.cd("bin");
// 遍历插件目录
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
    QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
    QObject *plugin = pluginLoader.instance();
    if (plugin) {
        //取回json文件的内容
        QJsonObject w = pluginLoader.metaData().value("MetaData").toObject();
        qDebug()<<w;
        regexpInterface = qobject_cast<RegExpInterface *>(plugin);
        if (regexpInterface)
            return true;
    }
}

 

自己写的插件框架:定义消息传递基类+使用(独立)

//-------------------------------------------------------------------BlzBaseMsgData.h
/**
 * 继承和多态:统一消息传递类型(暂时没有使用)
 * 单头文件:定义消息传递基类(只存放所有模块的消息传递基础数据,非详细数据)
 */
#pragma once
#include <QObject>
#include <QDateTime>

/******************消息 相关全局变量******************/
const int MSG_FALSE = -1;       //失败
const int MSG_TRUE = 0;         //成功

//数据类的基类:消息中的数据
class BlzBaseData
{
public:
    virtual ~BlzBaseData() = default;
};


//消息类(用于信号槽):统一函数参数
struct BlzMessage
{
    //QString from;   //消息来源地
    //QString dest;   //消息目的地
    //QString msg;    //消息简述

    int nId;                            //消息id
    int nResult = MSG_TRUE;             //执行结果
    std::shared_ptr<BlzBaseData> pBaseData = nullptr;   //消息数据
};
Q_DECLARE_METATYPE(BlzMessage); //确保类型可以通过信号槽传递


/******************模块类的基类******************/
//模块类的基类:统一模块对外发送消息接口和接收消息接口
class BlzInterface: public QObject
{
    Q_OBJECT
public:
    BlzInterface(QObject* parent): QObject(parent) {}
signals:
    void signalSendMesage(BlzMessage& blzMessage);
public slots:
    virtual void slotRecvMessage(BlzMessage& blzMessage) {}
};

/******************宏函数:防止每个模块中消息id重复******************/
#define MESSAGE_ID_GENERATE(a, b)   ( ((a) << (b)) % UINT_MAX )
#define MESSAGE_ID_BLZ10                0x10    //Blz10
#define MESSAGE_ID_BLZ11                0x11    //Blz11
#define MESSAGE_ID_BLZ12                0x12    //Blz12
#define MESSAGE_ID_BLZRTC               0x13    //BlzRTC 
//-------------------------------------------------------------------end

//-------------------------------------------------------------------BlzRTCMsgData.h
#pragma once
/**
 * 单一个头文件:定义消息传递类型(只存放该模块的消息传递详细数据)
 */
#include "Common/BlzBaseMsgData.h"

/******************消息 id******************/
const unsigned int BLZRTC_SET_DATETIME = MESSAGE_ID_GENERATE(MESSAGE_ID_BLZRTC, 0x01);

/******************消息 class******************/
class BlzRTCData : public BlzBaseData
{
public:
    BlzRTCData() {}
    ~BlzRTCData() override = default;

    QDateTime m_dateTime;
};
//-------------------------------------------------------------------end

//-------------------------------------------------------------------BlzRTC.h
#pragma once
#include <QObject>
#include "Common/BlzBaseMsgData.h"
#include "BlzRTCMsgData.h"
#include "hs_rtc.h"

class BlzRTC : public BlzInterface
{
    Q_OBJECT
public:
    BlzRTC(BlzInterface* parent = nullptr);
    ~BlzRTC() override = default;

    bool setDateTime(QDateTime& dt);

private:
    //设置系统日期时间
    void setDateTime(const BlzMessage& blzMessage);

public slots:
    void slotRecvMessage(BlzMessage& blzMessage) override;
};
//-------------------------------------------------------------------end

//-------------------------------------------------------------------BlzRTC.cpp
#include "BlzRTC.h"

BlzRTC::BlzRTC(BlzInterface* parent) : BlzInterface(parent) {}

bool BlzRTC::setDateTime(QDateTime& dt)
{
    return rtc_set_DateTime(dt);
}

void BlzRTC::setDateTime(const BlzMessage& blzMessage)
{
    if (blzMessage.pBaseData == nullptr)
    {
        return;
    }
    std::shared_ptr<BlzRTCData> pData = std::dynamic_pointer_cast<BlzRTCData>(blzMessage.pBaseData);
}

void BlzRTC::slotRecvMessage(BlzMessage& blzMessage)
{
    switch (blzMessage.nId)
    {
    case BLZRTC_SET_DATETIME:
        setDateTime(blzMessage);
        break;
    default:
        break;
    }
}
//-------------------------------------------------------------------end

插件框架项目目录(独立)

1、基础接口类(`PluginBase`)
- **职责**:定义插件必须实现的接口,作为所有插件的基类。
- **纯虚函数**:
- `init()`:插件初始化(如资源加载)。
- `exec()`:插件核心功能执行。
- `destroy()`:插件销毁(如释放资源)。
- `getPluginInfo()`:获取插件元信息(名称、版本等)。

2、插件管理器类(`PluginManager`)
- **职责**:管理插件的加载、卸载、调用和生命周期。
- **核心功能**:
- 动态加载插件库(`loadPlugin()`)。
- 枚举已加载插件(`listPlugins()`)。
- 执行指定插件(`executePlugin()`)。
- 统一管理插件的初始化和销毁。

3、工具类(`PlatformUtils`、`Logger`)
- **`PlatformUtils`**:封装动态库加载的平台差异(Windows 的 `LoadLibrary`/Linux 的 `dlopen`)。
- **`Logger`**:提供日志输出功能,用于框架调试和插件监控。

4、示例插件实现

5、使用流程
**编译插件**:将示例插件编译为动态库(Windows 生成 `.dll`,Linux 生成 `.so`)。
**加载插件**:通过 `PluginManager::loadPlugin("path/to/plugin.so")` 加载。
**执行插件**:调用 `executePlugin` 触发插件功能。
**卸载插件**:程序退出前调用 `unloadAllPlugins` 释放资源。

6、扩展建议
**插件通信**:通过消息机制(如观察者模式)实现插件与主程序或插件间通信。
**安全校验**:添加插件签名校验,防止恶意代码注入。
**热更新**:支持运行时重新加载插件,无需重启主程序。
**依赖管理**:为插件添加依赖声明,由管理器自动解析加载顺序。

plugin_framework                     //框架基于动态链接库(dll/so)实现插件加载
----include/                         //头文件目录
--------plugin_base.h                //插件基类与接口定义
--------plugin_manager.h             //插件管理器类
--------utils/                       //工具头文件
------------platform.h               //平台相关API(动态库加载)
------------logger.h                 //日志工具
----src/                                  //源文件目录
--------plugin_base.cpp
--------plugin_manager.cpp
--------utils/
------------platform.cpp
------------logger.cpp
----plugins/                       //插件示例目录
--------example_plugin/            //示例插件
------------example_plugin.h       //插件头文件(继承基类)
------------example_plugin.cpp     //插件源文件
--------cmakelist.txt              //插件编译配置(生成动态库)
----test/                                  //测试目录
--------test_main.cpp                      //框架测试代码
----cmakelist.txt                      //框架编译配置
----readme.txt

end

posted @ 2022-01-06 11:50  Citrusliu  阅读(626)  评论(0)    收藏  举报