IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Qt - 插件编程 02- 设计简单的插件管理器 -> 正文阅读

[C++知识库]Qt - 插件编程 02- 设计简单的插件管理器

声明:在《Qt - 插件编程 01》的基础上进行修改

1、分别创建Plugin01 和 Plugin02

2、修改两个工程中的接口程序:PluginInterface.h,删除之前的虚函数,添加两个虚函数如下:

#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H

#include <QtPlugin>
//后面的字符串具有唯一性
#define PluginInterface_iid "org.galaxyworld.plugins.PluginInterface/1.0"

class PluginInterface
{
public:
    virtual ~PluginInterface() {}
    //新添加的虚函数
    virtual QString get_name() const = 0;   //获取插件名称
    virtual QString show_text() const = 0;  
};

Q_DECLARE_INTERFACE(PluginInterface,PluginInterface_iid)
#endif // PLUGININTERFACE_H

3、 ①? Plugin01类实例化虚函数如下:

#ifndef PLUGIN01_H
#define PLUGIN01_H

#include "Plugin01_global.h"
#include <QObject>
#include "PluginInterface.h"
//共享库输出
class PLUGIN01_EXPORT Plugin01 : public QObject, public PluginInterface
{
    Q_OBJECT
    //这个宏告诉Qt类实现了哪些接口。这在实现插件时使用。
    Q_INTERFACES(PluginInterface)
    //Q_PLUGIN_METADATA 让moc生成导出函数qt_plugin_instance(),供QPluginLoader()调用,创建接口实例,不过返回的是一个QObject*.
    //Q_INTERFACES 让qobject_cast()能正确进行QObject*到接口指针的转换,借此,我们可以判断插件合法性。
    Q_PLUGIN_METADATA(IID "my_plugin01")

public:
    Plugin01();
    QString get_name() const
    {
        return "Plugin01";
    }
    QString show_text() const
    {
        return "this is Plugin01";
    }
};

#endif // PLUGIN01_H

? ? ? ? ?② Plugin02类实例化虚函数如下:

#ifndef PLUGIN02_H
#define PLUGIN02_H

#include "Plugin02_global.h"
#include <QObject>
#include "PluginInterface.h"
//共享??出
class PLUGIN02_EXPORT Plugin02 : public QObject, public PluginInterface
{
    Q_OBJECT
    //这个宏告诉Qt类实现了哪些接口。这在实现插件时使用。
    Q_INTERFACES(PluginInterface)
    //Q_PLUGIN_METADATA 让moc生成导出函数qt_plugin_instance(),供QPluginLoader()调用,创建接口实例,不过返回的是一个QObject*.
    //Q_INTERFACES 让qobject_cast()能正确进行QObject*到接口指针的转换,借此,我们可以判断插件合法性。
    Q_PLUGIN_METADATA(IID "my_plugin02")

public:
    Plugin02();
    QString get_name() const
    {
        return "Plugin02";
    }
    QString show_text() const
    {
        return "this is Plugin02";
    }
};

#endif // PLUGIN02_H

4、设计简易的插件管理器:加载插件、卸载插件、插件信息通信等(作为插件间通信媒介,转发插件消息,插件间的通信通过插件管理器来处理)

创建类插件管理器类:PluginManager

pluginmanager.h文件内容如下:

#include <QObject>
#include <QHash>
class QPluginLoader;

class PluginManager : public QObject
{
    Q_OBJECT

public:
    static PluginManager *instance()
    {
        if(m_instance == nullptr)
            m_instance = new PluginManager();
        return m_instance;
    }

    void loadAllPlugins();
    void loadPlugin(const QString &filepath);
    void unloadPlugin(const QString &filepath);
    void unloadAllPlugins();
    QPluginLoader* getPlugin(const QString &name);
    QVariant getPluginName(QPluginLoader *loader);

private:
    explicit PluginManager(QObject *parent = nullptr);
    ~PluginManager();

    QHash<QString, QPluginLoader *> m_loaders; //插件路径--QPluginLoader实例
    QHash<QString, QString> m_names; //插件路径--插件名称

    static PluginManager *m_instance;
    class GarbageCollector
    {
        ~GarbageCollector()
        {
            if (PluginManager::instance())
            {
                delete PluginManager::instance();
                PluginManager::m_instance = nullptr;
            }
        }
    };
    static GarbageCollector gc;
};

pluginmanager.cpp文件内容如下:

#include "pluginmanager.h"
#include <QPluginLoader>
#include <QDir>
#include <QDebug>
#include "PluginInterface.h"

PluginManager* PluginManager::m_instance;

PluginManager::PluginManager(QObject *parent) : QObject(parent)
{

}

PluginManager::~PluginManager()
{
    unloadAllPlugins();
}

//加载所有插件
void PluginManager::loadAllPlugins()
{
    QDir pluginsdir(QDir::currentPath());
    pluginsdir.cd("plugins");

    QFileInfoList pluginsInfo = pluginsdir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

    //加载插件
    for(QFileInfo fileinfo : pluginsInfo)
        loadPlugin(fileinfo.absoluteFilePath());
}

//加载某个插件
void PluginManager::loadPlugin(const QString &filepath)
{
    if(!QLibrary::isLibrary(filepath))
        return;

    //加载插件
    QPluginLoader *loader = new QPluginLoader(filepath);
    QString plugin_name;
    if(loader->load())
    {
        PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
        if(plugin)
        {
            plugin_name = plugin->get_name();
            m_loaders.insert(filepath, loader);
            m_names.insert(filepath,plugin_name);
            qDebug()<<"plugin name: "<<plugin->get_name()<<"plugin info: "<<plugin->show_text();
        }
        else
        {
            delete loader;
            loader = nullptr;
        }
    }
    else
    {
        qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();
    }
}

//卸载所有插件
void PluginManager::unloadAllPlugins()
{
    for(QString filepath : m_loaders.keys())
        unloadPlugin(filepath);
}

void PluginManager::unloadPlugin(const QString &filepath)
{
    QPluginLoader *loader = m_loaders.value(filepath);
    //卸载插件,并从内部数据结构中移除
    if(loader->unload())
    {
        m_loaders.remove(filepath);
        delete loader;
        loader = nullptr;
        qDebug()<<"unload success";
    }
}

//获取某个插件名称
QVariant PluginManager::getPluginName(QPluginLoader *loader)
{
    if(loader)
        return m_names.value(m_loaders.key(loader));
    else
        return "";
}

//根据名称获得插件
QPluginLoader *PluginManager::getPlugin(const QString &name)
{
    return m_loaders.value(m_names.key(name));
}

5、构建Plugin01 和 Plugin02,生成 libPlugin01.dll 和 libPlugin02.dll? 。

6、创建一个简单的Widget窗口,添加两个QPushButton,

然后添加pluginmanager.h和pluginmanager.cpp,

?#include “pluginmanager.h”

实现如下两个按钮的槽函数,第一个函数添加了一个是否第一次加载的判断,如果加载过了就先卸载,不然unload不起作用。

void Widget::on_pushButton_clicked()
{
    PluginManager *pm = PluginManager::instance();
    if(!isFirstload)
        pm->unloadAllPlugins();  //先清理之前的插件,不然重复加载unload会一直是false状态,无法卸载???
    isFirstload = false;
    pm->loadAllPlugins();
}


void Widget::on_pushButton_2_clicked()
{
    PluginManager * pm = PluginManager::instance();
    pm->unloadAllPlugins();
}

7、运行效果,先点击第一个按钮加载两个插件,再点击第二个按钮卸载两个插件。

?

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-28 22:43:27  更:2021-12-28 22:45:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:10:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码