def setup(self): # # servers # # testing server - not exposed (used for development) self.localFrame = ctk.ctkCollapsibleButton(self.parent) self.localFrame.setLayout(qt.QVBoxLayout()) self.localFrame.setText("Servers") self.layout.addWidget(self.localFrame) self.localFrame.collapsed = False self.toggleServer = qt.QPushButton("Start Testing Server") self.localFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.localFrame.layout().addWidget(self.verboseServer) # advanced options - not exposed to end users # developers can uncomment these lines to access testing server self.toggleServer.hide() self.verboseServer.hide() # Listener settings = qt.QSettings() self.toggleListener = qt.QPushButton() self.toggleListener.checkable = True if hasattr(slicer, 'dicomListener'): self.toggleListener.text = "Stop Listener" slicer.dicomListener.process.connect('stateChanged(int)', self.onListenerStateChanged) else: self.toggleListener.text = "Start Listener" self.localFrame.layout().addWidget(self.toggleListener) self.toggleListener.connect('clicked()', self.onToggleListener) self.runListenerAtStart = qt.QCheckBox( "Start Listener when Slicer Starts") self.localFrame.layout().addWidget(self.runListenerAtStart) self.runListenerAtStart.checked = settingsValue( 'DICOM/RunListenerAtStart', False, converter=toBool) self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart) # the Database frame (home of the ctkDICOM widget) self.dicomFrame = ctk.ctkCollapsibleButton(self.parent) self.dicomFrame.setLayout(qt.QVBoxLayout()) self.dicomFrame.setText("DICOM Database and Networking") self.layout.addWidget(self.dicomFrame) self.detailsPopup = DICOMLib.DICOMDetailsPopup() # XXX Slicer 4.5 - Remove these. Here only for backward compatibility. self.dicomBrowser = self.detailsPopup.dicomBrowser self.tables = self.detailsPopup.tables # connect to the 'Show DICOM Browser' button self.showBrowserButton = qt.QPushButton('Show DICOM Browser') self.dicomFrame.layout().addWidget(self.showBrowserButton) self.showBrowserButton.connect('clicked()', self.detailsPopup.open) # connect to the main window's dicom button mw = slicer.util.mainWindow() if mw: try: action = slicer.util.findChildren(mw, name='LoadDICOMAction')[0] action.connect('triggered()', self.detailsPopup.open) except IndexError: logging.error( 'Could not connect to the main window DICOM button') if hasattr(slicer, 'dicomListener'): slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile slicer.dicomDatabase.connect('databaseChanged()', self.onDatabaseChanged) # the recent activity frame self.activityFrame = ctk.ctkCollapsibleButton(self.parent) self.activityFrame.setLayout(qt.QVBoxLayout()) self.activityFrame.setText("Recent DICOM Activity") self.layout.addWidget(self.activityFrame) self.recentActivity = DICOMLib.DICOMRecentActivityWidget( self.activityFrame, detailsPopup=self.detailsPopup) self.activityFrame.layout().addWidget(self.recentActivity.widget) self.requestUpdateRecentActivity() # Add spacer to layout self.layout.addStretch(1)
def setup(self): # # servers # # testing server - not exposed (used for development) self.localFrame = ctk.ctkCollapsibleButton(self.parent) self.localFrame.setLayout(qt.QVBoxLayout()) self.localFrame.setText("Servers") self.layout.addWidget(self.localFrame) self.localFrame.collapsed = False self.toggleServer = qt.QPushButton("Start Testing Server") self.localFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.localFrame.layout().addWidget(self.verboseServer) # advanced options - not exposed to end users # developers can uncomment these lines to access testing server self.toggleServer.hide() self.verboseServer.hide() # Listener settings = qt.QSettings() self.toggleListener = qt.QPushButton() if hasattr(slicer, 'dicomListener'): self.toggleListener.text = "Stop Listener" slicer.dicomListener.process.connect('stateChanged(int)',self.onListenerStateChanged) else: self.toggleListener.text = "Start Listener" self.localFrame.layout().addWidget(self.toggleListener) self.toggleListener.connect('clicked()', self.onToggleListener) self.runListenerAtStart = qt.QCheckBox("Start Listener when Slicer Starts") self.localFrame.layout().addWidget(self.runListenerAtStart) if settings.contains('DICOM/RunListenerAtStart'): self.runListenerAtStart.checked = bool(settings.value('DICOM/RunListenerAtStart')) self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart) # the Database frame (home of the ctkDICOM widget) self.dicomFrame = ctk.ctkCollapsibleButton(self.parent) self.dicomFrame.setLayout(qt.QVBoxLayout()) self.dicomFrame.setText("DICOM Database and Networking") self.layout.addWidget(self.dicomFrame) # initialize the dicomDatabase # - pick a default and let the user know if not slicer.dicomDatabase: self.promptForDatabaseDirectory() # # create and configure the app widget - this involves # reaching inside and manipulating the widget hierarchy # - TODO: this configurability should be exposed more natively # in the CTK code to avoid the findChildren calls # self.dicomApp = ctk.ctkDICOMAppWidget() DICOM.setDatabasePrecacheTags(self.dicomApp) if self.hideSearch: # hide the search options - doesn't work yet and doesn't fit # well into the frame slicer.util.findChildren(self.dicomApp, 'SearchOption')[0].hide() self.detailsPopup = DICOMLib.DICOMDetailsPopup(self.dicomApp,setBrowserPersistence=self.setBrowserPersistence) self.tree = self.detailsPopup.tree self.showBrowser = qt.QPushButton('Show DICOM Browser') self.dicomFrame.layout().addWidget(self.showBrowser) self.showBrowser.connect('clicked()', self.detailsPopup.open) # connect to the main window's dicom button mw = slicer.util.mainWindow() try: action = slicer.util.findChildren(mw,name='actionLoadDICOM')[0] action.connect('triggered()',self.detailsPopup.open) except IndexError: print('Could not connect to the main window DICOM button') # connect to our menu file entry so it raises the browser menuFile = slicer.util.lookupTopLevelWidget('menuFile') if menuFile: for action in menuFile.actions(): if action.text == 'DICOM': action.connect('triggered()',self.detailsPopup.open) # make the tree view a bit bigger self.tree.setMinimumHeight(250) if hasattr(slicer, 'dicomListener'): slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile self.contextMenu = qt.QMenu(self.tree) self.exportAction = qt.QAction("Export to Study", self.contextMenu) self.contextMenu.addAction(self.exportAction) self.exportAction.enabled = False self.deleteAction = qt.QAction("Delete", self.contextMenu) self.contextMenu.addAction(self.deleteAction) self.contextMenu.connect('triggered(QAction*)', self.onContextMenuTriggered) slicer.dicomDatabase.connect('databaseChanged()', self.onDatabaseChanged) self.dicomApp.connect('databaseDirectoryChanged(QString)', self.onDatabaseDirectoryChanged) selectionModel = self.tree.selectionModel() # TODO: can't use this because QList<QModelIndex> is not visible in PythonQt #selectionModel.connect('selectionChanged(QItemSelection, QItemSelection)', self.onTreeSelectionChanged) self.tree.connect('clicked(QModelIndex)', self.onTreeClicked) self.tree.setContextMenuPolicy(3) self.tree.connect('customContextMenuRequested(QPoint)', self.onTreeContextMenuRequested) # enable to the Send button of the app widget and take it over # for our purposes - TODO: fix this to enable it at the ctkDICOM level self.sendButton = slicer.util.findChildren(self.dicomApp, text='Send')[0] self.sendButton.enabled = False self.sendButton.connect('triggered()', self.onSendClicked) # the recent activity frame self.activityFrame = ctk.ctkCollapsibleButton(self.parent) self.activityFrame.setLayout(qt.QVBoxLayout()) self.activityFrame.setText("Recent DICOM Activity") self.layout.addWidget(self.activityFrame) self.recentActivity = DICOMLib.DICOMRecentActivityWidget(self.activityFrame,detailsPopup=self.detailsPopup) self.activityFrame.layout().addWidget(self.recentActivity.widget) self.requestUpdateRecentActivity() # Add spacer to layout self.layout.addStretch(1)
def test_Part1DICOM(self): """ Test the DICOM part of the test using the head atlas """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=18822', 'Dcmtk-db.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping") dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) if slicer.dicomDatabase: originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Start Local DICOM Q/R SCP') import subprocess import os configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg' processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/' dcmqrscpExeOptions = ( '/bin', '/../CTK-build/CMakeExternals/Install/bin', '/../DCMTK-install/bin', '/../DCMTK-build/bin', ) dcmqrscpExePath = None dcmqrscpExeName = '/dcmqrscp' if slicer.app.os == 'win': dcmqrscpExeName = dcmqrscpExeName + '.exe' for path in dcmqrscpExeOptions: testPath = slicer.app.slicerHome + path + dcmqrscpExeName if os.path.exists(testPath): dcmqrscpExePath = testPath break if not dcmqrscpExePath: raise (UserWarning("Could not find dcmqrscp executable")) args = (dcmqrscpExePath, '-c', configFilePath) popen = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=processCurrentPath) self.delayDisplay('Retrieve DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') dicomRetrieve = ctk.ctkDICOMRetrieve() dicomRetrieve.setKeepAssociationOpen(True) dicomRetrieve.setDatabase(slicer.dicomDatabase) dicomRetrieve.setCallingAETitle('SlicerAE') dicomRetrieve.setCalledAETitle('DCMTK') dicomRetrieve.setPort(12345) dicomRetrieve.setHost('localhost') dicomRetrieve.getStudy( '1.2.124.113932.1.170.223.162.178.20050502.160340.12640015') popen.kill() dicomWidget.detailsPopup.open() # click on the first row of the tree index = dicomWidget.tree.indexAt(qt.QPoint(0, 0)) dicomWidget.onTreeClicked(index) self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() self.delayDisplay('Change Level') layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) self.delayDisplay('Change Window') self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) self.delayDisplay('Change Layout') layoutManager = slicer.app.layoutManager() layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) self.delayDisplay('Zoom') self.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) self.delayDisplay('Pan') self.clickAndDrag(redWidget, button='Middle', start=(10, 10), end=(40, 40)) self.delayDisplay('Center') redWidget.sliceController().fitSliceToBackground() self.delayDisplay('Lightbox') redWidget.sliceController().setLightboxTo6x6() self.delayDisplay('Conventional Layout') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) self.delayDisplay('No Lightbox') redWidget.sliceController().setLightboxTo1x1() self.delayDisplay('Four Up Layout') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) self.delayDisplay('Shift Mouse') self.clickAndDrag(redWidget, button='None', start=(100, 100), end=(140, 140), modifiers=['Shift']) self.delayDisplay('Conventional, Link, Slice Model') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True) self.delayDisplay('Rotate') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.delayDisplay('Zoom') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView, button='Right') self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def test_LabelToDICOMSEGConverterRoundTrip(self): print("CTEST_FULL_OUTPUT") dir(slicer.modules) """ Load the data using DICOM module """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=10881', 'JANCT-CT.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') reportingTempDir = slicer.app.temporaryPath + '/LabelToDICOMSEGConverter' qt.QDir().mkpath(reportingTempDir) dicomFilesDirectory = reportingTempDir + '/dicomFiles' self.cleanupDir(dicomFilesDirectory) qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = reportingTempDir + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) self.cleanupDir(tempDatabaseDirectory) if slicer.dicomDatabase: self.originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: self.originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') self.importDICOM(slicer.dicomDatabase, dicomFilesDirectory) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.examineForLoading() loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() # initialize the module with the report and volume volumes = slicer.util.getNodes('vtkMRMLScalarVolumeNode*') self.assertTrue(len(volumes) == 1) (name, volume) = volumes.items()[0] self.delayDisplay('Loaded volume name %s' % volume.GetName()) self.delayDisplay('Configure Module') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule( 'LabelToDICOMSEGConverter') module = slicer.modules.labeltodicomsegconverter.widgetRepresentation( ).self() # add label node volumesLogic = slicer.modules.volumes.logic() labelNode = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, volume, "Segmentation") labelNode.SetAttribute('AssociatedNodeID', volume.GetID()) labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt') image = volume.GetImageData() thresh = vtk.vtkImageThreshold() if vtk.vtkVersion().GetVTKMajorVersion() < 6: thresh.SetInput(image) else: thresh.SetInputData(image) thresh.ThresholdBetween(10, 400) thresh.SetInValue(10) thresh.SetOutValue(0) thresh.Update() labelNode.SetAndObserveImageData(thresh.GetOutput()) module.segmentationSelector.setCurrentNode(labelNode) module.volumeSelector.setCurrentNode(volume) self.delayDisplay('Input label initialized') module.outputDir = reportingTempDir + '/Output' # Save the report exportDir = reportingTempDir + '/Output' qt.QDir().mkpath(exportDir) self.cleanupDir(exportDir) module.onLabelExport() self.delayDisplay('Report saved') self.importDICOM(slicer.dicomDatabase, exportDir) slicer.mrmlScene.Clear(0) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.examineForLoading() loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin self.delayDisplay('Wait', 10000) dicomWidget.detailsPopup.loadCheckedLoadables() volumes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') for n, v in volumes.items(): print('Label volume found: ' + v.GetID()) self.assertTrue(len(volumes) == 1) for name, volume in volumes.items(): image = volume.GetImageData() previousImage = thresh.GetOutput() diff = vtk.vtkImageDifference() if vtk.vtkVersion().GetVTKMajorVersion() < 6: diff.SetInput(thresh.GetOutput()) else: diff.SetInputData(thresh.GetOutput()) diff.SetImage(image) diff.Update() if diff.GetThresholdedError() > 1: self.delayDisplay('Reloaded image does not match') self.assertTrue(False) self.delayDisplay('Test passed') self.delayDisplay("Restoring original database directory") if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged( self.originalDatabaseDirectory) except Exception, e: if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged( self.originalDatabaseDirectory) import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.assertTrue(False)
def test_Part1DICOM(self, enableScreenshotsFlag=0, screenshotScaleFactor=1): """ Test the DICOM part of the test using the head atlas """ self.enableScreenshots = enableScreenshotsFlag self.screenshotScaleFactor = screenshotScaleFactor import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=124183', 'dataset1_Thorax_Abdomen.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping") dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatabase' qt.QDir().mkpath(tempDatabaseDirectory) if slicer.dicomDatabase: originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() dicomWidget.detailsPopup.open() # load the data by series UID dicomWidget.detailsPopup.offerLoadables( '1.3.12.2.1107.5.1.4.50025.30000005060811542834300000776', 'Series') dicomWidget.detailsPopup.examineForLoading() self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() self.takeScreenshot('LoadingADICOMVolume-Loaded', 'Loaded DICOM Volume', -1) layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) self.takeScreenshot('LoadingADICOMVolume-WL', 'Changed level and window', -1) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True) self.takeScreenshot('LoadingADICOMVolume-LinkView', 'Linked and visible', -1) self.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) self.takeScreenshot('LoadingADICOMVolume-Zoom', 'Zoom', -1) threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.takeScreenshot('LoadingADICOMVolume-Rotate', 'Rotate', -1) threeDView.resetFocalPoint() self.takeScreenshot('LoadingADICOMVolume-Center', 'Center the view', -1) layoutManager.setLayout(slicer.vtkMRMLLayoutNode. SlicerLayoutConventionalWidescreenView) self.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen', 'Conventional Widescreen Layout', -1) slicer.util.mainWindow().moduleSelector().selectModule( 'VolumeRendering') self.takeScreenshot('VolumeRendering-Module', 'Volume Rendering', -1) volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation( ) abdomenVolume = slicer.mrmlScene.GetFirstNodeByName( '6: CT_Thorax_Abdomen') volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume) self.takeScreenshot('VolumeRendering-SelectVolume', 'Select the volume 6: CT_Thorax_Abdomen', -1) presetsScene = slicer.modules.volumerendering.logic( ).GetPresetsScene() ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3') volumeRenderingWidgetRep.applyPreset(ctCardiac3) self.takeScreenshot('VolumeRendering-SelectPreset', 'Select the Preset CT-Cardiac-3') self.delayDisplay('Skipping: Select VTK CPU Ray Casting') volumeRenderingNode = slicer.mrmlScene.GetFirstNodeByName( 'VolumeRendering') volumeRenderingNode.SetVisibility(1) self.takeScreenshot('VolumeRendering-ViewRendering', 'View Volume Rendering', -1) self.delayDisplay('Skipping Move the Shift slider') redWidget.sliceController().setSliceVisible(False) self.takeScreenshot('VolumeRendering-SlicesOff', 'Turn off visibility of slices in 3D', -1) threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.takeScreenshot('VolumeRendering-RotateVolumeRendering', 'Rotate volume rendered image', -1) volumeRenderingNode.SetVisibility(0) self.takeScreenshot('VolumeRendering-TurnOffVolumeRendering', 'Turn off volume rendered image', -1) volumeRenderingNode.SetCroppingEnabled(1) annotationROI = slicer.mrmlScene.GetFirstNodeByName( 'AnnotationROI') annotationROI.SetDisplayVisibility(1) self.takeScreenshot('VolumeRendering-DisplayROI', 'Enable cropping and display ROI', -1) redWidget.sliceController().setSliceVisible(True) self.takeScreenshot('VolumeRendering-SlicesOn', 'Turn on visibility of slices in 3D', -1) annotationROI.SetXYZ(-79.61, 154.16, -232.591) annotationROI.SetRadiusXYZ(43.4, 65.19, 70.5) self.takeScreenshot('VolumeRendering-SizedROI', 'Position the ROI over a kidney', -1) volumeRenderingNode.SetVisibility(1) self.takeScreenshot('VolumeRendering-ROIRendering', 'ROI volume rendered', -1) annotationROI.SetXYZ(15, 146, -186) annotationROI.SetRadiusXYZ(138, 57, 61) self.takeScreenshot('VolumeRendering-BothKidneys', 'Rendered both kidneys', -1) self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def getDebuggerAutoConnect(self): settings = qt.QSettings() if settings.contains('Developer/PythonRemoteDebugAutoConnect'): return settings.value( 'Developer/PythonRemoteDebugAutoConnect').lower() == 'true' return False
def __init__(self,files): self.files = files settings = qt.QSettings() self.sendAddress = settings.value('DICOM.sendAddress') self.sendPort = settings.value('DICOM.sendPort') self.open
def setBrowserPersistence(self,state): self.browserPersistent = state self.setModality(not self.browserPersistent) settings = qt.QSettings() browserPersistentSettingString = 'true' if self.browserPersistent else 'false' settings.setValue('DICOM/BrowserPersistent', browserPersistentSettingString)
def onHorizontalViewCheckBox(self): settings = qt.QSettings() self.tableSplitter.setOrientation(self.horizontalViewCheckBox.checked) settings.setValue('DICOM/horizontalTables', int(self.horizontalViewCheckBox.checked))
def section_LoadInputData(self): try: # Download and unzip test CT DICOM data import urllib downloads = (( 'http://slicer.kitware.com/midas3/download/item/137843/TestDicomCT.zip', self.dicomZipFilePath), ) downloaded = 0 for url, filePath in downloads: if not os.path.exists(filePath) or os.stat( filePath).st_size == 0: if downloaded == 0: self.delayDisplay( 'Downloading input data to folder\n' + self.dicomZipFilePath + '.\n\n It may take a few minutes...', self.delayMs) print('Requesting download from %s...' % (url)) urllib.urlretrieve(url, filePath) downloaded += 1 else: self.delayDisplay( 'Input data has been found in folder ' + self.dicomZipFilePath, self.delayMs) if downloaded > 0: self.delayDisplay('Downloading input data finished', self.delayMs) numOfFilesInDicomDataDir = len([ name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name) ]) if (numOfFilesInDicomDataDir != self.expectedNumOfFilesInDicomDataDir): slicer.app.applicationLogic().Unzip(self.dicomZipFilePath, self.dicomDataDir) self.delayDisplay("Unzipping done", self.delayMs) numOfFilesInDicomDataDirTest = len([ name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name) ]) self.assertTrue(numOfFilesInDicomDataDirTest == self.expectedNumOfFilesInDicomDataDir) # Open test database and empty it qt.QDir().mkpath(self.dicomDatabaseDir) if slicer.dicomDatabase: self.originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: self.originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', self.dicomDatabaseDir) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(self.dicomDatabaseDir) self.assertTrue(slicer.dicomDatabase.isOpen) # Import test data in database indexer = ctk.ctkDICOMIndexer() self.assertTrue(indexer) indexer.addDirectory(slicer.dicomDatabase, self.dicomDataDir) self.assertTrue(len(slicer.dicomDatabase.patients()) == 1) self.assertTrue(slicer.dicomDatabase.patients()[0]) # Load test data numOfScalarVolumeNodesBeforeLoad = len( slicer.util.getNodes('vtkMRMLScalarVolumeNode*')) numOfSubjectHierarchyNodesBeforeLoad = len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.loadCheckedLoadables() self.assertTrue( len(slicer.util.getNodes('vtkMRMLScalarVolumeNode*')) == numOfScalarVolumeNodesBeforeLoad + 1) self.assertTrue( len(slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')) == numOfSubjectHierarchyNodesBeforeLoad + 3) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e), self.delayMs * 2)
def onTableDensityComboBox(self, state): settings = qt.QSettings() settings.setValue('DICOM/tableDensity', state)
def setSetting(settingStr, value): settingStr = "LongitudinalPETCT/"+settingStr qt.QSettings().setValue(settingStr,str(value))
def __resetDatabase(self): """ As stated. """ self.database = qt.QSettings(self.filepath, self.dbFormat)
def getSecret(self): settings = qt.QSettings() if settings.contains('Developer/PtvsdSecret'): secret = settings.value('Developer/PtvsdSecret') return secret return "slicer"
def onRunListenerAtStart(self): settings = qt.QSettings() settings.setValue('DICOM/RunListenerAtStart', self.runListenerAtStart.checked)
def onPopupGeometryChanged(self): settings = qt.QSettings() self.popupGeometry = self.window.geometry settings.setValue('DICOM/detailsPopup.geometry', self.window.geometry)
def loadModules(self, path, depth=1): # Get list of modules in specified path modules = ModuleInfo.findModules(path, depth) # Determine which modules in above are not already loaded factory = slicer.app.moduleManager().factoryManager() loadedModules = factory.instantiatedModuleNames() candidates = [m for m in modules if m.key not in loadedModules] # Prompt to load additional module(s) if len(candidates): dlg = LoadModulesDialog(self.parent.window()) dlg.setModules(candidates) if dlg.exec_() == qt.QDialog.Accepted: modulesToLoad = dlg.selectedModules # Add module(s) to permanent search paths, if requested if dlg.addToSearchPaths: settings = slicer.app.revisionUserSettings() rawSearchPaths = list( _settingsList(settings, "Modules/AdditionalPaths")) searchPaths = [qt.QDir(path) for path in rawSearchPaths] modified = False for module in modulesToLoad: rawPath = os.path.dirname(module.path) path = qt.QDir(rawPath) if not path in searchPaths: searchPaths.append(path) rawSearchPaths.append(rawPath) modified = True if modified: settings.setValue("Modules/AdditionalPaths", rawSearchPaths) # Enable developer mode (shows Reload&Test section, etc.), if requested if dlg.enableDeveloperMode: qt.QSettings().setValue('Developer/DeveloperMode', 'true') # Register requested module(s) failed = [] for module in modulesToLoad: factory.registerModule(qt.QFileInfo(module.path)) if not factory.isRegistered(module.key): failed.append(module) if len(failed): md = qt.QMessageBox(self.parent.window()) md.icon = qt.QMessageBox.Critical md.standardButtons = qt.QMessageBox.Close md.windowTitle = "Module loading failed" if len(failed) > 1: md.text = "The following modules could not be registered:" else: md.text = "The '%s' module could not be registered:" % failed[ 0].key failedFormat = "<ul><li>%(key)s<br/>(%(path)s)</li></ul>" md.informativeText = "".join( [failedFormat % m.__dict__ for m in failed]) md.exec_() return # Instantiate and load requested module(s) if not factory.loadModules( [module.key for module in modulesToLoad]): md = qt.QMessageBox(self.parent.window()) md.icon = qt.QMessageBox.Critical md.standardButtons = qt.QMessageBox.Close md.windowTitle = "Error loading module(s)" md.text = ( "The module factory manager reported an error. " "One or more of the requested module(s) and/or " "dependencies thereof may not have been loaded.") md.exec_()
def load(self, loadable): imageSeriesAndFiles = self.__seperateFilesListIntoImageSeries( loadable.files) studiesAndImageSeries = self.__seperateImageSeriesAndFilesIntoStudies( imageSeriesAndFiles) petImageSeriesInStudies = self.__extractSpecificModalitySeriesForStudies( studiesAndImageSeries, imageSeriesAndFiles, self.petTerm) ctImageSeriesInStudies = self.__extractSpecificModalitySeriesForStudies( studiesAndImageSeries, imageSeriesAndFiles, self.ctTerm) patientID = None patientName = None patientBirthDate = None patientSex = None patientHeight = None probeFiles = None if imageSeriesAndFiles.keys(): probeImgSerUID = imageSeriesAndFiles.keys()[0] probeFiles = imageSeriesAndFiles[probeImgSerUID] if probeFiles: patientID = self.__getSeriesInformation(probeFiles, self.tags['patientID']) patientName = self.__getSeriesInformation(probeFiles, self.tags['patientName']) patientBirthDate = self.__getSeriesInformation( probeFiles, self.tags['patientBirthDate']) patientSex = self.__getSeriesInformation(probeFiles, self.tags['patientSex']) patientHeight = self.__getSeriesInformation( probeFiles, self.tags['patientHeight']) reportNode = None # import into existing Report node matchingReports = [] reportNodes = slicer.mrmlScene.GetNodesByClass( 'vtkMRMLLongitudinalPETCTReportNode') reportNodes.SetReferenceCount(reportNodes.GetReferenceCount() - 1) for i in xrange(reportNodes.GetNumberOfItems()): rn = reportNodes.GetItemAsObject(i) if (rn.GetAttribute("DICOM.PatientID") == patientID) | ( (rn.GetAttribute("DICOM.PatientName") == patientName) & (rn.GetAttribute("DICOM.PatientBirthDate") == patientBirthDate) & (rn.GetAttribute("DICOM.PatientSex") == patientSex)): matchingReports.append(i) if matchingReports: selectables = [] selectables.append("Create new PET/CT Report") for id in matchingReports: rn = reportNodes.GetItemAsObject(id) selectables.append("Import into " + rn.GetName() + " --- Number of available studies: " + str(rn.GetNumberOfStudyNodeIDs())) dialogTitle = "Import PET/CT Studies into existing Report" dialogLabel = "One or more Reports for the selected Patient already exist!" selected = qt.QInputDialog.getItem(None, dialogTitle, dialogLabel, selectables, 0, False) if (selected in selectables) & (selected != selectables[0]): reportNode = reportNodes.GetItemAsObject( selectables.index(selected) - 1) # create new Report node if not reportNode: reportNode = slicer.mrmlScene.CreateNodeByClass( 'vtkMRMLLongitudinalPETCTReportNode') reportNode.SetReferenceCount(reportNode.GetReferenceCount() - 1) slicer.mrmlScene.AddNode(reportNode) reportNode.SetName("Report for " + patientName) reportNode.SetAttribute('DICOM.PatientID', patientID) reportNode.SetAttribute('DICOM.PatientName', patientName) reportNode.SetAttribute('DICOM.PatientBirthDate', patientBirthDate) reportNode.SetAttribute('DICOM.PatientSex', patientSex) reportNode.SetAttribute('DICOM.PatientHeight', patientHeight) # setup Report's default color node and table for Finding types colorLogic = slicer.modules.colors.logic() defaultColorNodeID = colorLogic.GetDefaultEditorColorNodeID() colorTableNode = slicer.mrmlScene.GetNodeByID(defaultColorNodeID) reportNode.SetColorTableNodeID(colorTableNode.GetID()) for studyUID in studiesAndImageSeries.keys(): if reportNode.IsStudyInReport(studyUID): continue petDescriptions = [] #petImageSeriesInStudies[studyUID] ctDescriptions = [] studyDescription = None for petUID in petImageSeriesInStudies[studyUID]: petDescriptions.append( self.__getImageSeriesDescription( imageSeriesAndFiles[petUID])) if not studyDescription: date = self.__getSeriesInformation( imageSeriesAndFiles[petUID], self.tags['studyDate']) date = date[:4] + "." + date[4:6] + "." + date[6:8] time = self.__getSeriesInformation( imageSeriesAndFiles[petUID], self.tags['studyTime']) time = time[:2] + ":" + time[2:4] + ":" + time[4:6] studyDescription = "Multiple PET and/or CT image series have been found for the Study from " + date + " " + time + ". Please change the selection if a different pair of image series should be loaded." for ctUID in ctImageSeriesInStudies[studyUID]: ctDescriptions.append( self.__getImageSeriesDescription( imageSeriesAndFiles[ctUID])) selectedIndexes = [0, 0] if (len(petDescriptions) > 1) | (len(ctDescriptions) > 1): dialog = PETCTSeriesSelectorDialog(None, studyDescription, petDescriptions, ctDescriptions, 0, 0) dialog.parent.exec_() selectedIndexes = dialog.getSelectedSeries() # UID of current PET series petImageSeriesUID = petImageSeriesInStudies[studyUID][ selectedIndexes[0]] # UID of current CT series ctImageSeriesUID = None if ctDescriptions: # for PET only support ctImageSeriesUID = ctImageSeriesInStudies[studyUID][ selectedIndexes[1]] # create PET SUV volume node petVolumeNode = self.scalarVolumePlugin.load( self.getCachedLoadables( imageSeriesAndFiles[petImageSeriesUID])[0]) petDir = self.__getDirectoryOfImageSeries( self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['sopInstanceUID'])) parameters = {} parameters["PETVolume"] = petVolumeNode.GetID() parameters["PETDICOMPath"] = petDir parameters["SUVVolume"] = petVolumeNode.GetID() quantificationCLI = qt.QSettings().value( 'LongitudinalPETCT/quantificationCLIModule') if quantificationCLI == None: quantificationCLI = "petsuvimagemaker" cliNode = None cliNode = slicer.cli.run( getattr(slicer.modules, quantificationCLI), cliNode, parameters) # create PET label volume node volLogic = slicer.modules.volumes.logic() petLabelVolumeNode = volLogic.CreateAndAddLabelVolume( slicer.mrmlScene, petVolumeNode, petVolumeNode.GetName() + "_LabelVolume") ctVolumeNode = None # create CT volume node if ctImageSeriesUID: ctVolumeNode = self.scalarVolumePlugin.load( self.getCachedLoadables( imageSeriesAndFiles[ctImageSeriesUID])[0]) # create Study node studyNode = slicer.mrmlScene.CreateNodeByClass( 'vtkMRMLLongitudinalPETCTStudyNode') studyNode.SetReferenceCount(studyNode.GetReferenceCount() - 1) studyNode.SetName("Study_" + self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['studyDate']) ) studyNode.SetAttribute( 'DICOM.StudyID', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['studyID'])) studyNode.SetAttribute( 'DICOM.StudyInstanceUID', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['studyInstanceUID'])) studyNode.SetAttribute( 'DICOM.StudyDate', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['studyDate'])) studyNode.SetAttribute( 'DICOM.StudyTime', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['studyTime'])) studyNode.SetAttribute( 'DICOM.RadioPharmaconStartTime', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['radioPharmaconStartTime'])) studyNode.SetAttribute( 'DICOM.DecayFactor', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['decayCorrection'])) studyNode.SetAttribute( 'DICOM.DecayCorrection', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['decayFactor'])) studyNode.SetAttribute( 'DICOM.FrameReferenceTime', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['frameRefTime'])) studyNode.SetAttribute( 'DICOM.RadionuclideHalfLife', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['radionuclideHalfLife'])) studyNode.SetAttribute( 'DICOM.PETSeriesTime', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['seriesTime'])) if ctImageSeriesUID: studyNode.SetAttribute( 'DICOM.CTSeriesTime', self.__getSeriesInformation( imageSeriesAndFiles[ctImageSeriesUID], self.tags['seriesTime'])) studyNode.SetAttribute( 'DICOM.PatientWeight', self.__getSeriesInformation( imageSeriesAndFiles[petImageSeriesUID], self.tags['patientWeight'])) studyNode.SetAndObservePETVolumeNodeID(petVolumeNode.GetID()) if ctVolumeNode: studyNode.SetAndObserveCTVolumeNodeID(ctVolumeNode.GetID()) studyNode.SetAndObservePETLabelVolumeNodeID( petLabelVolumeNode.GetID()) slicer.mrmlScene.AddNode(studyNode) reportNode.AddStudyNodeID(studyNode.GetID()) return reportNode