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

本文详细介绍了如何在CTK框架中实现事件监听、类通信以及信号槽通信。通过创建接收和发送插件,展示了订阅和发布事件的过程,同时比较了类通信和信号槽通信的区别。此外,还提到了插件依赖管理和获取元数据的方法。
摘要由CSDN通过智能技术生成

一、前言

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;
使用 ctkpluginframework 插件系统构建项目,可以将应用程序拆分成多个独立的插件,每个插件可以独立编译、打包和部署。这种方式可以提高应用程序的可维护性和可扩展性,使得不同的开发人员可以独立进行开发和测试。 下面是一个使用 ctkpluginframework 插件系统构建项目的实战示例: 1. 创建一个主应用程序 首先,我们需要创建一个主应用程序,它将作为整个应用程序的入口点。可以使用 Qt Creator 创建一个新的 Qt 应用程序项目。在创建项目时,选择“Qt Widgets Application”,并勾选“Create Main Window”选项。 2. 集成 ctkpluginframework 库 在 Qt Creator 中,打开项目的 .pro 文件,并添加下面这行代码: ``` LIBS += -lctkPluginFramework ``` 这将链接 ctkpluginframework 库到项目中。 3. 创建插件接口 在项目中创建一个新的接口类,例如: ```cpp class MyPluginInterface { public: virtual ~MyPluginInterface() {} virtual void doSomething() = 0; }; ``` 这个接口将定义所有插件需要实现的方法。 4. 创建插件 创建一个新的 Qt 应用程序项目,并选择“Library”类型。在创建项目时,勾选“Export as a library”选项。 在项目中实现 MyPluginInterface 接口: ```cpp #include "myplugininterface.h" class MyPlugin : public QObject, public MyPluginInterface { Q_OBJECT Q_INTERFACES(MyPluginInterface) public: void doSomething() override; }; void MyPlugin::doSomething() { // 实现具体的插件逻辑 } ``` 5. 注册插件 在主应用程序中,使用 ctkPluginFramework API 注册插件: ```cpp #include <ctkPluginFramework.h> #include "myplugininterface.h" Q_IMPORT_PLUGIN(MyPlugin) QList<MyPluginInterface*> plugins; ctkPluginContext* context = ctkPluginFrameworkFactory::getFramework()->getPluginContext(); QList<ctkServiceReference> refs = context->getServiceReferences<MyPluginInterface>(); foreach (ctkServiceReference ref, refs) { plugins.append(qobject_cast<MyPluginInterface*>(context->getService(ref))); } ``` 这将获取所有实现 MyPluginInterface 接口的插件,并将它们添加到 plugins 列表中。 6. 加载插件 在应用程序启动时,使用 ctkPluginFramework API 加载插件: ```cpp ctkPluginFrameworkFactory factory; ctkPluginFramework* framework = factory.newFramework(); framework->init(); framework->start(); context = framework->getPluginContext(); QStringList pluginPaths; pluginPaths << "path/to/MyPlugin.dll"; foreach (QString pluginPath, pluginPaths) { QSharedPointer<ctkPlugin> plugin = context->installPlugin(QUrl::fromLocalFile(pluginPath)); plugin->start(); } ``` 这将加载 MyPlugin.dll 插件,并启动它。 7. 使用插件 在主应用程序中,可以使用插件提供的功能: ```cpp foreach (MyPluginInterface* plugin, plugins) { plugin->doSomething(); } ``` 这将调用所有已加载插件的 doSomething 方法。 以上就是使用 ctkpluginframework 插件系统构建项目的实战示例。需要注意的是,这只是一个简单的示例,实际应用中可能需要更多的代码来处理插件的生命周期、依赖项解析等问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值