news 2026/9/18 14:31:11

OHIF 3.10 到 3.11 迁移指南:基于 `promptHydrationDialog` 与 `hydrateSecondaryDisplaySet` 命令的新版 Hydration 体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OHIF 3.10 到 3.11 迁移指南:基于 `promptHydrationDialog` 与 `hydrateSecondaryDisplaySet` 命令的新版 Hydration 体系

OHIF 3.10 到 3.11 迁移指南:基于promptHydrationDialoghydrateSecondaryDisplaySet命令的新版 Hydration 体系

【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers

导读

本文面向从 OHIF 3.10 升级到 3.11 的开发者,聚焦本版本中最重要的架构变化之一——Hydration(水合)机制的重构:原先分散在各扩展中、各自维护一套 Promise 与对话框逻辑的promptHydrateSEG/promptHydrateRT提示函数,被统一收敛到@ohif/extension-cornerstone的通用工具utils.promptHydrationDialog;而真正执行水合的动作,则改为由hydrateSecondaryDisplaySet命令统一驱动。读完本文,你将掌握 3.11 中 SEG、RTSTRUCT、SR 三类二次显示集(secondary display set)水合提示与执行的新调用链、对话框可定制机制、以及如何把存量 3.10 代码迁移到新 API。

背景:3.11 的 Hydration 体系为什么重构

在 3.10 及更早版本中,SEG(分割)与 RTSTRUCT(放疗结构)的水合提示逻辑存在大量重复代码:每个扩展(cornerstone-dicom-segcornerstone-dicom-rt)各自定义了一套RESPONSE常量、各自实现_askHydrate对话框 Promise、各自处理视口交互与回调。这不仅造成代码重复,也让"是否弹窗、弹什么文案、确认后执行什么"这些行为难以在应用层统一定制。

3.11 的改动方向清晰可见:

  1. 对话框逻辑下沉为通用工具promptHydrateSEGpromptHydrateRT改为薄封装,内部统一委托给@ohif/extension-cornerstone导出的utils.promptHydrationDialog(参见 promptHydrationDialog.ts);
  2. 执行动作命令化:真正的水合动作不再由各扩展自行编写回调闭包,而是普遍通过hydrateSecondaryDisplaySet命令触发(参见 commandsModule.ts)。

这一设计让"提示"与"执行"解耦:提示层只负责询问用户并返回决策,执行层以命令形式注册、可被任意调用方复用。

通用提示工具:utils.promptHydrationDialog

3.11 将对话框提示逻辑统一封装在promptHydrationDialog中,并作为@ohif/extension-cornerstoneutils导出(见 utils/index.ts 与 utils/promptHydrationDialog.ts)。

签名与参数

export type HydrationCallback = (params: any) => Promise<boolean>; export interface HydrationDialogProps { servicesManager: AppTypes.ServicesManager; viewportId: string; displaySet: AppTypes.DisplaySet; preHydrateCallbacks?: HydrationCallback[]; // 可选,默认 [] hydrateCallback: HydrationCallback; // 用户确认后执行的回调 type: string; // 'SEG' | 'SR' | 'RTSTRUCT' } function promptHydrationDialog({ servicesManager, viewportId, displaySet, preHydrateCallbacks = [], hydrateCallback, type, }: HydrationDialogProps): Promise<boolean | HydrationSRResult>

各参数说明:

参数类型说明
servicesManagerAppTypes.ServicesManagerOHIF 服务管理器,内部用于取出uiViewportDialogServicecustomizationService以及_extensionManager上的appConfig
viewportIdstring发起水合提示的视口 ID,对话框将挂载到该视口上
displaySetAppTypes.DisplaySet待水合的二次显示集(SEG / RTSTRUCT / SR display set)
preHydrateCallbacksHydrationCallback[](可选)用户在对话框中确认后、执行hydrateCallback之前依次执行的钩子(如保存当前呈现状态 presentation state)
hydrateCallbackHydrationCallback用户确认后真正执行水合的回调,返回Promise<boolean>
typestring水合类型,取自HydrationType枚举:SEGSRRTSTRUCT

返回值是一个 Promise:非 SR 类型在用户确认并完成水合后解析为boolean(成功为true),用户取消则解析为false;SR 类型则返回结构化的HydrationSRResult(详见下文"SR 的特殊处理")。

类型常量与响应码

promptHydrationDialog内部定义了两组关键常量:

export const HydrationType = { SEG: 'SEG', SR: 'SR', RTSTRUCT: 'RTSTRUCT', } as const; const RESPONSE = { NO_NEVER: -1, CANCEL: 0, CREATE_REPORT: 1, ADD_SERIES: 2, SET_STUDY_AND_SERIES: 3, NO_NOT_FOR_SERIES: 4, HYDRATE: 5, };

其中RESPONSE.CANCEL(用户点"No"或点击对话框外部)与RESPONSE.HYDRATE(用户点"Yes"或按 Enter)是 RT/SEG 流程的两个关键分支:只有HYDRATE才会继续执行preHydrateCallbackshydrateCallback

是否弹窗的判定逻辑

这是 3.11 中最值得注意的行为变化之一。promptHydrationDialog通过appConfig决定是否真正弹出确认框:

const shouldPrompt = type === HydrationType.SR ? standardMode : !appConfig?.disableConfirmationPrompts;
  • RTSTRUCT / SEG:读取appConfig.disableConfirmationPrompts。为true时跳过对话框、直接视为用户确认(RESPONSE.HYDRATE),实现"无感水合";
  • SR:不看disableConfirmationPrompts,而是检查appConfig.measurementTrackingMode === 'standard'(即"标准测量跟踪模式"),只有在 standard 模式下才弹窗询问。

从源码注释(promptHydrationDialog.ts)可以看出这是刻意设计:RT/SEG 关心"是否关闭确认提示",SR 关心"是否处于标准测量跟踪模式"。这一分支逻辑在 promptHydrationDialog.test.ts 中有大量针对性测试覆盖,例如"disableConfirmationPrompts为 true 时非 SR 类型应直接返回 HYDRATE 响应"。

对话框内容与交互的可定制性

_askHydrate负责真正的对话框交互:

  • 文案可定制:消息内容通过customizationService.getCustomization(messageKey)获取,按类型映射到不同的 customization key:
switch (type) { case HydrationType.RTSTRUCT: return 'viewportNotification.hydrateRTMessage'; case HydrationType.SEG: return 'viewportNotification.hydrateSEGMessage'; case HydrationType.SR: return 'viewportNotification.hydrateSRMessage'; default: return 'viewportNotification.hydrateMessage'; }

这意味着应用可以通过 OHIF 的 customization 机制覆盖这些 key,自定义弹窗文案而无需改扩展源码。

  • 交互方式:对话框通过uiViewportDialogService.show(...)展示,提供 "No"(secondary样式,值CANCEL)与 "Yes"(primary样式,值HYDRATE)两个动作按钮;点击外部区域等同于取消;按 Enter 键等同于确认。

  • SEG 与 RT 的执行差异:用户确认后,SEG 与 RTSTRUCT 均在window.setTimeout(..., 0)中异步执行hydrateCallback,以让出主线程;区别在于回调入参——SEG 收到{ segDisplaySet, viewportId },RTSTRUCT 收到{ rtDisplaySet, viewportId, servicesManager }

SR 的特殊处理

SR 类型不走简单的 boolean 返回,而是返回HydrationSRResult

export interface HydrationSRResult { userResponse: number; displaySetInstanceUID: string; srSeriesInstanceUID: string; viewportId: string; StudyInstanceUID?: string; SeriesInstanceUIDs?: string[]; }

用户确认后,hydrateCallback(displaySet)的返回值中若包含StudyInstanceUID/SeriesInstanceUIDs,会一并并入结果,供上层(如测量跟踪上下文)继续使用;用户取消时同样返回带userResponse的结果对象而非false

迁移实操:promptHydrateRTpromptHydrateSEG的改造

原文档给出的迁移路径非常具体。以 RT 为例,改造前后对照如下。

promptHydrateRT:删除旧对话框逻辑,改为薄封装

原文档展示的extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts变化:

- const RESPONSE = { - NO_NEVER: -1, - CANCEL: 0, - HYDRATE_SEG: 5, - }; + import { utils, Types } from '@ohif/extension-cornerstone'; function promptHydrateRT({ servicesManager, rtDisplaySet, viewportId, preHydrateCallbacks, hydrateRTDisplaySet, -}: withAppTypes) { - const { uiViewportDialogService, customizationService } = servicesManager.services; - // ... lots of old promise and dialog logic - return new Promise(async function (resolve, reject) { - // ... - }); -} - -function _askHydrate( - // ... -) { - // ... -} +}: { + servicesManager: AppTypes.ServicesManager; + rtDisplaySet: AppTypes.DisplaySet; + viewportId: string; + preHydrateCallbacks?: Types.HydrationCallback[]; + hydrateRTDisplaySet: Types.HydrationCallback; +}) { + return utils.promptHydrationDialog({ + servicesManager, + viewportId, + displaySet: rtDisplaySet, + preHydrateCallbacks, + hydrateCallback: hydrateRTDisplaySet, + type: 'RTSTRUCT', + }); }

对照当前仓库的实际实现(extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts),最终形态与 diff 中的新代码完全一致:整个函数只剩 10 行左右的委托逻辑,旧的RESPONSE常量与_askHydrate已彻底删除。promptHydrateSEG的改造同理(见 extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts),只是type'SEG'、入参名为segDisplaySethydrateCallback

调用方:Viewport 内改由命令触发水合

原文档展示的extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx变化:

useEffect(() => { if (rtIsLoading) { return; } promptHydrateRT({ servicesManager, viewportId, rtDisplaySet, - preHydrateCallbacks: [storePresentationState], - hydrateRTDisplaySet, - }).then(isHydrated => { - if (isHydrated) { - setIsHydrated(true); - } + hydrateRTDisplaySet: async () => { + return commandsManager.runCommand('hydrateSecondaryDisplaySet', { + displaySet: rtDisplaySet, + viewportId, + }); + }, }); - }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]); + }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading, commandsManager]);

对照当前实现(OHIFCornerstoneRTViewport.tsx),可以看到两个要点:

  1. 回调交给命令hydrateRTDisplaySet不再指向旧的自定义水合函数,而是内联为commandsManager.runCommand('hydrateSecondaryDisplaySet', { displaySet: rtDisplaySet, viewportId })
  2. 依赖数组补全useEffect的依赖数组加入了commandsManager(当前实现还额外加入了activeViewportId,且增加了"非活动视口直接返回"的守卫判断,避免后台视口误触发水合提示)。

SEG 侧的实现对称(OHIFCornerstoneSEGViewport.tsx):promptHydrateSEGhydrateCallback同样调用hydrateSecondaryDisplaySet命令,并显式返回true

SR 的对应改造

SR 场景位于测量跟踪扩展中(promptHydrateStructuredReport.ts):它通过事件参数中的viewportIddisplaySetInstanceUID定位显示集,构造hydrateCallback调用hydrateSecondaryDisplaySet命令,然后委托给utils.promptHydrationDialog并传type: 'SR'。注意它额外做了enhancedSrDisplaySet的浅拷贝以补全displaySetInstanceUID字段,说明 SR 调用方需要为对话框提供完整的显示集标识。

执行核心:hydrateSecondaryDisplaySet命令

hydrateSecondaryDisplaySet注册于@ohif/extension-cornerstone的 commandsModule(commandsModule.ts),签名与核心逻辑如下:

hydrateSecondaryDisplaySet: async ({ displaySet, viewportId }) => { if (!displaySet) { return; } const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId); if (displaySet.isOverlayDisplaySet) { // 根据模态与视口类型推断分割表示类型 const segmentationType = displaySet.Modality !== 'SEG' ? SegmentationRepresentations.Contour : viewport && isVolume3DViewportType(viewport) ? SegmentationRepresentations.Surface : SegmentationRepresentations.Labelmap; commandsManager.runCommand('updateStoredSegmentationPresentation', { displaySet, type: segmentationType, }); } // isHydrated 表示"该显示集作为标准视图的一部分展示", // 该判定在这里完成,不依赖视口是否已存在 displaySet.isHydrated = true; const referencedDisplaySetInstanceUID = displaySet.referencedDisplaySetInstanceUID; // ... };

结合 commandsModule.ts 的后续分支,该命令的核心职责可以归纳为:

  1. 空参守卫displaySet不存在时直接返回;
  2. Overlay 显示集的表示类型推断:对于isOverlayDisplaySet(派生覆盖显示集),根据Modality与视口类型决定分割表示——非 SEG(如 RTSTRUCT)用Contour(轮廓),SEG 在 3D 体积视口用Surface(曲面),否则用Labelmap(标签图),并调用updateStoredSegmentationPresentation更新存储的分割呈现;
  3. 标记isHydrated = true:这是命令对显示集状态的直接写入,后续挂片协议与视口布局据此判定"该显示集已是标准视图的一部分";
  4. 按模态分发
    • SEG/RTSTRUCT:先通过storePositionPresentation将引用显示集的"位置呈现"(position/zoom/pan)关联到目标视口,再调用loadSegmentationDisplaySetsForViewport加载分割显示集,并支持读取panelSegmentation.disableEditing定制项以在加载后锁定分割段;
    • SR:调用hydrateStructuredReport命令,并根据返回的SeriesInstanceUIDs解析被引用的显示集继续处理。

此外,命令内部还对水合后的"多视口刷新"做了处理(见 utils/hydrationUtils.ts):SEG 水合后,除挂片协议匹配到的活动视口外,还会按帧参考系(frame of reference)合并所有已展示同一体积的网格视口,确保分割呈现同步应用到 MPR / 3D 瓦片,这体现了 3.11 中"水合 = 在它逻辑上应该出现的地方展示它"的设计意图。

迁移核对清单

在 3.10 → 3.11 升级中完成 Hydration 相关代码迁移,建议按以下清单核对:

  1. 删除各扩展内自有的RESPONSE常量与_askHydrate实现,将promptHydrateSEG/promptHydrateRT收敛为对utils.promptHydrationDialog的薄封装;
  2. 将 Viewport 中的水合执行动作替换为hydrateSecondaryDisplaySet命令,并确认useEffect依赖数组包含commandsManager(以及必要的activeViewportId守卫);
  3. 确认 SR 场景使用type: 'SR'promptHydrationDialog的 SR 分支,其返回值是HydrationSRResult而非boolean
  4. 核对应用配置disableConfirmationPrompts决定 RT/SEG 是否弹窗,measurementTrackingMode决定 SR 是否弹窗——若发现弹窗行为与 3.10 不同,优先检查appConfig中的这两项;
  5. 如需定制文案,通过 customization 覆盖viewportNotification.hydrateRTMessage/hydrateSEGMessage/hydrateSRMessage
  6. 跑一遍测试promptHydrationDialog的判定与回调行为已由 promptHydrationDialog.test.ts 覆盖(包括disableConfirmationPromptsmeasurementTrackingMode、SEG 的延迟执行、SR 结果结构等分支),升级后应确保这些用例通过。

小结

3.11 的 Hydration 体系本质上是把"分散的、各写各的"水合提示与执行逻辑,收敛为"promptHydrationDialog统一提示 +hydrateSecondaryDisplaySet统一执行"的两段式架构。对扩展开发者而言,这意味着更少的样板代码、更统一的定制入口,以及更可测试的行为边界——升级时只需按照本文的迁移清单替换旧函数与旧回调,即可无缝接入新体系。

【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 14:30:11

【Stable Diffusion】Animatediff V2静态图像生成视频

随着生成式 AI 技术的不断进步,动态效果生成变得更加便捷与高效。AnimateDiff V2 的集成,使得用户可以在 WebUI 中像生成静态图像一样简单地创建动画 GIF。这一创新不仅提升了用户体验,减少了依赖额外工具的需求,同时通过在生成过程中加入运动元素,为原本静态的图像赋予了…

作者头像 李华
网站建设 2026/9/18 14:29:29

物流史PPT重字恢复:从数据清洗到结构化分析

简介&#xff1a;这份物流基础课件以《物流的产生和发展》为主题&#xff0c;适合物流管理专业学生、教师及对现代物流起源感兴趣的自学者使用。PPT 从历史视角梳理物流概念的形成脉络&#xff0c;涵盖 1905 年美国琼西贝克提出军事后勤概念、1915 年阿奇萧在《市场流通中的若干…

作者头像 李华
网站建设 2026/9/18 14:25:10

多智能体 MAS 编排跨部门工作流,模型通道改走 TaoToken 行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 14:24:21

微信聊天记录备份与导出:PyWxDump 删库之后,你还能做什么

微信聊天记录备份与导出&#xff1a;PyWxDump 删库之后&#xff0c;你还能做什么 【免费下载链接】PyWxDump 删库 项目地址: https://gitcode.com/GitHub_Trending/py/PyWxDump 去年 10 月&#xff0c;有人克隆了微信聊天记录备份导出工具 PyWxDump&#xff0c;发现仓库…

作者头像 李华