diff --git a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.css b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.css new file mode 100644 index 000000000..fe6a96096 --- /dev/null +++ b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.css @@ -0,0 +1,53 @@ +.dicom-tag-browser-table { + margin-right: auto; + margin-left: auto; +} + +.dicom-tag-browser-table-wrapper { +/* height: 500px;*/ + /*overflow-y: scroll;*/ + overflow-x: scroll; +} + +.dicom-tag-browser-table tr { + padding-left: 10px; + padding-right: 10px; + color: #ffffff; + border-top: 1px solid #ddd; + white-space: nowrap; +} + +.stick { + position: sticky; + overflow: clip; +} + +.dicom-tag-browser-content { + overflow: hidden; + width: 100%; + padding-bottom: 50px; + /*height: 500px;*/ +} + +.dicom-tag-browser-instance-range .range { + height: 20px; +} + +.dicom-tag-browser-instance-range { + padding: 20px 0 20px 0; +} + +.dicom-tag-browser-table td.dicom-tag-browser-table-center { + text-align: center; +} + +.dicom-tag-browser-table th { + padding-left: 10px; + padding-right: 10px; + text-align: center; + color: "#20A5D6"; +} + +.dicom-tag-browser-table th.dicom-tag-browser-table-left { + text-align: left; +} diff --git a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx new file mode 100644 index 000000000..49155ca6a --- /dev/null +++ b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx @@ -0,0 +1,387 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { classes } from '@ohif/core'; +import Range from './Range'; +import DicomTagTable from './DicomTagTable'; +import dcmjs from 'dcmjs'; +import moment from 'moment'; +import './DicomTagBrowser.css'; +import { Select, Typography } from '@ohif/ui'; + +const { ImageSet } = classes; +const { DicomMetaDictionary } = dcmjs.data; +const { nameMap } = DicomMetaDictionary; + + +let instanceSelectList = null; +let instanceSelectTitle = null; + +const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => { + const [ + activeDisplaySetInstanceUID, + setActiveDisplaySetInstanceUID, + ] = useState(displaySetInstanceUID); + const [activeInstance, setActiveInstance] = useState(1); + const [tags, setTags] = useState([]); + const [meta, setMeta] = useState(''); + const [instanceList, setInstanceList] = useState([]); + const [displaySetList, setDisplaySetList] = useState([]); + const [isImageStack, setIsImageStack] = useState(false); + const [selectedDisplaySetValue, setSelectedDisplaySetValue] = useState({} + ); + const onSeriesSelect = value => { + console.log(value); + setActiveDisplaySetInstanceUID(value.value); + setActiveInstance(1); + setSelectedDisplaySetValue(value); + }; + + useEffect(() => { + var activeDisplaySet = displaySets.find( + ds => ds.displaySetInstanceUID === activeDisplaySetInstanceUID + ); + + displaySets.sort((a, b) => a.SeriesNumber - b.SeriesNumber); + + const newDisplaySetList = displaySets.map(displaySet => { + const { + displaySetInstanceUID, + SeriesDate, + SeriesTime, + SeriesNumber, + SeriesDescription, + Modality, + } = displaySet; + + /* Map to display representation */ + const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; + const date = moment(dateStr, 'YYYYMMDD:HHmmss'); + const displayDate = date.format('ddd, MMM Do YYYY'); + return { + value: displaySetInstanceUID, + label: `${SeriesNumber} (${Modality}): ${SeriesDescription}`, + description: displayDate, + onClick: () => { + setActiveDisplaySetInstanceUID(displaySetInstanceUID); + setActiveInstance(1); + + //instanceSelectList.props.children.props.value = 1; + }, + }; + }); + + let metadata; + if (!activeDisplaySet) { + if (!displaySets || displaySets.length == 0) { + return; + } else { + activeDisplaySet = displaySets[0]; + setActiveDisplaySetInstanceUID(displaySets[0].displaySetInstanceUID); + setSelectedDisplaySetValue(newDisplaySetList[0]); + } + + } else { + + setSelectedDisplaySetValue(newDisplaySetList.find( + ds => ds.value === activeDisplaySetInstanceUID + )); + } + const isImageStack = + activeDisplaySet instanceof ImageSet; /*&& + activeDisplaySet.isSOPClassUIDSupported === true*/; + + let instanceList; + if (isImageStack) { + const { images } = activeDisplaySet; + const image = images[activeInstance - 1]; + instanceList = images.map((image, index) => { + // const metadata = image.getData().metadata; + + const { InstanceNumber } = image; + + return { + value: index, + title: `Instance Number: ${InstanceNumber}`, + description: '', + onClick: () => { + setActiveInstance(index); + }, + }; + }); + metadata = image; + } else { + metadata = activeDisplaySet; + } + + + if (isImageStack) { + instanceSelectTitle = ( + + Instance Number + + ) + instanceSelectList = ( +
+

{value}

} + onChange={({ target }) => { + const instanceIndex = parseInt(target.value); + setActiveInstance(instanceIndex); + }} + /> +
+ ); + } else { + instanceSelectList = null; + } + setTags(getSortedTags(metadata)); + setMeta(metadata); + setInstanceList(instanceList); + setDisplaySetList(newDisplaySetList); + setIsImageStack(isImageStack); + }, [activeDisplaySetInstanceUID, activeInstance, displaySets]); + + return ( +
+
+ + Series + + {instanceSelectTitle}
+
+ +
+ + {props.showPercentage && {`${value}%`}} + {props.showValue && ( + + {props.valueRenderer + ? props.valueRenderer(props.value) + : props.value} + + )} + + ); +} + +Range.propTypes = { + value: PropTypes.number, + min: PropTypes.number.isRequired, + max: PropTypes.number.isRequired, + step: PropTypes.number, + id: PropTypes.string, + valueRenderer: PropTypes.func, + onChange: PropTypes.func, + showPercentage: PropTypes.bool, + showValue: PropTypes.bool, + ref: PropTypes.any, +}; + +Range.defaultProps = { + showPercentage: false, + showValue: false, +}; + +export default Range; diff --git a/extensions/default/src/commandsModule.js b/extensions/default/src/commandsModule.js index d5d9aca35..c66a0c788 100644 --- a/extensions/default/src/commandsModule.js +++ b/extensions/default/src/commandsModule.js @@ -1,8 +1,13 @@ +import DicomTagBrowser from './DicomTagBrowser/DicomTagBrowser'; +import React from 'react'; + const commandsModule = ({ servicesManager, commandsManager }) => { const { MeasurementService, HangingProtocolService, UINotificationService, + ViewportGridService, + DisplaySetService } = servicesManager.services; const actions = { @@ -23,6 +28,28 @@ const commandsModule = ({ servicesManager, commandsManager }) => { previousStage: () => { HangingProtocolService.previousProtocolStage(); }, + openDICOMTagViewer() { + const { activeViewportIndex, viewports } = ViewportGridService.getState(); + const activeViewportSpecificData = + viewports[activeViewportIndex]; + const { + displaySetInstanceUIDs, + } = activeViewportSpecificData; + + const displaySets = DisplaySetService.activeDisplaySets; + const { UIModalService } = servicesManager.services; + + const displaySetInstanceUID = displaySetInstanceUIDs[0] + UIModalService.show({ + content: DicomTagBrowser, + contentProps: { + displaySets, + displaySetInstanceUID, + onClose: UIModalService.hide, + }, + title: 'DICOM Tag Browser' + }); + } }; const definitions = { @@ -46,6 +73,9 @@ const commandsModule = ({ servicesManager, commandsManager }) => { storeContexts: [], options: {}, }, + openDICOMTagViewer: { + commandFn: actions.openDICOMTagViewer, + } }; return { diff --git a/modes/longitudinal/src/toolbarButtons.js b/modes/longitudinal/src/toolbarButtons.js index 12c8f2e1f..8daceca1a 100644 --- a/modes/longitudinal/src/toolbarButtons.js +++ b/modes/longitudinal/src/toolbarButtons.js @@ -377,6 +377,19 @@ const toolbarButtons = [ ], 'Reset' ), + _createActionButton( + 'TagBrowser', + 'list-bullets', + 'Dicom Tag Browser', + [ + { + commandName: 'openDICOMTagViewer', + commandOptions: {}, + context: 'DEFAULT', + }, + ], + 'Dicom Tag Browser' + ), _createActionButton( 'rotate-right', 'tool-rotate-right', diff --git a/platform/viewer/pluginConfig.json b/platform/viewer/pluginConfig.json index 13e38f962..f69de2770 100644 --- a/platform/viewer/pluginConfig.json +++ b/platform/viewer/pluginConfig.json @@ -45,4 +45,4 @@ ], "modesFactory": [], "umd": [] -} +} \ No newline at end of file