ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OHIF 3.7 升级到 3.8 迁移实战指南:工具栏按钮定义、PanelService 与枚举重构

OHIF 3.7 升级到 3.8 迁移实战指南:工具栏按钮定义、PanelService 与枚举重构 OHIF 3.7 升级到 3.8 迁移实战指南工具栏按钮定义、PanelService 与枚举重构【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers本文基于 OHIF Viewer 官方迁移文档 from-3p7-to-3p8.md 编写结合当前仓库中 basic / basic-dev-mode 模式的真实按钮定义、ToolbarService源码与ViewerLayout布局实现进行纵深解读。读者完成阅读后将能够独立把自定义扩展与 Mode 从 OHIF 3.7 平滑迁移到 3.8掌握新的对象式工具栏按钮定义、Evaluator 机制、工具监听器、面板服务化以及枚举导出路径的调整。一、迁移总览需要重点关注的两类变更OHIF 3.7 到 3.8 的升级主要围绕两件大事展开工具栏系统的全面重写activeTool概念被移除按钮类型toggle/action/tool被取消改为基于uiType的对象式定义并引入 Evaluator 机制来决定按钮状态周边基础设施调整包括面板属性重命名、URL 参数收敛、Header组件入参变化、面板列表移入PanelService以及枚举从Types导出迁移到Enums导出。其中工具栏部分是改动最大、最需要开发者动手迁移的区域下文将按「工具栏 → 服务 → 参数 → UI → 枚举」的顺序逐一展开。二、新的工具栏按钮定义核心迁移点2.1 移除 Active Tool 的默认激活逻辑3.8 中activeTool及其关联的 getter / setter 已被移除。当前活动工具不再由工具栏服务维护而是从 toolGroup 与 viewport 实时推导。这也解释了为什么在当前仓库的platform/core/src/services/ToolBarService/ToolbarService.ts中已经找不到setDefaultTool/getDefaultTool方法仅保留recordInteraction等交互记录方法。需要执行的操作删除所有通过toolbarService.setDefaultTool()设置默认工具、并通过toolbarService.recordInteraction()激活工具的代码。例如下面这段 3.7 时代的典型代码应整体删除let unsubscribe; toolbarService.setDefaultTool({ groupId: WindowLevel, itemId: WindowLevel, interactionType: tool, commands: [ { commandName: setToolActive, commandOptions: { toolName: WindowLevel, }, context: CORNERSTONE, }, ], }); const activateTool () { toolbarService.recordInteraction(toolbarService.getDefaultTool()); unsubscribe(); }; ({ unsubscribe } toolGroupService.subscribe( toolGroupService.EVENTS.VIEWPORT_ADDED, activateTool ));新的做法放弃先设默认工具再手动激活的流程改为纯粹的数据驱动——只负责定义按钮、声明按钮所属的 Section活动工具状态交给 Evaluator 与 toolGroup 自动联动toolbarService.addButtons([...toolbarButtons, ...moreTools]); toolbarService.createButtonSection(primary, [ MeasurementTools, Zoom, WindowLevel, Pan, Capture, Layout, MPR, Crosshairs, MoreTools, ]);addButtons()负责把按钮定义注册进ToolbarServicecreateButtonSection()负责声明按钮在某个 Section如primary中的排列顺序。当前仓库中modes/basic/src/modeCustomization.ts的registerModeToolbar正是这一思路的工程化封装它调用toolbarService.register(toArray(toolbarButtons))注册按钮再遍历toolbarSections中的每个 Section 调用toolbarService.updateSection(key, section)。2.2 更新按钮定义从type属性到uiType属性3.8 移除了按钮类型toggle、action、tool的概念。旧定义中用type: ohif.action声明顶层 UI 类型并在props内再写一层type: action新定义统一使用顶层的uiType属性并删除冗余的type字段。旧实现{ id: Capture, type: ohif.action, props: { icon: tool-capture, label: Capture, type: action, commands: [ { commandName: showDownloadViewportModal, commandOptions: {}, context: CORNERSTONE, }, ], }, },新实现{ id: Capture, uiType: ohif.radioGroup, props: { icon: tool-capture, label: Capture, commands: [ { commandName: showDownloadViewportModal, context: CORNERSTONE, }, ], evaluate: evaluate.action, }, },可以看到除了把type换成uiType之外commands中的空commandOptions: {}也被省略并且新增了evaluate属性。uiType由扩展的getToolbarModule提供ohif/extension-default内置了ohif.radioGroup、ohif.splitButton、ohif.divider等组件类型详见 Toolbar 模块文档。当前仓库的真实示例可以参看 modes/basic/src/toolbarButtons.ts 与 modes/basic-dev-mode/src/toolbarButtons.ts。例如 basic-dev-mode 中长度测量按钮的定义export const setToolActiveToolbar { commandName: setToolActive, commandOptions: { toolGroupIds: [default, mpr], }, context: CORNERSTONE, }; { id: Length, uiType: ohif.toolButton, props: { icon: tool-length, label: i18n.t(Buttons:Length), tooltip: i18n.t(Buttons:Length Tool), commands: { ...setToolActiveToolbar, commandOptions: { ...setToolActiveToolbar.commandOptions, toolName: Length, }, }, evaluate: evaluate.cornerstoneTool, }, },这里commands直接使用setToolActive命令配合toolName通过toolGroupIds: [default, mpr]声明该工具作用于哪些工具组配合evaluate: evaluate.cornerstoneTool实现仅当该工具是当前活动工具时高亮的效果。2.3 为按钮定义添加 EvaluatorsEvaluator 是 3.8 工具栏的核心机制它是一个被ToolbarService调用的函数根据应用上下文尤其是当前 viewport 与 toolGroup决定按钮的状态。**需要执行的操作**为每个按钮定义添加合适的evaluate属性。选择规则如下使用evaluate.cornerstoneTool按钮只应在它作为**活动主工具左键**时被高亮适用于 Length、Bidirectional、Zoom、Pan 等普通测量/导航工具使用evaluate.cornerstoneTool.toggle适用于开关型工具如参考线、影像叠加层 Image Overlay开或关两种状态。在 Toolbar 模块文档 中可以看到evaluate.cornerstoneTool的底层实现逻辑它先通过toolGroupService.getToolGroupForViewport(viewportId)拿到当前 viewport 所属的工具组再用toolbarService.getToolNameForButton(button)解析出按钮对应的工具名若工具组中不存在该工具则返回{ disabled: true, className: ...ohif-disabled }否则根据toolGroup.getActivePrimaryMouseButtonTool() toolName决定是否返回高亮样式。除上述两个外官方还提供了其他内置 EvaluatorEvaluator用途evaluate.cornerstoneTool依据 toolGroup 与活动主工具状态高亮按钮evaluate.cornerstoneTool.toggle处理参考线、影像叠加层等开关型工具evaluate.cornerstone.synchronizer依据 viewport 的同步器状态是否已同步evaluate.viewportProperties.toggle处理 invert、flip、rotate 等可切换的 viewport 属性evaluate.mprMPR 专用检查 displaySet 是否可重建evaluate.viewport.supported依据 viewport 类型禁用按钮如unsupportedViewportTypes: [volume3d, video, sm]evaluate.modality.supported依据模态控制按钮状态supportedModalities: [CT, MR]或unsupportedModalities: [US]Evaluator 返回的对象可以携带多个字段来控制按钮呈现disabled是否禁用、disabledText禁用时的提示文案、visible是否可见、isActive是否处于活动态、className附加的 CSS 类。组合多个 Evaluator当按钮需要同时满足多个条件时可以把evaluate写成数组例如禁止 Cine 播放器出现在 3D viewportevaluate: [ evaluate.cine, { name: evaluate.viewport.supported, unsupportedViewportTypes: [volume3d], }, ],注意使用多个 Evaluator 时各个 Evaluator 的disabledText会被合并进最终结果因此务必把带disabledText的 Evaluator 放在数组最后一位例如 tmtv 模式的RectangleROIStartEndThreshold工具evaluate: [ evaluate.cornerstone.segmentation, { name: evaluate.cornerstoneTool, disabledText: Select the PT Axial to enable this tool, }, ],原迁移文档中建议参考modes/longitudinal/src/toolbarButtons.ts在当前仓库中longitudinal 模式已被精简modes/longitudinal/src/下仅保留id.js与index.ts最新的对象式按钮定义与 Evaluator 用法请以 modes/basic/src/toolbarButtons.ts 和 modes/basic-dev-mode/src/toolbarButtons.ts 为准。2.4 工具监听器Tool Listeners部分工具需要监听事件来触发行为。3.8 提供的createButton工具支持listeners字段将事件与回调函数绑定。例如参考线工具监听ViewportGridService的两个事件createButton({ id: ReferenceLines, icon: tool-referenceLines, label: Reference Lines, tooltip: Show Reference Lines, commands: toggleEnabledDisabledToolbar, listeners: { [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesListeners, [ViewportGridService.EVENTS.VIEWPORTS_READY]: ReferenceLinesListeners, }, evaluate: evaluate.cornerstoneTool.toggle, }),这里的关键事件是VIEWPORTS_READY。它依赖每个 viewport 通过onElementEnabled回调调用viewportGridService.setViewportIsReady(viewportId, true)来触发如果你没有自定义 viewport 组件、也没有覆写onElementEnabled处理器那么一个由ViewportGrid.tsx通过 props 自动下发的默认处理器会替你完成该调用无需额外操作如果你自定义了viewport 组件并覆写了onElementEnabled则必须在自己实现的处理器中调用viewportGridService.setViewportIsReady(viewportId, true)否则VIEWPORTS_READY事件永远不会触发ViewportComponent displaySets{displaySets} viewportLabel{viewports.size 1 ? viewportLabel : } viewportId{viewportId} dataSource{dataSource} viewportOptions{viewportOptions} displaySetOptions{displaySetOptions} needsRerendering{displaySetsNeedsRerendering} isHangingProtocolLayout{isHangingProtocolLayout} onElementEnabled{() { viewportGridService.setViewportIsReady(viewportId, true); }} /三、Toolbar Service移除toolbarService.init()调用3.8 中toolbarService.init不再是函数toolbarService.init is not a function任何对该方法的调用都会报错。**需要执行的操作**从你的代码库中删除对toolbarService.init()的调用。初始化工作已由框架在服务创建阶段自动完成开发者只需使用addButtons/createButtonSection或register/updateSection等注册类 API。四、面板默认关闭属性重命名3.7 中的leftPanelDefaultClosed和rightPanelDefaultClosed在 3.8 中被分别重命名为leftPanelClosed和rightPanelClosed。在 extensions/default/src/ViewerLayout/index.tsx 中可以看到新命名已成为布局模板的标准 propsleftPanelClosed false、rightPanelClosed false并通过useResizablePanels参与左右面板的展开/收起状态管理。当前仓库的配置示例如 configurationFiles.md 与 modes 文档中均使用rightPanelClosed: true这种新写法。五、URL 参数统一使用seriesInstanceUIDs3.7 中存在seriesInstanceUID与seriesInstanceUIDs两个可选参数3.8 将它们合并为seriesInstanceUIDs。即使你只想筛选单个序列也必须使用seriesInstanceUIDs复数形式。在 platform/app/src/routes/Mode/defaultRouteInit.ts 中可以看到该参数的消费逻辑const seriesInstanceUIDs filters?.seriesInstanceUID;当传入的序列不在当前 study 中时会发出警告Each of the series in filter ... are not part of the current study. The entire study is being displayed。迁移时请将老链接中的?seriesInstanceUID统一改写为?seriesInstanceUIDs。六、UI 组件调整6.1 Header 组件需要新的入参ohif/ui中的Header组件在 3.8 中需要servicesManager和appConfig作为输入。当前仓库的 extensions/default/src/ViewerLayout/ViewerHeader.tsx 展示了消费方写法组件签名只显式接收appConfig再通过useSystem()获取servicesManager、extensionManager并从servicesManager.services中取出customizationService。如果你在自己的扩展或应用里直接使用ohif/ui的Header请确保向其传入这两个依赖。6.2 Panels 移入 PanelService3.8 中左、右面板的列表不再注入到 LayoutTemplate 中而是被移到PanelService需要通过服务按需获取。如果你使用 OHIF 默认的主布局则无需改动但如果你自定义了布局模板就必须更新它通过panelService.getPanels(side)获取面板列表。参考实现见 extensions/default/src/ViewerLayout/index.tsxconst { panelService, hangingProtocolService, customizationService } servicesManager.services; const hasPanels useCallback( (side): boolean !!panelService.getPanels(side).length, [panelService] );该组件还订阅了panelService.EVENTS.PANELS_CHANGED事件在面板变化时重新计算左右面板的显隐并可响应事件载荷中的leftPanelClosed/rightPanelClosed选项同步收起状态。因此自定义布局的正确做法是订阅PANELS_CHANGED 调用getPanels(left | right)而不是从 props 接收面板数组。七、重构枚举从Types导出迁移到Enums导出TimingEnum以及 OHIF core 导出的所有枚举已从Types导出迁移到Enums导出。当前仓库证据platform/core/src/enums/TimingEnum.ts定义了TimingEnumplatform/core/src/enums/index.ts 将其统一导出而 platform/core/src/index.ts 中通过import * as Enums from ./enums后以Enums命名空间整体导出。迁移操作将代码中形如import { TimingEnum } from ohif/core/types的导入改为从ohif/coreEnums导入例如import { Enums } from ohif/core后使用Enums.TimingEnum或直接import { TimingEnum } from ohif/core/enums。八、迁移核对清单升级完成后建议按以下清单逐项自检已删除toolbarService.setDefaultTool()/recordInteraction()激活默认工具的代码所有按钮定义已改为uiType对象式格式type字段已移除每个按钮都配置了合适的evaluateevaluate.cornerstoneTool或evaluate.cornerstoneTool.toggle等自定义 viewport 组件覆写onElementEnabled时已调用viewportGridService.setViewportIsReady(viewportId, true)已移除toolbarService.init()调用leftPanelDefaultClosed/rightPanelDefaultClosed已重命名为leftPanelClosed/rightPanelClosedURL 参数统一使用seriesInstanceUIDs自定义布局已改为从PanelService获取面板Header已传入servicesManager与appConfig枚举导入路径已从Types迁移到Enums上述变更涉及的服务与实现细节可进一步阅读 ToolbarService 源码、useToolbar Hook 以及 Toolbar 模块文档 加深理解。【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表