一、前言
在本系列文章:Qt插件开发总结3–插件间通信 一文中详细讲解了,插件-插件通信、插件-主程序通信。
但是这样虽然实现了通信,但是存在一定的弊端,之前的通信结构如下所示:
插件和主程序都必须依靠【插件管理器】作为中转站,来相互通信。如果一旦通信比较密集,那么插件管理器肯定负载跟不上,那通信的效率就会变得很慢,可以理解这是半双工通信,即同一时刻,只能有一个人在收或发。
为此,我们必须改变现有结构,来实现更高效率的通信。
二、方案
之前的通信转发都是在插件管理器的槽函数中,通信体量变大,消息肯定会在这堵塞。
所以我引入线程池,将消息转发功能,打包为线程池任务。
不熟悉线程池的朋友,可以查看我的这篇博客:Qt线程池
消息一到槽函数中,槽函数不做任何处理,直接将其打包并丢入线程池中,线程池自动分配线程来执行任务。这样就变成多窗口同步作业。
如上图所示,插件或主程序只需要释放信号,信号响应插件管理器的槽函数,槽函数打包线程池任务,线程池子线程执行任务,完成消息转发。
三、效果展示
四、关键代码
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);
public:
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>
#include <QThread>
#include <QThreadPool>
class MainWindow;
class TransMessageRunable;
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:
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;
TransMessageRunable* m_transMessageRunable;
};
#endif // PLUGINMANAGER_H
#include "PluginManager.h"
#include "mainwindow.h"
#include "TransMessageRunable.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();
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;
//线程池
//========================================================================
//设置线程池最大线程数量
QThreadPool::globalInstance()->setMaxThreadCount(10);
}
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)
{
TransMessageRunable* task = new TransMessageRunable(metaData);
task->setAutoDelete(true); //autoDelete属性默认为true QThreadPool会在run()函数运行结束后,自动删除了MyTask对象
QThreadPool::globalInstance()->start(task); //任务放进线程池
//QThread::sleep(1);
//QThreadPool::globalInstance()->waitForDone(); //等待任务结束
}
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、线程池任务类
#ifndef TRANSMESSAGERUNABLE_H
#define TRANSMESSAGERUNABLE_H
#include <QObject>
#include <QRunnable>
#include <QDebug>
#include "PluginManager.h"
#include "mainwindow.h"
class TransMessageRunable : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit TransMessageRunable(PluginMetaData metaData, QObject *parent = nullptr);
~TransMessageRunable();
PluginMetaData m_metaData;
protected:
void run();
};
#endif // TRANSMESSAGERUNABLE_H
#include "TransMessageRunable.h"
TransMessageRunable::TransMessageRunable(PluginMetaData metaData, QObject *parent) : QObject(parent)
{
m_metaData = metaData;
}
TransMessageRunable::~TransMessageRunable()
{
//qDebug()<<"delete Task";
}
void TransMessageRunable::run()
{
//和主程序通信
//------------------------------------------------------------
if(m_metaData.dest == QString::fromLocal8Bit("mainWin")) {
if(PluginManager::instance()->m_mainWin) {
PluginManager::instance()->m_mainWin->recvMsgFromManager(m_metaData);
}
return;
}
//和插件通信
//------------------------------------------------------------
auto loader = PluginManager::instance()->getPlugin(m_metaData.dest); //目标插件
if(loader) {
auto interface = qobject_cast<PluginInterface*>(loader->instance());
if(interface) {
interface->recMsgFromManager(m_metaData); //转发给对应的插件
}
}
}