天天看點

Qt插件開發總結--主界面添加插件菜單

文章目錄

  • ​​一、前言​​
  • ​​二、效果展示​​
  • ​​三、流程圖​​
  • ​​四、原理詳解​​
  • ​​1、插件資訊中定義插件菜單項​​
  • ​​2、插件接口中定義插件菜單項函數映射連結清單​​
  • ​​3、插件中初始化插件菜單項函數映射連結清單​​
  • ​​4、插件管理器加載插件時讀取插件資訊并分發到主界面建立菜單項​​
  • ​​5、主界面菜單項槽函數​​
  • ​​五、完整代碼​​
  • ​​1、工程結構​​
  • ​​2、PluginApp​​
  • ​​3、PluginInterface.h(插件接口)​​
  • ​​4、MainWin​​
  • ​​5、pluginA​​
  • ​​6、pluginB​​

一、前言

通過之前的博文,我們已經知道了:

  • 怎麼建立插件;Qt插件開發總結–插件的建立及使用
  • 怎麼管理插件;Qt插件開發總結–插件管理器
  • 怎麼實作插件間通信;Qt插件開發總結–插件間通信

添加插件的目的是為了擴充系統,是以當系統加載了插件,系統應該有插件功能接口:

  • 插件加載成功後,自動出現在主界面中;
  • 插件解除安裝成功後,自動從主界面消退;

這篇博文的目的:加載插件,插件中的==菜單項(QAction)==自動添加到主界面,主界面點選菜單項,調用插件内的槽函數;

二、效果展示

Qt插件開發總結--主界面添加插件菜單

三、流程圖

Qt插件開發總結--主界面添加插件菜單

四、原理詳解

1、插件資訊中定義插件菜單項

每個插件都有自己的插件資訊,例如:pluginA.jsom

{
    "author" : "Wang_JC",
    "date" : "2022/02/16",
    "name" : "pluginA",
    "version" : "1.0.0",
    "des" : "這是一個插件A",
    "dependencies" : [],
    "action" : ["action_A1","action_A2","action_A3"]
}      
Qt插件開發總結--主界面添加插件菜單

我們将插件菜單項定義到插件資訊的action項中,如上,插件A有三個菜單項,分别是:action_A1、action_A2、action_A3

2、插件接口中定義插件菜單項函數映射連結清單

#include <functional>
using namespace std;

QList<QString> _actionName; //通用元件Action名字
QList<function<void(bool)> > _actionFunction;   //通用元件Action函數集合      

3、插件中初始化插件菜單項函數映射連結清單

PluginA::PluginA(QObject *parent)
{
    _Plugin_Name = "PluginA";

    _actionName.push_back("action_A1");
    _actionName.push_back("action_A2");
    _actionName.push_back("action_A3");

    _actionFunction.push_back(slot_action_A1);
    _actionFunction.push_back(slot_action_A2);
    _actionFunction.push_back(slot_action_A3);
}      

4、插件管理器加載插件時讀取插件資訊并分發到主界面建立菜單項

插件加載函數:加載插件時掃描插件資訊,并檢測插件菜單項

void PluginManager::loadPlugin(const QString &filepath)
{
    if(!QLibrary::isLibrary(filepath))
        return;

    //檢測依賴
    if(!managerPrivate->check(filepath))
        return;

    if(managerPrivate->m_loaders.keys().contains(filepath)) {
        return;
    }

    //加載插件
    QPluginLoader *loader = new QPluginLoader(filepath);
    if(loader->load()) {
        PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
        if(plugin) {
            //掃描元資訊
            QJsonObject json;
            scanMetaData(filepath,json);    
            deal_metaData_action(loader,json);  //檢測插件資訊中是否有action

            plugin->Info(QString(" %1 加載成功!").arg(plugin->_Plugin_Name));
            managerPrivate->m_loaders.insert(filepath, loader);
            connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),
                    this,SLOT(recMsgFromPlugin(PluginMetaData)));
        }else {
            delete loader;
            loader = nullptr;
        }
    }else{
        qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();
    }
}      
插件加載函數,此函數和Qt插件開發總結–插件管理器這篇文章中對比,做了略微調整;

掃描JSON檔案中的插件資料

void PluginManager::scanMetaData(const QString &filepath,QJsonObject& json)
{
    //判斷是否為庫(字尾有效性)
    if(!QLibrary::isLibrary(filepath))
        return;

    //擷取中繼資料
    QPluginLoader *loader = new QPluginLoader(filepath);
    
    json = loader->metaData().value("MetaData").toObject();

    managerPrivate->m_names.insert(filepath, json.value("name").toVariant());
    managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());
    managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());

    delete loader;
    loader = nullptr;
}      
掃描插件資訊函數,此函數和Qt插件開發總結–插件管理器這篇文章中對比,做了略微調整;

檢測插件資訊中是否含有action(插件菜單),如果有則将插件菜單和插件執行個體存入插件管理器的插件執行個體-菜單項池中

void PluginManager::deal_metaData_action(QPluginLoader* loader,QJsonObject& json)
{
    QStringList list;
    if(json.keys().contains("action")) {
       QJsonArray JArray = json.value("action").toArray();
       for(int i=0;i<JArray.size();++i) {
           list << JArray.at(i).toString();

           MANAGER_ACTION_MAP manager_action_map;
           manager_action_map.action = JArray.at(i).toString();
           manager_action_map.plugin = loader;
           _actionMap.push_back(manager_action_map);
       }
    }

    if(!list.empty()) {
        emit sig_actions(list);    //如果檢測到插件菜單項,發送至主界面
    }
}      

主界面加載插件,并綁定插件管理器中發送的插件菜單項信号

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    Init_UI();

    //嗅探到的所有插件
    qDebug()<<"CK_allPluginsName: "<<PluginManager::instance()->CK_allPluginsName();

    //加載其中某個插件
    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginA"));
    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginB"));
}

void MainWindow::Init_UI()
{
    //主界面預留插件菜單欄Plugins
    menuBar = new QMenuBar(this);
    menuPlugin = new QMenu("Plugins", this);
    menuBar->addMenu(menuPlugin);
    this->setMenuBar(menuBar);

    connect(PluginManager::instance(),&PluginManager::sig_actions,this,&MainWindow::slot_PluginAction);    //插件管理器會将插件菜單項發送到主界面
}      

主界面依據插件管理器發送的插件菜單項信号,建立菜單

void MainWindow::slot_PluginAction(QStringList list)
{
    QAction * action = nullptr;
    for(int i=0; i<list.size(); ++i) {
        action = new QAction(QIcon(), list.at(i), this);
        menuPlugin->addAction(action);
        connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
    }
}      

5、主界面菜單項槽函數

  • 擷取主界面點選的Action的名稱;
  • 去插件管理器菜單項池中中比對,找到改菜單項對應的插件執行個體;
  • 将該執行個體轉換為接口指針,比對插件菜單項函數映射連結清單;
  • 找到主界面點選的Action對應插件裡的函數指針,并調用;
void MainWindow::slot_PluginsAction_trigger(bool isChecked)
{
    QAction* action = qobject_cast<QAction*>(sender());
    for(int i=0; i<PluginManager::instance()->_actionMap.size(); ++i) { //周遊插件管理器action映射表
        if(PluginManager::instance()->_actionMap.at(i).action == action->text()) {  //映射表中比對到Action對應的方法
            PluginInterface* plugin = qobject_cast<PluginInterface *>(PluginManager::instance()->_actionMap.at(i).plugin->instance());  //擷取該action對應的接口指針
            if(plugin) {
                for(int j=0; j<plugin->_actionName.size(); ++j) {   //周遊該接口指針内的action名字
                    if(plugin->_actionName[j] == action->text()) {
                        plugin->_actionFunction[j](true);
                        break;
                    }
                }
            }
            break;
        }
    }
}      

五、完整代碼

1、工程結構

Qt插件開發總結--主界面添加插件菜單
Qt插件開發總結--主界面添加插件菜單

2、PluginApp

檔案:PluginApp.pro

TEMPLATE = subdirs

SUBDIRS +=      

3、PluginInterface.h(插件接口)

#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H

#include <QObject>
#include <QJsonObject>
#include <functional>
using namespace std;

struct PluginMetaData
{
    QString from;   //消息來源
    QString dest;   //消息去向
    QString msg;    //消息

    QObject* object = nullptr;
    QJsonObject info = QJsonObject();
};
Q_DECLARE_METATYPE(PluginMetaData); //確定類型可以通過信号槽傳遞

//定義接口
class PluginInterface : public QObject
{
    Q_OBJECT
public:
    virtual ~PluginInterface(){}
    virtual void Info(QString) = 0;

    virtual void recMsgFromManager(PluginMetaData) = 0; //接收來自插件管理器的消息
    virtual void sendMsgToManager(PluginMetaData) = 0;  //發送消息到插件管理器

public:
    QString _Plugin_Name;

public:
    QList<QString> _actionName; //通用元件Action名字
    QList<function<void(bool)> > _actionFunction;   //通用元件Action函數集合

};

//一定是唯一的辨別符
#define PluginInterface_iid "Examples.Plugin.PluginInterface"

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(PluginInterface,PluginInterface_iid)
QT_END_NAMESPACE

#endif // PLUGININTERFACE_H      

4、MainWin

檔案:MainWin.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler).
# 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 it uses 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

SOURCES += \
    PluginManager.cpp \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    ../Plugin_Interface/PluginInterface.h \
    PluginManager.h \
    mainwindow.h

FORMS += \
    mainwindow.ui

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

檔案:mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include "PluginManager.h"
#include <QDir>
#include <QMenuBar>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void Init_UI();

    QMenuBar* menuBar;
    QMenu * menuPlugin;

private slots:
    void slot_PluginAction(QStringList);    //添加插件Action
    void slot_PluginsAction_trigger(bool isChecked);    //響應插件Action

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H      

檔案:mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    Init_UI();

    //嗅探到的所有插件
    qDebug()<<"CK_allPluginsName: "<<PluginManager::instance()->CK_allPluginsName();

    //加載其中某個插件
    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginA"));
    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginB"));
}

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

void MainWindow::Init_UI()
{
    menuBar = new QMenuBar(this);
    menuPlugin = new QMenu("Plugins", this);
    menuBar->addMenu(menuPlugin);
    this->setMenuBar(menuBar);

    connect(PluginManager::instance(),&PluginManager::sig_actions,this,&MainWindow::slot_PluginAction);
}

void MainWindow::slot_PluginAction(QStringList list)
{
    QAction * action = nullptr;
    for(int i=0; i<list.size(); ++i) {
        action = new QAction(QIcon(), list.at(i), this);
        menuPlugin->addAction(action);
        connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
    }
}
void MainWindow::slot_PluginsAction_trigger(bool isChecked)
{
    QAction* action = qobject_cast<QAction*>(sender());
    for(int i=0; i<PluginManager::instance()->_actionMap.size(); ++i) { //周遊插件管理器action映射表
        if(PluginManager::instance()->_actionMap.at(i).action == action->text()) {  //映射表中比對到Action對應的方法
            PluginInterface* plugin = qobject_cast<PluginInterface *>(PluginManager::instance()->_actionMap.at(i).plugin->instance());  //擷取該action對應的接口指針
            if(plugin) {
                for(int j=0; j<plugin->_actionName.size(); ++j) {   //周遊該接口指針内的action名字
                    if(plugin->_actionName[j] == action->text()) {
                        plugin->_actionFunction[j](true);
                        break;
                    }
                }
            }
            break;
        }
    }
}      

檔案:PluginManager.h

#ifndef PLUGINMANAGER_H
#define PLUGINMANAGER_H

#include "../Plugin_Interface/PluginInterface.h"
#include <QObject>
#include <QPluginLoader>
#include <QVariant>
#include <QAction>

typedef struct manager_action_map
{
    QString action;
    QPluginLoader* plugin;
}MANAGER_ACTION_MAP;

class PluginManager : public QObject
{
    Q_OBJECT
public:
    explicit PluginManager(QObject *parent = nullptr);
    ~PluginManager();

    static PluginManager *instance(){
         if(m_instance==nullptr)
             m_instance=new PluginManager();
         return m_instance;
     }

public:
    QList<MANAGER_ACTION_MAP> _actionMap;
    void deal_metaData_action(QPluginLoader* loader,QJsonObject& json);


public:
    //掃描JSON檔案中的插件中繼資料
    void scanMetaData(const QString &filepath,QJsonObject& json);

     //加載所有插件
     void loadAllPlugins();

     //加載其中某個插件
     void loadPlugin(const QString &filepath);

     //解除安裝所有插件
     void unloadAllPlugins();

     //解除安裝某個插件
     void unloadPlugin(const QString &filepath);

     //擷取所有插件名稱
     QList<QVariant> allPluginsName();

     //擷取所有插件
     QList<QPluginLoader *> allPlugins();

     //擷取某個插件名稱
     QVariant getPluginName(QPluginLoader *loader);

     //根據名稱獲得插件
     QPluginLoader* getPlugin(const QString &name);

     //擷取庫中所有插件名稱
     QHash<QString,QString> CK_allPluginsName();

signals:
     void sig_actions(QStringList);

public slots:
     void recMsgFromPlugin(PluginMetaData);

private:
     static PluginManager *m_instance;

     class PluginsManagerPrivate;
     PluginsManagerPrivate *managerPrivate;
};

#endif // PLUGINMANAGER_H      

檔案:PluginManager.cpp

#include "PluginManager.h"
#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>

PluginManager* PluginManager::m_instance=nullptr;

class PluginManager::PluginsManagerPrivate
{
public:
    PluginsManagerPrivate()
    {
        m_names.clear();
        m_versions.clear();
        m_dependencies.clear();
        m_loaders.clear();
    }
    ~PluginsManagerPrivate(){}

    QHash<QString, QVariant> m_names;               //插件路徑--插件名稱
    QHash<QString, QVariant> m_versions;            //插件路徑--插件版本
    QHash<QString, QVariantList> m_dependencies;    //插件路徑--插件額外依賴的其他插件
    QHash<QString, QPluginLoader *> m_loaders;      //插件路徑--QPluginLoader執行個體

    bool check(const QString &filepath)             //插件依賴檢測
    {
        bool status = true;

        foreach (QVariant item, m_dependencies.value(filepath)) {
            QVariantMap map = item.toMap();
            // 依賴的插件名稱、版本、路徑
            QVariant name = map.value("name");
            QVariant version = map.value("version");
            QString path = m_names.key(name);

            /********** 檢測插件是否依賴于其他插件 **********/
            // 先檢測插件名稱
            if (!m_names.values().contains(name)) {
                qDebug() << Q_FUNC_INFO << "  Missing dependency:" << name.toString() << "for plugin" << path;
                status = false;
                continue;
            }

            // 再檢測插件版本
            if (m_versions.value(path) != version) {
                qDebug() << Q_FUNC_INFO << "    Version mismatch:" << name.toString() << "version"
                         << m_versions.value(m_names.key(name)).toString() << "but" << version.toString() << "required for plugin" << path;
                status = false;
                continue;
            }

            // 然後,檢測被依賴的插件是否還依賴于另外的插件
            if (!check(path)) {
                qDebug() << Q_FUNC_INFO << "Corrupted dependency:" << name.toString() << "for plugin" << path;
                status = false;
                continue;
            }
        }

        return status;
    }

};

PluginManager::PluginManager(QObject *parent) : QObject(parent)
{
    managerPrivate = new PluginsManagerPrivate;
}
PluginManager::~PluginManager()
{
    delete managerPrivate;
}

void PluginManager::loadAllPlugins()
{
    QDir pluginsDir(qApp->applicationDirPath());    //pluginsDir: "../build-xxx-debug/debug"
    if(pluginsDir.dirName().toLower() == "debug" ||
            pluginsDir.dirName().toLower() == "release") {
        pluginsDir.cdUp();  //pluginsDir: "../build-xxx-debug"
        pluginsDir.cdUp();  //pluginsDir: "../"
    }
    pluginsDir.cd("plugins");

    QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

    //加載插件
    for(QFileInfo fileinfo : pluginsInfo)
        loadPlugin(fileinfo.absoluteFilePath());
}

void PluginManager::scanMetaData(const QString &filepath,QJsonObject& json)
{
    //判斷是否為庫(字尾有效性)
    if(!QLibrary::isLibrary(filepath))
        return;

    //擷取中繼資料
    QPluginLoader *loader = new QPluginLoader(filepath);

    //qDebug()<<loader->metaData().keys();
    json = loader->metaData().value("MetaData").toObject();
//    for(int i=0; i<json.keys().size(); ++i) {
//        qDebug()<<json.keys().at(i)<< " : "<<json.value(json.keys().at(i));
//    }

    managerPrivate->m_names.insert(filepath, json.value("name").toVariant());
    managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());
    managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());

    delete loader;
    loader = nullptr;
}

void PluginManager::loadPlugin(const QString &filepath)
{
    if(!QLibrary::isLibrary(filepath))
        return;

    //檢測依賴
    if(!managerPrivate->check(filepath))
        return;

    if(managerPrivate->m_loaders.keys().contains(filepath)) {
        return;
    }

    //加載插件
    QPluginLoader *loader = new QPluginLoader(filepath);
    if(loader->load()) {
        PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
        if(plugin) {
            //掃描元資訊
            QJsonObject json;
            scanMetaData(filepath,json);
            deal_metaData_action(loader,json);  //檢測元資訊中是否有action

            plugin->Info(QString(" %1 加載成功!").arg(plugin->_Plugin_Name));
            managerPrivate->m_loaders.insert(filepath, loader);
            connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),
                    this,SLOT(recMsgFromPlugin(PluginMetaData)));
        }else {
            delete loader;
            loader = nullptr;
        }
    }else{
        qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();
    }
}

void PluginManager::unloadAllPlugins()
{
    for(QString filepath : managerPrivate->m_loaders.keys())
        unloadPlugin(filepath);
}

void PluginManager::unloadPlugin(const QString &filepath)
{
    if(!managerPrivate->m_loaders.keys().contains(filepath)) {
        return;
    }

    QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);
    //解除安裝插件,并從内部資料結構中移除
    if(loader->unload()) {
        PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
        if(plugin) {
            plugin->Info("插件解除安裝成功!");
            managerPrivate->m_loaders.remove(filepath);
            delete loader;
            loader = nullptr;
        }
    }
}

QList<QPluginLoader *> PluginManager::allPlugins()
{
    return managerPrivate->m_loaders.values();
}

QList<QVariant> PluginManager::allPluginsName()
{
    return managerPrivate->m_names.values();
}

QVariant PluginManager::getPluginName(QPluginLoader *loader)
{
    if(loader)
        return managerPrivate->m_names.value(managerPrivate->m_loaders.key(loader));
    else
        return "";
}

QPluginLoader *PluginManager::getPlugin(const QString &name)
{
    return managerPrivate->m_loaders.value(managerPrivate->m_names.key(name));
}

void PluginManager::recMsgFromPlugin(PluginMetaData metaData)
{
    auto loader = getPlugin(metaData.dest); //目标插件
    if(loader) {
        auto interface = qobject_cast<PluginInterface*>(loader->instance());
        if(interface) {
            interface->recMsgFromManager(metaData); //轉發給對應的插件
        }
    }
}

QHash<QString,QString> PluginManager::CK_allPluginsName()
{
    QDir pluginsDir(qApp->applicationDirPath());    //pluginsDir: "../build-xxx-debug/debug"
    if(pluginsDir.dirName().toLower() == "debug" ||
            pluginsDir.dirName().toLower() == "release") {
        pluginsDir.cdUp();  //pluginsDir: "../build-xxx-debug"
        pluginsDir.cdUp();  //pluginsDir: "../"
    }
    pluginsDir.cd("plugins");

    QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

    //庫中插件
    QHash<QString,QString> pluginNames;
    for(QFileInfo fileinfo : pluginsInfo){
        if(fileinfo.fileName().contains(".dll")) {
            QString pluginName = fileinfo.fileName().mid(0,fileinfo.fileName().size()-4);
            QString pluginPath = fileinfo.filePath();
            pluginNames.insert(pluginName,pluginPath);
        }
    }

    return pluginNames;
}

void PluginManager::deal_metaData_action(QPluginLoader* loader,QJsonObject& json)
{
    QStringList list;
    if(json.keys().contains("action")) {
       QJsonArray JArray = json.value("action").toArray();
       for(int i=0;i<JArray.size();++i) {
           list << JArray.at(i).toString();

           MANAGER_ACTION_MAP manager_action_map;
           manager_action_map.action = JArray.at(i).toString();
           manager_action_map.plugin = loader;
           _actionMap.push_back(manager_action_map);
       }
    }

    if(!list.empty()) {
        emit sig_actions(list);
    }
}      

5、pluginA

檔案:pluginA.pro

QT += widgets

TEMPLATE = lib          #表明這個makefile是一個lib的makefile
CONFIG += plugin        #應用程式是一個插件

TARGET = pluginA        #插件名稱
DESTDIR = ../plugins    # 輸出目錄

HEADERS += \
    ../Plugin_Interface/PluginInterface.h \
    pluginA.h

SOURCES += \
    pluginA.cpp

DISTFILES += \
    pluginA.json      

檔案:pluginA.h

#ifndef PLUGINA_H
#define PLUGINA_H

#include <QObject>
#include <QtPlugin>
#include <QDebug>
#include "../Plugin_Interface/PluginInterface.h"

class PluginA : public PluginInterface
{
    Q_OBJECT
    Q_INTERFACES(PluginInterface)
    Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginA.json")
public:
    explicit  PluginA(QObject *parent = nullptr);
    void Info(QString info);

    void recMsgFromManager(PluginMetaData metaData);

signals:
    void sendMsgToManager(PluginMetaData);

public slots:
    static void slot_action_A1(bool isChecked);
    static void slot_action_A2(bool isChecked);
    static void slot_action_A3(bool isChecked);

};

#endif // PLUGINA_H      

檔案:pluginA.cpp

#include "pluginA.h"

PluginA::PluginA(QObject *parent)
{
    _Plugin_Name = "PluginA";

    _actionName.push_back("action_A1");
    _actionName.push_back("action_A2");
    _actionName.push_back("action_A3");

    _actionFunction.push_back(slot_action_A1);
    _actionFunction.push_back(slot_action_A2);
    _actionFunction.push_back(slot_action_A3);
}

void PluginA::Info(QString info)
{
    qDebug()<<info;
}

void PluginA::recMsgFromManager(PluginMetaData metaData)
{
    qDebug()<<"插件A接收到消息:"<<metaData.msg;
}

void PluginA::slot_action_A1(bool isChecked)
{
    qDebug()<<"PluginA::slot_action_A1()";
}
void PluginA::slot_action_A2(bool isChecked)
{
    qDebug()<<"PluginA::slot_action_A2()";
}
void PluginA::slot_action_A3(bool isChecked)
{
    qDebug()<<"PluginA::slot_action_A3()";
}      

檔案:pluginA.json

{
    "author" : "Wang_JC",
    "date" : "2022/02/16",
    "name" : "pluginA",
    "version" : "1.0.0",
    "des" : "這是一個插件A",
    "dependencies" : [],
    "action" : ["action_A1","action_A2","action_A3"]
}      

6、pluginB

檔案:pluginB.pro

QT += widgets

TEMPLATE = lib          #表明這個makefile是一個lib的makefile
CONFIG += plugin        #應用程式是一個插件

TARGET = pluginB        #插件名稱
DESTDIR = ../plugins    # 輸出目錄

HEADERS += \
    ../Plugin_Interface/PluginInterface.h \
    pluginB.h

SOURCES += \
    pluginB.cpp

DISTFILES += \
    pluginB.json      

檔案:pluginB.h

#ifndef PLUGINB_H
#define PLUGINB_H

#include <QObject>
#include <QtPlugin>
#include <QDebug>
#include "../Plugin_Interface/PluginInterface.h"

class PluginB : public PluginInterface
{
    Q_OBJECT
    Q_INTERFACES(PluginInterface)
    Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginB.json")
public:
    explicit  PluginB(QObject *parent = nullptr);
    void Info(QString info);

public:
    void recMsgFromManager(PluginMetaData metaData);

signals:
    void sendMsgToManager(PluginMetaData);

public slots:
    static void slot_action_B1(bool isChecked);
    static void slot_action_B2(bool isChecked);
    static void slot_action_B3(bool isChecked);

};

#endif // PLUGINB_H      
#include "pluginB.h"

PluginB::PluginB(QObject *parent)
{
    _Plugin_Name = "PluginB";

    _actionName.push_back("action_B1");
    _actionName.push_back("action_B2");
    _actionName.push_back("action_B3");

    _actionFunction.push_back(slot_action_B1);
    _actionFunction.push_back(slot_action_B2);
    _actionFunction.push_back(slot_action_B3);
}

void PluginB::Info(QString info)
{
    qDebug()<<info;
}

void PluginB::recMsgFromManager(PluginMetaData metaData)
{
    qDebug()<<"插件B接收到消息:"<<metaData.msg;
}

void PluginB::slot_action_B1(bool isChecked)
{
    qDebug()<<"PluginB::slot_action_B1()";
}
void PluginB::slot_action_B2(bool isChecked)
{
    qDebug()<<"PluginB::slot_action_B2()";
}
void PluginB::slot_action_B3(bool isChecked)
{
    qDebug()<<"PluginB::slot_action_B3()";
}      
{
    "author" : "Wang_JC",
    "date" : "2022/02/16",
    "name" : "pluginB",
    "version" : "1.0.0",
    "des" : "這是一個插件B",
    "dependencies" : [],
    "action" : ["action_B1","action_B2","action_B3"]
}