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

news2024/11/22 14:51:45

文章目录

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

一、前言

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

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

但是插件依赖,不能形成死锁,例如:有向环
在这里插入图片描述

  • 图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"
}

我们在主程序中嗅探、自动加载插件:
在这里插入图片描述
在加载插件的时候,我们可以读取插件元数据中的插件依赖信息,然后检测依赖;如果存在依赖,那就暂不加载;如果不存在,那就直接加载。
在这里插入图片描述
检测插件是否存在依赖:可以遍历当前插件依赖项
在这里插入图片描述
等到自动加载插件全部结束后,我们再检查依赖插件栈内是否有还未加载的插件,有的话,再手动加载一下:
在这里插入图片描述


三、效果展示

弄了4个插件:A、B、C、D,依赖关系如下:
请添加图片描述

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

所以加载插件时,插件B和插件C必须要在插件A之间加载,插件D无所谓
在这里插入图片描述


四、关键代码

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安防监控/视频汇聚EasyCVR视频监控平台级联上级,无法播放是什么原因?

EasyCVR视频监控平台&#xff0c;作为一款智能视频监控综合管理平台&#xff0c;凭借其强大的视频融合汇聚能力和灵活的视频能力&#xff0c;在各行各业的应用中发挥着越来越重要的作用。EasyCVR视频汇聚平台采用先进的图像处理技术和传输协议&#xff0c;能够确保高清、稳定的…

Qt (13)【Qt窗口 —— 颜色对话框 QColorDialog】

阅读导航 引言一、颜色对话框 QColorDialog简介二、常用方法介绍⭕参数说明 三、使用示例 引言 在深入探讨了Qt的QMessageBox之后&#xff0c;我们现在转向QColorDialog&#xff0c;一个让用户轻松选择颜色的实用对话框&#xff0c;为Qt应用增添色彩选择的便捷性。 一、颜色对…

Java语言程序设计基础篇_编程练习题题目:17.16 (字符的頻率)

题目&#xff1a;17.16 (字符的頻率) 编写一个程序&#xff0c;提示用户输入一个 ASCII 文本文件名&#xff0c;然后显示文件中每个字符出现的频率。 代码示例 编程练习题17_16FrequencyOfCharacters.java package chapter_17;import java.io.DataInputStream; import java…

【HarmonyOS 4.0】网络请求 - axios

axios 相当于鸿蒙应用项目的一个第三方库&#xff0c;鸿蒙应用项目使用ohpm管理&#xff08;包括安装&#xff0c;卸载等&#xff09;第三方库。除了axios&#xff0c;还有很多其他的第三方库可供开发者使用&#xff0c;所有的第三方库都收录在OpenHarmony三方库中心仓。 1. 安…

以低代码技术加速推动企业数字创新

在数字化转型的浪潮中&#xff0c;企业面临着前所未有的挑战和机遇。随着技术的不断进步&#xff0c;企业需要快速适应市场变化&#xff0c;创新产品和服务&#xff0c;以保持竞争力。低代码技术作为一种新兴的开发模式&#xff0c;正逐渐成为企业数字创新的重要推动力。 低代码…

电比例流量泵放大器

电比例泵控制是一种精准的液压传动控制系统&#xff0c;利用输入的电信号按比例来调制液压参数&#xff0c;以实现对流量、压力和方向的连续且成比例的控制。这种控制在现代工业中的应用非常广泛&#xff0c;尤其在需要精确控制的场合。 按控制元件分类&#xff1a;电液比例控…

无人机培训机构必备运营合格证及驾驶员训练机构合格证详解

无人机培训机构在运营过程中&#xff0c;必须持有必要的运营合格证及驾驶员训练机构合格证&#xff0c;以确保其培训活动的合法性、规范性和安全性。以下是对这两种合格证的详细解析&#xff1a; 无人机培训机构运营合格证 无人机培训机构运营合格证是国家或地区民航管理部门…

【软考中级攻略站】-软件设计师(1)-数值及其转换和数据表示

进制转换 n进制->十进制 步骤说明&#xff1a; 识别基数&#xff1a;首先确认你正在处理的是n进制数&#xff0c;这里的n就是该数的基数。 数位权重&#xff1a;从右向左数&#xff0c;每个位置上的数字都有一个权重&#xff0c;最右边的位置权重为0&#xff0c;依次向左…

【JUC】10-Java内存模型JMM

1. JMM 通过JMM来实现和主内存之间的抽象关系。屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果。 三大特性&#xff1a;原子性&#xff1a;互斥&#xff0c;同时成功或失败。有序性&#xff1a;指令重排序后有序。可见性&a…

MySQL递归获取商品分类以及所有下级分类商品

现在就是我们有一张商品表&#xff0c;还有一张分类表&#xff0c;商品表有一个字段用来记录当前商品分类id的&#xff0c;然后查询时候因为分类是有层级的&#xff0c;如果我们想通过顶层分类获取到它和所有子类的商品时就会需要用到MySQL的递归查询了。 首先是针对 MySQL 8.…

NCMMSC-CNVSRC 2024视觉语音识别竞赛圆满落幕

8月16日上午&#xff0c;NCMMSC-CNVSRC 2024 学术研讨会在乌鲁木齐召开的第十九届全国人机语音通讯学术会议&#xff08;NCMMSC 2024&#xff09;上举行。会上公布了本次视觉语音识别竞赛 CNVSRC 2024 的最终结果&#xff0c;并举行了颁奖仪式。 本次竞赛由 NCMMSC 2024 组委会…

springboot+vue 进销存管理系统

springbootvue 进销存管理系统 相关技术springbootmybatismysqlmavenvueelementui

ANet-1E1S1智能网关:物联网时代的能源计量新纪元,断点续传引领高效运维

在万物互联的物联网时代&#xff0c;数据的精准采集与高效传输成为了推动各行业智能化转型的关键。ANet-1E1S1以其断点续传技术与强大的RS485接口智能通信管理能力&#xff0c;为水、电、气、油等多领域能源管理带来了便捷与效率的提升。基于嵌入式Linux平台的通用型智能通信管…

高经费打造的史诗级视觉盛宴,惊叹于每一帧的奢华

8月29日&#xff0c;备受期待的《指环王&#xff1a;力量之戒》第二季终于上线了。这一季一上架就放出了三集&#xff0c;立刻引发了影迷们的热烈讨论。 自从2022年首季首播以来&#xff0c;《指环王&#xff1a;力量之戒》就一直备受瞩目。尽管首季受到了不少争议&#xff0c;…

【开源大模型生态2】数据、算力、算法,越来越猛!

人工智能(A)的快速发展依赖于三个核心要素:数据&#xff0c;算法&#xff0c;算力。这个观点已经得到了业界的高度认可。只有这三个要素同时满足了才能加速人工智能的大发展。随着人工智能大模型规模变大以及普及应用&#xff0c;人工智能对能源的需求也在不断加大&#xff0c;…

linux文件——文件系统与内存管理——理解打开文件, 写入数据与文件系统的关系

前言&#xff1a;本节课算是一点文件系统的补充内容。 但是说是文件系统的补充内容&#xff0c;其实我们也可以把这篇文章当作linux下的内存管理的文章来看待。 因为博主会从内存管理的角度&#xff0c; 将进程打开文件、写入数据的流程&#xff0c; 以非常底层的角度&#xff…

【C++】汇编分析,函数是如何调用,传参,返回

传参 有的是用寄存器传参&#xff0c;有的用push传参 我在MSVC编译测出来的是PUSH传参&#xff08;debug模式&#xff09;&#xff0c;具体过程如下 long func(long a, long b, long c, long d,long e, long f, long g, long h) {long sum;sum (a b c d e f g h);ret…

护栏碰撞监测系统的发展前景如何?

​ ​护栏碰撞监测系统的发展前景可谓十分广阔&#xff0c;主要体现在以下几个方面&#xff1a; ​ ​一、交通领域需求持续增长 ​ ​1. 道路建设不断拓展 ​ ​●在全球范围内&#xff0c;公路、高速公路以及城市道路等基础设施持续处于新建与扩建的进程之中。以…

再也不用来回切换了,多微同一界面聚合聊天

手上有多个微信&#xff0c;工作时经常要来回切换登录&#xff0c;才能处理聊天和发圈等工作&#xff0c;来回切换不但容易导致号的状态存在异常&#xff0c;还会浪费很多时间登录&#xff0c;忙的时候还可能会导致处理不及时。 今天必须要给你们分享这个超棒的东西&#xff0…

如何写接口自动化测试断言?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 你真的会写自动化测试断言吗&#xff1f; 在接口测试中&#xff0c;断言是一项非常重要的操作&#xff0c;它是用来校验接口返回结果是否符合预期的一种手段。…