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_OBJECTQ_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_OBJECTQ_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 _DEBUGctkPluginFrameworkLauncher::addSearchPath(path + "/CTKPlugins");//ctkPluginFrameworkLauncher::addSearchPath("E:/Demo(Qt5)/08_CTKPlugin/CTKPlugin/CTK/lib/ctk-0.1/plugins");
#elsectkPluginFrameworkLauncher::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 1QDirIterator 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();
}