文章目录

  • 一、前言
  • 二、基本策略
  • 三、效果展示
  • 四、关键代码
  • 4.1、主程序
  • 4.2、插件管理器
  • 4.3、插件A


一、前言

插件大致可分为:功能性插件、界面插件;一个软件由一堆插件堆起来,必然难以避免插件间相互引用,例如:插件A调用了插件C中的功能,那么插件A就依赖插件C,所以在程序启动加载插件时,就应该先加载插件C,再加载插件A。

如果插件A是用于初始化的模块,插件C是用于检测软件序列合法性的模块,如果先加载插件A,并直接调用功能模块,那么检测软件序列合法性这部分功能就是缺失的,所以必须要检测插件间的依赖关系,并排列插件加载顺序,才能使得软件功能正常。

但是插件依赖,不能形成死锁,例如:有向环

Qt插件开发总结6--插件间依赖_加载

  • 图1:先加载插件B和插件C,再加载插件A,即可;
  • 图2:先加载插件C,再加载插件B,再加载插件A,即可;
  • 图3:先加载插件C,再加载插件B,再加载插件A,即可;
  • 图4:加载插件A需要插件B,加载插件B需要插件C,加载插件C需要插件A,就形成死锁了;

二、基本策略

插件元数据中有一项:dependencies,这个就是用来标识该插件依赖的插件,例如:插件A依赖插件B、插件C

{
    "author" : "Wang_JC",
    "date" : "2022/02/16",
    "name" : "pluginA",
    "version" : "1.0.0",
    "des" : "这是一个插件A",
    "dependencies" : ["pluginB:1.0.0","pluginC:1.0.0"],
    "menu" : "menuFile",
    "action" : ["action_A1","action_A2","action_A3"],
    "widget" : "tabWidget"
}

我们在主程序中嗅探、自动加载插件:

Qt插件开发总结6--插件间依赖_#include_02


在加载插件的时候,我们可以读取插件元数据中的插件依赖信息,然后检测依赖;如果存在依赖,那就暂不加载;如果不存在,那就直接加载。

Qt插件开发总结6--插件间依赖_#include_03


检测插件是否存在依赖:可以遍历当前插件依赖项

Qt插件开发总结6--插件间依赖_qt_04


等到自动加载插件全部结束后,我们再检查依赖插件栈内是否有还未加载的插件,有的话,再手动加载一下:

Qt插件开发总结6--插件间依赖_#include_05


三、效果展示

弄了4个插件:A、B、C、D,依赖关系如下:

Qt插件开发总结6--插件间依赖_#include_06

  • 插件A:依赖插件B和插件C
  • 插件B:无依赖
  • 插件C:无依赖
  • 插件D:无依赖

所以加载插件时,插件B和插件C必须要在插件A之间加载,插件D无所谓

Qt插件开发总结6--插件间依赖_#include_07


四、关键代码

4.1、主程序

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include "PluginManager.h"
#include <QDir>
#include <QMenuBar>
#include <QToolBar>
#include <QTabWidget>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>

#include "./Widget/speeddashbroad.h"

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();

    void recvMsgFromManager(PluginMetaData metaData);

    QMenuBar* menuBar;
    QMenu* menuFile;
    QMenu* menuEdit;
    QMenu* menuView;
    QMenu* menuTool;

    QTabWidget* tabWidget;

public:
    SpeedDashBroad* sdb;

signals:
    void sendMsgToManager(PluginMetaData);

private slots:
    void slot_PluginAction_MenuBar(QString menu,QStringList actionList);    //添加插件菜单栏Action
    void slot_PluginsAction_trigger(bool isChecked);    //响应插件Action

    void slot_PluginWidget(QPluginLoader*,QString);    //添加插件widget


private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    //传递主程序指针给插件管理器
    PluginManager::instance()->m_mainWin = this;
    //绑定主程序和插件管理器消息信号槽
    connect(this,&MainWindow::sendMsgToManager,PluginManager::instance(),&PluginManager::recMsgFromPlugin);

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

    //加载所有插件
    foreach(QString pluginName , PluginManager::instance()->CK_allPluginsName().keys()) {
        PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value(pluginName));
    }

    //加载所有插件
//    PluginManager::instance()->loadAllPlugins();

    //加载存在依赖稍候处理的插件
    while(!PluginManager::instance()->m_remainPlugin.isEmpty()) {
        QString plugin_filePath = PluginManager::instance()->m_remainPlugin.pop();
        qDebug()<<endl<<endl<<"加载存在依赖稍候处理的插件: "<<plugin_filePath;

        PluginManager::instance()->loadPlugin(plugin_filePath);
    }

    //加载其中某个插件
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginA"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginB"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginC"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginD"));

//    QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");
//    PluginInterface* pluginInterface1 = qobject_cast<PluginInterface *>(loader1->instance());
//    pluginInterface1->showSomeThing("新增接口API");

//    QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");
//    PluginInterface* pluginInterface2 = qobject_cast<PluginInterface *>(loader2->instance());
//    pluginInterface2->showSomeThing("新增接口API");

//    //通信测试
//    //================================================================================
//    QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");
//    if(loader1) {
//        PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());
//        if(pluginA) {
//            PluginMetaData m;
//            m.dest = "pluginB";
//            m.from = "pluginA";
//            m.msg = "插件A发给插件B的消息";
//            pluginA->sendMsgToManager(m);
//        }
//    }

//    QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");
//    if(loader2) {
//        PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());
//        if(pluginB) {
//            PluginMetaData m;
//            m.dest = "pluginA";
//            m.from = "pluginB";
//            m.msg = "插件B发给插件A的消息";
//            pluginB->sendMsgToManager(m);
//        }
//    }

//    //------------------------------
//    if(loader2) {
//        PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());
//        if(pluginB) {
//            PluginMetaData m1;
//            m1.dest = "mainWin";
//            m1.from = "pluginB";
//            m1.msg = "插件B发给主程序的消息";
//            pluginB->sendMsgToManager(m1);
//        }
//    }

//    //------------------------------
//    PluginMetaData m2;
//    m2.dest = "pluginA";
//    m2.from = "mainWin";
//    m2.msg = "主程序发给插件A的消息";
//    this->sendMsgToManager(m2);

}

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

void MainWindow::Init_UI()
{
    QWidget* p = takeCentralWidget();   //删除中央窗体
    if (p) {
        delete p;
    }
    setDockNestingEnabled(true);        //允许嵌套dock

    //-------------------------------------------------

    menuBar = new QMenuBar(this);

    menuFile = new QMenu("文件", this);
    menuBar->addMenu(menuFile);
    this->setMenuBar(menuBar);

    menuEdit = new QMenu("编辑", this);
    menuBar->addMenu(menuEdit);
    this->setMenuBar(menuBar);

    menuView = new QMenu("视图", this);
    menuBar->addMenu(menuView);
    this->setMenuBar(menuBar);

    menuTool = new QMenu("工具", this);
    menuBar->addMenu(menuTool);
    this->setMenuBar(menuBar);

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

    //-------------------------------------------------

    tabWidget = new QTabWidget(this);
    tabWidget->setMinimumSize(1000, 800);   // 设置最小宽高
    setCentralWidget(tabWidget);            // 指定为中心窗口

    connect(PluginManager::instance(),&PluginManager::sig_widget,this,&MainWindow::slot_PluginWidget);

    //-------------------------------------------------

    sdb = new SpeedDashBroad;
    tabWidget->addTab(sdb,"表盘");
}

void MainWindow::slot_PluginAction_MenuBar(QString menu,QStringList actionList)
{
    QAction * action = nullptr;
    if(menu == QString::fromLocal8Bit("menuFile")) {
        for(int i=0; i<actionList.size(); ++i) {
            action = new QAction(QIcon(), actionList.at(i), this);
            menuFile->addAction(action);
            connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
        }
    }
    if(menu == QString::fromLocal8Bit("menuEdit")) {
        for(int i=0; i<actionList.size(); ++i) {
            action = new QAction(QIcon(), actionList.at(i), this);
            menuEdit->addAction(action);
            connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
        }
    }
    if(menu == QString::fromLocal8Bit("menuView")) {
        for(int i=0; i<actionList.size(); ++i) {
            action = new QAction(QIcon(), actionList.at(i), this);
            menuView->addAction(action);
            connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
        }
    }
    if(menu == QString::fromLocal8Bit("menuTool")) {
        for(int i=0; i<actionList.size(); ++i) {
            action = new QAction(QIcon(), actionList.at(i), this);
            menuTool->addAction(action);
            connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);
        }
    }
}

void MainWindow::recvMsgFromManager(PluginMetaData metaData)
{
    qDebug()<<"主程序接收到消息:"<<metaData.msg;
}

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;
        }
    }
}

void MainWindow::slot_PluginWidget(QPluginLoader* loader,QString widget)
{
    if(widget == QString::fromLocal8Bit("tabWidget")) {
        PluginInterface* pluginInterface = qobject_cast<PluginInterface *>(loader->instance());
        QString pluginName = pluginInterface->_Plugin_Name;
        QWidget* widget = pluginInterface->_widget;

        if(widget) {
            tabWidget->addTab(widget,pluginName);
        }
    }

    //也可以预留布局接入点,插件UI嵌入,看自己需求
    if(widget == QString::fromLocal8Bit("xxxLayout")) {

    }

}

4.2、插件管理器

#ifndef PLUGINMANAGER_H
#define PLUGINMANAGER_H

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

class MainWindow;

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;
     }

    MainWindow* m_mainWin;

    QStack<QString> m_remainPlugin;

public:
    QList<MANAGER_ACTION_MAP> _actionMap;
    void deal_metaData(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(QString,QStringList);
     void sig_widget(QPluginLoader*,QString);

public slots:
     void recMsgFromPlugin(PluginMetaData);

     void slot_test();

private:
     static PluginManager *m_instance;

     class PluginsManagerPrivate;
     PluginsManagerPrivate *managerPrivate;

};

#endif // PLUGINMANAGER_H
#include "PluginManager.h"
#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>
#include "mainwindow.h"

PluginManager* PluginManager::m_instance=nullptr;

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

        m_dependencies_temp.clear();
    }
    ~PluginsManagerPrivate(){}

    QHash<QString, QVariant> m_names;               //插件路径--插件名称
    QHash<QString, QVariant> m_versions;            //插件路径--插件版本
    QHash<QString, QVariantList> m_dependencies;    //插件路径--插件额外依赖的其他插件
    QHash<QString, QPluginLoader *> m_loaders;      //插件路径--QPluginLoader实例

    QHash<QString, QVariantList> m_dependencies_temp;

    bool check(const QString &filepath)             //插件依赖检测
    {
        //qDebug()<<QString("=============== bool check(%1) ===============").arg(filepath);
        bool status = true;

        foreach (QVariant item, m_dependencies_temp.value(filepath)) {
            QString dependencyPluginInfo = item.toString();
            // 依赖的插件名称、版本、路径
            QStringList List = dependencyPluginInfo.split(':');
            QString name_str = List[0];
            QString version_str = List[1];
            QString path = m_names.key(name_str);

            qDebug()<<"=== 插件依赖信息 ===";
            qDebug()<<"name_str: "<<name_str;
            qDebug()<<"version_str: "<<version_str;
            qDebug()<<"path: "<<path;

            QVariant name = QVariant(name_str);
            QVariant version = QVariant(version_str);

            /********** 检测插件是否依赖于其他插件 **********/
            // 先检测插件名称
            if (!m_names.values().contains(name)) {
                qDebug() << "=== 插件" << filepath <<"  缺少依赖插件:" << name.toString();
                status = false;
                continue;
            }

            // 再检测插件版本
            if (m_versions.value(path) != version) {
                qDebug() << "=== 依赖插件: " << name.toString() << "当前版本为: "
                         << m_versions.value(m_names.key(name)).toString() << "但是需要依赖插件版本为: " << version.toString();
                status = false;
                continue;
            }

            // 然后,检测被依赖的插件是否还依赖于另外的插件
            if (!check(path)) {
                qDebug() << "=== 依赖插件:" << name.toString() << "又依赖: " << path;
                status = false;
                continue;
            }
        }


        //qDebug()<<"status: "<<status;
        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;

    if(managerPrivate->m_names.keys().contains(filepath)) {
        //qDebug()<<QString("插件: %1 已加载,退出!!!").arg(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());
    //qDebug()<<"dependencies: "<<json.value("dependencies").toArray().toVariantList();
    //qDebug()<<"managerPrivate->m_dependencies: "<<managerPrivate->m_dependencies.values();

    delete loader;
    loader = nullptr;
}

void PluginManager::loadPlugin(const QString &filepath)
{
    //库文件类型检测
    if(!QLibrary::isLibrary(filepath))
        return;

    //读取当前插件依赖
    qDebug()<<"===========================================================================";
    QPluginLoader *loader_temp = new QPluginLoader(filepath);
    QJsonObject json_temp = loader_temp->metaData().value("MetaData").toObject();
    managerPrivate->m_dependencies_temp.insert(filepath, json_temp.value("dependencies").toArray().toVariantList());
    //qDebug()<<"managerPrivate->m_dependencies_temp: "<<managerPrivate->m_dependencies_temp;
    delete loader_temp;
    loader_temp = nullptr;

    qDebug()<<"@@@ 当前加载插件: "<<filepath;

    //检测依赖
    if(!managerPrivate->check(filepath)) {
        qDebug()<<"当前插件存在依赖,入栈,稍候处理: "<<filepath;
        m_remainPlugin.push(filepath);

        //清空当前插件依赖
        managerPrivate->m_dependencies_temp.clear();

        return;
    }

    //清空当前插件依赖
    managerPrivate->m_dependencies_temp.clear();

    //检测当前插件是否已加载
    if(managerPrivate->m_loaders.keys().contains(filepath)) {
        qDebug()<<"当前插件已加载!!!";
        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(loader,json);

            managerPrivate->m_loaders.insert(filepath, loader);
            connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),
                    this,SLOT(recMsgFromPlugin(PluginMetaData)));

            //绑定
            if(plugin->_Plugin_Name == QString::fromLocal8Bit("PluginA")) {
                //qDebug()<<"plugin->_Plugin_Name: "<<plugin->_Plugin_Name;
                connect(loader->instance(),SIGNAL(sig_test()),
                        this,SLOT(slot_test()));
            }

            plugin->Info(QString(" %1 加载成功!").arg(plugin->_Plugin_Name));

        }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)
{
    //和主程序通信
    //------------------------------------------------------------
    if(metaData.dest == QString::fromLocal8Bit("mainWin")) {
        if(m_mainWin) {
            m_mainWin->recvMsgFromManager(metaData);
        }
        return;
    }

    //和插件通信
    //------------------------------------------------------------
    auto loader = getPlugin(metaData.dest); //目标插件
    if(loader) {
        auto interface = qobject_cast<PluginInterface*>(loader->instance());
        if(interface) {
            interface->recMsgFromManager(metaData); //转发给对应的插件
        }
    }

}

void PluginManager::slot_test()
{
    //qDebug()<<"触发槽函数: PluginManager::slot_test()";
}

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(QPluginLoader* loader,QJsonObject& json)
{
    QString name;
    if(json.keys().contains("name")) {
        QJsonValue JValue = json.value("name").toString();
        name = JValue.toString();
    }
    //------------------------------------------------------------
    QString menu;
    QStringList actionList;
    if(json.keys().contains("menu")) {
        QJsonValue JValue = json.value("menu").toString();
        menu = JValue.toString();
    }
    if(json.keys().contains("action")) {
       QJsonArray JArray = json.value("action").toArray();
       for(int i=0;i<JArray.size();++i) {
           actionList << 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);
       }
    }

    QStringList dependencies_List;
    if(json.keys().contains("dependencies")) {
       QJsonArray JArray = json.value("dependencies").toArray();
       for(int i=0;i<JArray.size();++i) {
           dependencies_List << JArray.at(i).toString();
       }
    }
    //qDebug()<<"dependencies_List: "<<dependencies_List;

    //------------------------------------------------------------
    if(!menu.isEmpty() && !actionList.empty()) {
        emit sig_actions(menu,actionList);
    }
    //------------------------------------------------------------

    //------------------------------------------------------------
    QString widget;
    if(json.keys().contains("widget")) {
        QJsonValue JValue = json.value("widget").toString();
        widget = JValue.toString();

        if(!widget.isEmpty()) {
            sig_widget(loader,widget);  //发送:插件对象、主界面预留接入点
        }
    }
    //------------------------------------------------------------
}

4.3、插件A

pluginA.json

{
    "author" : "Wang_JC",
    "date" : "2022/02/16",
    "name" : "pluginA",
    "version" : "1.0.0",
    "des" : "这是一个插件A",
    "dependencies" : ["pluginB:1.0.0","pluginC:1.0.0"],
    "menu" : "menuFile",
    "action" : ["action_A1","action_A2","action_A3"],
    "widget" : "tabWidget"
}