声明:在《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、运行效果,先点击第一个按钮加载两个插件,再点击第二个按钮卸载两个插件。
?
?
|