CTK Plugin Framework插件框架学习--插件通信【事件监听】

文章目录

  • 一、前言
  • 二、事件
  • 三、类通信
    • 3.1、新建接收插件
    • 3.2、新建发送插件
    • 3.3、启用插件
  • 四、信号槽通信
    • 4.1、新建接收插件
    • 4.2、新建发送插件
    • 4.3、启用插件
  • 五、类通信和信号槽通信的区别
  • 六、插件依赖
  • 七、获取元数据

一、前言

CTK框架中的事件监听,其实就是观察者模式,流程大概如下:

  • 接收者注册监听事件(接收方想监听xxx信息)
  • 发送者发送事件(发送方发送xxx信息)
  • 接收者接收到事件并响应(接收方收到xxx事件后的动作)

相比调用插件接口监听事件插件间依赖关系更弱,不用指定事件的接收方和发送方是谁


要使用CTK框架的事件服务,在编译CTK库是需要编译出支持事件监听的动态库:liborg_commontk_eventadmin.dll

在编译输出的CTK文件夹中可以找到

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第1张图片

加载EventAdmin动态库main.cpp中新增如下代码

#include "ctkPluginFrameworkLauncher.h"

// 获取插件所在位置
// 在插件的搜索路径列表中添加一条路径
ctkPluginFrameworkLauncher::addSearchPath("../CTK/lib/ctk-0.1/plugins");
// 设置并启动 CTK 插件框架
ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");

// 停止插件
//ctkPluginFrameworkLauncher::stop();

二、事件

1、通信主要用到了ctkEventAdmin结构体,主要定义了如下接口:

事件 说明
postEvent 类通信形式,异步发送事件
sendEvent 类通信形式,同步发送事件
publishSignal 信号与槽通信形式发送事件
unpublishSignal 取消发送事件
subscribeSlot 信号与槽通信形式订阅事件,返回订阅的ID
unsubscribeSlot 取消订阅事件
updateProperties 更新某个订阅ID的主题

2、通信是数据:ctkDictionary

其实就是个hash表:typedef QHash ctkDictionary


三、类通信

原理:直接将信息使用CTK的eventAdmin接口send/post出去;

3.1、新建接收插件

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第2张图片
插件结构说明:

  • BlogEventHandler:接收类【订阅者】
  • BlogEventHandlerActivator:激活类

工程文件:BlogEventHandler.pro

QT += core
QT -= gui

TEMPLATE = lib
CONFIG += plugin
TARGET = BlogEventHandler
DESTDIR = $$OUT_PWD/bin/plugins

include($$PWD/../CTK/CTK_dependency.pri)

RESOURCES += \
    qresource.qrc

HEADERS += \
    BlogEventHandler.h \
    blog_event_handler_activator.h

SOURCES += \
    blog_event_handler_activator.cpp


激活类:BlogEventHandlerActivator

#ifndef BLOGEVENTHANDLERACTIVATOR_H
#define BLOGEVENTHANDLERACTIVATOR_H

#include 

class BlogEventHandler;

class BlogEventHandlerActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    Q_PLUGIN_METADATA(IID "BLOG_EVENT_HANDLER")

public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);

private:
    BlogEventHandler *m_pEventHandler;
};
#endif // BLOGEVENTHANDLERACTIVATOR_H

#include "blog_event_handler.h"
#include "blog_event_handler_activator.h"
#include 
#include 

void BlogEventHandlerActivator::start(ctkPluginContext* context)
{
    m_pEventHandler = new BlogEventHandler();

    ctkDictionary props;
    props[ctkEventConstants::EVENT_TOPIC] = "org/commontk/bloggenerator/published"; //订阅的主题
    props[ctkEventConstants::EVENT_FILTER] = "(author=wangjichuan)";    //事件过滤
    context->registerService<ctkEventHandler>(m_pEventHandler, props);
}

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

    delete m_pEventHandler;
}


接收类[订阅者]:BlogEventHandler

#ifndef BLOGEVENTHANDLER_H
#define BLOGEVENTHANDLER_H

#include 
#include 


// 事件处理程序(或订阅者)
class BlogEventHandler : public QObject, public ctkEventHandler
{
    Q_OBJECT
    Q_INTERFACES(ctkEventHandler)

public:
    BlogEventHandler();

    // 处理事件
    void handleEvent(const ctkEvent& event) Q_DECL_OVERRIDE;

};

#endif // BLOGEVENTHANDLER_H

#include "blog_event_handler.h"

BlogEventHandler::BlogEventHandler()
{

}

void BlogEventHandler::handleEvent(const ctkEvent& event)
{
    QString title = event.getProperty("title").toString();
    QString content = event.getProperty("content").toString();
    QString author = event.getProperty("author").toString();

    qDebug()<<"====================================================";
    qDebug()<<"=== EventHandler received the message ===";
    qDebug()<<"topic: "<<event.getTopic();
    qDebug()<<"----------- properties -----------";
    qDebug()<<"title: "<<title;
    qDebug()<<"content: "<<content;
    qDebug()<<"author: "<<author;
}


3.2、新建发送插件

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第3张图片
插件结构说明:

  • BlogManager:发送类【发布者】
  • BlogManagerActivator:激活类

添加代码


工程文件:BlogManager.pro

QT += core
QT -= gui

TEMPLATE = lib
CONFIG += plugin
TARGET = BlogManager
DESTDIR = $$OUT_PWD/bin/plugins

include($$PWD/../CTK/CTK_dependency.pri)

RESOURCES += \
    qresource.qrc

HEADERS += \
    blog_manager.h \
    blog_manager_activator.h

SOURCES += \
    blog_manager.cpp \
    blog_manager_activator.cpp


激活类BlogManagerActivator

#ifndef BLOG_MANAGER_ACTIVATOR_H
#define BLOG_MANAGER_ACTIVATOR_H

#include 

class BlogManager;

class BlogManagerActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    Q_PLUGIN_METADATA(IID "BLOG_MANAGER")

public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);

private:
    BlogManager *m_pBlogManager;
};

#endif // BLOG_MANAGER_ACTIVATOR_H

#include "blog_manager.h"
#include "blog_manager_activator.h"
#include 
void BlogManagerActivator::start(ctkPluginContext* context)
{
    m_pBlogManager = new BlogManager(context);

    Blog blog;
    blog.title = "CTK Event Admin";
    blog.content = "This is a simple blog";
    blog.author = "wangjichuan";
    m_pBlogManager->publishBlog(blog);
}

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

    delete m_pBlogManager;
}


发送类[发布者]:BlogManager

#ifndef BLOG_MANAGER_H
#define BLOG_MANAGER_H

#include 

typedef struct Blog_Info {
    QString title;
    QString author;
    QString content;
} Blog;

// 事件发布者
class BlogManager
{
public:
    BlogManager(ctkPluginContext* context);
    // 发布事件
    void publishBlog(const Blog& blog);

private:
    ctkPluginContext* m_pContext;
};

#endif // BLOG_MANAGER_H

#include "blog_manager.h"
#include 
#include 

BlogManager::BlogManager(ctkPluginContext* context)
    : m_pContext(context)
{

}

// 发布事件
void BlogManager::publishBlog(const Blog& blog)
{
    ctkServiceReference ref = m_pContext->getServiceReference<ctkEventAdmin>();
    if (ref) {
        ctkEventAdmin* eventAdmin = m_pContext->getService<ctkEventAdmin>(ref);

        ctkDictionary props;
        props["title"] = blog.title;
        props["content"] = blog.content;
        props["author"] = blog.author;
        ctkEvent event("org/commontk/bloggenerator/published", props);

        qDebug()<<"====================================================";
        qDebug()<<"=== Publisher sends a message ===";
        qDebug()<<"props: "<<props;


        eventAdmin->sendEvent(event);
    }
}


3.3、启用插件

注意;plugin->start(ctkPlugin::START_TRANSIENT)会立即启动插件,而plugin->start()不一定会立即启动插件

修改main.cpp

#include "mainwindow.h"

#include 

#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
#include 

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

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

    // 获取插件所在位置
    // 在插件的搜索路径列表中添加一条路径
    ctkPluginFrameworkLauncher::addSearchPath("../CTK/lib/ctk-0.1/plugins");
    ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");

    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(pluginContext->getService(reference));
        HelloService* service = pluginContext->getService<HelloService>(reference);
        if (service != Q_NULLPTR) {
            // 调用服务
            service->sayHello();
        }
    }

    //---------------------------------------------------------------------------------------------------------------------------------------

    try {
        // 安装插件
        QString BlogEventHandler_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogEventHandler/bin/plugins/BlogEventHandler.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogEventHandler_Path));
        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;
    }

    try {
        // 安装插件
        QString BlogManager_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogManager/bin/plugins/BlogManager.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogManager_Path));
        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;
    }


    // 停止插件
    //ctkPluginFrameworkLauncher::stop();

    //---------------------------------------------------------------------------------------------------------------------------------------

    MainWindow w;
    w.show();

    return a.exec();
}

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第4张图片


四、信号槽通信

原理:将Qt自己的信号与CTK的发送事件绑定、槽与事件订阅绑定;

4.1、新建接收插件

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第5张图片

插件结构说明:

  • BlogEventHandlerUsingSlot:接收类【订阅者】
  • BlogEventHandlerUsingSlotActivator:激活类

工程文件:BlogEventHandlerUsingSlot.pro

QT += core
QT -= gui

TEMPLATE = lib
CONFIG += plugin
TARGET = BlogEventHandlerUsingSlot
DESTDIR = $$OUT_PWD/bin/plugins

include($$PWD/../CTK/CTK_dependency.pri)

HEADERS += \
    blog_event_handler_usingSlot.h \
    blog_event_handler_usingSlot_activator.h \

SOURCES += \
    blog_event_handler_usingSlot.cpp \
    blog_event_handler_usingSlot_activator.cpp

RESOURCES += \
    qresource.qrc


接收类:BlogEventHandlerUsingSlot

#ifndef BLOGEVENTHANDLERUSINGSLOT_H
#define BLOGEVENTHANDLERUSINGSLOT_H

#include 
#include 


// 事件处理程序(或订阅者)
class BlogEventHandlerUsingSlot : public QObject
{
    Q_OBJECT

public:
    BlogEventHandlerUsingSlot();

public slots:
    void slot_BlogPublished(const ctkEvent& event);

};

#endif // BLOGEVENTHANDLERUSINGSLOT_H

#include "blog_event_handler_usingSlot.h"

BlogEventHandlerUsingSlot::BlogEventHandlerUsingSlot()
{

}

void BlogEventHandlerUsingSlot::slot_BlogPublished(const ctkEvent& event)
{
    QString title = event.getProperty("title").toString();
    QString content = event.getProperty("content").toString();
    QString author = event.getProperty("author").toString();

    qDebug()<<"====================================================";
    qDebug()<<"=== slot received the message ===";
    qDebug()<<"topic: "<<event.getTopic();
    qDebug()<<"----------- properties -----------";
    qDebug()<<"title: "<<title;
    qDebug()<<"content: "<<content;
    qDebug()<<"author: "<<author;
}


激活类:BlogEventHandlerUsingSlotActivator

#ifndef BLOGEVENTHANDLERACTIVATOR_H
#define BLOGEVENTHANDLERACTIVATOR_H

#include 

class BlogEventHandlerUsingSlot;

class BlogEventHandlerUsingSlotActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    Q_PLUGIN_METADATA(IID "BLOG_EVENT_HANDLER_USING_SLOT")

public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);

private:
    BlogEventHandlerUsingSlot *m_pEventHandler;
};
#endif // BLOGEVENTHANDLERACTIVATOR_H

#include "blog_event_handler_usingSlot.h"
#include "blog_event_handler_usingSlot_activator.h"
#include 
#include 
#include 

void BlogEventHandlerUsingSlotActivator::start(ctkPluginContext* context)
{
    m_pEventHandler = new BlogEventHandlerUsingSlot();

    ctkDictionary props;
    props[ctkEventConstants::EVENT_TOPIC] = "org/commontk/bloggenerator/published/SignalSlot"; //订阅的主题
    ctkServiceReference ref = context->getServiceReference<ctkEventAdmin>();
    if (ref) {
        ctkEventAdmin* eventAdmin = context->getService<ctkEventAdmin>(ref);
        eventAdmin->subscribeSlot(m_pEventHandler, SLOT(slot_BlogPublished(ctkEvent)), props, Qt::DirectConnection);
    }
}

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

    delete m_pEventHandler;
}


4.2、新建发送插件

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第6张图片
插件结构说明:

  • BlogEventHandlerUsingSignal:接收类【发布者】
  • BlogEventHandlerUsingSignalActivator:激活类

工程文件:BlogEventHandlerUsingSignal.pro

QT += core
QT -= gui

TEMPLATE = lib
CONFIG += plugin
TARGET = BlogManagerUsingSignal
DESTDIR = $$OUT_PWD/bin/plugins

include($$PWD/../CTK/CTK_dependency.pri)

HEADERS += \
    blog_manager_usingSignal.h \
    blog_manager_activator_usingSignal.h

SOURCES += \
    blog_manager_usingSignal.cpp \
    blog_manager_activator_usingSignal.cpp

RESOURCES += \
    qresource.qrc


接收类:BlogEventHandlerUsingSignal

#ifndef BLOG_MANAGER_H
#define BLOG_MANAGER_H

#include 
#include 
#include 

typedef struct Blog_Info {
    QString title;
    QString author;
    QString content;
} Blog;

// 事件发布者
class BlogManagerUsingSignal : public QObject
{
    Q_OBJECT

public:
    BlogManagerUsingSignal(ctkPluginContext* context);
    // 发布事件
    void publishBlog(const Blog& blog);

signals:
    void signal_blogPublished(const ctkDictionary&);

private:
    ctkPluginContext* m_pContext;
};

#endif // BLOG_MANAGER_H

#include "blog_manager_usingSignal.h"
#include 

BlogManagerUsingSignal::BlogManagerUsingSignal(ctkPluginContext* context)
    : m_pContext(context)
{
    ctkServiceReference ref = context->getServiceReference<ctkEventAdmin>();
    if (ref) {
        ctkEventAdmin* eventAdmin = context->getService<ctkEventAdmin>(ref);
        // 使用 Qt::DirectConnection 等同于 ctkEventAdmin::sendEvent()
        eventAdmin->publishSignal(this, SIGNAL(signal_blogPublished(ctkDictionary)), "org/commontk/bloggenerator/published/SignalSlot", Qt::DirectConnection);
    }
}

// 发布事件
void BlogManagerUsingSignal::publishBlog(const Blog& blog)
{
    ctkDictionary props;
    props["title"] = blog.title;
    props["content"] = blog.content;
    props["author"] = blog.author;

    emit signal_blogPublished(props);
}


激活类:BlogEventHandlerUsingSignalActivator

#ifndef BLOG_MANAGER_ACTIVATOR_H
#define BLOG_MANAGER_ACTIVATOR_H

#include 

class BlogManagerUsingSignal;

class BlogManagerActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    Q_PLUGIN_METADATA(IID "BLOG_MANAGER_USING_SIGNAL")

public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);

private:
    BlogManagerUsingSignal *m_pBlogManager;
};

#endif // BLOG_MANAGER_ACTIVATOR_H

#include "blog_manager_usingSignal.h"
#include "blog_manager_activator_usingSignal.h"

void BlogManagerActivator::start(ctkPluginContext* context)
{
    m_pBlogManager = new BlogManagerUsingSignal(context);

    Blog blog;
    blog.title = "CTK Event Admin";
    blog.content = "This is a simple blog";
    blog.author = "wangjichuan";
    m_pBlogManager->publishBlog(blog);
}

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

    delete m_pBlogManager;
}


4.3、启用插件

注意;plugin->start(ctkPlugin::START_TRANSIENT)会立即启动插件,而plugin->start()不一定会立即启动插件

修改main.cpp

#include "mainwindow.h"

#include 

#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
#include 

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

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

    // 获取插件所在位置
    // 在插件的搜索路径列表中添加一条路径
    ctkPluginFrameworkLauncher::addSearchPath("../CTK/lib/ctk-0.1/plugins");
    ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");

    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(pluginContext->getService(reference));
        HelloService* service = pluginContext->getService<HelloService>(reference);
        if (service != Q_NULLPTR) {
            // 调用服务
            service->sayHello();
        }
    }

    //---------------------------------------------------------------------------------------------------------------------------------------
    //注册事件调用-测试插件
    try {
        // 安装插件
        QString BlogEventHandler_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogEventHandler/bin/plugins/BlogEventHandler.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogEventHandler_Path));
        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;
    }

    try {
        // 安装插件
        QString BlogManager_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogManager/bin/plugins/BlogManager.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogManager_Path));
        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;
    }


    //---------------------------------------------------------------------------------------------------------------------------------------
    //信号槽通信-测试插件
    try {
        // 安装插件
        QString BlogEventHandlerUsingSlot_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogEventHandlerUsingSlot/bin/plugins/BlogEventHandlerUsingSlot.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogEventHandlerUsingSlot_Path));
        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;
    }

    try {
        // 安装插件
        QString BlogManagerUsingSignal_Path = "C:/Qt_Pro/build-CTK_PluginFramework-CMake-Debug/BlogManagerUsingSignal/bin/plugins/BlogManagerUsingSignal.dll";
        QSharedPointer<ctkPlugin> plugin = pluginContext->installPlugin(QUrl::fromLocalFile(BlogManagerUsingSignal_Path));
        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;
    }



    // 停止插件
    //ctkPluginFrameworkLauncher::stop();

    //---------------------------------------------------------------------------------------------------------------------------------------

    MainWindow w;
    w.show();

    return a.exec();
}

CTK Plugin Framework插件框架学习--插件通信【事件监听】_第7张图片


五、类通信和信号槽通信的区别

1、通过event事件通信,是直接调用CTK的接口,把数据发送到CTK框架;通过信号槽方式,会先在Qt的信号槽机制中转一次,再发送到CTK框架。故效率上来讲,event方式性能高于信号槽方式。

2、两种方式发送数据到CTK框架,这个数据包含:主题+属性。主题就是topic,属性就是ctkDictionary。 一定要注意signal方式的信号定义,参数不能是自定义的,一定要是ctkDictionary,不然会报信号槽参数异常错误。

3、两种方式可以混用,如发送event事件,再通过槽去接收;发送signal事件,再通过event是接收。

4、同步:sendEvent、Qt::DirectConnection;异步:postEvent、Qt::QueuedConnection

这里的同步是指:发送事件之后,订阅了这个主题的数据便会处理数据【handleEvent、slot】,处理的过程是在发送者的线程完成的。可以理解为在发送了某个事件之后,会立即执行所有订阅此事件的回调函数。

异步:发送事件之后,发送者便会返回不管,订阅了此事件的所有插件会根据自己的消息循环,轮到了处理事件后才会去处理。不过如果长时间没处理,CTK也有自己的超时机制。如果事件处理程序花费的时间比配置的超时时间长,那么就会被列入黑名单。一旦处理程序被列入黑名单,它就不会再被发送任何事件。


六、插件依赖

插件加载时一般根据首字母大小自动加载,所以在插件启用时,某个插件还没有被调用,所以发送事件没有接收方,这样就要考虑到插件依赖关系,在MANIFEST.MF中添加依赖:

Plugin-SymbolicName:Plugin-xxx-1
Plugin-Version:1.0.0
Require-Plugin:Plugin-xxx-2; plugin-version="[1.0,2.0)"; resolution:="mandatory"
  • Plugin-xxx-2:为需要依赖的插件名【就是另一个插件在MANIFEST.MF里的Plugin-SymbolicName】;
  • [1.0,2.0):为Plugin-xxx-2的版本,这里是左闭右开区间,默认是1.0;
  • resolution:有两个选择,optional、mandatory。前者是弱依赖,就算依赖的插件没有,当前插件也能正常使用,后者是强依赖,如果没有依赖的插件,当前插件就不能被start;

这样就向框架申明了,该插件加载时需要先加载Plugin-xxx-2插件,所有用户插件都应该有这样一份申明。


七、获取元数据

//获取元数据
......
QHash<QString, QString> headers = plugin->getHeaders();
ctkVersion version = ctkVersion::parseVersion(headers.value(ctkPluginConstants::PLUGIN_VERSION));
QString name = headers.value(ctkPluginConstants::PLUGIN_SYMBOLICNAME);
QString number = headers.value("Plugin-Number");

qDebug()<<"version: "<<version;
qDebug()<<"name: "<<name;
qDebug()<<"number: "<<number;

你可能感兴趣的:(#,Qt插件开发,qt,插件框架,ctk)