3D Slicer 架构分析

3D Slicer 架构分析

分析对象D:\Slicer,分支 5.8

生成日期 :2026-07-28

路径约定 :文中所有代码路径均相对于 Slicer 源码根目录

图表说明 :使用 Mermaid,需在支持 Mermaid 的 Markdown 阅读器中查看

(VSCode + Markdown Preview Mermaid Support / Typora / GitHub / Obsidian)


目录

第一部分:整体框架

  1. 总体分层
  2. [SuperBuild ------ 依赖构建层](#SuperBuild —— 依赖构建层)
  3. [Libs ------ 数据模型与基础库](#Libs —— 数据模型与基础库)
  4. [Base ------ 应用框架层](#Base —— 应用框架层)
  5. [Modules ------ 四类模块机制](#Modules —— 四类模块机制)
  6. [Applications 与扩展](#Applications 与扩展)
  7. 运行时数据流
  8. 第一部分小结

第二部分:扩展机制底层基础

  1. 核心结论
  2. 支柱一:模块搜索路径
  3. [支柱二:CTK AppLauncher](#支柱二:CTK AppLauncher)
  4. [支柱三:版本与 ABI 绑定](#支柱三:版本与 ABI 绑定)
  5. [支柱四:CMake 构建体系](#支柱四:CMake 构建体系)
  6. [扩展间依赖:find_package 复用](#扩展间依赖:find_package 复用)
  7. 支柱五:元数据与服务器
  8. 生命周期状态机
  9. 完整安装流程(运行时)
  10. 第二部分小结

附录

  • [A. 关键文件速查](#A. 关键文件速查)
  • [B. 关键数字](#B. 关键数字)
  • [C. 可继续深入的方向](#C. 可继续深入的方向)

全局概览

两句话概括

架构主线

Slicer 是"以 MRML 场景为唯一真相源、以事件驱动同步视图、以四类可插拔模块扩展功能"的分层架构。

贯穿始终的两条设计主线:Data / Display / Storage 三节点分离 ,以及 Logic / GUI 分离

扩展机制主线

扩展不是独立的插件框架,它复用的就是模块加载机制。

Slicer 只是把"模块搜索路径"暴露为一个可配置变量(Modules/AdditionalPaths),

剩下的全是打包、版本绑定和分发的工程问题。

两部分的关系

#mermaid-svg-kwx6UiwWVXT5Y2IB{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-kwx6UiwWVXT5Y2IB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kwx6UiwWVXT5Y2IB .error-icon{fill:#552222;}#mermaid-svg-kwx6UiwWVXT5Y2IB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kwx6UiwWVXT5Y2IB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .marker.cross{stroke:#333333;}#mermaid-svg-kwx6UiwWVXT5Y2IB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kwx6UiwWVXT5Y2IB p{margin:0;}#mermaid-svg-kwx6UiwWVXT5Y2IB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster-label text{fill:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster-label span{color:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster-label span p{background-color:transparent;}#mermaid-svg-kwx6UiwWVXT5Y2IB .label text,#mermaid-svg-kwx6UiwWVXT5Y2IB span{fill:#333;color:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .node rect,#mermaid-svg-kwx6UiwWVXT5Y2IB .node circle,#mermaid-svg-kwx6UiwWVXT5Y2IB .node ellipse,#mermaid-svg-kwx6UiwWVXT5Y2IB .node polygon,#mermaid-svg-kwx6UiwWVXT5Y2IB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .rough-node .label text,#mermaid-svg-kwx6UiwWVXT5Y2IB .node .label text,#mermaid-svg-kwx6UiwWVXT5Y2IB .image-shape .label,#mermaid-svg-kwx6UiwWVXT5Y2IB .icon-shape .label{text-anchor:middle;}#mermaid-svg-kwx6UiwWVXT5Y2IB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .rough-node .label,#mermaid-svg-kwx6UiwWVXT5Y2IB .node .label,#mermaid-svg-kwx6UiwWVXT5Y2IB .image-shape .label,#mermaid-svg-kwx6UiwWVXT5Y2IB .icon-shape .label{text-align:center;}#mermaid-svg-kwx6UiwWVXT5Y2IB .node.clickable{cursor:pointer;}#mermaid-svg-kwx6UiwWVXT5Y2IB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .arrowheadPath{fill:#333333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kwx6UiwWVXT5Y2IB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kwx6UiwWVXT5Y2IB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kwx6UiwWVXT5Y2IB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster text{fill:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB .cluster span{color:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB 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-kwx6UiwWVXT5Y2IB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kwx6UiwWVXT5Y2IB rect.text{fill:none;stroke-width:0;}#mermaid-svg-kwx6UiwWVXT5Y2IB .icon-shape,#mermaid-svg-kwx6UiwWVXT5Y2IB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kwx6UiwWVXT5Y2IB .icon-shape p,#mermaid-svg-kwx6UiwWVXT5Y2IB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kwx6UiwWVXT5Y2IB .icon-shape .label rect,#mermaid-svg-kwx6UiwWVXT5Y2IB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kwx6UiwWVXT5Y2IB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kwx6UiwWVXT5Y2IB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kwx6UiwWVXT5Y2IB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第二部分:扩展机制
第一部分:整体框架
复用
复用
Applications/SlicerApp
Modules

Core / Loadable / Scripted / CLI
Base

QTApp / QTGUI / QTCore / QTCLI / Logic
Libs

MRML / vtkITK / vtkTeem / vtkSegmentationCore
SuperBuild

VTK / ITK / CTK / Qt / Python / DCMTK
扩展 Extension
模块搜索路径

Modules/AdditionalPaths
CTK AppLauncher

环境注入
revision × OS × arch

ABI 绑定
SlicerConfig.cmake

构建体系
.s4ext / JSON catalog

元数据分发



第一部分:整体框架


1. 总体分层

#mermaid-svg-ArWq6zTRx3raG3VA{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-ArWq6zTRx3raG3VA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ArWq6zTRx3raG3VA .error-icon{fill:#552222;}#mermaid-svg-ArWq6zTRx3raG3VA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ArWq6zTRx3raG3VA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ArWq6zTRx3raG3VA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ArWq6zTRx3raG3VA .marker.cross{stroke:#333333;}#mermaid-svg-ArWq6zTRx3raG3VA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ArWq6zTRx3raG3VA p{margin:0;}#mermaid-svg-ArWq6zTRx3raG3VA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ArWq6zTRx3raG3VA .cluster-label text{fill:#333;}#mermaid-svg-ArWq6zTRx3raG3VA .cluster-label span{color:#333;}#mermaid-svg-ArWq6zTRx3raG3VA .cluster-label span p{background-color:transparent;}#mermaid-svg-ArWq6zTRx3raG3VA .label text,#mermaid-svg-ArWq6zTRx3raG3VA span{fill:#333;color:#333;}#mermaid-svg-ArWq6zTRx3raG3VA .node rect,#mermaid-svg-ArWq6zTRx3raG3VA .node circle,#mermaid-svg-ArWq6zTRx3raG3VA .node ellipse,#mermaid-svg-ArWq6zTRx3raG3VA .node polygon,#mermaid-svg-ArWq6zTRx3raG3VA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ArWq6zTRx3raG3VA .rough-node .label text,#mermaid-svg-ArWq6zTRx3raG3VA .node .label text,#mermaid-svg-ArWq6zTRx3raG3VA .image-shape .label,#mermaid-svg-ArWq6zTRx3raG3VA .icon-shape .label{text-anchor:middle;}#mermaid-svg-ArWq6zTRx3raG3VA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ArWq6zTRx3raG3VA .rough-node .label,#mermaid-svg-ArWq6zTRx3raG3VA .node .label,#mermaid-svg-ArWq6zTRx3raG3VA .image-shape .label,#mermaid-svg-ArWq6zTRx3raG3VA .icon-shape .label{text-align:center;}#mermaid-svg-ArWq6zTRx3raG3VA .node.clickable{cursor:pointer;}#mermaid-svg-ArWq6zTRx3raG3VA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ArWq6zTRx3raG3VA .arrowheadPath{fill:#333333;}#mermaid-svg-ArWq6zTRx3raG3VA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ArWq6zTRx3raG3VA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ArWq6zTRx3raG3VA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ArWq6zTRx3raG3VA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ArWq6zTRx3raG3VA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ArWq6zTRx3raG3VA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ArWq6zTRx3raG3VA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ArWq6zTRx3raG3VA .cluster text{fill:#333;}#mermaid-svg-ArWq6zTRx3raG3VA .cluster span{color:#333;}#mermaid-svg-ArWq6zTRx3raG3VA 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-ArWq6zTRx3raG3VA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ArWq6zTRx3raG3VA rect.text{fill:none;stroke-width:0;}#mermaid-svg-ArWq6zTRx3raG3VA .icon-shape,#mermaid-svg-ArWq6zTRx3raG3VA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ArWq6zTRx3raG3VA .icon-shape p,#mermaid-svg-ArWq6zTRx3raG3VA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ArWq6zTRx3raG3VA .icon-shape .label rect,#mermaid-svg-ArWq6zTRx3raG3VA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ArWq6zTRx3raG3VA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ArWq6zTRx3raG3VA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ArWq6zTRx3raG3VA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SuperBuild ------ 第三方依赖层
VTK
ITK / SimpleITK
CTK
Qt
Python
DCMTK
Libs ------ 数据模型与基础库
MRML

Core / Logic / Widgets

DisplayableManager
vtkITK
vtkTeem
vtkSegmentationCore
RemoteIO
Base ------ 应用框架层
QTApp
QTGUI
QTCore
QTCLI
Logic
CLI
Modules ------ 功能模块层
Core
Loadable
Scripted
CLI
Applications ------ 应用层
Applications/SlicerApp

可执行程序、启动入口

命名约定即可判断层次:

前缀 含义
vtk* VTK 风格的逻辑 / 数据类
qSlicer* Qt 风格的 GUI 类
vtkMRML* 场景数据节点
qMRML* 与 MRML 场景双向绑定的 Qt 控件

2. SuperBuild ------ 依赖构建层

SuperBuild/ 用 CMake ExternalProject 自动拉取并编译全部第三方库,再构建 Slicer 本体(inner build)。
#mermaid-svg-KHVUCsk60P6ZEb5L{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-KHVUCsk60P6ZEb5L .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KHVUCsk60P6ZEb5L .error-icon{fill:#552222;}#mermaid-svg-KHVUCsk60P6ZEb5L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KHVUCsk60P6ZEb5L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KHVUCsk60P6ZEb5L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KHVUCsk60P6ZEb5L .marker.cross{stroke:#333333;}#mermaid-svg-KHVUCsk60P6ZEb5L svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KHVUCsk60P6ZEb5L p{margin:0;}#mermaid-svg-KHVUCsk60P6ZEb5L .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster-label text{fill:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster-label span{color:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster-label span p{background-color:transparent;}#mermaid-svg-KHVUCsk60P6ZEb5L .label text,#mermaid-svg-KHVUCsk60P6ZEb5L span{fill:#333;color:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L .node rect,#mermaid-svg-KHVUCsk60P6ZEb5L .node circle,#mermaid-svg-KHVUCsk60P6ZEb5L .node ellipse,#mermaid-svg-KHVUCsk60P6ZEb5L .node polygon,#mermaid-svg-KHVUCsk60P6ZEb5L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KHVUCsk60P6ZEb5L .rough-node .label text,#mermaid-svg-KHVUCsk60P6ZEb5L .node .label text,#mermaid-svg-KHVUCsk60P6ZEb5L .image-shape .label,#mermaid-svg-KHVUCsk60P6ZEb5L .icon-shape .label{text-anchor:middle;}#mermaid-svg-KHVUCsk60P6ZEb5L .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KHVUCsk60P6ZEb5L .rough-node .label,#mermaid-svg-KHVUCsk60P6ZEb5L .node .label,#mermaid-svg-KHVUCsk60P6ZEb5L .image-shape .label,#mermaid-svg-KHVUCsk60P6ZEb5L .icon-shape .label{text-align:center;}#mermaid-svg-KHVUCsk60P6ZEb5L .node.clickable{cursor:pointer;}#mermaid-svg-KHVUCsk60P6ZEb5L .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KHVUCsk60P6ZEb5L .arrowheadPath{fill:#333333;}#mermaid-svg-KHVUCsk60P6ZEb5L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KHVUCsk60P6ZEb5L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KHVUCsk60P6ZEb5L .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KHVUCsk60P6ZEb5L .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KHVUCsk60P6ZEb5L .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KHVUCsk60P6ZEb5L .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster text{fill:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L .cluster span{color:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L 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-KHVUCsk60P6ZEb5L .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KHVUCsk60P6ZEb5L rect.text{fill:none;stroke-width:0;}#mermaid-svg-KHVUCsk60P6ZEb5L .icon-shape,#mermaid-svg-KHVUCsk60P6ZEb5L .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KHVUCsk60P6ZEb5L .icon-shape p,#mermaid-svg-KHVUCsk60P6ZEb5L .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KHVUCsk60P6ZEb5L .icon-shape .label rect,#mermaid-svg-KHVUCsk60P6ZEb5L .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KHVUCsk60P6ZEb5L .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KHVUCsk60P6ZEb5L .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KHVUCsk60P6ZEb5L :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SuperBuild

(outer build)
VTK
ITK

SimpleITK
CTK

CTKAppLauncher
Python

numpy / scipy

PythonQt
DCMTK
SlicerExecutionModel
teem / JsonCpp

LibArchive / OpenSSL

tbb / sqlite / curl
Slicer

(inner build)

依赖 作用
VTK 渲染、数据结构、Qt 集成
ITK + SimpleITK 图像处理与配准算法
CTK 通用医学影像 Qt 控件(DICOM 浏览器等)
Python + numpy/scipy + PythonQt 脚本层
DCMTK DICOM 读写
SlicerExecutionModel CLI 模块的 XML 接口代码生成
teem / JsonCpp / LibArchive / OpenSSL NRRD、序列化、扩展包、网络
tbb / sqlite / curl / zlib / bzip2 / LZMA 并行、数据库、网络、压缩

顶层 CMakeLists.txt 强制 C++17,并显式拒绝 CMake >= 4.0 以及若干有已知问题的版本(3.21.0、3.25.0~3.25.2)。


3. Libs ------ 数据模型与基础库

3.1 MRML(Medical Reality Markup Language)是整个 Slicer 的核心

Libs/MRML/Core/ 中有 88 个 vtkMRML*Node。核心抽象:

  • Libs/MRML/Core/vtkMRMLScene.h

    单例式的场景容器,持有所有节点,负责节点注册、ID 分配、Import/Export、Undo/Redo,

    并通过 VTK 事件机制(NodeAddedEvent / NodeRemovedEvent / ModifiedEvent)广播变更。

    整个应用状态就是一棵 MRML 场景树。

  • Libs/MRML/Core/vtkMRMLNode.h

    所有节点基类,支持 ReadXMLAttributes / WriteXML / Copy

    可挂 Attributes 和节点引用(NodeReference)。

3.2 节点"三件套"------Slicer 最重要的设计模式

#mermaid-svg-lPuMAlcynMezWGiV{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-lPuMAlcynMezWGiV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lPuMAlcynMezWGiV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lPuMAlcynMezWGiV .error-icon{fill:#552222;}#mermaid-svg-lPuMAlcynMezWGiV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lPuMAlcynMezWGiV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lPuMAlcynMezWGiV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lPuMAlcynMezWGiV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lPuMAlcynMezWGiV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lPuMAlcynMezWGiV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lPuMAlcynMezWGiV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lPuMAlcynMezWGiV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lPuMAlcynMezWGiV .marker.cross{stroke:#333333;}#mermaid-svg-lPuMAlcynMezWGiV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lPuMAlcynMezWGiV p{margin:0;}#mermaid-svg-lPuMAlcynMezWGiV g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-lPuMAlcynMezWGiV g.classGroup text .title{font-weight:bolder;}#mermaid-svg-lPuMAlcynMezWGiV .cluster-label text{fill:#333;}#mermaid-svg-lPuMAlcynMezWGiV .cluster-label span{color:#333;}#mermaid-svg-lPuMAlcynMezWGiV .cluster-label span p{background-color:transparent;}#mermaid-svg-lPuMAlcynMezWGiV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lPuMAlcynMezWGiV .cluster text{fill:#333;}#mermaid-svg-lPuMAlcynMezWGiV .cluster span{color:#333;}#mermaid-svg-lPuMAlcynMezWGiV .nodeLabel,#mermaid-svg-lPuMAlcynMezWGiV .edgeLabel{color:#131300;}#mermaid-svg-lPuMAlcynMezWGiV .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-lPuMAlcynMezWGiV .label text{fill:#131300;}#mermaid-svg-lPuMAlcynMezWGiV .labelBkg{background:#ECECFF;}#mermaid-svg-lPuMAlcynMezWGiV .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-lPuMAlcynMezWGiV .classTitle{font-weight:bolder;}#mermaid-svg-lPuMAlcynMezWGiV .node rect,#mermaid-svg-lPuMAlcynMezWGiV .node circle,#mermaid-svg-lPuMAlcynMezWGiV .node ellipse,#mermaid-svg-lPuMAlcynMezWGiV .node polygon,#mermaid-svg-lPuMAlcynMezWGiV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lPuMAlcynMezWGiV .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV g.clickable{cursor:pointer;}#mermaid-svg-lPuMAlcynMezWGiV g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-lPuMAlcynMezWGiV g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-lPuMAlcynMezWGiV .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-lPuMAlcynMezWGiV .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-lPuMAlcynMezWGiV .dashed-line{stroke-dasharray:3;}#mermaid-svg-lPuMAlcynMezWGiV .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-lPuMAlcynMezWGiV #compositionStart,#mermaid-svg-lPuMAlcynMezWGiV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #compositionEnd,#mermaid-svg-lPuMAlcynMezWGiV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #dependencyStart,#mermaid-svg-lPuMAlcynMezWGiV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #dependencyStart,#mermaid-svg-lPuMAlcynMezWGiV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #extensionStart,#mermaid-svg-lPuMAlcynMezWGiV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #extensionEnd,#mermaid-svg-lPuMAlcynMezWGiV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #aggregationStart,#mermaid-svg-lPuMAlcynMezWGiV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #aggregationEnd,#mermaid-svg-lPuMAlcynMezWGiV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #lollipopStart,#mermaid-svg-lPuMAlcynMezWGiV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV #lollipopEnd,#mermaid-svg-lPuMAlcynMezWGiV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lPuMAlcynMezWGiV .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-lPuMAlcynMezWGiV .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lPuMAlcynMezWGiV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lPuMAlcynMezWGiV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lPuMAlcynMezWGiV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持有
引用
引用
引用(非破坏性变换)
1
*
vtkMRMLScene
~NodeAddedEvent~
+AddNode()
+RemoveNode()
+GetNodeByID()
+Import() / Export()
vtkMRMLNode
+ID
+Name
+Attributes
+NodeReferences
+ReadXMLAttributes()
+WriteXML()
+Copy()
vtkMRMLDisplayableNode
存数据本身
图像 / 模型 / 点集
vtkMRMLDisplayNode
存显示属性
颜色 / 透明度 / 可见性
窗宽窗位
vtkMRMLStorageNode
存读写方式
文件路径 / 格式 / 压缩
vtkMRMLTransformNode
刚体 / 仿射 / 形变
可级联成树

节点类型 职责 基类
Data 节点 存数据本身(图像、模型、点集) vtkMRMLDisplayableNode
Display 节点 存显示属性(颜色、透明度、可见性、窗宽窗位) vtkMRMLDisplayNode
Storage 节点 存文件读写方式(路径、格式、压缩) vtkMRMLStorageNode

3.3 MRML 的其余四个子目录

目录 职责
Libs/MRML/Logic/ 无 GUI 的业务逻辑(vtkMRMLAbstractLogic 及派生),监听场景事件、操作节点
Libs/MRML/DisplayableManager/ MRML 节点 → VTK Actor 的桥梁。每种可显示节点有对应的 DisplayableManager,负责在 2D/3D 视图里创建、更新、销毁渲染对象。"数据变了画面自动更新"的实现处
Libs/MRML/Widgets/ qMRML* 控件:节点选择框、切片视图控件、树视图等,与场景双向绑定
Libs/MRML/CLI/ 命令行模块与 MRML 之间的数据桥接

3.4 其他基础库

作用
Libs/vtkITK/ ITK ↔ VTK 管线适配
Libs/vtkTeem/ 扩散张量 / NRRD
Libs/vtkSegmentationCore/ 分割的多表示法(标签图 / 闭合面 / 平面轮廓)自动转换
Libs/RemoteIO/ 远程数据读写
Libs/ITKFactoryRegistration/ ITK IO 工厂注册

4. Base ------ 应用框架层

目录 内容
Base/Logic/ vtkSlicerApplicationLogic(应用级逻辑、后台任务队列 vtkSlicerTask)、vtkSlicerModuleLogic(所有模块 Logic 的基类)
Base/QTCore/ 无 GUI 的应用内核qSlicerCoreApplicationqSlicerCoreIOManager(Reader/Writer 注册表)、模块工厂体系(qSlicerModuleFactoryManagerqSlicerLoadableModuleFactory)、Python 管理、扩展管理器
Base/QTGUI/ GUI 内核qSlicerApplicationqSlicerLayoutManager(视图布局)、模块面板 / 选择器 / 查找对话框、qSlicerAbstractModuleWidget
Base/QTCLI/ + Base/CLI/ CLI 模块的加载、XML 参数解析、自动生成 GUI、进程调度
Base/QTApp/ 主窗口、启动画面、应用生命周期模板
Base/Python/ slicer.util 等 Python 侧的粘合代码

关键设计:Core 与 GUI 分离

#mermaid-svg-YWamyDQavbQ1DIfG{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-YWamyDQavbQ1DIfG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YWamyDQavbQ1DIfG .error-icon{fill:#552222;}#mermaid-svg-YWamyDQavbQ1DIfG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YWamyDQavbQ1DIfG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YWamyDQavbQ1DIfG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YWamyDQavbQ1DIfG .marker.cross{stroke:#333333;}#mermaid-svg-YWamyDQavbQ1DIfG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YWamyDQavbQ1DIfG p{margin:0;}#mermaid-svg-YWamyDQavbQ1DIfG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YWamyDQavbQ1DIfG .cluster-label text{fill:#333;}#mermaid-svg-YWamyDQavbQ1DIfG .cluster-label span{color:#333;}#mermaid-svg-YWamyDQavbQ1DIfG .cluster-label span p{background-color:transparent;}#mermaid-svg-YWamyDQavbQ1DIfG .label text,#mermaid-svg-YWamyDQavbQ1DIfG span{fill:#333;color:#333;}#mermaid-svg-YWamyDQavbQ1DIfG .node rect,#mermaid-svg-YWamyDQavbQ1DIfG .node circle,#mermaid-svg-YWamyDQavbQ1DIfG .node ellipse,#mermaid-svg-YWamyDQavbQ1DIfG .node polygon,#mermaid-svg-YWamyDQavbQ1DIfG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YWamyDQavbQ1DIfG .rough-node .label text,#mermaid-svg-YWamyDQavbQ1DIfG .node .label text,#mermaid-svg-YWamyDQavbQ1DIfG .image-shape .label,#mermaid-svg-YWamyDQavbQ1DIfG .icon-shape .label{text-anchor:middle;}#mermaid-svg-YWamyDQavbQ1DIfG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YWamyDQavbQ1DIfG .rough-node .label,#mermaid-svg-YWamyDQavbQ1DIfG .node .label,#mermaid-svg-YWamyDQavbQ1DIfG .image-shape .label,#mermaid-svg-YWamyDQavbQ1DIfG .icon-shape .label{text-align:center;}#mermaid-svg-YWamyDQavbQ1DIfG .node.clickable{cursor:pointer;}#mermaid-svg-YWamyDQavbQ1DIfG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YWamyDQavbQ1DIfG .arrowheadPath{fill:#333333;}#mermaid-svg-YWamyDQavbQ1DIfG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YWamyDQavbQ1DIfG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YWamyDQavbQ1DIfG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YWamyDQavbQ1DIfG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YWamyDQavbQ1DIfG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YWamyDQavbQ1DIfG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YWamyDQavbQ1DIfG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YWamyDQavbQ1DIfG .cluster text{fill:#333;}#mermaid-svg-YWamyDQavbQ1DIfG .cluster span{color:#333;}#mermaid-svg-YWamyDQavbQ1DIfG 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-YWamyDQavbQ1DIfG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YWamyDQavbQ1DIfG rect.text{fill:none;stroke-width:0;}#mermaid-svg-YWamyDQavbQ1DIfG .icon-shape,#mermaid-svg-YWamyDQavbQ1DIfG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YWamyDQavbQ1DIfG .icon-shape p,#mermaid-svg-YWamyDQavbQ1DIfG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YWamyDQavbQ1DIfG .icon-shape .label rect,#mermaid-svg-YWamyDQavbQ1DIfG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YWamyDQavbQ1DIfG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YWamyDQavbQ1DIfG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YWamyDQavbQ1DIfG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 继承
qSlicerCoreApplication

(Base/QTCore)
qSlicerApplication

(Base/QTGUI)
无界面模式

--no-main-window

批处理 / 自动化测试
完整 GUI 模式

主窗口 / 布局管理 / 模块面板

这一分离使得 Slicer 可以在 无界面模式 下运行完整的数据与逻辑栈。


5. Modules ------ 四类模块机制

这是 Slicer 可扩展性的核心。四种类型,能力和开发成本递增:
#mermaid-svg-GEvcZ6wcEKxT8oVK{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-GEvcZ6wcEKxT8oVK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GEvcZ6wcEKxT8oVK .error-icon{fill:#552222;}#mermaid-svg-GEvcZ6wcEKxT8oVK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GEvcZ6wcEKxT8oVK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .marker.cross{stroke:#333333;}#mermaid-svg-GEvcZ6wcEKxT8oVK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GEvcZ6wcEKxT8oVK p{margin:0;}#mermaid-svg-GEvcZ6wcEKxT8oVK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster-label text{fill:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster-label span{color:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster-label span p{background-color:transparent;}#mermaid-svg-GEvcZ6wcEKxT8oVK .label text,#mermaid-svg-GEvcZ6wcEKxT8oVK span{fill:#333;color:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .node rect,#mermaid-svg-GEvcZ6wcEKxT8oVK .node circle,#mermaid-svg-GEvcZ6wcEKxT8oVK .node ellipse,#mermaid-svg-GEvcZ6wcEKxT8oVK .node polygon,#mermaid-svg-GEvcZ6wcEKxT8oVK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .rough-node .label text,#mermaid-svg-GEvcZ6wcEKxT8oVK .node .label text,#mermaid-svg-GEvcZ6wcEKxT8oVK .image-shape .label,#mermaid-svg-GEvcZ6wcEKxT8oVK .icon-shape .label{text-anchor:middle;}#mermaid-svg-GEvcZ6wcEKxT8oVK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .rough-node .label,#mermaid-svg-GEvcZ6wcEKxT8oVK .node .label,#mermaid-svg-GEvcZ6wcEKxT8oVK .image-shape .label,#mermaid-svg-GEvcZ6wcEKxT8oVK .icon-shape .label{text-align:center;}#mermaid-svg-GEvcZ6wcEKxT8oVK .node.clickable{cursor:pointer;}#mermaid-svg-GEvcZ6wcEKxT8oVK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .arrowheadPath{fill:#333333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GEvcZ6wcEKxT8oVK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GEvcZ6wcEKxT8oVK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GEvcZ6wcEKxT8oVK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster text{fill:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK .cluster span{color:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK 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-GEvcZ6wcEKxT8oVK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GEvcZ6wcEKxT8oVK rect.text{fill:none;stroke-width:0;}#mermaid-svg-GEvcZ6wcEKxT8oVK .icon-shape,#mermaid-svg-GEvcZ6wcEKxT8oVK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GEvcZ6wcEKxT8oVK .icon-shape p,#mermaid-svg-GEvcZ6wcEKxT8oVK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GEvcZ6wcEKxT8oVK .icon-shape .label rect,#mermaid-svg-GEvcZ6wcEKxT8oVK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GEvcZ6wcEKxT8oVK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GEvcZ6wcEKxT8oVK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GEvcZ6wcEKxT8oVK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 能力递增
CLI 模块

XML 描述参数

GUI 自动生成
Scripted 模块

Python

可自定义界面
Loadable 模块

C++ 共享库

可注册 MRML 节点

可注册 DisplayableManager
Core 模块

内建、不可卸载

类型 语言 位置 特点
CLI C++ / 任意可执行文件 Modules/CLI/ 用 XML 描述参数,SlicerExecutionModel 自动生成 GUI 和命令行解析;独立进程或共享库运行;最易写,但无自定义界面
Loadable C++ Modules/Loadable/ 编译成共享库,运行时被 qSlicerLoadableModuleFactory 发现加载;可自定义 Qt 界面、注册新 MRML 节点类型、注册 DisplayableManager。功能最完整。共 22 个内置模块
Scripted Python Modules/Scripted/ 通过 qSlicerScriptedLoadableModule 加载,能力接近 Loadable,开发迭代最快
Core C++ Modules/Core/ 内建进主程序,不可卸载,如 EventBroker

示例:

  • CLIThresholdScalarVolumeN4ITKBiasFieldCorrectionResampleDTIVolume
  • LoadableVolumesModelsMarkupsSegmentationsTransformsSequencesVolumeRenderingSubjectHierarchyTerminologies
  • ScriptedDICOMSegmentEditorSampleDataScreenCaptureExtensionWizardWebServer

5.1 每个 Loadable 模块的标准三件套(MVC)

Modules/Loadable/Volumes/ 为例:
#mermaid-svg-Zecbd6rBuoE6Xsen{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-Zecbd6rBuoE6Xsen .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Zecbd6rBuoE6Xsen .error-icon{fill:#552222;}#mermaid-svg-Zecbd6rBuoE6Xsen .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Zecbd6rBuoE6Xsen .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Zecbd6rBuoE6Xsen .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Zecbd6rBuoE6Xsen .marker.cross{stroke:#333333;}#mermaid-svg-Zecbd6rBuoE6Xsen svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Zecbd6rBuoE6Xsen p{margin:0;}#mermaid-svg-Zecbd6rBuoE6Xsen .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster-label text{fill:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster-label span{color:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster-label span p{background-color:transparent;}#mermaid-svg-Zecbd6rBuoE6Xsen .label text,#mermaid-svg-Zecbd6rBuoE6Xsen span{fill:#333;color:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen .node rect,#mermaid-svg-Zecbd6rBuoE6Xsen .node circle,#mermaid-svg-Zecbd6rBuoE6Xsen .node ellipse,#mermaid-svg-Zecbd6rBuoE6Xsen .node polygon,#mermaid-svg-Zecbd6rBuoE6Xsen .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Zecbd6rBuoE6Xsen .rough-node .label text,#mermaid-svg-Zecbd6rBuoE6Xsen .node .label text,#mermaid-svg-Zecbd6rBuoE6Xsen .image-shape .label,#mermaid-svg-Zecbd6rBuoE6Xsen .icon-shape .label{text-anchor:middle;}#mermaid-svg-Zecbd6rBuoE6Xsen .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Zecbd6rBuoE6Xsen .rough-node .label,#mermaid-svg-Zecbd6rBuoE6Xsen .node .label,#mermaid-svg-Zecbd6rBuoE6Xsen .image-shape .label,#mermaid-svg-Zecbd6rBuoE6Xsen .icon-shape .label{text-align:center;}#mermaid-svg-Zecbd6rBuoE6Xsen .node.clickable{cursor:pointer;}#mermaid-svg-Zecbd6rBuoE6Xsen .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Zecbd6rBuoE6Xsen .arrowheadPath{fill:#333333;}#mermaid-svg-Zecbd6rBuoE6Xsen .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Zecbd6rBuoE6Xsen .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Zecbd6rBuoE6Xsen .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Zecbd6rBuoE6Xsen .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Zecbd6rBuoE6Xsen .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Zecbd6rBuoE6Xsen .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster text{fill:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen .cluster span{color:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen 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-Zecbd6rBuoE6Xsen .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Zecbd6rBuoE6Xsen rect.text{fill:none;stroke-width:0;}#mermaid-svg-Zecbd6rBuoE6Xsen .icon-shape,#mermaid-svg-Zecbd6rBuoE6Xsen .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Zecbd6rBuoE6Xsen .icon-shape p,#mermaid-svg-Zecbd6rBuoE6Xsen .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Zecbd6rBuoE6Xsen .icon-shape .label rect,#mermaid-svg-Zecbd6rBuoE6Xsen .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Zecbd6rBuoE6Xsen .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Zecbd6rBuoE6Xsen .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Zecbd6rBuoE6Xsen :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Model
Controller
View
Modules/Loadable/Volumes/
MRML/

模块新增的 MRML 节点类型
Logic/

vtkSlicerVolumesModuleLogic

纯算法与场景操作,不依赖 Qt
Widgets/

qMRML* 自定义控件
qSlicerVolumesModule.cxx

模块注册入口
qSlicerVolumesModuleWidget.cxx

Qt 界面

这个分层保证了 Logic 可被 Python 和无头模式直接调用,界面只是薄壳


6. Applications 与扩展

  • Applications/SlicerApp/ ------ 主程序入口。通过 CMake 的 "Custom App" 机制,
    第三方可复用整套框架打造自有品牌应用。
  • Extensions/ ------ 扩展打包 / 发布的 CMake 支撑;实际扩展代码托管在 Extensions Index,
    qSlicerExtensionsManagerModel 在运行时下载安装,无需重编译主程序。
    详见本文第二部分。

7. 运行时数据流

把上述各层串起来,以"加载一个体数据"为例:
模块 Logic qMRML 控件 DisplayableManager vtkMRMLScene vtkMRMLStorageNode qSlicerFileReader qSlicerCoreIOManager 模块 Logic qMRML 控件 DisplayableManager vtkMRMLScene vtkMRMLStorageNode qSlicerFileReader qSlicerCoreIOManager #mermaid-svg-0ddMCFW0WXq50rvv{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-0ddMCFW0WXq50rvv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0ddMCFW0WXq50rvv .error-icon{fill:#552222;}#mermaid-svg-0ddMCFW0WXq50rvv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0ddMCFW0WXq50rvv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0ddMCFW0WXq50rvv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0ddMCFW0WXq50rvv .marker.cross{stroke:#333333;}#mermaid-svg-0ddMCFW0WXq50rvv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0ddMCFW0WXq50rvv p{margin:0;}#mermaid-svg-0ddMCFW0WXq50rvv .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ddMCFW0WXq50rvv text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-0ddMCFW0WXq50rvv .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0ddMCFW0WXq50rvv .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-0ddMCFW0WXq50rvv .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-0ddMCFW0WXq50rvv .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-0ddMCFW0WXq50rvv #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-0ddMCFW0WXq50rvv .sequenceNumber{fill:white;}#mermaid-svg-0ddMCFW0WXq50rvv #sequencenumber{fill:#333;}#mermaid-svg-0ddMCFW0WXq50rvv #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-0ddMCFW0WXq50rvv .messageText{fill:#333;stroke:none;}#mermaid-svg-0ddMCFW0WXq50rvv .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ddMCFW0WXq50rvv .labelText,#mermaid-svg-0ddMCFW0WXq50rvv .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-0ddMCFW0WXq50rvv .loopText,#mermaid-svg-0ddMCFW0WXq50rvv .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-0ddMCFW0WXq50rvv .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0ddMCFW0WXq50rvv .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-0ddMCFW0WXq50rvv .noteText,#mermaid-svg-0ddMCFW0WXq50rvv .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-0ddMCFW0WXq50rvv .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ddMCFW0WXq50rvv .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ddMCFW0WXq50rvv .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ddMCFW0WXq50rvv .actorPopupMenu{position:absolute;}#mermaid-svg-0ddMCFW0WXq50rvv .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-0ddMCFW0WXq50rvv .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ddMCFW0WXq50rvv .actor-man circle,#mermaid-svg-0ddMCFW0WXq50rvv line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-0ddMCFW0WXq50rvv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户 点击 "Load Volume" 1 按扩展名匹配 Reader 2 创建 VolumeNode + DisplayNode + StorageNode 3 ReadData() 4 调用 ITK / vtkITK 读盘 5 AddNode() × 3 6 NodeAddedEvent 7 NodeAddedEvent 8 NodeAddedEvent 9 创建 VTK Actor → 2D/3D 视图出图 10 节点下拉框自动刷新 11 更新自身状态 12 拖动窗宽窗位滑块 13 仅修改 DisplayNode 14 ModifiedEvent 15 更新 Actor 属性(不重新读数据) 16 用户

关键点:改显示属性只碰 DisplayNode,数据节点完全不动------这就是三节点分离的实际收益。


8. 第一部分小结

Slicer 是"以 MRML 场景为唯一真相源、以事件驱动同步视图、以四类可插拔模块扩展功能"的分层架构。

贯穿始终的两条主线:

  1. Data / Display / Storage 三节点分离
  2. Logic / GUI 分离


第二部分:扩展机制底层基础


9. 核心结论

扩展(Extension)不是一套独立的插件框架,它复用的就是模块(Module)的加载机制。

扩展 = 一组按约定目录结构打包的模块 + 一份元数据 + 一套分发 / 版本绑定设施。

Slicer 没有为扩展发明任何新的插件加载技术,它只是把"模块搜索路径"这个变量暴露出来,

剩下的全是打包、版本绑定和分发的工程问题。
#mermaid-svg-2PJTRVIjg5r9M7Sk{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-2PJTRVIjg5r9M7Sk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2PJTRVIjg5r9M7Sk .error-icon{fill:#552222;}#mermaid-svg-2PJTRVIjg5r9M7Sk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2PJTRVIjg5r9M7Sk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .marker.cross{stroke:#333333;}#mermaid-svg-2PJTRVIjg5r9M7Sk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2PJTRVIjg5r9M7Sk p{margin:0;}#mermaid-svg-2PJTRVIjg5r9M7Sk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster-label text{fill:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster-label span{color:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster-label span p{background-color:transparent;}#mermaid-svg-2PJTRVIjg5r9M7Sk .label text,#mermaid-svg-2PJTRVIjg5r9M7Sk span{fill:#333;color:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .node rect,#mermaid-svg-2PJTRVIjg5r9M7Sk .node circle,#mermaid-svg-2PJTRVIjg5r9M7Sk .node ellipse,#mermaid-svg-2PJTRVIjg5r9M7Sk .node polygon,#mermaid-svg-2PJTRVIjg5r9M7Sk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .rough-node .label text,#mermaid-svg-2PJTRVIjg5r9M7Sk .node .label text,#mermaid-svg-2PJTRVIjg5r9M7Sk .image-shape .label,#mermaid-svg-2PJTRVIjg5r9M7Sk .icon-shape .label{text-anchor:middle;}#mermaid-svg-2PJTRVIjg5r9M7Sk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .rough-node .label,#mermaid-svg-2PJTRVIjg5r9M7Sk .node .label,#mermaid-svg-2PJTRVIjg5r9M7Sk .image-shape .label,#mermaid-svg-2PJTRVIjg5r9M7Sk .icon-shape .label{text-align:center;}#mermaid-svg-2PJTRVIjg5r9M7Sk .node.clickable{cursor:pointer;}#mermaid-svg-2PJTRVIjg5r9M7Sk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .arrowheadPath{fill:#333333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2PJTRVIjg5r9M7Sk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2PJTRVIjg5r9M7Sk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2PJTRVIjg5r9M7Sk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster text{fill:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk .cluster span{color:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk 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-2PJTRVIjg5r9M7Sk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2PJTRVIjg5r9M7Sk rect.text{fill:none;stroke-width:0;}#mermaid-svg-2PJTRVIjg5r9M7Sk .icon-shape,#mermaid-svg-2PJTRVIjg5r9M7Sk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2PJTRVIjg5r9M7Sk .icon-shape p,#mermaid-svg-2PJTRVIjg5r9M7Sk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2PJTRVIjg5r9M7Sk .icon-shape .label rect,#mermaid-svg-2PJTRVIjg5r9M7Sk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2PJTRVIjg5r9M7Sk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2PJTRVIjg5r9M7Sk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2PJTRVIjg5r9M7Sk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 扩展 Extension
支柱一:模块搜索路径

Modules/AdditionalPaths
支柱二:CTK AppLauncher

运行时环境注入
支柱三:版本 / ABI 绑定

revision × OS × arch
支柱四:CMake 构建体系

SlicerConfig.cmake
支柱五:元数据与分发

.s4ext / JSON catalog


10. 支柱一:模块搜索路径(真正的"加载"机制)

这是最关键、也最简单的一环。见

Base/QTCore/qSlicerExtensionsManagerModel.cxx:1167extensionModulePaths()

cpp 复制代码
QString path = this->extensionInstallPath(extensionName);   // <ExtInstallPath>/<ExtName>
return appendToPathList(QStringList(), QStringList()
    << path + "/" Slicer_CLIMODULES_SUBDIR                  // cli-modules
    << path + "/" + Slicer_CLIMODULES_LIB_DIR               // lib/Slicer-5.8/cli-modules
    << path + "/" + Slicer_QTLOADABLEMODULES_LIB_DIR        // lib/Slicer-5.8/qt-loadable-modules
#ifdef Slicer_USE_PYTHONQT
    << path + "/" + Slicer_QTSCRIPTEDMODULES_LIB_DIR        // lib/Slicer-5.8/qt-scripted-modules
#endif
    );

10.1 扩展包的目录结构必须和 Slicer 安装树同构

#mermaid-svg-FubbpSHXLhTcuQfv{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-FubbpSHXLhTcuQfv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FubbpSHXLhTcuQfv .error-icon{fill:#552222;}#mermaid-svg-FubbpSHXLhTcuQfv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FubbpSHXLhTcuQfv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FubbpSHXLhTcuQfv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FubbpSHXLhTcuQfv .marker.cross{stroke:#333333;}#mermaid-svg-FubbpSHXLhTcuQfv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FubbpSHXLhTcuQfv p{margin:0;}#mermaid-svg-FubbpSHXLhTcuQfv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FubbpSHXLhTcuQfv .cluster-label text{fill:#333;}#mermaid-svg-FubbpSHXLhTcuQfv .cluster-label span{color:#333;}#mermaid-svg-FubbpSHXLhTcuQfv .cluster-label span p{background-color:transparent;}#mermaid-svg-FubbpSHXLhTcuQfv .label text,#mermaid-svg-FubbpSHXLhTcuQfv span{fill:#333;color:#333;}#mermaid-svg-FubbpSHXLhTcuQfv .node rect,#mermaid-svg-FubbpSHXLhTcuQfv .node circle,#mermaid-svg-FubbpSHXLhTcuQfv .node ellipse,#mermaid-svg-FubbpSHXLhTcuQfv .node polygon,#mermaid-svg-FubbpSHXLhTcuQfv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FubbpSHXLhTcuQfv .rough-node .label text,#mermaid-svg-FubbpSHXLhTcuQfv .node .label text,#mermaid-svg-FubbpSHXLhTcuQfv .image-shape .label,#mermaid-svg-FubbpSHXLhTcuQfv .icon-shape .label{text-anchor:middle;}#mermaid-svg-FubbpSHXLhTcuQfv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FubbpSHXLhTcuQfv .rough-node .label,#mermaid-svg-FubbpSHXLhTcuQfv .node .label,#mermaid-svg-FubbpSHXLhTcuQfv .image-shape .label,#mermaid-svg-FubbpSHXLhTcuQfv .icon-shape .label{text-align:center;}#mermaid-svg-FubbpSHXLhTcuQfv .node.clickable{cursor:pointer;}#mermaid-svg-FubbpSHXLhTcuQfv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FubbpSHXLhTcuQfv .arrowheadPath{fill:#333333;}#mermaid-svg-FubbpSHXLhTcuQfv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FubbpSHXLhTcuQfv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FubbpSHXLhTcuQfv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FubbpSHXLhTcuQfv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FubbpSHXLhTcuQfv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FubbpSHXLhTcuQfv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FubbpSHXLhTcuQfv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FubbpSHXLhTcuQfv .cluster text{fill:#333;}#mermaid-svg-FubbpSHXLhTcuQfv .cluster span{color:#333;}#mermaid-svg-FubbpSHXLhTcuQfv 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-FubbpSHXLhTcuQfv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FubbpSHXLhTcuQfv rect.text{fill:none;stroke-width:0;}#mermaid-svg-FubbpSHXLhTcuQfv .icon-shape,#mermaid-svg-FubbpSHXLhTcuQfv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FubbpSHXLhTcuQfv .icon-shape p,#mermaid-svg-FubbpSHXLhTcuQfv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FubbpSHXLhTcuQfv .icon-shape .label rect,#mermaid-svg-FubbpSHXLhTcuQfv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FubbpSHXLhTcuQfv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FubbpSHXLhTcuQfv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FubbpSHXLhTcuQfv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 结构同构
Slicer 安装树
lib/Slicer-5.8/cli-modules/
lib/Slicer-5.8/qt-loadable-modules/
lib/Slicer-5.8/qt-scripted-modules/
扩展安装目录 <ExtInstallPath>/MyExtension/
cli-modules/
lib/Slicer-5.8/cli-modules/
lib/Slicer-5.8/qt-loadable-modules/
lib/Slicer-5.8/qt-scripted-modules/

10.2 安装 = 往 QSettings 追加路径

qSlicerExtensionsManagerModel.cxx:690-692

cpp 复制代码
QStringList additionalPaths = app->toSlicerHomeAbsolutePaths(
    settings.value("Modules/AdditionalPaths").toStringList());
QStringList newModulePaths = q->extensionModulePaths(extensionName);
settings.setValue("Modules/AdditionalPaths", /* 合并后转相对路径存回 */);

下次启动时,模块工厂读这个键:

文件 负责
Base/QTCore/qSlicerLoadableModuleFactory.cxx:122 C++ 共享库模块
Base/QTGUI/qSlicerScriptedLoadableModuleFactory.cxx:163 Python 模块
cpp 复制代码
QStringList additionalModulePaths = app->toSlicerHomeAbsolutePaths(
    settings->value("Modules/AdditionalPaths").toStringList());
QStringList qtModulePaths = additionalModulePaths + defaultQTModulePaths;

注意顺序additionalModulePaths 排在内置路径之前

因此扩展可以覆盖同名内置模块。

10.3 推论:为什么装完扩展必须重启

#mermaid-svg-ofmJCd6rkqg0alay{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-ofmJCd6rkqg0alay .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ofmJCd6rkqg0alay .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ofmJCd6rkqg0alay .error-icon{fill:#552222;}#mermaid-svg-ofmJCd6rkqg0alay .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ofmJCd6rkqg0alay .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ofmJCd6rkqg0alay .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ofmJCd6rkqg0alay .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ofmJCd6rkqg0alay .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ofmJCd6rkqg0alay .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ofmJCd6rkqg0alay .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ofmJCd6rkqg0alay .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ofmJCd6rkqg0alay .marker.cross{stroke:#333333;}#mermaid-svg-ofmJCd6rkqg0alay svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ofmJCd6rkqg0alay p{margin:0;}#mermaid-svg-ofmJCd6rkqg0alay .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ofmJCd6rkqg0alay .cluster-label text{fill:#333;}#mermaid-svg-ofmJCd6rkqg0alay .cluster-label span{color:#333;}#mermaid-svg-ofmJCd6rkqg0alay .cluster-label span p{background-color:transparent;}#mermaid-svg-ofmJCd6rkqg0alay .label text,#mermaid-svg-ofmJCd6rkqg0alay span{fill:#333;color:#333;}#mermaid-svg-ofmJCd6rkqg0alay .node rect,#mermaid-svg-ofmJCd6rkqg0alay .node circle,#mermaid-svg-ofmJCd6rkqg0alay .node ellipse,#mermaid-svg-ofmJCd6rkqg0alay .node polygon,#mermaid-svg-ofmJCd6rkqg0alay .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ofmJCd6rkqg0alay .rough-node .label text,#mermaid-svg-ofmJCd6rkqg0alay .node .label text,#mermaid-svg-ofmJCd6rkqg0alay .image-shape .label,#mermaid-svg-ofmJCd6rkqg0alay .icon-shape .label{text-anchor:middle;}#mermaid-svg-ofmJCd6rkqg0alay .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ofmJCd6rkqg0alay .rough-node .label,#mermaid-svg-ofmJCd6rkqg0alay .node .label,#mermaid-svg-ofmJCd6rkqg0alay .image-shape .label,#mermaid-svg-ofmJCd6rkqg0alay .icon-shape .label{text-align:center;}#mermaid-svg-ofmJCd6rkqg0alay .node.clickable{cursor:pointer;}#mermaid-svg-ofmJCd6rkqg0alay .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ofmJCd6rkqg0alay .arrowheadPath{fill:#333333;}#mermaid-svg-ofmJCd6rkqg0alay .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ofmJCd6rkqg0alay .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ofmJCd6rkqg0alay .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ofmJCd6rkqg0alay .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ofmJCd6rkqg0alay .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ofmJCd6rkqg0alay .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ofmJCd6rkqg0alay .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ofmJCd6rkqg0alay .cluster text{fill:#333;}#mermaid-svg-ofmJCd6rkqg0alay .cluster span{color:#333;}#mermaid-svg-ofmJCd6rkqg0alay 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-ofmJCd6rkqg0alay .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ofmJCd6rkqg0alay rect.text{fill:none;stroke-width:0;}#mermaid-svg-ofmJCd6rkqg0alay .icon-shape,#mermaid-svg-ofmJCd6rkqg0alay .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ofmJCd6rkqg0alay .icon-shape p,#mermaid-svg-ofmJCd6rkqg0alay .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ofmJCd6rkqg0alay .icon-shape .label rect,#mermaid-svg-ofmJCd6rkqg0alay .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ofmJCd6rkqg0alay .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ofmJCd6rkqg0alay .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ofmJCd6rkqg0alay :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用启动
模块工厂扫描路径

(一次性)
dlopen 加载共享库
运行中
用户安装新扩展
写入 Modules/AdditionalPaths

但本次会话不再扫描
标记为 Installed 但未 Loaded
提示重启
用户卸载 / 更新扩展
scheduleForUninstall / Update

仅记标记,不动文件

(共享库已被 dlopen 锁住)

真正的文件操作由 uninstallScheduledExtensions() / updateScheduledExtensions()

在下次启动的早期阶段执行。

10.4 开发者绕过重启的入口

命令行 --additional-module-pathsBase/QTGUI/qSlicerApplication.cxx:1344-1367),

它甚至能直接接受一个扩展目录,内部自动展开成上面那 4 个子路径。


11. 支柱二:CTK AppLauncher ------ 运行时环境注入

模块路径解决了"找得到",但 C++ 扩展还依赖第三方 .so / .dll

Python 扩展还需要 PYTHONPATH

Slicer 的可执行文件其实是 CTKAppLauncher 的一层壳:
#mermaid-svg-kIE3CKpP0U8OoIgO{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-kIE3CKpP0U8OoIgO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kIE3CKpP0U8OoIgO .error-icon{fill:#552222;}#mermaid-svg-kIE3CKpP0U8OoIgO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kIE3CKpP0U8OoIgO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kIE3CKpP0U8OoIgO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kIE3CKpP0U8OoIgO .marker.cross{stroke:#333333;}#mermaid-svg-kIE3CKpP0U8OoIgO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kIE3CKpP0U8OoIgO p{margin:0;}#mermaid-svg-kIE3CKpP0U8OoIgO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster-label text{fill:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster-label span{color:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster-label span p{background-color:transparent;}#mermaid-svg-kIE3CKpP0U8OoIgO .label text,#mermaid-svg-kIE3CKpP0U8OoIgO span{fill:#333;color:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO .node rect,#mermaid-svg-kIE3CKpP0U8OoIgO .node circle,#mermaid-svg-kIE3CKpP0U8OoIgO .node ellipse,#mermaid-svg-kIE3CKpP0U8OoIgO .node polygon,#mermaid-svg-kIE3CKpP0U8OoIgO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kIE3CKpP0U8OoIgO .rough-node .label text,#mermaid-svg-kIE3CKpP0U8OoIgO .node .label text,#mermaid-svg-kIE3CKpP0U8OoIgO .image-shape .label,#mermaid-svg-kIE3CKpP0U8OoIgO .icon-shape .label{text-anchor:middle;}#mermaid-svg-kIE3CKpP0U8OoIgO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kIE3CKpP0U8OoIgO .rough-node .label,#mermaid-svg-kIE3CKpP0U8OoIgO .node .label,#mermaid-svg-kIE3CKpP0U8OoIgO .image-shape .label,#mermaid-svg-kIE3CKpP0U8OoIgO .icon-shape .label{text-align:center;}#mermaid-svg-kIE3CKpP0U8OoIgO .node.clickable{cursor:pointer;}#mermaid-svg-kIE3CKpP0U8OoIgO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kIE3CKpP0U8OoIgO .arrowheadPath{fill:#333333;}#mermaid-svg-kIE3CKpP0U8OoIgO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kIE3CKpP0U8OoIgO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kIE3CKpP0U8OoIgO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kIE3CKpP0U8OoIgO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kIE3CKpP0U8OoIgO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kIE3CKpP0U8OoIgO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster text{fill:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO .cluster span{color:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO 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-kIE3CKpP0U8OoIgO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kIE3CKpP0U8OoIgO rect.text{fill:none;stroke-width:0;}#mermaid-svg-kIE3CKpP0U8OoIgO .icon-shape,#mermaid-svg-kIE3CKpP0U8OoIgO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kIE3CKpP0U8OoIgO .icon-shape p,#mermaid-svg-kIE3CKpP0U8OoIgO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kIE3CKpP0U8OoIgO .icon-shape .label rect,#mermaid-svg-kIE3CKpP0U8OoIgO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kIE3CKpP0U8OoIgO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kIE3CKpP0U8OoIgO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kIE3CKpP0U8OoIgO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户双击 Slicer
CTKAppLauncher

(Slicer.exe)
读取 .ini 配置
拼装环境变量

PATH / LD_LIBRARY_PATH

DYLD_LIBRARY_PATH

PYTHONPATH / QT_PLUGIN_PATH
exec SlicerApp-real
AdditionalLauncherSettings.ini

(扩展生成)

构建树上,扩展通过 Extensions/CMake/SlicerBlockAdditionalLauncherSettings.cmake

生成 AdditionalLauncherSettings.ini,把自己的 lib/bin/

超级构建里的第三方库目录,以及所有依赖扩展的对应路径都追加进去:

cmake 复制代码
list(INSERT Slicer_LAUNCH_COMMAND ${launch_index}
     "--launcher-additional-settings" ${Slicer_ADDITIONAL_LAUNCHER_SETTINGS_FILE})
...
foreach(dep ${EXTENSION_DEPENDS})
  list(APPEND EXTENSION_LIBRARY_PATHS_BUILD ${${dep}_LIBRARY_PATHS_LAUNCHER_BUILD})
endforeach()

这就是为什么你能在扩展的构建树里 make 完直接跑起来调试,不需要先安装。


12. 支柱三:版本与 ABI 绑定

C++ 扩展和主程序共享 VTK / ITK / Qt 的 ABI,二进制不兼容会直接崩溃。

Slicer 用三元组做硬绑定:
#mermaid-svg-8WNPwQlx3rIl8lit{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-8WNPwQlx3rIl8lit .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8WNPwQlx3rIl8lit .error-icon{fill:#552222;}#mermaid-svg-8WNPwQlx3rIl8lit .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8WNPwQlx3rIl8lit .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8WNPwQlx3rIl8lit .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8WNPwQlx3rIl8lit .marker.cross{stroke:#333333;}#mermaid-svg-8WNPwQlx3rIl8lit svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8WNPwQlx3rIl8lit p{margin:0;}#mermaid-svg-8WNPwQlx3rIl8lit .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8WNPwQlx3rIl8lit .cluster-label text{fill:#333;}#mermaid-svg-8WNPwQlx3rIl8lit .cluster-label span{color:#333;}#mermaid-svg-8WNPwQlx3rIl8lit .cluster-label span p{background-color:transparent;}#mermaid-svg-8WNPwQlx3rIl8lit .label text,#mermaid-svg-8WNPwQlx3rIl8lit span{fill:#333;color:#333;}#mermaid-svg-8WNPwQlx3rIl8lit .node rect,#mermaid-svg-8WNPwQlx3rIl8lit .node circle,#mermaid-svg-8WNPwQlx3rIl8lit .node ellipse,#mermaid-svg-8WNPwQlx3rIl8lit .node polygon,#mermaid-svg-8WNPwQlx3rIl8lit .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8WNPwQlx3rIl8lit .rough-node .label text,#mermaid-svg-8WNPwQlx3rIl8lit .node .label text,#mermaid-svg-8WNPwQlx3rIl8lit .image-shape .label,#mermaid-svg-8WNPwQlx3rIl8lit .icon-shape .label{text-anchor:middle;}#mermaid-svg-8WNPwQlx3rIl8lit .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8WNPwQlx3rIl8lit .rough-node .label,#mermaid-svg-8WNPwQlx3rIl8lit .node .label,#mermaid-svg-8WNPwQlx3rIl8lit .image-shape .label,#mermaid-svg-8WNPwQlx3rIl8lit .icon-shape .label{text-align:center;}#mermaid-svg-8WNPwQlx3rIl8lit .node.clickable{cursor:pointer;}#mermaid-svg-8WNPwQlx3rIl8lit .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8WNPwQlx3rIl8lit .arrowheadPath{fill:#333333;}#mermaid-svg-8WNPwQlx3rIl8lit .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8WNPwQlx3rIl8lit .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8WNPwQlx3rIl8lit .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8WNPwQlx3rIl8lit .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8WNPwQlx3rIl8lit .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8WNPwQlx3rIl8lit .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8WNPwQlx3rIl8lit .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8WNPwQlx3rIl8lit .cluster text{fill:#333;}#mermaid-svg-8WNPwQlx3rIl8lit .cluster span{color:#333;}#mermaid-svg-8WNPwQlx3rIl8lit 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-8WNPwQlx3rIl8lit .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8WNPwQlx3rIl8lit rect.text{fill:none;stroke-width:0;}#mermaid-svg-8WNPwQlx3rIl8lit .icon-shape,#mermaid-svg-8WNPwQlx3rIl8lit .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8WNPwQlx3rIl8lit .icon-shape p,#mermaid-svg-8WNPwQlx3rIl8lit .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8WNPwQlx3rIl8lit .icon-shape .label rect,#mermaid-svg-8WNPwQlx3rIl8lit .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8WNPwQlx3rIl8lit .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8WNPwQlx3rIl8lit .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8WNPwQlx3rIl8lit :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Slicer_REVISION
扩展包标识
Slicer_OS
Slicer_ARCHITECTURE
包名

CMake/SlicerExtensionCPack.cmake
服务器索引

/catalog/All/<revision>/<os>
revision-specific settings

revisionUserSettings()

配套设计:

机制 说明
revision-specific settings 扩展相关设置全部存在 revisionUserSettings() 里,而非全局设置。换个 Slicer 版本就是另一套扩展列表,互不污染
相对路径存储 Modules/AdditionalPathstoSlicerHomeRelativePaths() 转成相对路径存储,读取时用 toSlicerHomeAbsolutePaths() 还原。安装目录整体搬家后仍然有效

13. 支柱四:CMake 构建体系

13.1 扩展的唯一入口点

cmake 复制代码
find_package(Slicer REQUIRED)
include(${Slicer_USE_FILE})

SlicerConfig.cmake 把主程序构建时的一切反向导出给扩展:
#mermaid-svg-bKIcXcQmAj84x1pL{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-bKIcXcQmAj84x1pL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bKIcXcQmAj84x1pL .error-icon{fill:#552222;}#mermaid-svg-bKIcXcQmAj84x1pL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bKIcXcQmAj84x1pL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bKIcXcQmAj84x1pL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bKIcXcQmAj84x1pL .marker.cross{stroke:#333333;}#mermaid-svg-bKIcXcQmAj84x1pL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bKIcXcQmAj84x1pL p{margin:0;}#mermaid-svg-bKIcXcQmAj84x1pL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bKIcXcQmAj84x1pL .cluster-label text{fill:#333;}#mermaid-svg-bKIcXcQmAj84x1pL .cluster-label span{color:#333;}#mermaid-svg-bKIcXcQmAj84x1pL .cluster-label span p{background-color:transparent;}#mermaid-svg-bKIcXcQmAj84x1pL .label text,#mermaid-svg-bKIcXcQmAj84x1pL span{fill:#333;color:#333;}#mermaid-svg-bKIcXcQmAj84x1pL .node rect,#mermaid-svg-bKIcXcQmAj84x1pL .node circle,#mermaid-svg-bKIcXcQmAj84x1pL .node ellipse,#mermaid-svg-bKIcXcQmAj84x1pL .node polygon,#mermaid-svg-bKIcXcQmAj84x1pL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bKIcXcQmAj84x1pL .rough-node .label text,#mermaid-svg-bKIcXcQmAj84x1pL .node .label text,#mermaid-svg-bKIcXcQmAj84x1pL .image-shape .label,#mermaid-svg-bKIcXcQmAj84x1pL .icon-shape .label{text-anchor:middle;}#mermaid-svg-bKIcXcQmAj84x1pL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bKIcXcQmAj84x1pL .rough-node .label,#mermaid-svg-bKIcXcQmAj84x1pL .node .label,#mermaid-svg-bKIcXcQmAj84x1pL .image-shape .label,#mermaid-svg-bKIcXcQmAj84x1pL .icon-shape .label{text-align:center;}#mermaid-svg-bKIcXcQmAj84x1pL .node.clickable{cursor:pointer;}#mermaid-svg-bKIcXcQmAj84x1pL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bKIcXcQmAj84x1pL .arrowheadPath{fill:#333333;}#mermaid-svg-bKIcXcQmAj84x1pL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bKIcXcQmAj84x1pL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bKIcXcQmAj84x1pL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bKIcXcQmAj84x1pL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bKIcXcQmAj84x1pL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bKIcXcQmAj84x1pL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bKIcXcQmAj84x1pL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bKIcXcQmAj84x1pL .cluster text{fill:#333;}#mermaid-svg-bKIcXcQmAj84x1pL .cluster span{color:#333;}#mermaid-svg-bKIcXcQmAj84x1pL 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-bKIcXcQmAj84x1pL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bKIcXcQmAj84x1pL rect.text{fill:none;stroke-width:0;}#mermaid-svg-bKIcXcQmAj84x1pL .icon-shape,#mermaid-svg-bKIcXcQmAj84x1pL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bKIcXcQmAj84x1pL .icon-shape p,#mermaid-svg-bKIcXcQmAj84x1pL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bKIcXcQmAj84x1pL .icon-shape .label rect,#mermaid-svg-bKIcXcQmAj84x1pL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bKIcXcQmAj84x1pL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bKIcXcQmAj84x1pL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bKIcXcQmAj84x1pL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Slicer 主构建
SlicerConfig.cmake

  • UseSlicer.cmake
    编译器设置 / C++17
    所有 target 的 include 目录
    宏定义与安装路径变量
    Python 包装规则
    构建宏:

slicerMacroBuildLoadableModule()

slicerMacroBuildScriptedModule()

SEMMacroBuildCLI()
扩展构建

扩展用的和内置模块完全一样的 CMake 宏 ------这正是

"扩展和内置模块没有本质区别"的体现。

13.2 打包与分发链路

#mermaid-svg-b6j2jb6aHiyATxhd{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-b6j2jb6aHiyATxhd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b6j2jb6aHiyATxhd .error-icon{fill:#552222;}#mermaid-svg-b6j2jb6aHiyATxhd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b6j2jb6aHiyATxhd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b6j2jb6aHiyATxhd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b6j2jb6aHiyATxhd .marker.cross{stroke:#333333;}#mermaid-svg-b6j2jb6aHiyATxhd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b6j2jb6aHiyATxhd p{margin:0;}#mermaid-svg-b6j2jb6aHiyATxhd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b6j2jb6aHiyATxhd .cluster-label text{fill:#333;}#mermaid-svg-b6j2jb6aHiyATxhd .cluster-label span{color:#333;}#mermaid-svg-b6j2jb6aHiyATxhd .cluster-label span p{background-color:transparent;}#mermaid-svg-b6j2jb6aHiyATxhd .label text,#mermaid-svg-b6j2jb6aHiyATxhd span{fill:#333;color:#333;}#mermaid-svg-b6j2jb6aHiyATxhd .node rect,#mermaid-svg-b6j2jb6aHiyATxhd .node circle,#mermaid-svg-b6j2jb6aHiyATxhd .node ellipse,#mermaid-svg-b6j2jb6aHiyATxhd .node polygon,#mermaid-svg-b6j2jb6aHiyATxhd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b6j2jb6aHiyATxhd .rough-node .label text,#mermaid-svg-b6j2jb6aHiyATxhd .node .label text,#mermaid-svg-b6j2jb6aHiyATxhd .image-shape .label,#mermaid-svg-b6j2jb6aHiyATxhd .icon-shape .label{text-anchor:middle;}#mermaid-svg-b6j2jb6aHiyATxhd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b6j2jb6aHiyATxhd .rough-node .label,#mermaid-svg-b6j2jb6aHiyATxhd .node .label,#mermaid-svg-b6j2jb6aHiyATxhd .image-shape .label,#mermaid-svg-b6j2jb6aHiyATxhd .icon-shape .label{text-align:center;}#mermaid-svg-b6j2jb6aHiyATxhd .node.clickable{cursor:pointer;}#mermaid-svg-b6j2jb6aHiyATxhd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b6j2jb6aHiyATxhd .arrowheadPath{fill:#333333;}#mermaid-svg-b6j2jb6aHiyATxhd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b6j2jb6aHiyATxhd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b6j2jb6aHiyATxhd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b6j2jb6aHiyATxhd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b6j2jb6aHiyATxhd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b6j2jb6aHiyATxhd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b6j2jb6aHiyATxhd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b6j2jb6aHiyATxhd .cluster text{fill:#333;}#mermaid-svg-b6j2jb6aHiyATxhd .cluster span{color:#333;}#mermaid-svg-b6j2jb6aHiyATxhd 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-b6j2jb6aHiyATxhd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b6j2jb6aHiyATxhd rect.text{fill:none;stroke-width:0;}#mermaid-svg-b6j2jb6aHiyATxhd .icon-shape,#mermaid-svg-b6j2jb6aHiyATxhd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b6j2jb6aHiyATxhd .icon-shape p,#mermaid-svg-b6j2jb6aHiyATxhd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b6j2jb6aHiyATxhd .icon-shape .label rect,#mermaid-svg-b6j2jb6aHiyATxhd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b6j2jb6aHiyATxhd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b6j2jb6aHiyATxhd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b6j2jb6aHiyATxhd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 驱动
扩展源码
CMake 构建
CMake/SlicerExtensionCPack.cmake
生成 CPack 包

包名含 revision / OS / arch
生成 .s4ext 描述文件

安装到 share/
Extensions/CMake/

SlicerBlockUploadExtension.cmake
扩展服务器

(Girder)
CDash 仪表板
SlicerExtensionsDashboardDriverScript.cmake

工厂机批量构建整个 Extensions Index

文件 作用
CMake/SlicerExtensionCPack.cmake 生成 CPack 包,包名含 revision/OS/arch;同时生成 .s4ext 并安装到 share/
Extensions/CMake/SlicerBlockUploadExtension.cmake 上传到扩展服务器 + CDash
Extensions/CMake/SlicerExtensionsDashboardDriverScript.cmake 工厂机批量构建整个 Extensions Index 的驱动脚本
CMake/SlicerExtensionTemplatesGenerator.cmake 生成模板骨架,配合 Modules/Scripted/ExtensionWizard/ 模块

13.3 模板

模板本体在 Extensions/Testing/,共五种:

模板 用途
CLIExtensionTemplate CLI 模块扩展
LoadableExtensionTemplate C++ Loadable 模块扩展
ScriptedLoadableExtensionTemplate Python 模块扩展
ScriptedSegmentEditorEffectExtensionTemplate 分割编辑器效果插件
SuperBuildExtensionTemplate 需带第三方依赖的扩展

这些模板同时被当作回归测试用例------保证模板永远可编译。


14. 扩展间依赖:find_package 复用

扩展 A 想用扩展 B 的 C++ 类(如很多扩展依赖 SlicerIGT 或 Sequences):
扩展服务器 扩展 A (依赖方) SlicerExtensionGenerateConfig.cmake 扩展 B (被依赖方) 扩展服务器 扩展 A (依赖方) SlicerExtensionGenerateConfig.cmake 扩展 B (被依赖方) #mermaid-svg-v2eeR7mSf5FcI2yg{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-v2eeR7mSf5FcI2yg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v2eeR7mSf5FcI2yg .error-icon{fill:#552222;}#mermaid-svg-v2eeR7mSf5FcI2yg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v2eeR7mSf5FcI2yg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v2eeR7mSf5FcI2yg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v2eeR7mSf5FcI2yg .marker.cross{stroke:#333333;}#mermaid-svg-v2eeR7mSf5FcI2yg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v2eeR7mSf5FcI2yg p{margin:0;}#mermaid-svg-v2eeR7mSf5FcI2yg .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v2eeR7mSf5FcI2yg text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-v2eeR7mSf5FcI2yg .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-v2eeR7mSf5FcI2yg .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-v2eeR7mSf5FcI2yg #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-v2eeR7mSf5FcI2yg .sequenceNumber{fill:white;}#mermaid-svg-v2eeR7mSf5FcI2yg #sequencenumber{fill:#333;}#mermaid-svg-v2eeR7mSf5FcI2yg #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-v2eeR7mSf5FcI2yg .messageText{fill:#333;stroke:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v2eeR7mSf5FcI2yg .labelText,#mermaid-svg-v2eeR7mSf5FcI2yg .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .loopText,#mermaid-svg-v2eeR7mSf5FcI2yg .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-v2eeR7mSf5FcI2yg .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-v2eeR7mSf5FcI2yg .noteText,#mermaid-svg-v2eeR7mSf5FcI2yg .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-v2eeR7mSf5FcI2yg .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v2eeR7mSf5FcI2yg .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v2eeR7mSf5FcI2yg .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v2eeR7mSf5FcI2yg .actorPopupMenu{position:absolute;}#mermaid-svg-v2eeR7mSf5FcI2yg .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-v2eeR7mSf5FcI2yg .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v2eeR7mSf5FcI2yg .actor-man circle,#mermaid-svg-v2eeR7mSf5FcI2yg line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-v2eeR7mSf5FcI2yg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 元数据 DEPENDS / build_dependencies include(${Slicer_EXTENSION_GENERATE_CONFIG}) 1 遍历全局属性 SLICER_MODULE_TARGETS SLICER_MODULE_LOGIC_TARGETS SLICER_MODULE_MRML_TARGETS SLICER_MODULE_WIDGET_TARGETS SLICER_WRAP_HIERARCHY_TARGETS 2 生成 BConfig.cmake 3 find_package(B REQUIRED) 4 导出 include 目录 + target 名 5 installExtension(..., installDependencies=true) 自动连带安装依赖 6

依赖方的写法:

cmake 复制代码
find_package(Sequences REQUIRED)
set(MODULE_INCLUDE_DIRECTORIES ${vtkSlicerSequencesModuleMRML_INCLUDE_DIRS})
set(MODULE_TARGET_LIBRARIES     vtkSlicerSequencesModuleMRML)

依赖关系同时声明在元数据的 DEPENDS / build_dependencies 字段里,

服务器据此在运行时自动连带安装依赖

installExtension(..., installDependencies = true)

Base/QTCore/qSlicerExtensionsManagerModel.h:298)。

注意:若 B 未生成 BConfig.cmake

SlicerBlockAdditionalLauncherSettings.cmake 会给出明确的 WARNING 提示。


15. 支柱五:元数据与服务器

15.1 两种格式并存

格式 说明
.s4ext 老的 key-value 描述文件,由 Extensions/CMake/SlicerFunctionGenerateExtensionDescription.cmake 生成,随包安装。是本地判断"某扩展已安装"的凭据:extensionDescriptionFile() 返回 <installPath>.s4ext
JSON catalog entry 新格式,提交到 ExtensionsIndex 仓库的就是这个。正式 schema:Schemas/slicer-extension-catalog-entry-schema-v1.0.1.json

字段定义在 Extensions/CMake/SlicerExtensionDescriptionSpec.cmake

字段 必填 说明
SCM 源码管理系统(git)
SCMURL 只读 checkout URL
SCMREVISION 版本号
DEPENDS 空格分隔的依赖扩展列表,无依赖时值为 NA
CATEGORY / ICONURL / SCREENSHOTURLS / HOMEPAGE / CONTRIBUTORS / DESCRIPTION / STATUS / ENABLED / BUILD_SUBDIRECTORY 展示与构建元信息

JSON 模板示例(Extensions/LoadableExtensionTemplate.json):

json 复制代码
{
  "$schema": "https://raw.githubusercontent.com/Slicer/Slicer/main/Schemas/slicer-extension-catalog-entry-schema-v1.0.0.json#",
  "build_dependencies": [],
  "build_subdirectory": ".",
  "category": "Examples",
  "scm_type": "local",
  "scm_revision": "",
  "scm_url": "Testing/LoadableExtensionTemplate"
}

15.2 服务器交互

全部封装在 Base/QTCore/qSlicerExtensionsManagerModel.cxx

(约 13 万字符的单文件,是这块的绝对核心):

组件 职责
qSlicerExtensionsManagerModel 模型、状态机、服务器 API(Girder v1)、安装 / 卸载事务
Base/QTCore/qSlicerExtensionDownloadTask.h 异步下载任务
LibArchive 解压
Base/QTGUI/qSlicerExtensionsManagerDialog.cxx 管理器对话框
Base/QTGUI/qSlicerExtensionsServerWidget.cxx 服务器端扩展浏览
Base/QTGUI/qSlicerExtensionsLocalWidget.cxx 本地已装扩展列表
Base/QTCore/qSlicerApplicationUpdateManager.cxx 应用自身的更新检查

16. 生命周期状态机

Model 里每个扩展维护多个正交 状态位

InstalledColumn / LoadedColumn / BookmarkedColumn / scheduled 标记):
#mermaid-svg-Tc6kVcVUL1VR5Vcd{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-Tc6kVcVUL1VR5Vcd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .error-icon{fill:#552222;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .marker.cross{stroke:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd p{margin:0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Tc6kVcVUL1VR5Vcd g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Tc6kVcVUL1VR5Vcd g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tc6kVcVUL1VR5Vcd .edgeLabel .label text{fill:#333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .label div .edgeLabel{color:#333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .cluster-label,#mermaid-svg-Tc6kVcVUL1VR5Vcd .nodeLabel{color:#131300;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .note-edge{stroke-dasharray:5;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-note text{fill:black;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagram .edgeLabel{color:red;}#mermaid-svg-Tc6kVcVUL1VR5Vcd #dependencyStart,#mermaid-svg-Tc6kVcVUL1VR5Vcd #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Tc6kVcVUL1VR5Vcd .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tc6kVcVUL1VR5Vcd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 服务器上可见
downloadAndInstallExtension()
aboutToLoadExtensions()

(仅在启动早期快照一次)
scheduleExtensionForUninstall()
scheduleExtensionForUpdate()
重启后

uninstallScheduledExtensions()
重启后

updateScheduledExtensions()
cancelExtensionScheduledForUninstall()
cancelExtensionScheduledForUpdate()
Available
Installed
Loaded
ScheduledUninstall
ScheduledUpdate
Bookmarked 是正交状态位

可与任意状态并存

为什么 Installed 与 Loaded 必须分开

qSlicerExtensionsManagerModel.cxx:3407aboutToLoadExtensions()

Base/QTCore/qSlicerCoreApplication.cxx:560 在启动早期调用:

cpp 复制代码
// Extensions that are currently installed are about to be loaded, so set
// "loaded" flag based on current "installed" flag.
// We may install extensions later, but they will not be loaded immediately
// (only on the next application restart).
d->LoadedExtensions.clear();
for (int rowIdx = 0; rowIdx < d->Model.rowCount(); ++rowIdx)
{
  bool installed = /* InstalledRole */;
  loadedItem->setData(installed, LoadedRole);
  ...
}

它把当时的 Installed 状态冻结 为 Loaded。之后本次会话内再装的扩展,

UI 上会显示"已安装但未加载",提示需要重启。

GUI 层 Base/QTGUI/qSlicerExtensionsManagerDialog.cxx:76 也只靠比对

Modules/AdditionalPaths 前后是否变化来决定要不要弹"重启"提示。


17. 完整安装流程(运行时)

QSettings (revision-specific) 文件系统 扩展服务器 (Girder v1) qSlicerExtensionDownloadTask qSlicerExtensionsManagerModel ExtensionsServerWidget QSettings (revision-specific) 文件系统 扩展服务器 (Girder v1) qSlicerExtensionDownloadTask qSlicerExtensionsManagerModel ExtensionsServerWidget #mermaid-svg-quWbz1OzFzwxbVDY{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-quWbz1OzFzwxbVDY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-quWbz1OzFzwxbVDY .error-icon{fill:#552222;}#mermaid-svg-quWbz1OzFzwxbVDY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-quWbz1OzFzwxbVDY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-quWbz1OzFzwxbVDY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-quWbz1OzFzwxbVDY .marker.cross{stroke:#333333;}#mermaid-svg-quWbz1OzFzwxbVDY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-quWbz1OzFzwxbVDY p{margin:0;}#mermaid-svg-quWbz1OzFzwxbVDY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-quWbz1OzFzwxbVDY text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-quWbz1OzFzwxbVDY .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-quWbz1OzFzwxbVDY .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-quWbz1OzFzwxbVDY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-quWbz1OzFzwxbVDY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-quWbz1OzFzwxbVDY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-quWbz1OzFzwxbVDY .sequenceNumber{fill:white;}#mermaid-svg-quWbz1OzFzwxbVDY #sequencenumber{fill:#333;}#mermaid-svg-quWbz1OzFzwxbVDY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-quWbz1OzFzwxbVDY .messageText{fill:#333;stroke:none;}#mermaid-svg-quWbz1OzFzwxbVDY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-quWbz1OzFzwxbVDY .labelText,#mermaid-svg-quWbz1OzFzwxbVDY .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-quWbz1OzFzwxbVDY .loopText,#mermaid-svg-quWbz1OzFzwxbVDY .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-quWbz1OzFzwxbVDY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-quWbz1OzFzwxbVDY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-quWbz1OzFzwxbVDY .noteText,#mermaid-svg-quWbz1OzFzwxbVDY .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-quWbz1OzFzwxbVDY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-quWbz1OzFzwxbVDY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-quWbz1OzFzwxbVDY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-quWbz1OzFzwxbVDY .actorPopupMenu{position:absolute;}#mermaid-svg-quWbz1OzFzwxbVDY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-quWbz1OzFzwxbVDY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-quWbz1OzFzwxbVDY .actor-man circle,#mermaid-svg-quWbz1OzFzwxbVDY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-quWbz1OzFzwxbVDY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户 点击 "Install" 1 downloadAndInstallExtension(extensionId) 2 GET /catalog/All/<revision>/<os> 3 元数据(含 DEPENDS) 4 解析依赖,递归安装依赖项 5 启动异步下载 6 下载扩展包 7 归档文件 8 downloadFinished 9 LibArchive 解压到 <ExtInstallPath>/<ExtName>/ 10 写入 <ExtName>.s4ext 11 extensionModulePaths() 计算 4 个子路径 12 追加到 Modules/AdditionalPaths (转相对路径) 13 extensionInstalled 信号 14 标记为 Installed(未 Loaded)→ 提示重启 15 用户


18. 第二部分小结

层次 承担的职责 关键实现
加载 目录约定 + Modules/AdditionalPaths 模块工厂(复用内置模块机制)
运行环境 库路径 / PYTHONPATH 注入 CTK AppLauncher + AdditionalLauncherSettings.ini
二进制安全 revision × OS × arch 硬绑定 SlicerExtensionCPack + revision-specific settings
构建 SlicerConfig.cmake 导出全部构建上下文 与内置模块同一套 CMake 宏
依赖 扩展间 find_package SlicerExtensionGenerateConfig + DEPENDS 元数据
分发 .s4ext / JSON catalog + Girder API qSlicerExtensionsManagerModel

设计上最值得注意的一点

Slicer 没有为扩展发明任何新的插件加载技术。

它只是把"模块搜索路径"这个变量暴露出来,剩下的全是打包、版本绑定和分发的工程问题。

这也是为什么一个内置模块可以几乎零改动地拆成扩展,反之亦然。



附录


附录 A:关键文件速查

A.1 扩展机制

文件 说明
Base/QTCore/qSlicerExtensionsManagerModel.{h,cxx} 扩展机制绝对核心,约 13 万字符
Base/QTCore/qSlicerExtensionDownloadTask.{h,cxx} 异步下载
Base/QTCore/qSlicerApplicationUpdateManager.{h,cxx} 应用自身更新
Base/QTCore/qSlicerLoadableModuleFactory.cxx C++ 模块发现
Base/QTGUI/qSlicerScriptedLoadableModuleFactory.cxx Python 模块发现
Base/QTGUI/qSlicerExtensionsManagerDialog.cxx 管理器对话框、重启判定
Base/QTGUI/qSlicerExtensionsServerWidget.cxx 服务器扩展浏览
Base/QTGUI/qSlicerExtensionsLocalWidget.cxx 本地扩展列表
Base/QTGUI/qSlicerSettingsExtensionsPanel.cxx 扩展设置面板
CMake/SlicerExtensionCPack.cmake 打包
CMake/SlicerExtensionGenerateConfig.cmake 扩展间依赖导出
CMake/SlicerExtensionConfig.cmake.in Config 模板
CMake/SlicerExtensionTemplatesGenerator.cmake 模板生成
CMake/SlicerBlockInstallExtensionPackages.cmake 内置扩展打包进主程序
Extensions/CMake/SlicerExtensionDescriptionSpec.cmake 元数据字段定义
Extensions/CMake/SlicerFunctionGenerateExtensionDescription.cmake .s4ext 生成
Extensions/CMake/SlicerBlockAdditionalLauncherSettings.cmake Launcher 环境注入
Extensions/CMake/SlicerBlockUploadExtension.cmake 上传
Extensions/CMake/SlicerExtensionsDashboardDriverScript.cmake 批量构建驱动
Schemas/slicer-extension-catalog-entry-schema-v1.0.1.json JSON 元数据 schema
Modules/Scripted/ExtensionWizard/ 扩展创建向导(Python 模块)
Extensions/Testing/ 五种扩展模板 + 回归测试

A.2 核心框架

文件 说明
Libs/MRML/Core/vtkMRMLScene.{h,cxx} 场景容器,应用状态的唯一真相源
Libs/MRML/Core/vtkMRMLNode.{h,cxx} 所有节点基类
Libs/MRML/Core/vtkMRMLDisplayableNode.h Data 节点基类
Libs/MRML/Core/vtkMRMLDisplayNode.h Display 节点基类
Libs/MRML/Core/vtkMRMLStorageNode.h Storage 节点基类
Libs/MRML/Core/vtkMRMLTransformNode.h 变换节点
Libs/MRML/DisplayableManager/ MRML → VTK Actor 桥梁
Base/Logic/vtkSlicerApplicationLogic.{h,cxx} 应用级逻辑、后台任务队列
Base/Logic/vtkSlicerModuleLogic.h 所有模块 Logic 基类
Base/QTCore/qSlicerCoreApplication.{h,cxx} 无 GUI 应用内核
Base/QTCore/qSlicerCoreIOManager.{h,cxx} Reader/Writer 注册表
Base/QTCore/qSlicerModuleFactoryManager.{h,cxx} 模块工厂管理
Base/QTGUI/qSlicerApplication.{h,cxx} GUI 应用内核
Base/QTGUI/qSlicerLayoutManager.{h,cxx} 视图布局管理
Base/QTGUI/qSlicerAbstractModuleWidget.h 模块界面基类

附录 B:关键数字

数量
vtkMRML*Node 类(Libs/MRML/Core/ 88
Modules/Loadable/ 子目录 22
Modules/Scripted/ 子目录 17(含 DICOMLib / DICOMPlugins 两个支持库,非独立模块)
Modules/CLI/ 子目录 38
扩展模板种类 5
SuperBuild/External_*.cmake 37

附录 C:可继续深入的方向

  • 写一个扩展的完整实操路径(从 ExtensionWizard 到上架 Extensions Index)
  • qSlicerExtensionsManagerModel 的下载安装事务细节(含失败回滚)
  • SuperBuild 型扩展如何携带第三方依赖打包
  • DisplayableManager 的渲染更新机制细节
  • MRML 场景的 Undo/Redo 与 Import/Export 实现
  • vtkSlicerApplicationLogic 的后台任务队列与线程模型
  • SlicerExecutionModel 如何从 XML 生成 CLI 模块的 GUI
相关推荐
snow@li2 小时前
命名规范:企业级微服务模块前缀命名规范全景梳理
微服务·云原生·架构
roman_日积跬步-终至千里2 小时前
【从零开始学架构】系统设计的价值:从业务目标到可演进系统
架构
煎饼学大模型12 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
韩楚风18 小时前
【参天引擎】一次宕机后的数据恢复,让我把 Cantian 持久化与恢复的六大机制全搞明白了
服务器·网络·数据库·分布式·mysql·架构·cantian
Slice_cy19 小时前
Mint 自研框架设计与实现:从重复开发走向配置驱动(五)
前端·后端·架构
heimeiyingwang19 小时前
【架构实战】Helm Chart 进阶:依赖管理、测试与 CI/CD 集成
ci/cd·架构
四眼肥鱼19 小时前
【Nextjs】macos 系统运行报错:Error: Cannot find module '../lightningcss.darwin-x64.node'
前端·架构·前端框架
GitLqr19 小时前
别被“Flutter 传感器延迟 150ms”带偏了:这可能只是你的实现方式错了
flutter·架构·kotlin
SamDeepThinking19 小时前
微信支付对接实战:从下单到回调的完整落地过程
后端·程序员·架构