Qt框架学习 --- CTK

news2025/1/12 12:29:14

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、准备阶段
  • 二、使用介绍
    • 1.核心思想
    • 2.源码
      • 2.1.框架部分资源目录树
      • 2.2.框架部分源码
      • 2.3.插件部分资源目录树
      • 2.4.插件部分源码
    • 3.文件结构
    • 4.运行效果
  • 总结


前言

随着开发的深入,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.框架部分源码

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

  • App.pro
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
# 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
 #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
#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
#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举例吧

  • WelcomeQui.pro
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
#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
#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 激活类
#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
#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 实现类
#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
#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

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

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

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

相关文章

Aurora中显示中文

Aurora是可以在word里面作为插件使用&#xff0c;可以画一些三线表&#xff0c;是一款非常好用的工具&#xff0c;写论文必备。 我们可以通过现在excel里面创建表格&#xff0c;然后将excel转成latex格式&#xff0c;具体做法参考如下&#xff1a; Aurora Equation——Latex表…

软件测试|使用Pytest、Allure Step和Allure Attach创建详细测试报告

引言 在软件开发过程中&#xff0c;测试是不可或缺的一部分。为了更好地展示测试结果并定位问题&#xff0c;结合Pytest测试框架和Allure测试报告工具可以创建清晰、详细的测试报告。本文将介绍如何使用Pytest、Allure的allure.step()和allure.attach()功能来创建具有丰富信息…

VMware正被博通“收拾”,我们是不是可以“嚣张”一点?

引言&#xff1a;一朝躺在大佬怀&#xff0c;今生荣华伸手来。 【全球存储观察 &#xff5c; 科技热点关注】 如是所闻&#xff0c;2023年元旦前两个月&#xff0c;大家看到博通以610亿美元的高价&#xff0c;成功完成收购了闻名遐迩的虚拟化大佬VMware。日期为2023年11月22日。…

Unity中Shader面片一直面向摄像机

文章目录 前言一、实现思路1、 我们要实现模型面片一直跟着摄像机旋转,那么就需要用到旋转矩阵2、确定 原坐标系 和 目标坐标系3、确定旋转后坐标系基向量二、确定旋转后 坐标系基向量 在 原坐标系 下的值1、Z轴基向量2、假设Y轴基向量 和 世界空间下 的Y轴方向一致竖直向上3、…

Kafka消息存储

一、层次结构 具体到某个broker上则是, 数据目录/分区名/日志相关文件集合。其中日志文件集合内包括.log文件, index索引文件和.timeindex时间戳索引文件。 二、.log 结构 .log中记录具体的消息。一般消息由header和body组成, 这点儿在Kafka消息中也同样适用。 message MES…

自动化创建ETX用户帐号

在芯片设计行业&#xff0c;ETX是常见的远程访问环境。用户在通过ETX访问远程环境前必须首先加入ETX系统&#xff0c;然后通过profile分配相关的环境的访问权限。 通常这些操作在ETX WEB页面手工操作&#xff0c;如果我们期望实现用户帐号注册全自动化&#xff0c;就需要将以上…

糊涂(Hutool)工具常见的使用方法

简介&#xff1a; Hutool是一个小而全的Java工具类库&#xff0c;通过静态方法封装&#xff0c;降低相关API的学习成本&#xff0c;提高工作效率&#xff0c;使Java拥有函数式语言般的优雅&#xff0c;让Java语言也可以“甜甜的”。 安装&#xff1a; 在我们的maven中引入糊涂…

Linux网络编程(一-网络相关知识点)

目录 一、网络相关知识简介 二、网络协议的分层模型 2.1 OSI七层模型 2.2 TCP/IP五层模型 2.3 协议层报文间的封装与拆封 三、IP协议 3.1 MAC地址 3.2 IP地址 3.3 MAC地址与IP地址区别 一、网络相关知识简介 互联网通信的本质是数字通信&#xff0c;任何数字通信都离…

国内首款支持苹果Find My芯片-伦茨科技ST17H6x

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

K8S集群重新初始化--详细过程

K8S集群重新初始化 1、master节点1.1、在master节点执行下面reset命令&#xff1a;1.2、手动清除配置信息&#xff0c;这一步很关键&#xff1a;1.3、重新引导集群1.4、创建配置目录&#xff0c;并复制权限配置文件到用户目录下&#xff1a;1.5 查看集群状态1.6 安装Calico网络…

通过两台linux主机配置ssh实现互相免密登陆

一、准备工作 1:两台Linux主机&#xff0c;需要能ping通 2:检查防火墙是否处于关闭状态,没关闭的话关闭&#xff0c;防止防火墙拦截流量 查看防火墙状态&#xff1a;systemctl status firewalld 关闭防火墙&#xff1a;systemctl stop firewalld 3:使用getenforce命令查…

Google Pixel 与 iPhone手机:哪个更好?

iPhone稳定可靠&#xff0c;Pixel性价比高且创新。两者各有千秋&#xff0c;满足不同需求 谷歌的 Pixel 手机是 Android 最接近 iPhone 的手机&#xff0c;也是真正原生的Android手机。在iPhone 15 Pro Max 与华为 Mate 60 Pro的比较中不难看出&#xff0c;iPhone依然有着极强…

Java实现任务栏图标闪烁功能,点击任务栏打开Java窗口,使用GUI的JFrame实现

JFrame是指一个计算机语言-java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭。 GUI主要功能是实现人与计算机等电子设备的人机交互。它是用户与操作系统之间进行数据传递和互动操控的工具,用户可以通过一定的操作实现对电子设备的…

PyTorch|view(),改变张量维度

在构建自己的网络时&#xff0c;了解数据经过每个层后的形状变化是必须的&#xff0c;否则&#xff0c;网络大概率会出现问题。PyToch张量有一个方法&#xff0c;叫做view(),使用这个方法&#xff0c;我们可以很容易的对张量的形状进行改变&#xff0c;从而符合网络的输入要求。…

【自学笔记】01Java基础-07面向对象基础-04接口与内部类详解

记录学习Java基础中有关接口类和内部类的知识。 1 接口 interface 关键字用于定义接口类&#xff0c;接口类是一系列方法的声明&#xff0c;一般只有方法的特征没有方法的实现&#xff0c;因此可以被不同的类接入实现&#xff0c;而这些实现可以具有不同的行为&#xff08;功…

《Training language models to follow instructions》论文解读--训练语言模型遵循人类反馈的指令

目录 1摘要 2介绍 方法及实验细节 3.1高层次方法论 3.2数据集 3.3任务 3.4人体数据收集 3.5模型 3.6评价 4 结果 4.1 API分布结果 4.2公共NLP数据集的结果 4.3定性结果 问题 1.什么是rm分数 更多资料 1摘要 使语言模型更大并不能使它们更好地遵循用户的意图。例…

C#学习教程一

VS2022 一:C#简介 基础: 全称:c sharp 面向对象 高级编程语言:(GC动态控制内存) C#、Java、Python... 低级编程语言:(经常与硬件打交道、内存、寄存器等) c/C++ 汇编... .net=dot net 过程: C/C++ 通过编译器转换成机器指令 C#通过编译器转换成IL中间语言…

怎么用 Excel 做出专业的 project 甘特图?10个步骤和60个模板

使用Excel来创建Project甘特图的步骤包括&#xff1a;1、基本设置和布局调整、2、数据输入和时间线配置、3、任务依赖性和进度跟踪、4、视觉效果优化、5、数据更新和维护、6、模板保存和共享。尤其突出基本设置和布局调整&#xff0c;它是构建一个清晰、有效的甘特图的基础。 甘…

【2023 - 探索】博0到博1,游戏新地图的探索日志

【2023 - 探索】博0到博1&#xff0c;游戏新地图的探索日志 写在最前面CSDN探索日志2023的探险 探索日志年终回顾探索 冒险回顾实习6月开始跟着老师做科研年中的一些其他事情9月开始上课开学后11月&#xff0c;读者互动 新年展望新年祝福 写在最前面 2023&#xff0c;我解锁了新…

Safari历史记录怎么恢复?掌握好这3个方法!

大家在使用Safari浏览器时难免会遇到一些问题&#xff0c;比如Safari历史记录误删除或意外丢失&#xff0c;想必这是一件很令人烦恼的事情。 那么&#xff0c;当苹果手机上的Safari历史记录不见时&#xff0c;我们该如何找回呢&#xff1f;safari历史记录怎么恢复&#xff1f;…