Qt 插件管理器机制

           以往的Qt程序都是一个主程序,各功能模块以类或者以动态库的形式加载。此种方法不是说不好,各种架构都有适用它自己的场合。然而Qt提供了插件机制,一种类似面向组件编程的思维方式,可以将各功能模块以插件的形式进行拆分,并在使用中进行动态的加载,这样在协调开发时可以分别对插件进行维护,后期也可以单独对某个插件进行升级。也可以进行更好的扩展。

           参考某大神的博客,实现了一个插件管理器的简单例子。

主程序:app

负责加载所有插件,调用插件

插件管理器:pluginmanager

管理所有插件,包括加载、卸载等。并暴露方法给主程序。

插件:databaseplugin(数据库操作)、logplugin(日志操作)  

具体的功能

 

时序图如下:

Qt 插件管理器机制主要代码:

主程序:

#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)  这一句一定要加上

工程目录:

Qt 插件管理器机制

 

git地址:https://github.com/keiler2018/QtPluginManagerTest

参考:https://blog.****.net/liang19890820