CTK框架介绍和环境搭建

简介: CTK 为支持生物医学图像计算的公共开发包,其全称为 Common Toolkit。CTK插件框架的设计有很大的灵感来自OSGi并且使得应用程序由许多不同的组件组合成一个可扩展模型。这个模型允许通过那些组件间共享对象的服务通信。

CTK框架介绍和环境搭建


概述

本人第一次接触CTK的时候是看的一去、二三里大佬的博客。


1.CTK框架实际应用比较可靠,但网上资料很少,官网提供的API链接也已经无法打开了,目前还没有找到很好的官方的使用介绍。

2.网上目前找到的CTK的介绍或者是博客都没有非常具体的项目,都是一些简单的demo。

3.按照下面列出的参考的文档和大佬们的博客,来写代码的时候会碰到一些奇奇怪怪的问题,我这里也是重写一下这个框架的介绍和基本的使用方法,方便后来的人使用。


本篇文章参考的: 来唧唧歪歪


环境准备

●  CTK环境: windows编译CTK_turbolove的博客-CSDN博客


●  Qt-Advanced-Docking-System:这个是一个Qt的DockWidget库,比较好用,有兴趣的可以看github上找,因为这个库的作者是台独的支持者,我在他的库里面骂了他,然后Github账号红了,我这里就不贴链接了


●  Qt5.15.2:Windows在线安装QT5.15.2_qt在线安装_turbolove的博客-CSDN博客


CTK介绍

CTK 为支持生物医学图像计算的公共开发包,其全称为 Common Toolkit。CTK插件框架的设计有很大的灵感来自OSGi并且使得应用程序由许多不同的组件组合成一个可扩展模型。这个模型允许通过那些组件间共享对象的服务通信。


当前,CTK 工作的主要范围包括:


●  DICOM:提供了从 PACS 和本地数据库中查询和检索的高级类。包含 Qt 部件,可以轻松地设置服务器连接,并发送查询和查看结果。

●  DICOM Application Hosting:目标是创建 DICOM Part 19 Application Hosting specifications 的 C++ 参考实现。它提供了用于创建主机和托管应用程序的基础设。

●  Widgets:用于生物医学成像应用的 Qt Widgets 集合。

●  Plugin Framework:用于 C++ 的动态组件系统,以 OSGi 规范为模型。它支持一个开发模型,在这个模型中,应用程序(动态地)由许多不同(可重用的)组件组成,遵循面向服务的方法。

●  Command Line Interfaces:一种允许将算法编写为自包含可执行程序的技术,可以在多个终端用户应用程序环境中使用,而无需修改。


使用 CTK Plugin Framework 的好处

由于 CTK Plugin Framework 基于 OSGi,因此它继承了一种非常成熟且完全设计的组件系统,这在 Java 中用于构建高度复杂的应用程序,它将这些好处带给了本地(基于 Qt 的)C++ 应用程序,并适应于 CTK Plugin Framework:。


以下内容摘自 《OSGi官方文档》 (aliyun.com),并且做了一定的修改:


●  降低复杂性


使用 CTK Plugin Framework 开发意味着开发插件,它们隐藏了内部实现,并通过定义良好的服务来和其它插件通信。隐藏内部机制意味着以后可以自由地更改实现,这不仅有助于 Bug 数量的减少,还使得插件的开发变得更加简单,因为只需要实现已经定义好的一定数量的功能接口即可。


●  复用


使用 CTK Plugin Framework 开发意味着,我们可以规定标准化的组件模型,使得在应用程序中使用第三方组件变得非常容易。


●  现实情况


CTK Plugin Framework 是一个动态框架,它可以动态地更新插件和服务。在现实世界中,有很多场景都和动态服务模型相匹配。因此,应用程序可以在其所属的领域中重用 Service Registry 的强大基元(注册、获取、用富有表现力的过滤语言列表、等待服务的出现和消失)。这不仅节省了编写代码,还提供了全局可见性、调试工具以及比为专用解决方案实现的更多的功能。在这样的动态环境下编写代码听起来似乎是个噩梦,但幸运的是,有支持类和框架可以消除大部分(如果不是全部的话)痛苦。


●  开发简单


CTK Plugin Framework 不仅仅是组件的标准,它还指定了如何安装和管理组件。标准化的管理 API 使得在现有和未来的系统中集成 CTK Plugin Framework 变得非常容易。


●  动态更新


CTK Plugin Framework 组件模型是一个动态模型,插件可以在不关闭整个系统的情况下被安装、启动、停止、更新和卸载。


●  自适应


OSGi 组件模型是从头设计的,以允许组件的混合和匹配。这就要求必须指定组件的依赖关系,并且需要组件在其可选依赖性并不总是可用的环境中生存。Service Registry 是一个动态注册表,其中插件可以注册、获取和监听服务。这种动态服务模型允许插件找出系统中可用的功能,并调整它们所能提供的功能。这使得代码更加灵活,并且能够更好地适应变化。


●  透明性


插件和服务在CTK 插件环境中是最高级的。管理 API 提供了对插件的内部状态的访问,以及插件之间的连接方式。可以停止部分应用程序来调试某个问题,或者可以引入诊断插件。


●  版本控制


在 CTK Plugin Framework 中,所有的插件都经过严格的版本控制,只有能够协作的插件才会被连接在一起。


●  简单


CTK 插件相关的 API 非常简单,核心 API 不到 25 个类。这个核心 API 足以编写插件、安装、启动、停止、更新和卸载它们,并且还包含了所有的监听类。


●  懒加载


懒加载是软件中一个很好的点,OSGi 技术有很多的机制来保证只有当类真正需要的时候才开始加载它们。例如,插件可以用饿汉式启动,但是也可以被配置为仅当其它插件使用它们时才启动。服务可以被注册,但只有在使用时才创建。这些懒加载场景,可以节省大量的运行时成本。


●  非独占性


CTK Plugin Framework 不会接管整个应用程序,你可以选择性地将所提供的功能暴露给应用程序的某些部分,或者甚至可以在同一个进程中运行该框架的多个实例。


●  非侵入


在一个 CTK 插件环境中,不同插件均有自己的环境。它们可以使用任何设施,框架对此并无限制。CTK 服务没有特殊的接口需求,每个 QObject 都可以作为一个服务,每个类(也包括非 QObject)都可以作为一个接口。


CTK的项目结构

我这里使用的是CMake对项目进行管理的,因此我这边直接贴出CMake代码


主项目的代码

CMakeLists.txt

cmake_minimum_required(VERSION 3.19)
project(CTKPlugin)
# debug需要覆盖对应的dll, 并且需要删除configuration文件夹,让它重新生成
# 指定C++标准
set(CMAKE_CXX_STANDARD 17)
# 指定输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
# 自动编译QT文件
# set(CMAKE_PREFIX_PATH "C:/Qt/6.5.1/msvc2019_64")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 开启包含当前编译目录
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# 指定QT版本和对应的库
set(QT_VERSION 5)
set(REQUIRED_LIBS Core Gui Widgets OpenGL Sql
#        Core5Compat
        )
set(REQUIRED_LIBS_QUALIFIED Qt${QT_VERSION}::Core Qt${QT_VERSION}::Gui Qt${QT_VERSION}::Widgets Qt${QT_VERSION}::OpenGL Qt${QT_VERSION}::Sql
#        Qt${QT_VERSION}::Core5Compat
        )
# 包含的插件项目目录
add_subdirectory(core)
# 寻找QT库
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
# 包含的头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src
        ${CMAKE_CURRENT_SOURCE_DIR}/third_party/CTK/include/ctk-0.1
        ${CMAKE_CURRENT_SOURCE_DIR}/third_party/qmqtt/include
        )
# 需要编译的头文件
file(GLOB HEADERS
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
        )
# 需要编译的cpp文件
file(GLOB SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
        )
# 需要编译的ui文件
file(GLOB UIS
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui
        )
# 根据不同的模式加载不同的CTK库(DEBUG和RELEASE)
if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1)
else ()
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1)
endif ()
# 指定格式为utf-8
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
# 使用指定的源文件来生成目标可执行文件
add_executable(${PROJECT_NAME} WIN32 main.cpp ${HEADERS} ${SOURCES} ${UIS})
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIB_CTK} ${LIB_MQTT})

main.cpp

#include <QApplication>
#include <iostream>
#include <QStyleFactory>
#include <QDir>
#include <QDirIterator>
#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
#include "imainwindow.h"
int main(int argc, char* argv[])
{
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    app.setApplicationName("ctktest");
    QString path = QCoreApplication::applicationDirPath();
    // 在插件的搜索路径列表中添加一条路径
#ifdef _DEBUG
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/debug");
#else
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/release");
#endif // _DEBUG
    // 设置并启动 CTK 插件框架
    try {
        ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");
    }
    catch (ctkException e)
    {
        std::cout << e.message().toStdString() << std::endl;
    }
    // 启动插件工厂
    ctkPluginFrameworkFactory* ctkFrameWorkFactory = new ctkPluginFrameworkFactory;
    QSharedPointer<ctkPluginFramework> framework= ctkFrameWorkFactory->getFramework();
    try{
        framework->init();
        framework->start();
    }
    catch(const ctkPluginException& e)
    {
        std::cout << "framework init fail" << std::endl;
    }
    return app.exec();
}

以上只要可以将插件的工厂正常启动则表示插件库加载完成。


注意: ctkPluginFrameworkFactory* ctkFrameWorkFactory = new ctkPluginFrameworkFactory;


这边我看基本上所有的教程中都是这么写的ctkPluginFrameworkFactory ctkFrameWorkFactory而我这边给换成了指针。这个是一个小坑,我踩了因此这边也给到一个特殊标注。如果是使用的临时变量,临时变量在main函数退出的时候会析构,这里可能是因为ctkFrameWorkFactory析构太早的问题,导致整个程序在最后退出的时候崩溃,如果换成指针则让程序完成退出之后收回内存,这样不会产生因ctkFrameWorkFactory析构而退出崩溃的问题。


或者是什么其他原因呢?这个地方有没有大佬可以帮忙指正一下,因为除了这种方式以外,我没有找到比较好的方式去解决这个问题造成的崩溃。


Core插件的代码

Core插件和主程序的CMake在同级目录即可。


CMakeLists.txt

cmake_minimum_required(VERSION 3.19)
project(Core)
#指定C++标准
set(CMAKE_CXX_STANDARD 17)
#指定输出目录,这里将插件输出到统一的目录方便后续的加载工作
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../output/plugins)
#自动编译QT文件
#set(CMAKE_PREFIX_PATH "C:/Qt/6.5.1/msvc2019_64")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#开启包含当前编译目录
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#指定QT版本和对应的库
set(QT_VERSION 5)
set(REQUIRED_LIBS Core Gui Widgets Network WebEngineCore WebEngineWidgets
#        Core5Compat
        )
set(REQUIRED_LIBS_QUALIFIED Qt${QT_VERSION}::Core Qt${QT_VERSION}::Gui Qt${QT_VERSION}::Widgets Qt${QT_VERSION}::Network Qt${QT_VERSION}::WebEngineCore Qt${QT_VERSION}::WebEngineWidgets
#        Qt${QT_VERSION}::Core5Compat
        )
#寻找QT库
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
# 这里又包含了一个子项目
add_subdirectory(QCodeEditor)
include_directories(src
        ../third_party/CTK/include/ctk-0.1
        ../third_party/qadvance/include
        QCodeEditor/include
        )
file(GLOB HEADERS
        src/*.h
        )
file(GLOB SOURCES
        src/*.cpp
        )
file(GLOB UIS
        src/*.ui
        )
# 指定格式为utf-8
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/../third_party/CTK/libd/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/CTK/libd/ctk-0.1)
    file(GLOB LIB_ADVANCEDOC ${PROJECT_SOURCE_DIR}/../third_party/qadvance/libd/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/qadvance/libd)
else ()
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/../third_party/CTK/lib/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/CTK/lib/ctk-0.1)
    file(GLOB LIB_ADVANCEDOC ${PROJECT_SOURCE_DIR}/../third_party/qadvance/lib/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/qadvance/lib)
endif ()
#使用指定的源文件来生成目标可执行文件
add_library(${PROJECT_NAME} SHARED resources.qrc resources2.qrc ${HEADERS} ${SOURCES} ${UIS})
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIB_CTK} ${LIB_ADVANCEDOC} QCodeEditor)

本篇文章先写到这,后续会出更加详细的介绍

目录
相关文章
|
1月前
【LVGL快速入门】LVGL开源框架入门教程之框架使用(三)
【LVGL快速入门】LVGL开源框架入门教程之框架使用(三)
【LVGL快速入门】LVGL开源框架入门教程之框架使用(三)
|
1月前
|
容器
【LVGL快速入门】LVGL开源框架入门教程之框架使用(二)
【LVGL快速入门】LVGL开源框架入门教程之框架使用(二)
|
1月前
【LVGL快速入门】LVGL开源框架入门教程之框架使用(一)
【LVGL快速入门】LVGL开源框架入门教程之框架使用(一)
|
1月前
|
调度
【LVGL快速入门】LVGL开源框架入门教程之框架移植(四)
【LVGL快速入门】LVGL开源框架入门教程之框架移植(四)
|
1月前
|
C语言 图形学 芯片
【LVGL快速入门】LVGL开源框架入门教程之框架移植(一)
LVGL开源框架入门教程之框架移植(一)
204 2
|
1月前
【LVGL快速入门】LVGL开源框架入门教程之框架移植(二)
【LVGL快速入门】LVGL开源框架入门教程之框架移植(二)
|
1月前
【LVGL快速入门】LVGL开源框架入门教程之框架移植(三)
【LVGL快速入门】LVGL开源框架入门教程之框架移植(三)
|
6月前
|
缓存 Rust 前端开发
【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建
【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建
643 0
|
存储 移动开发 小程序
uniapp环境搭建以及基础配置详解
安装编辑器 HbuilderX(HbuilderX 是通用的前端开发工具,但为 uni-app 做了特别强化)。 下载 APP 开发板,可开箱即用。 安装微信开发者工具。
787 0
uniapp环境搭建以及基础配置详解