Qt框架学习 --- CTK

系列文章目录


文章目录


前言

随着开发的深入,CTK框架还是要关注一下。了解CTK还是有必要的。本篇文章主要描述CTK框架加载带界面的插件,集成到主界面的操作。


一、准备阶段

什么是CTK? CTK怎么编译?这些就不赘述了,提供几个参考博客即可。

环境:Qt5.15.2 + vs2019(Qt6放弃吧,目前为止编不了;mingw也放弃吧,目前虽然能编过去,但是运行起来就崩溃。当前时间2024.1.11)

编译参考:https://blog.csdn.net/Mr_robot_strange/article/details/128547331

ctk框架使用demo参考:https://github.com/Waleon/CTK-examples

本次demo就是从Waleon的一个模块更改的。

二、使用介绍

1.核心思想

ctk框架核心主要有2点:框架和插件。框架加载插件,之间的通讯使用事件或者信号槽。

2.源码

2.1.框架部分资源目录树

2.2.框架部分源码

框架的核心就是框架和需要调用的服务类(一个纯虚类,同插件共用一个)

cpp 复制代码
QT += core gui widgets
TEMPLATE = app
CONFIG += console
TARGET = App
DESTDIR = $$OUT_PWD/../bin
include($$PWD/../CTK.pri)
SOURCES += \
    MainWindow.cpp \
    main.cpp
FORMS += \
    MainWindow.ui
HEADERS += \
    MainWindow.h
  • CTK.pri
cpp 复制代码
# CTK 安装路径
CTK_INSTALL_PATH = $$PWD/../CTKInstall_vs
# CTK 插件相关库所在路径(例如:CTKCore.lib、CTKPluginFramework.lib)
CTK_LIB_PATH = $$CTK_INSTALL_PATH/lib/ctk-0.1
# CTK 插件相关头文件所在路径(例如:ctkPluginFramework.h)
CTK_INCLUDE_PATH = $$CTK_INSTALL_PATH/include/ctk-0.1
# CTK 插件相关头文件所在路径(主要因为用到了 service 相关东西)
CTK_INCLUDE_FRAMEWORK_PATH = $$PWD/../../../CTK-master/Libs/PluginFramework
LIBS += -L$$CTK_LIB_PATH -lCTKCore -lCTKPluginFramework
INCLUDEPATH += $$CTK_INCLUDE_PATH \
               $$CTK_INCLUDE_FRAMEWORK_PATH
  • MainWindow.h
cpp 复制代码
 #ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void addWidget(QList<QWidget*> wigList);
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
  • MainWindow.cpp
cpp 复制代码
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::addWidget(QList<QWidget *> wigList)
{
    if (wigList.isEmpty()) { return; }
    for (auto wig : wigList) {
        ui->widget->layout()->addWidget(wig);
    }
}
  • main.cpp
cpp 复制代码
#include <QCoreApplication>
#include <QApplication>
#include <QDirIterator>
#include <QtDebug>
#include <ctkPluginFrameworkFactory.h>
#include <ctkPluginFramework.h>
#include <ctkPluginException.h>
#include <ctkPluginContext.h>

#include "../Service/welcome_service.h"
#include "MainWindow.h"
int main(int argc, char *argv[])
{
    // QCoreApplication app(argc, argv);
    QApplication app(argc, argv);

    ctkPluginFrameworkFactory frameWorkFactory;
    QSharedPointer<ctkPluginFramework> framework = frameWorkFactory.getFramework();
    try {
        // 初始化并启动插件框架
        framework->init();
        framework->start();
        qDebug() << "CTK Plugin Framework start ...";
    } catch (const ctkPluginException &e) {
        qDebug() << "Failed to initialize the plugin framework: " << e.what();
        return -1;
    }
    qDebug() << "********************";
    // 获取插件上下文
    ctkPluginContext* context = framework->getPluginContext();
    // 获取插件所在位置
    QString path = QCoreApplication::applicationDirPath() + "/plugins";
    // 遍历路径下的所有插件
    QDirIterator itPlugin(path, QStringList() << "*.dll" << "*.so", QDir::Files);
    while (itPlugin.hasNext()) {
        QString strPlugin = itPlugin.next();
        try {
            // 安装插件
            QSharedPointer<ctkPlugin> plugin = context->installPlugin(QUrl::fromLocalFile(strPlugin));
            // 启动插件
            plugin->start(ctkPlugin::START_TRANSIENT);
            qDebug() << "Plugin start:" << QFileInfo(strPlugin).fileName();
        } catch (const ctkPluginException &e) {
            qDebug() << "Failed to start plugin" << e.what();
            return -1;
        }
    }
    qDebug() << "********************";
    // 1. 获取所有服务
    QList<ctkServiceReference> refs = context->getServiceReferences<WelcomeService>();
    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();
            WelcomeService* service = qobject_cast<WelcomeService *>(context->getService(ref));
            if (service != Q_NULLPTR)
                service->welcome();
        }
    }
    qDebug() << "********************";
    // 2. 使用过滤表达式,获取感兴趣的服务
    refs = context->getServiceReferences<WelcomeService>("(&(name=CTK))");
    foreach (ctkServiceReference ref, refs) {
        if (ref) {
            WelcomeService* service = qobject_cast<WelcomeService *>(context->getService(ref));
            if (service != Q_NULLPTR)
                service->welcome();
        }
    }
    qDebug() << "********************";
    // 3. 获取某一个服务(由 Service Ranking 和 Service ID 决定)
    ctkServiceReference ref = context->getServiceReference<WelcomeService>();
    if (ref) {
        WelcomeService* service = qobject_cast<WelcomeService *>(context->getService(ref));
        if (service != Q_NULLPTR)
            service->welcome();
    }
    QList<QWidget*> wigList;
    refs = context->getServiceReferences<WelcomeService>("(&(name=Qui))");
    foreach (ctkServiceReference ref, refs) {
        if (ref) {
            WelcomeService* service = qobject_cast<WelcomeService *>(context->getService(ref));
            if (service != Q_NULLPTR) {
                wigList << service->widget();
                wigList << service->widget();
            }
        }
    }
    MainWindow w;
    w.addWidget(wigList);
    w.show();
    return app.exec();
}
  • MainWindow.ui

2.3.插件部分资源目录树

2.4.插件部分源码

插件的核心就是实现类(实现一个纯虚类,框架只调用纯虚类的方法 )和激活类。就拿WelcomeQui举例吧

cpp 复制代码
QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17 plugin
TEMPLATE = lib
TARGET = WelcomeQui
DESTDIR = $$OUT_PWD/../../bin/plugins
include($$PWD/../../CTK.pri)
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
    MainWindow.cpp \
    WelcomeQuiActivator.cpp \
    WelcomeQuiImpl.cpp
HEADERS += \
    MainWindow.h \
    WelcomeQuiActivator.h \
    WelcomeQuiImpl.h
FORMS += \
    MainWindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
    Resource.qrc
  • MainWindow.h
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
  • MainWindow.cpp
cpp 复制代码
#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}
  • WelcomeQuiActivator.h 激活类
cpp 复制代码
#ifndef WELCOMEQUIACTIVATOR_H
#define WELCOMEQUIACTIVATOR_H

#include <QObject>
#include <ctkPluginActivator.h>

class WelcomeQuiImpl;

class WelcomeQuiActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    Q_PLUGIN_METADATA(IID "WELCOME_QUI")

public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);
private:
    WelcomeQuiImpl *m_pImpl;
signals:
};
#endif // WELCOMEQUIACTIVATOR_H
  • WelcomeQuiActivator.cpp
cpp 复制代码
#include "WelcomeQuiActivator.h"
#include "WelcomeQuiImpl.h"
#include <QDebug>

void WelcomeQuiActivator::start(ctkPluginContext* context)
{
    ctkDictionary properties;
    properties.insert(ctkPluginConstants::SERVICE_RANKING, 3);
    properties.insert("name", "Qui");

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

void WelcomeQuiActivator::stop(ctkPluginContext* context)
{
    Q_UNUSED(context)
    delete m_pImpl;
}
  • WelcomeQuiImpl.h 实现类
cpp 复制代码
#ifndef WELCOMEQUIIMPL_H
#define WELCOMEQUIIMPL_H

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

class MainWindow;

class WelcomeQuiImpl : public QObject, public WelcomeService
{
    Q_OBJECT
    Q_INTERFACES(WelcomeService)

public:
    explicit WelcomeQuiImpl(QObject *parent = nullptr);
    ~WelcomeQuiImpl();
    void welcome() override;
    QWidget *widget() override;

private:
    MainWindow *_mainwindow = nullptr;
signals:
};

#endif // WELCOMEQUIIMPL_H
  • WelcomeQuiImpl.cpp
cpp 复制代码
#include "WelcomeQuiImpl.h"
#include "MainWindow.h"
#include <QDebug>
WelcomeQuiImpl::WelcomeQuiImpl(QObject *parent)
    : QObject{parent}
{}
WelcomeQuiImpl::~WelcomeQuiImpl()
{
    delete _mainwindow;
}
void WelcomeQuiImpl::welcome()
{
    qDebug() << "welcome Qui";
}
QWidget *WelcomeQuiImpl::widget()
{
    _mainwindow = new MainWindow;
    return _mainwindow;
}
  • MainWindow.ui
  • MANIFEST.MF
cpp 复制代码
Plugin-SymbolicName: Welcome.Qui
Plugin-ActivationPolicy: eager
Plugin-Category: Demos
Plugin-ContactAddress: https://github.com
Plugin-Description: A plugin for welcome Qui
Plugin-Name: WelcomeQui
Plugin-Vendor: shawn
Plugin-Version: 0.0.1

3.文件结构

框架源码目录和插件源码目录的位置。service里面放的是引入ctk库的pri文件,用qt5.15.2+vs2019编译好的ctk的库放在源码的同级目录。bin是生成的目录,里面是exe和plugins文件夹,plugins文件夹里面是编好的插件库dll

4.运行效果

不加载插件的时候,效果是这样的


加载插件后,效果是这样的


UI插件的widget已经被嵌入主界面中了


总结

本demo完全参考博主一去二三里的开源代码实现。

参考:
https://blog.csdn.net/mr_robot_strange/category_11663281.html
https://github.com/Waleon/CTK-examples
https://www.cnblogs.com/judes/p/13285743.html

本篇文章对应demo地址:
https://download.csdn.net/download/yonug1107716573/88731039

相关推荐
无心水5 小时前
24、系统架构师论文写作专题 核心考点与学习笔记
笔记·学习·系统架构·架构师·安全架构·软考架构师·软考系统架构设计师
蒸蒸yyyyzwd5 小时前
邓明 Agent 系统面试-意图识别学习笔记
笔记·学习
BroNekoX9 小时前
我将基于 QtQuick 框架开发的音乐播放器性能做到顶级
qt
La Pulga10 小时前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
蒸蒸yyyyzwd10 小时前
秋招学习笔记 day106
笔记·学习
小李不想当小白11 小时前
WDG看门狗(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
小小晓.13 小时前
Qt 对话框、QLineEdit 与布局管理实战详解
开发语言·qt
lisw0514 小时前
作为人类代理的大语言模型及其角色分类!
人工智能·学习·语言模型
Z21-X14 小时前
云原生运维实战笔记:企业网站综合上云部署与云运维服务全景解析
运维·笔记·学习·云原生
坤坤子吖14 小时前
Linux网络编程:UDP进阶,从字典服务器到简单聊天室
linux·服务器·网络·c++·笔记·学习