尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OHIF ViewedDataService:DICOM 阅片“已查看”状态跟踪服务的设计与实现

OHIF ViewedDataService:DICOM 阅片“已查看”状态跟踪服务的设计与实现 OHIF ViewedDataServiceDICOM 阅片“已查看”状态跟踪服务的设计与实现【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers本文基于 OHIF 官方文档 ViewedDataService讲解ViewedDataService这一轻量级会话级服务的定位、事件模型与完整 API并结合cornerstone扩展中的真实源码服务实现、注册与清理、滚动条消费端说明它如何支撑阅片界面中“已查看切片”的增量 UI 更新。读完后你将能在自己的扩展或组件中正确调用该服务、订阅其变更事件并理解切片进度条“已阅填充”效果的底层链路。一、服务概述跟踪当前会话中“已查看”的数据项ViewedDataService跟踪哪些 dataId 在当前会话中被标记为“已查看”viewed。其内部实现非常克制用一个Setstring存储 id并在 viewed 状态发生变化时发布单一事件。从源码看ViewedDataService.tsprivate viewedDataIds new Setstring(); public markDataViewed(dataId: string): void { if (!dataId || this.viewedDataIds.has(dataId)) { return; } this.viewedDataIds.add(dataId); this._broadcastEvent(this.EVENTS.VIEWED_DATA_CHANGED, { viewedDataId: dataId, }); }典型的用法场景来自文档 Overview 部分用户翻到某一切片/数据项时将其标记为 viewed查询某数据项是否已被查看用于初始化seedUI 状态订阅 viewed 变化实现增量更新在需要重置上下文时清空 viewed 状态。它的设计目标是做纯内存、会话级的状态容器默认不持久化、不落盘刷新页面或退出模式即归零详见文末 Notes 部分与生命周期分析。二、事件模型VIEWED_DATA_CHANGED该服务只发布一个事件事件描述VIEWED_DATA_CHANGED当某个数据项被新标记为 viewed或全部 viewed 数据被清空时触发。在源码中事件名常量定义为ViewedDataService.tsclass ViewedDataService extends PubSubService { public static readonly EVENTS { VIEWED_DATA_CHANGED: event::viewedDataChanged, };事件载荷类型Event payloadtype ViewedDataPayload { viewedDataId?: string; viewedDataCleared?: boolean; };两种载荷语义单个数据项被标记为 viewed 时{ viewedDataId: string }全部 viewed 数据被清空时{ viewedDataCleared: true }。注意“只在新标记时触发”这一语义markDataViewed对 falsy 的dataId以及已存在于集合中的 id 直接返回不广播事件见上文源码 L27-L36。订阅方因此不会收到重复噪声。事件发布与订阅机制继承自平台核心PubSubServicepubSubServiceInterface.ts它提供subscribe、_broadcastEvent、_unsubscribe等基础能力ViewedDataService的subscribeViewedDataChanges本质上就是对VIEWED_DATA_CHANGED事件的一个具名包装。三、API 参考文档定义了四个公开方法全部在 ViewedDataService.ts 中有对应实现1.markDataViewed(dataId: string): void将一个数据项标记为 viewed仅当满足以下两个条件时才发出VIEWED_DATA_CHANGEDdataId为 truthy非空字符串该 id 此前尚未被标记为 viewed。2.isDataViewed(dataId: string): boolean返回dataId当前是否在 viewed 集合中。实现上对 falsy 输入直接返回false源码 L38-L44。这是同步查询适合在渲染前一次性“播种”UI 状态。3.clearViewedData(): void清空全部已记录的 viewed dataId并以{ viewedDataCleared: true }载荷广播VIEWED_DATA_CHANGED源码 L46-L51。4.subscribeViewedDataChanges(listener): Subscription订阅VIEWED_DATA_CHANGED载荷返回带unsubscribe()的订阅句柄。文档给出的标准用法示例const subscription viewedDataService.subscribeViewedDataChanges(payload { if (payload.viewedDataCleared) { // 重置本地 viewed 状态 return; } if (payload.viewedDataId) { // 在本地标记单个数据项为 viewed } }); // 之后 subscription.unsubscribe();这一“先查询播种、后订阅增量”的两段式模式是消费该服务的推荐姿势——下文滚动条的例子正是如此。四、服务注册与生命周期注册ViewedDataService定义了静态注册元数据public static REGISTRATION { name: viewedDataService, altName: ViewedDataService, create: () { return new ViewedDataService(); }, };注册名servicesManager中的 keyviewedDataService备用注册名ViewedDataService它由cornerstone扩展在preRegistration钩子中随其他 Cornerstone 服务一起注册index.tsxpreRegistration: async function (props) { const { servicesManager } props; servicesManager.registerService(CornerstoneViewportService.REGISTRATION); servicesManager.registerService(ToolGroupService.REGISTRATION); // ... 其他服务 servicesManager.registerService(ViewedDataService.REGISTRATION); await init.call(this, props); }因此消费方可以直接从servicesManager.services.viewedDataService取到实例在cornerstone扩展的类型体系里它也被显式声明为AppTypes.tsimport ViewedDataServiceType from ../services/ViewedDataService; // services 接口中 viewedDataService?: ViewedDataServiceType;退出模式时清空cornerstone扩展的onModeExit钩子中会主动清空 viewed 状态index.tsxservicesManager.services.viewedDataService?.clearViewedData();从源码结构看这意味着 viewed 记录与“一次模式会话”对齐退出模式后所有“已查看切片”的记忆随之清除避免跨会话残留。这也解释了订阅端为什么要处理viewedDataCleared分支——重置场景是设计上被预期的一等路径而非异常。五、实战链路切片进度条如何消费 ViewedDataService该服务当前最典型的消费方是视口的切片进度滚动条Slice Progress Scrollbar位于extensions/cornerstone/src/Viewport/Overlays/ViewportSliceProgressScrollbar/。它把“已查看”状态渲染为滚动条轨道上的填充色块与“已加载/已缓存”填充loaded fill形成对照。1. 组件侧读取定制项并注入服务在 ViewportSliceProgressScrollbar.tsx 中组件从servicesManager.services解构出viewedDataService并通过customizationService读取相关定制项const { cineService, cornerstoneViewportService, customizationService, viewedDataService } servicesManager.services; const showViewedFill customizationService.getCustomization(viewportScrollbar.showViewedFill) ! false; const viewedDwellMsRaw customizationService.getCustomization(viewportScrollbar.viewedDwellMs); const viewedDwellMs typeof viewedDwellMsRaw number viewedDwellMsRaw 0 ? viewedDwellMsRaw : 0;2. 消费端 HookuseViewedSliceBytes核心逻辑在 hooks.ts 的useViewedSliceBytes中完整体现了文档描述的四段式用法播种堆栈/切片数变化时遍历imageIds用viewedDataService.isDataViewed(imageId)同步查询把对应字节位置置 1增量订阅通过subscribeViewedDataChanges监听变更——收到viewedDataCleared时整体归零bytes.fill(0)收到viewedDataId时按imageIdToIndex映射只置位单个字节避免全量重算标记用户停留在某切片时调用markDataViewed(imageId)且支持“停留计时”——viewedDwellMs 0时切片即标记否则等待定时器到期才标记并在切片继续变化或卸载时清理定时器退订effect 清理函数中调用subscription.unsubscribe()与文档示例完全一致。const subscription viewedDataService.subscribeViewedDataChanges( ({ viewedDataId, viewedDataCleared }) { if (viewedDataCleared) { resetViewed(bytes { bytes.fill(0); }); return; } const index imageIdToIndex.get(viewedDataId); if (index ! undefined) { setViewedByte(index); } } );这里也印证了文档中 payload 的双分支设计订阅端必须同时处理“新增 viewed”与“整体清空”两种情况。3. 相关定制项滚动条行为可通过window.config.customizationService调整示例见 sampleCustomizations.tsxwindow.config { customizationService: [ { // 是否显示“已查看”填充轨道默认 true viewportScrollbar.showViewedFill: { $set: false }, // 当前切片停留多少毫秒后才标记为 viewed0 表示立即标记 viewportScrollbar.viewedDwellMs: { $set: 500 }, }, ], };其中viewedDwellMs正是控制markDataViewed调用时机的参数快速翻片场景下设为 0 可在切到即标记设为正数则可避免“一闪而过”的切片被计入已阅。六、Notes定位与边界服务注册名viewedDataServiceservicesManager.services的 key备用注册名ViewedDataServicealtName作用域会话级内存状态默认不持久化。结合源码可以确认服务内部仅有private viewedDataIds new Setstring()一个状态字段没有任何存储/网络依赖其生命周期由cornerstone扩展的preRegistration注册与onModeExit清空两个钩子约束。对扩展开发者而言这意味着该服务是只读快照式的会话记忆跨页面刷新不保留如需持久化“阅片进度”应在此基础上自行对接存储服务订阅方只需关心VIEWED_DATA_CHANGED一个事件且必须同时处理两种载荷形态由于markDataViewed自带去重与幂等短路重复调用安全但不会触发多余事件。七、小结ViewedDataService是 OHIF 中一个“小而完整”的参考样本一个Set、一个事件、四个方法却清晰地展示了 OHIF 服务体系的典型模式——静态REGISTRATION元数据注册、PubSubService事件驱动、customizationService行为调节、onModeExit生命周期清理。理解它之后阅读其他单事件服务如同步、缓存类服务的实现路径都会更加直接。【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表