CTK插件框架学习-服务工厂(06)

CTK插件框架学习-信号槽(05)icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/137240105

一、服务工厂定义

  1. 注册插件时使用服务工厂注册,使用getService根据调用者插件资源文件内容获取在服务工厂内的对应实现
  2. 在服务工厂中可以知道是哪个插件正在调用服务工厂
  3. 懒汉模式,在需要时(通过服务工厂获取时)才创建出对象实例
  4. 可以根据需要在服务工厂内创建出多个其他插件对应需要的功能

二、服务工厂插件

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();
}

相关推荐

  1. Qt 框架

    2024-04-09 08:12:03       8 阅读
  2. 【前端工具

    2024-04-09 08:12:03       28 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-09 08:12:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-09 08:12:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-09 08:12:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-09 08:12:03       18 阅读

热门阅读

  1. ARP安全介绍

    2024-04-09 08:12:03       11 阅读
  2. SSL/TLS协议

    2024-04-09 08:12:03       13 阅读
  3. pyqt5 QImage QPixmap Opencv图像 相互转换

    2024-04-09 08:12:03       13 阅读
  4. mybatis根据批量更新多个字段

    2024-04-09 08:12:03       13 阅读
  5. 利用ES6 Set去重

    2024-04-09 08:12:03       11 阅读
  6. 初探 Spring Boot 源码:揭秘其高效魔法

    2024-04-09 08:12:03       12 阅读
  7. ESP32和STM32的区别

    2024-04-09 08:12:03       12 阅读