目录
1.引言
在CTK框架(三): 插件的安装讲解了插件的安装、启动、停止和卸载方法,对于一个插件可以这样写;但是如果是在一个大型的应用程序中,里面有很多插件,就需要一个管理这些插件的服务,CTK官方就提供给了一个:PluginAdmin。
PluginAdmin它本身也是一个插件,它提供服务的接口有:
cpp
struct PluginAdmin
{
virtual ~PluginAdmin() {}
virtual void installAllPlugin() = 0;
virtual void startAllPlugin() = 0;
virtual void stopAllPlugin() = 0;
virtual void uninstallAllPlugin() = 0;
};
- installAllPlugin:安装系统所有插件
- startAllPlugin:启动系统所有插件
- stopAllPlugin:停止系统所有插件
- uninstallAllPlugin: 卸载系统所有插件
2.实现原理
跟其它CTK插件服务的一样,在PluginAdmin插件里面实现了接口PluginAdmin,代码如下:
pluginadminservice.h
cpp
#ifndef PLUGINADMINSERVICE_H
#define PLUGINADMINSERVICE_H
#include <QObject>
#include <service/pluginadmin/pluginAdmin.h>
#include <ctkPluginContext.h>
#include <service/event/ctkEventAdmin.h>
class PluginAdminService :public QObject,public PluginAdmin
{
Q_OBJECT
Q_INTERFACES(PluginAdmin)
public:
explicit PluginAdminService(ctkPluginContext* context,QObject *parent = nullptr);
~PluginAdminService();
public:
void installAllPlugin() Q_DECL_OVERRIDE;
void startAllPlugin() Q_DECL_OVERRIDE;
void stopAllPlugin() Q_DECL_OVERRIDE;
void uninstallAllPlugin() Q_DECL_OVERRIDE;
signals:
private slots:
private:
ctkPluginContext *m_context;
ctkDictionary m_dictionary;
ctkProperties m_properties;
//事件管理服务插件
ctkEventAdmin *m_eventAdmin;
QSharedPointer<ctkPluginFramework> m_framework;
QStringList m_libFilter;
QString m_path;
};
#endif // PLUGINADMINSERVICE_H
pluginadminservice.cpp
cpp
#include "pluginadminservice.h"
#include <service/event/ctkEventConstants.h>
#include <QCoreApplication>
#include <QDirIterator>
#include <QJsonArray>
#include <QThread>
#include <ctkPluginException.h>
#include <ctkPluginFrameworkFactory.h>
#include <ctkPluginFrameworkLauncher.h>
PluginAdminService::PluginAdminService(ctkPluginContext* context, QObject* parent) :
QObject(parent),
m_context(context),
m_eventAdmin(nullptr),
m_framework(nullptr)
{
//注册插件管理服务
context->registerService<PluginAdmin>(this);
ctkServiceReference eventRef = context->getServiceReference<ctkEventAdmin>();
m_eventAdmin = qobject_cast<ctkEventAdmin*>(context->getService(eventRef));
if (eventRef) {
context->ungetService(eventRef);
}
m_path = QCoreApplication::applicationDirPath() + "/plugins";
ctkPluginFrameworkLauncher::addSearchPath(m_path, true);
#if defined(Q_OS_WIN)
m_libFilter << "*.dll";
#elif defined(Q_OS_LINUX)
m_libFilter << "*.so";
#elif defined(Q_OS_MAC)
m_libFilter << "*.dylib";
#endif
}
PluginAdminService::~PluginAdminService()
{
}
void PluginAdminService::installAllPlugin()
{
QDirIterator dirIter(m_path, m_libFilter, QDir::Files);
QString fileLocation;
while (dirIter.hasNext()) {
try {
fileLocation = dirIter.next();
if (!fileLocation.contains(QString("pluginadmin"))) {
m_context->installPlugin(QUrl::fromLocalFile(fileLocation));
qDebug() << QString::fromLocal8Bit("1.1 安装插件:") << fileLocation;
}
else {
qDebug() << QString::fromLocal8Bit("1.2 未重复安装插件:") << fileLocation;
}
}
catch (const ctkPluginException& exc) {
qCritical() << fileLocation << exc.what();
}
}
}
void PluginAdminService::startAllPlugin()
{
foreach(const QSharedPointer<ctkPlugin> &plugin, m_context->getPlugins()) {
try {
if (QString("system.plugin") != plugin->getSymbolicName()
&& QString("pluginadmin") != plugin->getSymbolicName()) {
plugin->start();
qDebug() << QString::fromLocal8Bit("2.1 启动插件:") << plugin->getSymbolicName();
}
else {
qDebug() << QString::fromLocal8Bit("2.2 未重复启动插件:") << plugin->getSymbolicName();
}
}
catch (const ctkPluginException& exc) {
qCritical() << exc.what();
}
}
}
void PluginAdminService::stopAllPlugin()
{
foreach(const QSharedPointer<ctkPlugin> &plugin, m_context->getPlugins()) {
try {
if (QString("system.plugin") != plugin->getSymbolicName()
&& QString("pluginadmin") != plugin->getSymbolicName()) {
qDebug() << QString::fromLocal8Bit("3.1 停止插件:") << plugin->getSymbolicName();
plugin->stop();
}
else {
qDebug() << QString::fromLocal8Bit("3.2 未停止运行插件:") << plugin->getSymbolicName();
}
}
catch (const ctkPluginException& exc) {
qCritical() << exc.what();
}
}
}
void PluginAdminService::uninstallAllPlugin()
{
foreach(const QSharedPointer<ctkPlugin> &plugin, m_context->getPlugins()) {
try {
if (QString("system.plugin") != plugin->getSymbolicName()
&& QString("pluginadmin") != plugin->getSymbolicName()) {
qDebug() << QString::fromLocal8Bit("4.1 卸载插件:") << plugin->getSymbolicName();
plugin->uninstall();
}
}
catch (const ctkPluginException& exc) {
qCritical() << exc.what();
}
}
foreach(const QSharedPointer<ctkPlugin> &plugin, m_context->getPlugins()) {
qDebug() << QString::fromLocal8Bit("4.2 未卸载插件:") << plugin->getSymbolicName();
}
}
其实实现也不难,以安装插件的installAllPlugin函数实现为例,如果是windows,就是在应用程序的插件目录下面循环查找.dll文件,依次调用ctkPluginContext的installPlugin函数,安装插件,所以这个插件目录不能存放不是插件的.dll文件,否则就会出现安装异常。
启动器
pluginadminactivator.h
cpp
#ifndef PLUGINADMINACTIVATOR_H
#define PLUGINADMINACTIVATOR_H
#include "pluginadminthread.h"
#include "pluginadmindialog.h"
#include <ctkPluginActivator.h>
#include <QObject>
class PluginAdminActivator : public QObject, public ctkPluginActivator
{
Q_OBJECT
Q_INTERFACES(ctkPluginActivator)
Q_PLUGIN_METADATA(IID "pluginadmin")
public:
PluginAdminActivator();
void start(ctkPluginContext* context) Q_DECL_OVERRIDE;
void stop(ctkPluginContext* context) Q_DECL_OVERRIDE;
private:
QThread *m_pluginAdminThread;
PluginAdminDialog* m_pluginAdminDialog;
};
#endif // PLUGINADMINACTIVATOR_H
pluginadminactivator.cpp
cpp
#include "pluginadminactivator.h"
#include <QDebug>
PluginAdminActivator::PluginAdminActivator() :
m_pluginAdminThread(nullptr)
{
}
void PluginAdminActivator::start(ctkPluginContext* context)
{
qDebug() << "插件启动";
m_pluginAdminThread = new PluginAdminThread(context);
m_pluginAdminThread->start();
m_pluginAdminDialog = new PluginAdminDialog(context);
m_pluginAdminDialog->setWindowTitle(QString::fromLocal8Bit("插件管理器"));
m_pluginAdminDialog->show();
}
void PluginAdminActivator::stop(ctkPluginContext* context)
{
Q_UNUSED(context)
if (m_pluginAdminThread)
{
m_pluginAdminThread->exit();
m_pluginAdminThread->wait();
m_pluginAdminThread->deleteLater();
m_pluginAdminThread = nullptr;
}
}
#if (QT_VERSION < QT_VERSION_CHECK(5,0,0))
Q_EXPORT_PLUGIN2(PluginAdmin, PluginAdminActivator)
#endif
3.实际应用
3.1.界面控制
PluginAdmin提供了一个简单的插件管理界面,加载PluginAdmin插件会弹出如图所示界面:
直接在界面就可以管理系统的所有插件了。
3.2.访问服务管理插件
跟CTK插件访问服务的方式一样,访问PluginAdmin提供的服务,一般在系统退出的时候调用,代码如下:
cpp
//系统退出函数
void MainWindow::closeEvent(QCloseEvent* e)
{
ctkServiceReference reference = context->getServiceReference<PluginAdmin>();
if (reference ) {
PluginAdmin* pPluginManager = context->getService<PluginAdmin>(reference);
assert(pPluginManager );
pPluginManager->stopAllPlugin();
}
}
4.总结
PluginAdmin是CTK插件框架中的一个重要组件,它负责管理插件的生命周期和插件之间的交互。通过PluginAdmin,可以很方便的管理插件。这使得CTK插件框架能够支持高度模块化和可扩展的应用程序开发。