系列文章目录
第一章 Qt插件创建并调用插件
第二章 Qt创建插件管理器统一管理插件
第三章 Qt插件之间相互通信
第四章 Qt创建并加载一个窗口插件
第五章 Qt插件工程作为子工程
前言
一、创建插件
插件的通信通过插件管理器来管理,插件管理器转发插件的消息。在插件基类中,加入通信结构体,加入插件发送消息和接收消息的纯虚函数。
1.插件的接口类的定义
#include<QtPlugin>
#include<QJsonObject>
struct PluginMetaData{
QString src;
QString dest;
QString msg;
QObject *object = nullptr;
QJsonObject info = QJsonObject();
};
Q_DECLARE_METATYPE(PluginMetaData);
class PluginInterface
{
public:
virtual ~PluginInterface() {}
virtual QString getName() const = 0;
virtual QString showText() const = 0;
virtual void recMsgFromManager(PluginMetaData) = 0;
virtual void sendMsgManager(PluginMetaData) = 0;
};
Q_DECLARE_INTERFACE(PluginInterface,"org.galaxyworld.plugins.PluginInterface/1.0")
Q_DECLARE_METATYPE(type)
该宏用于声明自定义类型以便能够在信号槽机制中使用。需要将自定义类型作为参数传递给该宏,在运行时才能正确处理相应类型的信号与槽连接。这个宏让QOject及其子类知道这个类型。
Q_DECLARE_INTERFACE(ClassName, Identifier)
该宏将标识符(字符串)与名为 ClassName 的接口类相关联。标识符必须是唯一的。
Q_DECLARE_INTERFACE 不能在命名空间内。
2.插件01
#include "Plugin01_global.h"
#include<QObject>
#include"PluginInterface.h"
#include<QDebug>
class PLUGIN01_EXPORT Plugin01 : public QObject , PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "plugin01")
public:
Plugin01();
virtual QString getName() const override
{
return "Plugin01";
}
virtual QString showText() const override
{
return "this is Plugin01";
}
virtual void recMsgFromManager(PluginMetaData metaData) override
{
qDebug() << "插件Plugin01接收到消息:" << metaData.msg;
}
signals:
virtual void sendMsgManager(PluginMetaData)override;
};
3.插件02
#include "Plugin02_global.h"
#include<QObject>
#include"PluginInterface.h"
#include<QDebug>
class PLUGIN02_EXPORT Plugin02 : public QObject , PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "plugin02")
public:
Plugin02();
virtual QString getName() const override
{
return "Plugin02";
}
virtual QString showText() const override
{
return "this is Plugin02";
}
virtual void recMsgFromManager(PluginMetaData metaData) override
{
qDebug() << "插件Plugin02接收到消息:" << metaData.msg;
}
signals:
virtual void sendMsgManager(PluginMetaData)override;
Q_INTERFACES(…)
该宏用于在具体的实现类中声明所支持的接口。将要实现的接口类型作为参数传递给该宏即可。
Q_PLUGIN_METADATA(...)
此宏用于声明插件对象的元数据(元数据是场景对象的一部分)。
宏需要声明通过对象实现的接口的 IID,并引用包含插件元数据的文件。这个宏应该只在 Qt 插件的源代码中出现一次。使用此宏的类必须是可默认构造的。FILE 是可选的,指向一个 json 文件。json 文件必须驻留在构建系统指定的包含目录之中。 当 moc 找不到指定的文件时,它会以错误退出。
二、插件管理器
1.定义插件管理器
class PluginManger : public QObject
{
Q_OBJECT
public:
static PluginManger *instance()
{
if(nullptr == m_instance)
{
m_instance = new PluginManger;
}
return m_instance;
}
void loadAllPlugins();
void unloadAllPlugins();
void loadPlugin(const QString &filePath);
void unloadPlugin(const QString &filePath);
QPluginLoader *getPlugin(const QString &name);
QVariant getPluginName(QPluginLoader *loader);
public slots:
void recMsgFromManager(PluginMetaData metadata);
private:
explicit PluginManger(QObject *parent = nullptr);
~PluginManger();
QHash<QString,QPluginLoader *> loaders;
QHash<QString,QString> names;
static PluginManger *m_instance;
class GarbageCollector
{
~GarbageCollector()
{
if(PluginManger::instance())
{
delete PluginManger::instance();
PluginManger::m_instance = nullptr;
}
}
};
static GarbageCollector gc;
2.定义插件管理器中声明的函数
void PluginManger::loadAllPlugins()
{
QDir pluginsdir(QDir::currentPath());
qDebug() << QDir::currentPath();
pluginsdir.cd("..\\Lib");
QFileInfoList pluginsInfo = pluginsdir.entryInfoList(\
QDir::Files | QDir::NoDotAndDotDot);
for(QFileInfo fileInfo : pluginsInfo)
loadPlugin(fileInfo.absoluteFilePath());
}
void PluginManger::unloadAllPlugins()
{
for(QString filePath : loaders.keys())
unloadPlugin(filePath);
}
void PluginManger::loadPlugin(const QString &filePath)
{
if(!QLibrary::isLibrary(filePath))
return;
QPluginLoader *loader = new QPluginLoader(filePath);
QString pluginName;
if(loader->load())
{
PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
if(plugin)
{
pluginName = plugin->getName();
loaders.insert(filePath,loader);
names.insert(filePath,pluginName);
qDebug() << "插件名称:" << plugin->getName() << "插件信息" << plugin->showText();
/*****************************************************************************************************************************/
connect(loader->instance(),SIGNAL(sendMsgManager(PluginMetaData)),this,SLOT(recMsgFromManager(PluginMetaData)));
/*****************************************************************************************************************************/
}else {
delete loader;
loader = nullptr;
}
}else {
qDebug() << "loadPlugin:" << filePath << loader->errorString();
}
}
void PluginManger::unloadPlugin(const QString &filePath)
{
QPluginLoader *loader = loaders.value(filePath);
if(loader->unload())
{
loaders.remove(filePath);
delete loader;
loader = nullptr;
}
}
QPluginLoader *PluginManger::getPlugin(const QString &name)
{
return loaders.value(names.key(name));
}
QVariant PluginManger::getPluginName(QPluginLoader *loader)
{
if(loader)
return names.value(loaders.key(loader));
else {
return "";
}
}
void PluginManger::recMsgFromManager(PluginMetaData metadata)
{
auto loader = getPlugin(metadata.dest);
if(loader)
{
auto interface = qobject_cast<PluginInterface *>(loader->instance());
if(interface)
{
interface->recMsgFromManager(metadata);
}
}
}
三、插件通过插件管理器进行相互通信
PluginManger *pm = PluginManger::instance();
auto loader = pm->getPlugin("Plugin01");
if(loader)
{
PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
PluginMetaData m;
m.dest = "Plugin02";
m.src = "Plugin01";
m.msg = "插件1给插件2发的消息~~~";
emit plugin->sendMsgManager(m);
}else {
qDebug() << "插件不存在";
}
通信过程本质:
父类调用子类的方法。