CTK插件框架学习-信号槽(05)https://mp.csdn.net/mp_blog/creation/editor/137240105
一、服务工厂定义
- 注册插件时使用服务工厂注册,使用getService根据调用者插件资源文件内容获取在服务工厂内的对应实现
- 在服务工厂中可以知道是哪个插件正在调用服务工厂
- 懒汉模式,在需要时(通过服务工厂获取时)才创建出对象实例
- 可以根据需要在服务工厂内创建出多个其他插件对应需要的功能
二、服务工厂插件
IPrintfService.h
#pragma once
#include <QObject>
/*
* 1、通过ctkPluginConstants::SERVICE_RANKING和ctkPluginConstants::SERVICE_ID来调用不同的插件
* 2、插件不同但是在同一个dll内
* 3、插件获取策略:
* 插件容器中id最小的服务,id为插件注册时的SERVICE_RANKING属性
* 插件容器内id相同的情况,返回pid最小的服务
* 4、插件每次调用其他插件时只会生成一个实例,不会因多次调用产生多个服务实例
*/
class IPrintfService
{
public:
virtual ~IPrintfService() {}
virtual void printf() = 0;
};
//此宏将当前这个接口类声明为接口,后面的字符串是这个接口的唯一标识。
Q_DECLARE_INTERFACE(IPrintfService, "zr.TestServiceFactory.IPrintfService")
PrintfServiceImp1类
========================PrintfServiceImp1.h===========================
#pragma once
#include "IPrintfService.h"
#include <QObject>
class ctkPluginContext;
class PrintfServiceImp1 :
public QObject, public IPrintfService
{
Q_OBJECT
//当一个类继承这个接口类,表明需要实现这个接口类
Q_INTERFACES(IPrintfService)
public:
PrintfServiceImp1();
void printf();
};
========================PrintfServiceImp1.cpp=========================
#include "PrintfServiceImp1.h"
#include <qdebug.h>
PrintfServiceImp1::PrintfServiceImp1()
{
}
void PrintfServiceImp1::printf()
{
qDebug() << "ServiceImp1 printf zr.A";
}
PrintfServiceImp2类
=============================PrintfServiceImp2.h================================
#pragma once
#include "IPrintfService.h"
#include <QObject>
class ctkPluginContext;
class PrintfServiceImp2 :
public QObject, public IPrintfService
{
Q_OBJECT
//当一个类继承这个接口类,表明需要实现这个接口类
Q_INTERFACES(IPrintfService)
public:
PrintfServiceImp2();
void printf();
};
=============================PrintfServiceImp2.cpp=============================
#include "PrintfServiceImp2.h"
#include <qdebug.h>
PrintfServiceImp2::PrintfServiceImp2()
{
}
void PrintfServiceImp2::printf()
{
qDebug() << "ServiceImp2 printf zr.B";
}
ServiceFactory类
============================ServiceFactory.h=============================
#pragma once
#include <qobject.h>
#include <ctkServiceFactory.h>
#include <ctkPluginConstants.h>
#include <ctkVersion.h>
#include "IPrintfService.h"
class ServiceFactory :
public QObject, public ctkServiceFactory
{
Q_OBJECT
Q_INTERFACES(ctkServiceFactory)
public:
ServiceFactory();
QObject* getService(QSharedPointer<ctkPlugin> plugin, ctkServiceRegistration registration);
void ungetService(QSharedPointer<ctkPlugin> plugin, ctkServiceRegistration registration, QObject* service);
private:
QObject* getServiceByName(QString name);
private:
int m_count;
};
============================ServiceFactory.cpp===========================
#include "ServiceFactory.h"
#include <qdebug.h>
#include "PrintfServiceImp1.h"
#include "PrintfServiceImp2.h"
ServiceFactory::ServiceFactory()
{
m_count = 0;
}
QObject * ServiceFactory::getService(QSharedPointer<ctkPlugin> plugin, ctkServiceRegistration registration)
{
Q_UNUSED(registration)
qDebug() << "getSymbolicName: " << plugin->getSymbolicName();
qDebug() << "plugin count: " << m_count++;
QHash<QString, QString> headers = plugin->getHeaders();
//ctkVersion version = ctkVersion::parseVersion(headers.value(ctkPluginConstants::PLUGIN_VERSION));
//QString name = headers.value(ctkPluginConstants::PLUGIN_NAME);
//qDebug() << "PLUGIN_NAME: " << name;
QString pluginName = headers.value(ctkPluginConstants::PLUGIN_NAME);
QObject* obj = getServiceByName(pluginName);
return obj;
}
void ServiceFactory::ungetService(QSharedPointer<ctkPlugin> plugin, ctkServiceRegistration registration, QObject * service)
{
Q_UNUSED(plugin)
Q_UNUSED(registration)
Q_UNUSED(service)
qDebug() << "getSymbolicName: " << plugin->getSymbolicName();
QHash<QString, QString> headers = plugin->getHeaders();
}
QObject * ServiceFactory::getServiceByName(QString name)
{
if (name.contains("zr.A"))
{
return new PrintfServiceImp1();
}
else
{
return new PrintfServiceImp2();
}
}
PluginActivator类
=============================PluginActivator.h================================
#pragma once
#include <qobject.h>
#include "ctkPluginActivator.h"
#include "ctkPluginContext.h"
#include "PrintfServiceImp1.h"
#include "PrintfServiceImp2.h"
class PluginActivator :
public QObject, ctkPluginActivator
{
Q_OBJECT
Q_INTERFACES(ctkPluginActivator)//向Qt的插件框架声明,希望将xxx插件放入到框架中。
Q_PLUGIN_METADATA(IID "TestServiceFactory")//向qt框架申明插件(qt5版本)
public:
PluginActivator();
void start(ctkPluginContext *context);
void stop(ctkPluginContext *context);
private:
};
=============================PluginActivator.cpp==============================
#include "PluginActivator.h"
#include <QDebug>
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
#include "ServiceFactory.h"
#include "IPrintfService.h"
PluginActivator::PluginActivator()
{
}
void PluginActivator::start(ctkPluginContext *context)
{
ServiceFactory* serviceFactory = new ServiceFactory();
context->registerService<IPrintfService>(serviceFactory);
}
void PluginActivator::stop(ctkPluginContext *context)
{
Q_UNUSED(context);
}
三、测试插件
新建插件可以参考CTK插件框架学习-新建插件(02)
1、在新插件中调用服务工厂
//使用服务工厂
ctkServiceReference ref = context->getServiceReference<IPrintfService>();
if (ref)
{
IPrintfService* service = qobject_cast<IPrintfService*>(context->getService(ref));
if (service != nullptr)
{
service->printf();
}
}
2、新插件MANIFEST.MF文件内容如下:
Plugin-SymbolicName:TestUseServiceFactoryPluginA
Plugin-Version: 1.0.0//版本号添加多级或带字母会导致插件加载失败
Plugin-Name: zr.A
键值对类型可以参考ctkPluginConstants.h文件,根据服务工厂内使用的字段类型进行修改,
如在服务工厂内以获取插件名称的方式区分不同插件,则定义(Plugin-Name: zr.A),
然后通过以下方式获取属性值进行区分
QHash<QString, QString> headers = plugin->getHeaders();
QString pluginName = headers.value(ctkPluginConstants::PLUGIN_NAME);
四、加载插件
#include "CTKPlugin.h"
#include <QtWidgets/QApplication>
#include <iostream>
#include <QStyleFactory>
#include <QDir>
#include <QDirIterator>
#include <QDebug>
#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
#include "../TestServiceFactory/IPrintfService.h"
/*
* 1、注意:Plugin-SymbolicName要满足这里的前缀是:TARGET/META-INF格式。TARGET的名字最好和工程名一致,不然可能出现device not open错误。
* 2、如果CTK初始化、插件安装启动等是在一个类中,则与CTK相关的变量应定义成类的属性,不能是成员变量,否则获取不到服务
* 3、CTK插件组成:
(1)每个插件有自己的注册器Activator,继承自QObject和ctkPluginActivator的一个类,并实现ctkPluginActivator的start、stop函数
(2)每个插件必须有一个资源文件,名称一般与插件名称一致,前缀必须为TARGET/META-INF,例:插件名称/META-INF
(3)每个插件必须添加一个元数据文件,名字必须为MANIFEST.MF,并添加到资源文件中
* 4、QSharedPointer framework这个对象既可以作为对象也可以作为对象指针,但要作为插件框架使用必须要用指针方法调用
* 5、生成的插件名(TARGET)不要有下划线,因为CTK会默认将插件名中的下划线替换成点号,最后导致找不到插件
*/
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv);
a.setApplicationName("ctktest");//Linux下没有名称报错
QString path = QCoreApplication::applicationDirPath();
//启用通信插件框架
#ifdef _DEBUG
ctkPluginFrameworkLauncher::addSearchPath(path + "/CTKPlugins");
//ctkPluginFrameworkLauncher::addSearchPath("E:/Demo(Qt5)/08_CTKPlugin/CTKPlugin/CTK/lib/ctk-0.1/plugins");
#else
ctkPluginFrameworkLauncher::addSearchPath(path + "/CTKPlugins");
#endif // _DEBUG
// 设置并启动 CTK 插件框架
try {
ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");
}
catch (ctkException e)
{
std::cout << e.message().toStdString() << std::endl;
}
// 启动插件工厂
ctkPluginFrameworkFactory* ctkFrameWorkFactory = new ctkPluginFrameworkFactory;
QSharedPointer<ctkPluginFramework> framework = ctkFrameWorkFactory->getFramework();
try {
framework->init();
framework->start();
}
catch (const ctkPluginException& e)
{
std::cout << "framework init fail" << std::endl;
}
//QString dir = QCoreApplication::applicationDirPath();
//dir += "/plugins/TestPlugin.dll";
//QUrl url = QUrl::fromLocalFile(dir);
QSharedPointer<ctkPlugin> plugin;
#if 1
QDirIterator iter(path + "/plugins/", { "*.dll" }, QDir::NoFilter, QDirIterator::Subdirectories);
while (iter.hasNext()) {
//qDebug() << iter.next();
QString dllPath = iter.next();
QUrl url = QUrl::fromLocalFile(dllPath);
try
{
plugin = framework->getPluginContext()->installPlugin(url);
qDebug() << QString("Plugin[%1_%2] installed...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());
//获取MANIFEST.MF中的数据
QHash<QString, QString> headers = plugin->getHeaders();
ctkVersion version = ctkVersion::parseVersion(headers.value(ctkPluginConstants::PLUGIN_VERSION));
QString name = headers.value(ctkPluginConstants::PLUGIN_NAME);
}
catch (ctkPluginException e) {
std::cout << e.message().toStdString() << e.getType() << std::endl;
}
try {
plugin->start(ctkPlugin::START_TRANSIENT);//表示立即启用插件,不设置参数的话加载后也不会立即打印输出
qDebug() << QString("Plugin[%1_%2] started").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());
}
catch (ctkPluginException e) {
std::cout << e.message().toStdString() << e.getType() << std::endl;
}
}
#else
#endif
//在main函数中调用服务工厂获取到的getSymbolicName为"system.plugin"
ctkServiceReference ref = framework->getPluginContext()->getServiceReference<IPrintfService>();
if (ref)
{
IPrintfService* service = qobject_cast<IPrintfService*>(framework->getPluginContext()->getService(ref));
if (service != nullptr)
{
service->printf();
}
}
//ctkPlugin::State sta = plugin->getState();
//ctkPluginFrameworkLauncher::stop();
//plugin->stop();
//plugin->uninstall();
//sta = plugin->getState();
CTKPlugin c;
c.show();
return a.exec();
}