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

news2024/11/23 23:56:54

文章目录

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

一、前言

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

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

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


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

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

在这里插入图片描述

加载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<QString,QVariant> ctkDictionary


三、类通信

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

3.1、新建接收插件

在这里插入图片描述
插件结构说明:

  • 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 <ctkPluginActivator.h>

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 <service/event/ctkEventConstants.h>
#include <QtDebug>

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 <QObject>
#include <service/event/ctkEventHandler.h>


// 事件处理程序(或订阅者)
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、新建发送插件

在这里插入图片描述
插件结构说明:

  • 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 <ctkPluginActivator.h>

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 <QtDebug>
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 <ctkPluginContext.h>

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 <service/event/ctkEventAdmin.h>
#include <QtDebug>

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 <QApplication>

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

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

在这里插入图片描述


四、信号槽通信

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

4.1、新建接收插件

在这里插入图片描述

插件结构说明:

  • 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 <QObject>
#include <service/event/ctkEvent.h>


// 事件处理程序(或订阅者)
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 <ctkPluginActivator.h>

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 <service/event/ctkEventConstants.h>
#include <service/event/ctkEventAdmin.h>
#include <QtDebug>

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、新建发送插件

在这里插入图片描述
插件结构说明:

  • 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 <QObject>
#include <ctkPluginContext.h>
#include <service/event/ctkEventAdmin.h>

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 <QtDebug>

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 <ctkPluginActivator.h>

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 <QApplication>

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

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

在这里插入图片描述


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

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;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dolphin Streaming实时计算,助力商家端算法第二增长曲线

丨目录&#xff1a;1.背景2.业务问题3.业界解决方案4.技术方案5.应用示例6.业务收益7.总结1. 背景随着业务朝向精细化经营增长&#xff0c;阿里妈妈商家端营销产品更加聚焦客户投放体验&#xff0c;旨在帮助商家提升经营效果&#xff0c;在变化的市场中找到确定增长。近年来&am…

【OpenGL学习】OpenGL介绍

一、OpenGL是什么&#xff1f; 在早期一般认为OpenGL是一个API(Application Programming Interface, 应用程序编程接口)&#xff0c;包含了一系列可以操作图形、图像的函数。然而&#xff0c;OpenGL本身并不是一个API&#xff0c;它仅仅是一个由Khronos组织制定并维护的规范(S…

商城模式“分享购”意味着什么?关键逻辑是什么?

商城模式“分享购”近期炽手可热的卖家打赏模式电子商务平台&#xff0c;根据申请注册交易赠予贡献度&#xff0c;推动者每日权重计算分派领到固定不动积分兑换数量空投物资&#xff0c;积分兑换前去出售买卖转现&#xff0c;店家根据选购积分兑换完成对客户的交易打赏主播&…

【遥感综述】

遥感综述小集合 Image fusion meets deep learning: A survey and perspective&#xff08;张浩&#xff0c;马佳义&#xff09; Sharpening fusion 多光谱图像锐化和高光谱图像锐化是两种典型的锐化融合任务。 Multi-spectral sharpening多光谱锐化 多光谱锐化是将低空间…

Android开发必修—Activity,View,Window三者之间的密切联系【深度解析】

简介 Activity就像工匠&#xff0c;Window就像是窗户&#xff0c;View就像是窗花&#xff0c;LayoutInflater像剪刀&#xff0c;Xml配置像窗花图纸。 Android根据他们不同的职能让他们各斯其活&#xff0c;同时也相互配合展示给我们灵活、精致的界面。 一张图理清所有层级关系&…

量子计算机的推出会影响网络安全吗

在谈量子计算机之前&#xff0c;首先得了解什么是量子计算机。量子计算机是一类遵循量子力学规律进行高速数学和逻辑运算、存储及处理量子信息的物理装置。当某个装置处理和计算的是量子信息&#xff0c;运行的是量子算法时&#xff0c;它就是量子计算机。量子计算机在分析大量…

飞凌OK3568-C嵌入式Linux开发板开箱体验

本篇来介绍一款具有AI计算能力的Linux开发板&#xff1a;OK3568-C。 1 OK3568-C介绍 OK3568-C是飞凌嵌入式设计的一款开发板&#xff0c;板子的包装如下&#xff0c;盒子内有两层&#xff0c;上层是开发板&#xff0c;下层是配件。 1.1 核心板介绍 开发板的SOC采用Rockchip的…

MySQL进阶:索引的使用及理解

MySQL索引前言什么是索引?索引的优缺点?MySql索引添加索引的sql语句添加PRIMARY KEY&#xff08;主键索引&#xff09;添加UNIQUE(唯一索引)添加INDEX(普通索引)添加FULLTEXT(全文索引)添加多列索引索引的底层数据结构哈希索引BTree树索引为什么索引会提高查找速度?最左前缀…

数据结构与算法2—链表

1. 链表 线性表的链式存储结构就是用一组任意的存储单元&#xff08;可以是不连续的&#xff09;存储线性表的数据元素。采用链式存储结构的表示的线性表简称链表。链式存储方式可用于表示线性结构&#xff0c;也可用于表示非线性结构。 链表通常有两个域 data域——存放结点…

北大硕士LeetCode算法专题课--递归和回溯

算法专题课 北大硕士LeetCode算法专题课-栈、队列相关问题_骨灰级收藏家的博客-CSDN博客 北大硕士LeetCode算法专题课--链表相关问题_骨灰级收藏家的博客-CSDN博客 北大硕士LeetCode算法专题课-查找相关问题_骨灰级收藏家的博客-CSDN博客 北大硕士LeetCode算法专题课-字符串…

一.【linux】常用操作命令

目录 1 . 查看当前登录系统用户信息 【who】 2 . 查看终端用户whoami 3 . 查看日期date 4 . 查看系统信息uname 5 . 日历cal 6 . 计算器bc 8 . 显示或修改计算机主机名的命令hostname 9 . 在显示器上显示文字的命令echo 10 . 清除计算机屏幕上的信息命令clear 11 . 查…

【尚硅谷】Java数据结构与算法笔记04 - 栈

文章目录一、实际需求二、栈的介绍三、应用场景四、数组模拟栈4.1 思路分析4.2 Java代码实现五、栈实现综合计算器5.1 中缀表达式5.2 后缀表达式一、实际需求 二、栈的介绍 栈的英文为(stack)栈是一个先入后出(FILO-First In Last Out)的有序列表。栈(stack)是限制线性表中元素…

nacos 2.1.0集群生产环境多节点部署

nacos 2.1.0集群生产环境多节点部署 版本 2.1.0版本发布日期 2022-04-29官网 集群部署说明GitHub GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. 下载地址&…

大数据面试题(七):Flume核心高频面试题

文章目录 Flume核心高频面试题 一、Flume有哪些组件&#xff0c;flume的source、channel、sink具体是做什么的 二、你是如何实现flume数据传输的监控的 三、Flume的source&#xff0c;sink&#xff0c;channel的作用&#xff1f;你们source是什么类型&#xff1f; 四、Flu…

2023年浙江建筑施工电工(建筑特种作业)模拟试题及答案

百分百题库提供特种工&#xff08;电工&#xff09;考试试题、特种工&#xff08;电工&#xff09;考试真题、特种工&#xff08;电工&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 159.临时⽤电⼯程验收应填写&#xff0…

基于Vue 全家桶实现网易云音乐 WebApp

基于 Vue(2.5) vuex vue-router vue-axios better-scroll Scss ES6 等开发一款移动端音乐 WebApp&#xff0c;UI 界面参考了安卓版的网易云音乐、flex 布局适配常见移动端。因为服务器的原因&#xff0c;所以可能多人访问的时候有些东西会加载不出来&#xff0c;如果可以的…

P2069 松鼠吃果子

题目描述 输入输出样例 输入 #1 10 4 输出 #1 9 说明/提示 注&#xff1a;吃掉的果子依次为 3&#xff0c;8&#xff0c;5&#xff08;回到下面重做第 3 跳&#xff09;&#xff0c;9&#xff08;回到下面重做第 4 跳&#xff09;。 写着感觉好别扭 感觉和字符串没啥关…

接口管理工具YApi怎么用?颜值高、易管理、超好用!

众多接口管理工具如雨后春笋搬冒出。让人欣慰的是&#xff0c;有许多优秀作品来自国内&#xff0c;包含YApi和rap。 看着中文的官网&#xff0c;熟悉的汉语&#xff0c;不禁让人暗爽。当然这也就带来另一个弊端&#xff0c;因为使用基数少&#xff0c;所以参考资料少。我们想学…

发送tcp/udp请求

本文章介绍下通过nc工具和iperf工具&#xff0c;发送tcp/udp请求一、nc工具&#xff08;netcat工具&#xff09;这个工具linux系统默认是自带的&#xff0c;以下是命令的常用参数1.1 发送tcp请求在服务端监听端口nc -l port客户端连接并发送请求nc -v host port在服务端收到了信…

自定义动态IRule进行路由转发

gateway的负载均衡以及feign服务之间的调用或者RestTemplate请求&#xff0c;都可以使用自定义IRule规则进行路由转发。 自定义IRule 固定IRule&#xff1a;将IRule的一个实现类注入到spring容器中 Configuration public class MyIRuleConfig { Beanpublic IRule myIRule() {…