MITK 工程四大组结构分析
Applications / CppMicroServices / Modules / Plugins 的作用与关系
概述
MITK 工程在解决方案中分为四个组。先说明一点:在源码树中 CppMicroServices
实际位于 Modules/CppMicroServices/,但因为它是独立的第三方项目
(有自己的 LICENSE、README、CMake 体系),在 VS 解决方案里被单列为一个组。
| 组 | 数量 | 一句话定位 |
|---|---|---|
| CppMicroServices | 1 | 服务总线:C++ 版 OSGi 微服务框架(接口注册/发现) |
| Modules | 77 | 引擎和零件:算法与控件动态库,可脱离工作台复用 |
| Plugins | 82 | 仪表盘:BlueBerry 工作台插件(View/Editor/Perspective) |
| Applications | 2 | 点火钥匙:可执行程序壳,main() + 插件清单 |
一、CppMicroServices ------ 服务注册中枢(最底层)
位置:Modules/CppMicroServices/(core/module、core/service、core/util)
C++ 版的 OSGi 微服务框架(来自独立开源项目)。提供"接口注册/发现"机制:
模块 A 注册一个服务接口的实现,模块 B 只凭接口类型就能查到它,双方零编译依赖。
cpp
// Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp: L48 ------ 实际用法
m_ServiceRegistration = us::GetModuleContext()->RegisterService<InteractionEventObserver>(this, props);
MITK 内部大量能力靠它解耦:
- IO 读写器(各文件格式的 Reader/Writer 以服务注册)
- 渲染服务、交互事件观察者(InteractionEventObserver)
- DataStorage 服务(IDataStorageService)
二、Modules ------ 算法与功能库(核心主体,77 个)
纯 C++ 动态库,每个用 mitk_create_module() 声明(见 Modules/Core/CMakeLists.txt)。
内部分层
| 层 | 例子 | 说明 |
|---|---|---|
| 基础核心 | Core(DataStorage、BaseRenderer、RenderingManager、SliceNavigationController)、Log |
一切的地基 |
| 算法领域 | Segmentation、DICOM、IGT、ImageStatistics、ContourModel |
医学影像算法 |
| Qt 控件 | QtWidgets(QmitkRenderWindow、QmitkStdMultiWidget)、各 *UI 后缀模块 |
可复用 UI 控件,但不含工作台逻辑 |
| 应用支撑 | AppUtil(mitkBaseApplication)、CoreCmdApps |
启动器骨架 / 纯命令行工具 |
模块声明示例(Modules/Core/CMakeLists.txt)
cmake
mitk_create_module(
DEPENDS
PUBLIC
MitkLog
CppMicroServices # ← Module 依赖服务总线
PACKAGE_DEPENDS
PUBLIC
Boost
nlohmann_json
ITK|IOImageBase+...
VTK|FiltersTexture+... # ← 第三方库按需引组件
)
关键特征
Module 不依赖 BlueBerry/Plugins ------可以脱离工作台在任何 Qt/命令行程序里单独使用
(CoreCmdApps 就是纯命令行用法)。这是 MITK "算法可独立复用"的设计基石。
三、Plugins ------ 工作台功能插件(UI 组装层,82 个)
CTK/BlueBerry 插件,每个用 mitk_create_plugin() 声明,带 plugin.xml
(贡献 View/Editor/Perspective 扩展)。
两大类
| 前缀 | 角色 | 例子 |
|---|---|---|
org.blueberry.* |
BlueBerry 框架本身(Eclipse RCP 移植层) | org.blueberry.ui.qt(Workbench、presentation 框架)、org.blueberry.core.runtime(ExtensionRegistry) |
org.mitk.gui.qt.* |
MITK 功能界面 | datamanager、segmentation、mxnmultiwidgeteditor、stdmultiwidgeteditor |
插件声明示例(org.mitk.gui.qt.datamanager)
cmake
# CMakeLists.txt ------ 编译期:链接 Module
mitk_create_plugin(
EXPORT_DIRECTIVE MITK_QT_DATAMANAGER
EXPORTED_INCLUDE_SUFFIXES src
MODULE_DEPENDS MitkQtWidgetsExt) # ← 依赖 Modules 层
cmake
# manifest_headers.cmake ------ 运行期:插件间依赖
set(Plugin-Name "MITK Data Manager")
set(Require-Plugin org.mitk.gui.qt.application org.mitk.gui.qt.common)
角色
Plugin 是 Module 与工作台之间的"胶水":把 Module 里的控件/算法包装成
View/Editor 插入 Workbench。两套依赖声明各司其职:
MODULE_DEPENDS------ 编译期链接哪些 Module 库Require-Plugin------ 运行期需要哪些其他插件先激活
四、Applications ------ 可执行程序(最顶层,仅 2 个)
Applications/AppList.cmake:
cmake
set(MITK_APPS
Workbench^^MITK_BUILD_APP_Workbench^^MitkWorkbench # 默认 ON
FlowBench^^MITK_BUILD_APP_FlowBench^^MitkFlowBench # 默认 OFF
)
main() 有多薄(Applications/Workbench/MitkWorkbench.cpp)
cpp
int main(int argc, char **argv)
{
mitk::BaseApplication app(argc, argv); // 来自 Modules/AppUtil
app.setSingleMode(true);
app.setApplicationName("MITK Workbench");
app.setOrganizationName("DKFZ");
...
return app.run(); // 其余全交给插件框架
}
真正内容全部由插件提供,App 只在 target_libraries.cmake 里声明装载哪些插件:
cmake
# Applications/Workbench/target_libraries.cmake
set(target_libraries
org_blueberry_ui_qt # 工作台框架
org_mitk_gui_qt_extapplication # 扩展应用外壳(传递拉入其余插件)
)
CTK 的依赖图工具(DGraph,见 AppList.cmake 注释)会自动解析插件传递依赖,
把需要的全部插件启用。
五、依赖关系(严格单向,自下而上)
#mermaid-svg-zscSvVonNvMQqcoK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zscSvVonNvMQqcoK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zscSvVonNvMQqcoK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zscSvVonNvMQqcoK .error-icon{fill:#552222;}#mermaid-svg-zscSvVonNvMQqcoK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zscSvVonNvMQqcoK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zscSvVonNvMQqcoK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zscSvVonNvMQqcoK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zscSvVonNvMQqcoK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zscSvVonNvMQqcoK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zscSvVonNvMQqcoK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zscSvVonNvMQqcoK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zscSvVonNvMQqcoK .marker.cross{stroke:#333333;}#mermaid-svg-zscSvVonNvMQqcoK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zscSvVonNvMQqcoK p{margin:0;}#mermaid-svg-zscSvVonNvMQqcoK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zscSvVonNvMQqcoK .cluster-label text{fill:#333;}#mermaid-svg-zscSvVonNvMQqcoK .cluster-label span{color:#333;}#mermaid-svg-zscSvVonNvMQqcoK .cluster-label span p{background-color:transparent;}#mermaid-svg-zscSvVonNvMQqcoK .label text,#mermaid-svg-zscSvVonNvMQqcoK span{fill:#333;color:#333;}#mermaid-svg-zscSvVonNvMQqcoK .node rect,#mermaid-svg-zscSvVonNvMQqcoK .node circle,#mermaid-svg-zscSvVonNvMQqcoK .node ellipse,#mermaid-svg-zscSvVonNvMQqcoK .node polygon,#mermaid-svg-zscSvVonNvMQqcoK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zscSvVonNvMQqcoK .rough-node .label text,#mermaid-svg-zscSvVonNvMQqcoK .node .label text,#mermaid-svg-zscSvVonNvMQqcoK .image-shape .label,#mermaid-svg-zscSvVonNvMQqcoK .icon-shape .label{text-anchor:middle;}#mermaid-svg-zscSvVonNvMQqcoK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zscSvVonNvMQqcoK .rough-node .label,#mermaid-svg-zscSvVonNvMQqcoK .node .label,#mermaid-svg-zscSvVonNvMQqcoK .image-shape .label,#mermaid-svg-zscSvVonNvMQqcoK .icon-shape .label{text-align:center;}#mermaid-svg-zscSvVonNvMQqcoK .node.clickable{cursor:pointer;}#mermaid-svg-zscSvVonNvMQqcoK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zscSvVonNvMQqcoK .arrowheadPath{fill:#333333;}#mermaid-svg-zscSvVonNvMQqcoK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zscSvVonNvMQqcoK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zscSvVonNvMQqcoK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zscSvVonNvMQqcoK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zscSvVonNvMQqcoK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zscSvVonNvMQqcoK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zscSvVonNvMQqcoK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zscSvVonNvMQqcoK .cluster text{fill:#333;}#mermaid-svg-zscSvVonNvMQqcoK .cluster span{color:#333;}#mermaid-svg-zscSvVonNvMQqcoK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zscSvVonNvMQqcoK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zscSvVonNvMQqcoK rect.text{fill:none;stroke-width:0;}#mermaid-svg-zscSvVonNvMQqcoK .icon-shape,#mermaid-svg-zscSvVonNvMQqcoK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zscSvVonNvMQqcoK .icon-shape p,#mermaid-svg-zscSvVonNvMQqcoK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zscSvVonNvMQqcoK .icon-shape .label rect,#mermaid-svg-zscSvVonNvMQqcoK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zscSvVonNvMQqcoK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zscSvVonNvMQqcoK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zscSvVonNvMQqcoK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Applications 壳 ------ main + 插件清单
装载:运行期由 CTK 解析 plugin.xml
MODULE_DEPENDS 编译期链接
DEPENDS PUBLIC CppMicroServices
CppMicroServices 服务总线
接口注册 / 发现
Modules 算法与控件库 ------ 可独立复用
QtWidgets 与各 UI 模块 ------ Qt 控件层
Core ------ 数据 / 渲染 / 交互核心
Segmentation / DICOM / IGT ------ 算法层
Plugins 工作台功能 ------ View / Editor / Perspective
Require-Plugin 运行期依赖
org.blueberry.* 框架层
org.mitk.gui.qt.* 功能界面
MitkWorkbench = BaseApplication + 插件列表
规则
- 依赖只能向下 :App → Plugin → Module → CppMicroServices;
反向不允许------Module 永远不知道 Plugin 的存在。 - 横向解耦靠两套机制 :
- Module 之间:CppMicroServices 服务(编译期只依赖接口)
- Plugin 之间:BlueBerry 扩展点(plugin.xml 声明,运行期装配)
- 分层收益 :
- 算法(Modules)可以不带 GUI 复用到第三方程序
- 界面(Plugins)可按需增删,不改一行核心代码
- 换一个应用(Applications)只是换一份插件清单
小结
CppMicroServices 是总线,Modules 是引擎和零件,
Plugins 是装在工作台上的仪表盘,Applications 只是点火钥匙。
关键文件索引
| 内容 | 文件 |
|---|---|
| 应用清单 | Applications/AppList.cmake |
| Workbench 入口 | Applications/Workbench/MitkWorkbench.cpp |
| App 插件清单 | Applications/Workbench/target_libraries.cmake |
| Module 声明宏示例 | Modules/Core/CMakeLists.txt(mitk_create_module) |
| Plugin 声明宏示例 | Plugins/org.mitk.gui.qt.datamanager/CMakeLists.txt(mitk_create_plugin) |
| Plugin 运行期依赖 | Plugins/org.mitk.gui.qt.datamanager/manifest_headers.cmake |
| 微服务框架 | Modules/CppMicroServices/(core/module、core/service) |
| 微服务用法示例 | Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp: L48 |