CTK插件框架学习-插件注册调用(03)

CTK插件框架学习-新建插件(02)https://mp.csdn.net/mp_blog/creation/editor/136923735

一、CTK插件组成

  • 接口类:对外暴露的接口,供其他插件调用
  • 实现类:实现接口内的方法
  • 激活类:负责将插件注册到CTK框架中

二、接口、插件、服务三者关系

1、一对一

一个接口类由一个实现类实现,对应一个插件,注册一个服务

参见

CTK插件框架学习-新建插件(02)https://mp.csdn.net/mp_blog/creation/editor/136923735

2、多对一(多态)

多个接口类由一个实现类实现,对应一个插件,注册多个服务

  • 接口类1

    cpp 复制代码
    #pragma once
    #include <QObject>
    
    class IService1
    {
    public:
    	virtual ~IService1() {}
    	virtual void printf1() = 0;
    };
    
    //此宏将当前这个接口类声明为接口,后面的字符串是这个接口的唯一标识。
    Q_DECLARE_INTERFACE(IService1, "zr.IService1")
  • 接口类2

    cpp 复制代码
    #pragma once
    #pragma once
    #include <QObject>
    
    class IService2
    {
    public:
    	virtual ~IService2() {}
    	virtual void printf2() = 0;
    };
    
    //此宏将当前这个接口类声明为接口,后面的字符串是这个接口的唯一标识。
    Q_DECLARE_INTERFACE(IService2, "zr.IService2")
  • 实现类

    cpp 复制代码
    ==============================TestPlugin2.ch==================================
    #pragma once
    
    #include "IService1.h"
    #include "IService2.h"
    
    class ctkPluginContext;
    class TestPlugin2 : public QObject, public IService1, public IService2
    {
    	Q_OBJECT
    	//当一个类继承这个接口类,表明需要实现这个接口类
    	Q_INTERFACES(IService1)
    	Q_INTERFACES(IService2)
    
    public:
        TestPlugin2(ctkPluginContext* contex);
    
    	virtual void printf1();
    	virtual void printf2();
    };
    
    ==============================TestPlugin2.cpp=================================
    
    #include "TestPlugin2.h"
    #include <qdebug.h>
    
    TestPlugin2::TestPlugin2(ctkPluginContext* contex)
    {
    }
    
    void TestPlugin2::printf1()
    {
    	qDebug() << "IService1 printf1";
    }
    
    void TestPlugin2::printf2()
    {
    	qDebug() << "IService2 printf2";
    }
  • 插件测试

cpp 复制代码
#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 "../TestPlugin2/IService1.h"
#include "../TestPlugin2/IService2.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();

	// 启动插件工厂
	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;
	}


	QSharedPointer<ctkPlugin> plugin;

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

			//获取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);//表示立即启用插件,不设置参数的话加载后也不会立即打印输出
	}
	catch (ctkPluginException e) {
		std::cout << e.message().toStdString() << e.getType() << std::endl;
	}


	//2、测试插件(多个接口一个实现一个服务一个插件)
	IService1* service1 = NULL;
	ctkServiceReference ref2 = framework->getPluginContext()->getServiceReference<IService1>();
	if (ref2)
	{
		service1 = framework->getPluginContext()->getService<IService1>(ref2);
	}
	if (service1)
	{
		service1->printf1();
	}
	IService2* service2 = NULL;
	ctkServiceReference ref3 = framework->getPluginContext()->getServiceReference<IService2>();
	if (ref3)
	{
		service2 = framework->getPluginContext()->getService<IService2>(ref3);
	}
	if (service2)
	{
		service2->printf2();
	}

	

	//ctkPlugin::State sta = plugin->getState();
	//ctkPluginFrameworkLauncher::stop();
	//plugin->stop(); 
	//plugin->uninstall();
	//sta = plugin->getState();

	

	CTKPlugin c;
	c.show();
    return a.exec();
}

3、一对多

一个接口类由多个实现类实现,对应多个插件,注册一个服务

cpp 复制代码
/*
* 1、通过ctkPluginConstants::SERVICE_RANKING和ctkPluginConstants::SERVICE_ID来调用不同的插件
* 2、插件不同但是在同一个dll内
* 3、插件获取策略:
*		插件容器中id最小的服务,id为插件注册时的SERVICE_RANKING属性
*		插件容器内id相同的情况,返回pid最小的服务
* 4、插件每次调用其他插件时只会生成一个实例,不会因多次调用产生多个服务实例
*/
  • 接口类

    cpp 复制代码
    #pragma once
    #include <QObject>
    
    /*
    * 1、通过ctkPluginConstants::SERVICE_RANKING和ctkPluginConstants::SERVICE_ID来调用不同的插件
    * 2、插件不同但是在同一个dll内
    * 3、插件获取策略:
    *		插件容器中id最小的服务,id为插件注册时的SERVICE_RANKING属性
    *		插件容器内id相同的情况,返回pid最小的服务
    * 4、插件每次调用其他插件时只会生成一个实例,不会因多次调用产生多个服务实例
    */
    class IService
    {
    public:
    	virtual ~IService() {}
    	virtual void printf() = 0;
    };
    
    //此宏将当前这个接口类声明为接口,后面的字符串是这个接口的唯一标识。
    Q_DECLARE_INTERFACE(IService, "zr.IService")
  • 实现类1

    cpp 复制代码
    #pragma once
    
    #include "IService.h"
    #include <QObject>
    
    class ctkPluginContext;
    class ServiceImp1 :
    	public QObject, public IService
    {
    	Q_OBJECT
    	//当一个类继承这个接口类,表明需要实现这个接口类
    	Q_INTERFACES(IService)
    	
    public:
    	ServiceImp1(ctkPluginContext* contex);
    	void printf();
    
    };
  • 实现类2

    cpp 复制代码
    #pragma once
    #include "IService.h"
    #include <QObject>
    
    
    class ctkPluginContext;
    class ServiceImp2 :
    	public QObject, public IService
    {
    	Q_OBJECT
    	//当一个类继承这个接口类,表明需要实现这个接口类
    	Q_INTERFACES(IService)
    
    public:
    	ServiceImp2(ctkPluginContext* contex);
    	void printf();
    };
  • 服务类

    cpp 复制代码
    =========================PluginActivator.h=============================
    #pragma once
    #include <qobject.h>
    #include "ctkPluginActivator.h"
    #include "ctkPluginContext.h"
    #include "ServiceImp1.h"
    #include "ServiceImp2.h"
    
    class PluginActivator :
    	public QObject, ctkPluginActivator
    {
    	Q_OBJECT
    	Q_INTERFACES(ctkPluginActivator)//向Qt的插件框架声明,希望将xxx插件放入到框架中。
    	Q_PLUGIN_METADATA(IID "TestPlugin3")//向qt框架申明插件(qt5版本)
    
    public:
    	PluginActivator();
    	void start(ctkPluginContext *context);
    	void stop(ctkPluginContext *context);
    private:
    	QScopedPointer<ServiceImp1> m_serviceImp1;//智能指针,自动析构回收
    	QScopedPointer<ServiceImp2> m_serviceImp2;//智能指针,自动析构回收
    };
    
    =========================PluginActivator.cpp=============================
    
    #include "PluginActivator.h"
    #include <QDebug>
    #include "ctkPluginContext.h"
    #include "ctkPluginFrameworkLauncher.h"
    
    PluginActivator::PluginActivator()
    {
    
    }
    void PluginActivator::start(ctkPluginContext *context)
    {
    	qDebug() << "my plugin3 start";
    	m_serviceImp1.reset(new ServiceImp1(context));
    	m_serviceImp2.reset(new ServiceImp2(context));
    
    	ctkDictionary dic1;
    	dic1.insert(ctkPluginConstants::SERVICE_RANKING, 1);
    	dic1.insert("name", "ServiceImp1");
    	context->registerService<IService>(m_serviceImp1.get(), dic1);
    
    	ctkDictionary dic2;
    	dic2.insert(ctkPluginConstants::SERVICE_RANKING, 2);
    	dic2.insert("name", "ServiceImp2");
    	context->registerService<IService>(m_serviceImp2.get(), dic2);
    
    }
    
    void PluginActivator::stop(ctkPluginContext *context)
    {
    	qDebug() << "my plugin3 stop";
    	Q_UNUSED(context)// Q_UNUSED,如果一个函数的有些参数没有用到、某些变量只声明不使用,但是又不想编译器、编辑器报警报,其他没有什么实际性作用
    
    	ctkServiceReference  ref1 = context->getServiceReference<IService>();
    	context->ungetService(ref1);
    
    	m_serviceImp1.reset(NULL);
    	m_serviceImp2.reset(NULL);
    
    	ctkPlugin::State sta = context->getPlugin()->getState();
    
    }
  • 插件测试

    cpp 复制代码
    #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 "../TestPlugin3/IService.h"
    /*
    * 1、注意:Plugin-SymbolicName要满足这里的前缀是:TARGET/META-INF格式。TARGET的名字最好和工程名一致,不然可能出现device not open错误。
    * 2、如果CTK初始化、插件安装启动等是在一个类中,则与CTK相关的变量应定义成类的属性,不能是成员变量,否则获取不到服务
    * 53、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();
    
    	// 启动插件工厂
    	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;
    
    	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);
    
    			//获取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);//表示立即启用插件,不设置参数的话加载后也不会立即打印输出
    	}
    	catch (ctkPluginException e) {
    		std::cout << e.message().toStdString() << e.getType() << std::endl;
    	}
    
    
    	//3、测试插件(一个接口多个实现一个服务多个插件<一个dll内>)
    	//3.1获取所有服务
    	
    	QList<ctkServiceReference> ref3List = framework->getPluginContext()->getServiceReferences<IService>();
    	foreach (ctkServiceReference var, ref3List)
    	{
    		if (var)
    		{
    			qDebug() << "service name:" << var.getProperty("name").toString();
    			qDebug() << "service ranking:" << var.getProperty(ctkPluginConstants::SERVICE_RANKING).toLongLong();
    			qDebug() << "service id:" << var.getProperty(ctkPluginConstants::SERVICE_ID).toLongLong();
    			IService* service3 = qobject_cast<IService*>(framework->getPluginContext()->getService(var));
    			if (service3)
    			{
    				service3->printf();
    			}
    		}
    	}
    
    	//3.2获取某些服务
    	IService* service3 = NULL;
    	ref3List = framework->getPluginContext()->getServiceReferences<IService>("(&(name=ServiceImp1))");
    	foreach(ctkServiceReference var, ref3List)
    	{
    		if (var)
    		{
    			qDebug() << "service name:" << var.getProperty("name").toString();
    			qDebug() << "service ranking:" << var.getProperty(ctkPluginConstants::SERVICE_RANKING).toLongLong();
    			qDebug() << "service id:" << var.getProperty(ctkPluginConstants::SERVICE_ID).toLongLong();
    			IService* service3 = qobject_cast<IService*>(framework->getPluginContext()->getService(var));
    			if (service3)
    			{
    				service3->printf();
    			}
    		}
    	}
    
    	//3.3获取一个服务,由service ranking 和service id决定
    	ctkServiceReference ref = framework->getPluginContext()->getServiceReference<IService>();
    	if (ref) {
    		IService* service3 = qobject_cast<IService*>(framework->getPluginContext()->getService(ref));
    		if (service3)
    		{
    			service3->printf();
    		}
    	}
    
    
    
    
    	//ctkPlugin::State sta = plugin->getState();
    	//ctkPluginFrameworkLauncher::stop();
    	//plugin->stop(); 
    	//plugin->uninstall();
    	//sta = plugin->getState();
    
    	
    
    	CTKPlugin c;
    	c.show();
        return a.exec();
    }
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
范特西.i4 天前
QT聊天项目(8)
开发语言·qt
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode