以往的Qt程序都是一个主程序,各功能模块以类或者以动态库的形式加载。此种方法不是说不好,各种架构都有适用它自己的场合。然而Qt提供了插件机制,一种类似面向组件编程的思维方式,可以将各功能模块以插件的形式进行拆分,并在使用中进行动态的加载,这样在协调开发时可以分别对插件进行维护,后期也可以单独对某个插件进行升级。也可以进行更好的扩展。
参考某大神的博客,实现了一个插件管理器的简单例子。
主程序:app
负责加载所有插件,调用插件
插件管理器:pluginmanager
管理所有插件,包括加载、卸载等。并暴露方法给主程序。
插件:databaseplugin(数据库操作)、logplugin(日志操作)
具体的功能
时序图如下:
主要代码:
主程序:
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
PluginManager::instance()->loadAllPlugins();//插件管理器 加载所有插件
PluginManager::instance()->log(QStringLiteral("测试日志插件"));
PluginManager::instance()->insert(QStringLiteral("测试数据库插件"));
}
Widget::~Widget()
{
}
插件管理器:
#ifndef QTPLUGINMANAGER_H
#define QTPLUGINMANAGER_H
#include "pluginmanager_global.h"
#include "plugininterface.h"
#include "./plugins/logplugin/loginterface.h"
#include "./plugins/databaseplugin/databaseinterface.h"
#include <QObject>
#include <QPluginLoader>
#include <QVariant>
class PluginsManagerPrivate ;
class QTPLUGINMANAGERSHARED_EXPORT PluginManager:public QObject
{
Q_OBJECT
public:
PluginManager();
~PluginManager();
static PluginManager *instance(){
if(m_instance==nullptr)
m_instance=new PluginManager();
return m_instance;
}
//加载所有插件
void loadAllPlugins();
//获取所有插件名称
QList<QVariant> allPluginsName();
/**日志插件**/
void log(QString);//日志打印
/**测试数据库插件**/
void insert(QString);//插入数据
private:
static PluginManager *m_instance;
PluginsManagerPrivate *managerPrivate;
//扫描JSON文件中的插件元数据
void scanMetaData(const QString &filepath);
//加载其中某个插件
void loadPlugin(const QString &filepath);
//卸载所有插件
void unloadAllPlugins();
//卸载某个插件
void unloadPlugin(const QString &filepath);
//获取所有插件
QList<QPluginLoader *> allPlugins();
//获取某个插件名称
QVariant getPluginName(QPluginLoader *loader);
//根据名称获得插件
QPluginLoader* getPlugin(const QString &name);
};
#endif // QTPLUGINMANAGER_H
- 此处使用了单列模式供主程序使用。
- 供主程序使用的接口,也放在了这个.h 文件中,这里个人感觉处理的不好,后期在扩展其它插件时,需要不停的在这里添加方法,这个文件会越来越臃肿,但是目前没有想到其它更好的方法。
#include "pluginmanager.h"
#include "pluginsmanagerprivate.h"
#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>
PluginManager* PluginManager::m_instance=nullptr;
PluginManager::PluginManager()
{
managerPrivate = new PluginsManagerPrivate();
}
PluginManager::~PluginManager()
{
if(managerPrivate)
delete managerPrivate;
}
void PluginManager::loadAllPlugins()
{
QDir pluginsdir = QDir(qApp->applicationDirPath());
pluginsdir.cd("plugins");
QFileInfoList pluginsInfo = pluginsdir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
//初始化插件中的元数据
for(QFileInfo fileinfo : pluginsInfo){
qDebug()<<"loadAllPlugins:"<<fileinfo.absoluteFilePath();
scanMetaData(fileinfo.absoluteFilePath());
}
//加载插件
for(QFileInfo fileinfo : pluginsInfo)
loadPlugin(fileinfo.absoluteFilePath());
}
void PluginManager::scanMetaData(const QString &filepath)
{
//判断是否为库(后缀有效性)
if(!QLibrary::isLibrary(filepath))
return ;
qDebug()<<"scanMetaData:"<<filepath;
//获取元数据
QPluginLoader *loader = new QPluginLoader(filepath);
qDebug()<<loader->metaData().keys();
QJsonObject json = loader->metaData().value("MetaData").toObject();
QVariant var = json.value("name").toVariant();
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;
//加载插件
QPluginLoader *loader = new QPluginLoader(filepath);
if(loader->load())
{
PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
if(plugin)
{
managerPrivate->m_loaders.insert(filepath, loader);
//plugin->connect_information(this, SLOT(onPluginInformation(QString&)), true);
}
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)
{
QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);
//卸载插件,并从内部数据结构中移除
if(loader->unload())
{
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();
}
void PluginManager::log(QString str)
{
QPluginLoader *loader = getPlugin("logPlugin");
if(loader)
{
LogInterface *log = dynamic_cast<LogInterface*>(loader->instance());
if(log)
log->log(str);
}
}
void PluginManager::insert(QString str)
{
QPluginLoader *loader = getPlugin("databasePlugin");
if(loader)
{
DatabaseInterface *database = dynamic_cast<DatabaseInterface*>(loader->instance());
if(database)
database->insert(str);
}
}
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));
}
插件:
#ifndef LOGINTERFACE_H
#define LOGINTERFACE_H
#include <QObject>
#include "../../plugininterface.h"
class LogInterface:public PluginInterface
{
public:
virtual ~LogInterface() {}
virtual void log(QString)=0;
};
#endif // LOGINTERFACE_H
- 此处继承PluginInterface
- 具体的方法在此处定义
#ifndef LOGPLUGIN_H
#define LOGPLUGIN_H
#include <loginterface.h>
class LogPlugin : public QObject,public LogInterface
{
Q_OBJECT
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.LogPlugin" FILE "logplugin.json")
#endif // QT_VERSION >= 0x050000
Q_INTERFACES(PluginInterface)
public:
LogPlugin(QObject *parent = 0);
void log(QString);
};
#endif // LOGPLUGIN_H
-
Q_INTERFACES(PluginInterface) 这一句一定要加上
工程目录:
git地址:https://github.com/keiler2018/QtPluginManagerTest
参考:https://blog.csdn.net/liang19890820