插件管理系统实现

前言

上文写了插件管理系统的设计思路,这一篇文章主要介绍插件的实现。主要从三个方面介绍实现;

  1. 插件加载的实现
  2. 插件管理的实现;
  3. 插件dll 的实现;

文中不会完整的介绍实现过程,仅介绍重要和实现过程中要注意的事项;

插件加载的实现

插件的加载显示信息,决定了插件要实现哪些接口(后面章节中的“插件dll 的实现”),例如要显示插件的名称、版本号、插件类型、插件描述,这些都是插件的基本信息。

插件的加载显示在QTreeWidget 或QListWidget中

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    m_pluginManager = PluginManager::getPluginManager(this);
    m_pluginManager->loadPlugin();

    QVector<PluginBase*>  plugins = m_pluginManager->allLoadPlugins();
    for(int i=0;i<plugins.size();i++)
    {
        QListWidgetItem *item = new QListWidgetItem();
        QVariant variant = QVariant::fromValue(reinterpret_cast<quintptr>(plugins[i]));
        item->setData(Qt::UserRole,variant );
        item->setText(plugins[i]->pluginName());
        ui->listWidget->addItem(item);
        connect(ui->listWidget,SIGNAL(itemClicked(QListWidgetItem *item)),this,SLOT(itemFunc(QListWidgetItem *item)));
    }
}
void Widget::itemFunc(QListWidgetItem *item)
{
     PluginBase *plugin = reinterpret_cast<PluginBase *>(item->data(Qt::UserRole).value<quintptr>());
    QWidget*widget = plugin->pluginSetupUI();
    if(!widget)
    {
        qDebug()<<Q_FUNC_INFO<<"null.....";
        return ;
    }

    widget->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
    widget->setParent(ui->widget);
    ui->widget->setLayout(widget->layout());
    widget->showMaximized();
}

其中较为重要的代码为

QVariant variant = QVariant::fromValue(reinterpret_cast<quintptr>(plugins[i]));

item->setData(Qt::UserRole,variant );

PluginBase *plugin = reinterpret_cast<PluginBase *>(item->data(Qt::UserRole).value<quintptr>());
上述代码来自写的插件演示demo程序,加载插件放置文件夹下的插件,并在list中显示出来,点击list 把插件设置区的插件实现的UI展示出来。

插件管理的实现

插件管理的实现主要是实现插件的安装与卸载,插件数据接收与发送

bool PluginManager::loadPlugin(const QString &pluginPath)
{
    if(!QLibrary::isLibrary(pluginPath))
    {
        qDebug()<<Q_FUNC_INFO<<pluginPath<<" not a plugin";
        return false;
    }
    //加载插件
    QPluginLoader *loader = new QPluginLoader(pluginPath);
    if(loader->load())
    {
        PluginBase *plugin = qobject_cast<PluginBase *>(loader->instance());
        if(plugin)
        {
            plugin->registerPluginResponseFunc(&PluginManager::pluginResponse,this);
            plugin->init();

            m_loaders.append(loader);
            m_plugins.append(plugin);
            m_pluginPath.append(pluginPath);
            return true;
        }
        else
        {
            delete loader;
            loader = NULL;
            qDebug()<<Q_FUNC_INFO<<"Conversion PluginBase failure ";
        }
    }
    else
    {
        qDebug() << Q_FUNC_INFO <<"loadPlugin:"<<pluginPath<<loader->errorString();
    }
    return  false;
}

void PluginManager::unloadPlugin(const QString &pluginPath)
{
    int index = m_pluginPath.indexOf(pluginPath);
    if(index<0)
        return ;

    QPluginLoader *loader = m_loaders[index];
    if(loader)
    {
        m_plugins[index]->exit();
        //卸载插件,并从内部数据结构中移除
        if(loader->unload())
        {
            m_loaders.remove(index);
            m_pluginPath.remove(index);
            m_plugins.remove(index);

            delete loader;
            loader = NULL;
        }
        else
        {
            qDebug()<<Q_FUNC_INFO<<"unload fail"<<loader->isLoaded();
        }
    }
}
//插件数据发送,功能处理
bool PluginManager::pluginWorkFunction(const QString &functionName, const QJsonObject &data)
{
    bool ret = false;
    for(int i=0;i<m_plugins.size();i++)
    {
        ret = m_plugins[i]->pluginWorkFunction(functionName,data);
        if(!ret)
        {
            qDebug()<<Q_FUNC_INFO<<m_plugins[i]->pluginName()
                   <<m_plugins[i]->version()
                   <<m_plugins[i]->description()
                  <<functionName<<"pluginWorkFunction execution failure";
        }
    }
    return ret;
}

bool PluginManager::pluginResponse(const QString &functionName, const QJsonObject &data)
{
    //处理所有来自插件反馈给主程序的数据
    bool ret = true;
    if(m_responseFunc.contains(functionName))
    {
        ret = m_responseFunc[functionName](data);
        return ret;
    }
    else
    {
        return false;
    }
}

插件dll 的实现

1. qt 创建普通的动态库工程

2. 继承插件类,继承的插件基类增加

Q_DECLARE_INTERFACE(PluginBase,"xxxx.PluginInterface")
class PluginBase
{
public:
    virtual ~PluginBase(){};

    /**
     * @brief pluginName 插件名称会显示在插件管理系统中
     * @return 插件名称
     */
    virtual QString pluginName()=0;

    /**
     * @brief version 插件版本会显示在插件管理系统中
     * @return 插件版本 如格式V1.0.0
     * @note 命名格式建议使用V1.X.X 格式
     */
    virtual QString version()=0;

    /**
     * @brief description
     * @return 插件的描述 如xx系统对接插件
     */
    virtual QString description()=0;

    /**
     * @brief pluginType plugin 类型
     * @return 返回插件类型名称
     * @note 在插件管理界面,不同的插件类型会被归于一组展示
     */
    virtual QString pluginType()=0;

    ...........

    virtual bool init()=0;

    virtual bool exit()=0;

    template<typename T1,typename T2>
    void registerPluginResponseFunc(T1 memberfunc,T2* pThis)
    {
        m_responseCallback= std::bind(memberfunc,pThis,std::placeholders::_1,std::placeholders::_2);
    }

protected:
    typedef std::function<bool (const QString&functionName,const QJsonObject&data)> Callback;
    Callback m_responseCallback;

};
Q_DECLARE_INTERFACE(PluginBase,"xxxx.PluginInterface")
class MyPluin : public QObject ,public PluginBase
{
    Q_OBJECT
    Q_INTERFACES(PluginBase)
    Q_PLUGIN_METADATA(IID "xxxx.PluginInterface" FILE "pluginInfo.json" )
public:
    explicit MyPluin(QObject *parent=NULL);
    virtual ~MyPluin();

    //! 插件名
    QString pluginName();
    //! 插件版本 如格式V1.0.0
    QString version();
    //! 插件的描述 如xx系统对接插件
    QString description();

    //! plugin 类型
    QString pluginType();

    //! plugin功能函数
    bool pluginWorkFunction(const QString&functionName,const QJsonObject&data);

    //! 功能区的UI
    bool pluginWorkUI(const QString&functionName);

    //! 设置区的UI
    QWidget* pluginSetupUI();

    //! 插件初始化/加载
    bool init();

    //! 插件卸载/退出
    bool exit();

private:

    QWidget* m_widget;

};

注意生成的dll 在windows上没有任务厂家和版本信息,在pro qt工程中要增加version.rc

#if defined(UNDER_CE)
#include <winbase.h>
#else
#include <winver.h>
#endif

#include "version.h"

VS_VERSION_INFO VERSIONINFO
    FILEVERSION VER_FILEVERSION
    PRODUCTVERSION VER_PRODUCTVERSION
BEGIN
	BLOCK "StringFileInfo"
	BEGIN
		BLOCK "080404b0"
		BEGIN
			VALUE "CompanyName", "xxxx有限公司\0"
			VALUE "FileDescription", "xx演示demo用的插件\0"
            VALUE "FileVersion", VER_FILEVERSION_STR
            VALUE "ProductVersion", VER_PRODUCTVERSION_STR
			VALUE "LegalCopyright", "版权所有 (C) xxxx有限公司\0"
			VALUE "LegalTrademarks", "xxxx有限公司\0"
			VALUE "OriginalFilename", "PluginDemo.dll\0"
			VALUE "ProductName", "xxxx插件\0"
			VALUE "InternalName", "PluginDemo\0"
	   END
	END
	
	BLOCK "VarFileInfo"
	BEGIN
		VALUE "Translation", 0x804, 1200
	END        
END

pro中增加

RC_FILE += version.rc

遇到的问题

1. unload卸载时插件无法解除占用,无法删除插件dll文件,除非应用程序退出; 目前想要卸载时删除dll,只能提示软件重启再删除,qt官方的解释:https://bugreports.qt.io/browse/QTBUG-68880

2. 在插件中是否可以像qt应用程序那样自定义UI,使用UI设计师进行ui设计,并且连接信号与槽了,答案是可以,但要注意,前提是你要自已创建一个widget类例如

class SetupWidget : public QWidget
{
    Q_OBJECT
public:
    explicit SetupWidget(QWidget *parent = nullptr);


private slots:
    void on_buttonPlugin_clicked();

private:
    Ui::Widget *ui;

};

创建的UI文件不要在直接与前面的MyPluin关联,这样在操作UI时,在UI设计师中右键建立槽连接时,槽函数的创建才会关联到SetupWidget。

3. 插件的json不是必须,在插件安装中反而因为有多个文件,插件不方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值