0
点赞
收藏
分享

微信扫一扫

CTK Plugin Framework插件框架学习--插件通信【注册接口调用】


文章目录

  • ​​一、前言​​
  • ​​二、插件完善​​
  • ​​2.1、添加接口文件​​
  • ​​2.2、添加接口实现类​​
  • ​​2.3、服务注册(Activator注册服务)​​
  • ​​三、接口调用​​
  • ​​四、接口 - 插件 - 服务的关系​​
  • ​​4.1、1对1​​
  • ​​4.2、多对1​​
  • ​​4.3、1对多​​

一、前言

通过​​Qt基于CTK Plugin Framework搭建插件框架–创建插件​​一文,我们知道了CTK创建插件的基本流程,但是在这篇文章中,我们只是创建了一个空插件,一个只有激活类的插件,没有任何功能。

一个CTK标准插件应该包含有:接口类、接口实现类、激活类;

  • 接口类就只做接口声明;
  • 实现类就只实现接口;
  • 激活类就负责将服务整合到ctk框架中;

二、插件完善

2.1、添加接口文件

首先我们需要确定插件向外部暴露的功能有什么,例如:添加说“Hello,CTK!”的操作

添加接口文件【HelloService.h】(通常是一个C++头文件,一个虚基类)

CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_qt

【HelloService.h】中添加如下代码

#ifndef HELLO_SERVICE_H
#define HELLO_SERVICE_H

#include <QtPlugin>

class HelloService
{
public:
virtual ~HelloService() {}
virtual void sayHello() = 0;
};

#define HelloService_iid "org.commontk.service.demos.HelloService"
Q_DECLARE_INTERFACE(HelloService, HelloService_iid)
//此宏将当前这个接口类声明为接口,后面的一长串就是这个接口的唯一标识。
#endif // HELLO_SERVICE_H

CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_#define_02

2.2、添加接口实现类

添加接口实现类【HelloImpl】

#ifndef HELLO_IMPL_H
#define HELLO_IMPL_H

#include "HelloService.h"
#include <QObject>

class ctkPluginContext;

class HelloImpl : public QObject, public HelloService
{
Q_OBJECT
Q_INTERFACES(HelloService)
/*
此宏与Q_DECLARE_INTERFACE宏配合使用。
Q_DECLARE_INTERFACE:声明一个接口类
Q_INTERFACES:当一个类继承这个接口类,表明需要实现这个接口类
*/

public:
HelloImpl(ctkPluginContext* context);
void sayHello() Q_DECL_OVERRIDE;
};

#endif // HELLO_IMPL_H

#include "hello_impl.h"
#include <ctkPluginContext.h>
#include <QtDebug>

HelloImpl::HelloImpl(ctkPluginContext* context)
{

}

void HelloImpl::sayHello()
{
qDebug() << "Hello,CTK!";
}

2.3、服务注册(Activator注册服务)

激活类里有一个独占智能指针,指向接口类【使用多态,指针都指向父类】,然后在start里new一个实现类,注册这个实现类为服务,功能是实现接口类的接口,然后将智能指针指向这个实现类。

可以理解为以后向框架索取这个服务的时候,实际获取的就是这个new出来的实现类。如果不用智能指针,就需要在stop里手动delete这个实现类。

CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_c++_03


CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_qt_04

三、接口调用

插件启用后,就可以调用接口了

#include "mainwindow.h"

#include <QApplication>

#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include <QDebug>

#include "../HelloCTK/HelloService.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("CTK_PluginFramework");//给框架创建名称,Linux下没有会报错


ctkPluginFrameworkFactory frameworkFactory;
QSharedPointer<ctkPluginFramework> framework = frameworkFactory.getFramework();

// 初始化并启动插件框架
try {
framework->init();
framework->start();
qDebug() << "======================================";
qDebug() << "CTK plugin framework start...";
qDebug() << "======================================";
} catch (const ctkPluginException &e) {
qDebug() << "CTK plugin framework init err: " << e.what();
return -1;
}

// 获取插件服务的contex
ctkPluginContext* pluginContext = framework->getPluginContext();
try {
// 安装插件
QString HelloCTK_dir = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/HelloCTK/bin/plugins/HelloCTK.dll";
QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(HelloCTK_dir));
qDebug() << QString("Plugin[%1_%2] installed...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());
// 启动插件
plugin->start(ctkPlugin::START_TRANSIENT);
qDebug() << QString("Plugin[%1_%2] started").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());
} catch (const ctkPluginException &e) {
qDebug() << QString("Failed install or run plugin: ") << e.what();
return -2;
}

// 获取服务引用
ctkServiceReference reference = pluginContext->getServiceReference<HelloService>();
if (reference) {
// 获取指定 ctkServiceReference 引用的服务对象
//HelloService* service = qobject_cast<HelloService *>(pluginContext->getService(reference));
HelloService* service = pluginContext->getService<HelloService>(reference);
if (service != Q_NULLPTR) {
// 调用服务
service->sayHello();
}
}

MainWindow w;
w.show();

return a.exec();
}

CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_插件框架_05


CTK Plugin Framework插件框架学习--插件通信【注册接口调用】_qt_06


服务就是接口的实例,每生成一个服务就会调用一次注册器的start。把接口当做类,服务是根据类new出的对象,插件就是动态库dll。

四、接口 - 插件 - 服务的关系

4.1、1对1

1个接口类由1个类实现,输出1个服务和1个插件

上面项目为典型的1对1关系;

4.2、多对1

1个类实现多个接口类,输出多个服务和1个插件

无论像使用哪个服务最终都通过这同一插件来实现;

接口类1

#include <QtPlugin>

class Service1
{
public:
virtual ~Service1() {}
virtual void sayHello() = 0;
};

#define Service1_iid "org.commontk.service.demos.Service1"
Q_DECLARE_INTERFACE(Service1, Service1_iid)
//此宏将当前这个接口类声明为接口,后面的一长串就是这个接口的唯一标识。

接口类2

#include <QtPlugin>

class Service2
{
public:
virtual ~Service2() {}
virtual void sayBye() = 0;
};

#define Service2_iid "org.commontk.service.demos.Service2"
Q_DECLARE_INTERFACE(Service2, Service2_iid)
//此宏将当前这个接口类声明为接口,后面的一长串就是这个接口的唯一标识。

实现类:实现多个接口

#include "Service1.h"
#include "Service2.h"
#include <QObject>

class ctkPluginContext;

class HelloImpl : public QObject, public Service1, public Service2
{
Q_OBJECT
Q_INTERFACES(Service1)
Q_INTERFACES(Service2)
/*
此宏与Q_DECLARE_INTERFACE宏配合使用。
Q_DECLARE_INTERFACE:声明一个接口类
Q_INTERFACES:当一个类继承这个接口类,表明需要实现这个接口类
*/

public:
HelloImpl(ctkPluginContext* context);
void sayHello() Q_DECL_OVERRIDE;
void sayBye() Q_DECL_OVERRIDE;
};

#include "hello_impl.h"
#include <QtDebug>

HelloImpl::HelloImpl(ctkPluginContext* context)
{

}

void HelloImpl::sayHello()
{
qDebug() << "Hello,CTK!";
}

void HelloImpl::sayBye()
{
qDebug() << "Bye Bye,CTK!";
}

获取不同服务

// 获取服务引用
ctkServiceReference ref = context->getServiceReference<Service1>();
if (ref) {
Service1* service = qobject_cast<Service1 *>(context->getService(ref));
if (service != Q_NULLPTR)
service->sayHello();
}

ref = context->getServiceReference<Service2>();
if (ref) {
Service2* service = qobject_cast<Service2 *>(context->getService(ref));
if (service != Q_NULLPTR)
service->sayBye();
}

4.3、1对多

1个接口类,多个实现类,输出1个服务和多个插件

也就是某一个问题提供了多种解决思路,可以将接口类理解为一个问题,实现类则是解决思路;
例如,接口类有个接口,需要说:你好!实现类可以是中文说、也可以是英语说、也可以是法语说、也可以是俄语说,…

通过​​ctkPluginConstants::SERVICE_RANKING​​​和​​ctkPluginConstants::SERVICE_ID​​来调用不同的插件

虽然有多个插件,但都是被编译到同一个dll中。

服务的获取策略如下:

  • 容器会返回排行最低的服务,返回注册时​​SERVICE_RANKING​​属性值最小的服务;
  • 如果有多个服务的排行值相等,那么容器将返回PID值最小的那个服务;

某插件每次调用另一个插件的时候,只会生成一个实例,然后把实例存到内存当中,不会因为多次调用而生成多个服务实例。

接口类

#include <QtPlugin>

class Service
{
public:
virtual ~Service() {}
virtual void welcome() = 0;
};

#define Service_iid "org.commontk.service.demos.Service"
Q_DECLARE_INTERFACE(Service, Service_iid)
//此宏将当前这个接口类声明为接口,后面的一长串就是这个接口的唯一标识。

实现类1和激活类1

#include "Service.h"
#include <QObject>

class ctkPluginContext;

class WelcomeCTKImpl : public QObject, public Service
{
Q_OBJECT
Q_INTERFACES(Service)
/*
此宏与Q_DECLARE_INTERFACE宏配合使用。
Q_DECLARE_INTERFACE:声明一个接口类
Q_INTERFACES:当一个类继承这个接口类,表明需要实现这个接口类
*/

public:
WelcomeCTKImpl(ctkPluginContext* context);
void welcome() Q_DECL_OVERRIDE;
};

#include "WelcomeCTKImpl.h"
#include <QtDebug>

WelcomeCTKImpl::WelcomeCTKImpl(ctkPluginContext* context)
{

}

void WelcomeCTKImpl::welcome()
{
qDebug() << "Welcome,CTK!";
}

void WelcomeCTKActivator::start(ctkPluginContext* context)
{
ctkDictionary properties;
properties.insert(ctkPluginConstants::SERVICE_RANKING, 2);
properties.insert("name", "CTK");

m_pImpl = new WelcomeCTKImpl();
context->registerService<WelcomeService>(m_pImpl, properties);
}

void WelcomeCTKActivator::stop(ctkPluginContext* context)
{
Q_UNUSED(context)

delete m_pImpl;
}

实现类2和激活类2

#include "Service.h"
#include <QObject>

class ctkPluginContext;

class WelcomeQtImpl : public QObject, public Service
{
Q_OBJECT
Q_INTERFACES(Service)
/*
此宏与Q_DECLARE_INTERFACE宏配合使用。
Q_DECLARE_INTERFACE:声明一个接口类
Q_INTERFACES:当一个类继承这个接口类,表明需要实现这个接口类
*/

public:
WelcomeQtImpl(ctkPluginContext* context);
void welcome() Q_DECL_OVERRIDE;
};

#include "WelcomeQtImpl.h"
#include <QtDebug>

WelcomeQtImpl::WelcomeQtImpl(ctkPluginContext* context)
{

}

void WelcomeQtImpl::welcome()
{
qDebug() << "Welcome,Qt!";
}

void WelcomeQtActivator::start(ctkPluginContext* context)
{
ctkDictionary properties;
properties.insert(ctkPluginConstants::SERVICE_RANKING, 1);
properties.insert("name", "Qt");

m_pImpl = new WelcomeQtImpl();
context->registerService<WelcomeService>(m_pImpl, properties);
}

void WelcomeQtActivator::stop(ctkPluginContext* context)
{
Q_UNUSED(context)

delete m_pImpl;
}

获取服务

// 1. 获取所有服务
QList<ctkServiceReference> refs = context->getServiceReferences<Service>();
foreach (ctkServiceReference ref, refs) {
if (ref) {
qDebug() << "Name:" << ref.getProperty("name").toString()
<< "Service ranking:" << ref.getProperty(ctkPluginConstants::SERVICE_RANKING).toLongLong()
<< "Service id:" << ref.getProperty(ctkPluginConstants::SERVICE_ID).toLongLong();
Service* service = qobject_cast<Service *>(context->getService(ref));
if (service != Q_NULLPTR)
service->welcome();
}
}

// 2. 使用过滤表达式,获取感兴趣的服务
refs = context->getServiceReferences<Service>("(&(name=CTK))");
foreach (ctkServiceReference ref, refs) {
if (ref) {
Service* service = qobject_cast<Service *>(context->getService(ref));
if (service != Q_NULLPTR)
service->welcome();
}
}

// 3. 获取某一个服务(由 Service Ranking 和 Service ID 决定)
ctkServiceReference ref = context->getServiceReference<Service>();
if (ref) {
Service* service = qobject_cast<Service *>(context->getService(ref));
if (service != Q_NULLPTR)
service->welcome();
}


举报

相关推荐

0 条评论