def __init__(self, parent, width=50, height=100): super(DICOMPluginSelector, self).__init__(parent) self.setMinimumHeight(height) self.setMinimumWidth(width) verticalBox = qt.QVBoxLayout() self.checkBoxByPlugin = {} for pluginClass in slicer.modules.dicomPlugins: self.checkBoxByPlugin[pluginClass] = qt.QCheckBox(pluginClass) verticalBox.addWidget(self.checkBoxByPlugin[pluginClass]) # Pack vertical box with plugins into a scroll area widget verticalBoxWidget = qt.QWidget() scrollAreaBox = qt.QVBoxLayout() verticalBoxWidget.setLayout(verticalBox) scrollArea = qt.QScrollArea() scrollArea.setWidget(verticalBoxWidget) scrollAreaBox.addWidget(scrollArea) self.setLayout(scrollAreaBox) settings = qt.QSettings() if settings.contains('DICOM/disabledPlugins/size'): size = settings.beginReadArray('DICOM/disabledPlugins') disabledPlugins = [] for i in range(size): settings.setArrayIndex(i) disabledPlugins.append(str(settings.allKeys()[0])) settings.endArray() for pluginClass in slicer.modules.dicomPlugins: if pluginClass in disabledPlugins: self.checkBoxByPlugin[pluginClass].checked = False else: # Activate plugins for the ones who are not in the disabled list # and also plugins installed with extensions self.checkBoxByPlugin[pluginClass].checked = True else: # All DICOM plugins would be enabled by default for pluginClass in slicer.modules.dicomPlugins: self.checkBoxByPlugin[pluginClass].checked = True
def __init__(self, dicomBrowser=None, parent="mainWindow"): VTKObservationMixin.__init__(self) qt.QWidget.__init__( self, slicer.util.mainWindow() if parent == "mainWindow" else parent) self.pluginInstances = {} self.fileLists = [] self.extensionCheckPending = False self.settings = qt.QSettings() self.dicomBrowser = dicomBrowser if dicomBrowser is not None else slicer.app.createDICOMBrowserForMainDatabase( ) self.browserPersistent = settingsValue('DICOM/BrowserPersistent', False, converter=toBool) self.advancedView = settingsValue('DICOM/advancedView', 0, converter=int) self.horizontalTables = settingsValue('DICOM/horizontalTables', 0, converter=int) self.setup() self.dicomBrowser.connect('directoryImported()', self.onDirectoryImported) self.dicomBrowser.connect('sendRequested(QStringList)', self.onSend) # Load when double-clicked on an item in the browser self.dicomBrowser.dicomTableManager().connect( 'patientsDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked) self.dicomBrowser.dicomTableManager().connect( 'studiesDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked) self.dicomBrowser.dicomTableManager().connect( 'seriesDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked)
def restoreDefaultValues(self): self.topLeftCheckBox.checked = True self.topLeft = 1 self.topRightCheckBox.checked = True self.topRight = 1 self.bottomLeftCheckBox.checked = True self.bottomLeft = 1 self.fontSizeSpinBox.value = 14 self.timesFontRadioButton.checked = True self.fontFamily = 'Times' self.backgroundDICOMAnnotationsPersistence = 0 self.backgroundPersistenceCheckBox.checked = False self.scalarBarEnableCheckBox.checked = False self.scalarBarEnabled = 0 self.rangeLabelFormat = '%G' self.rangeLabelFormatLineEdit.text = '%G' settings = qt.QSettings() settings.setValue('DataProbe/sliceViewAnnotations.enabled', self.sliceViewAnnotationsEnabled) settings.setValue('DataProbe/sliceViewAnnotations.topLeft', self.topLeft) settings.setValue('DataProbe/sliceViewAnnotations.topRight', self.topRight) settings.setValue('DataProbe/sliceViewAnnotations.bottomLeft', self.bottomLeft) settings.setValue('DataProbe/sliceViewAnnotations.fontFamily', self.fontFamily) settings.setValue('DataProbe/sliceViewAnnotations.fontSize', self.fontSize) settings.setValue( 'DataProbe/sliceViewAnnotations.bgDICOMAnnotationsPersistence', self.backgroundDICOMAnnotationsPersistence) settings.setValue('DataProbe/sliceViewAnnotations.scalarBarEnabled', self.scalarBarEnabled) settings.setValue( 'DataProbe/sliceViewAnnotations.scalarBarSelectedLayer', self.scalarBarSelectedLayer) settings.setValue('DataProbe/sliceViewAnnotations.rangeLabelFormat', self.rangeLabelFormat) self.updateSliceViewFromGUI()
def __init__(self, incomingDataDir, incomingPort=None): super(DICOMStoreSCPProcess, self).__init__() self.incomingDataDir = incomingDataDir if not os.path.exists(self.incomingDataDir): os.mkdir(self.incomingDataDir) if incomingPort: assert isinstance(incomingPort, int) self.port = str(incomingPort) else: settings = qt.QSettings() self.port = settings.value('StoragePort') if not self.port: settings.setValue('StoragePort', '11112') self.port = settings.value('StoragePort') self.storescpExecutable = os.path.join( self.exeDir, self.STORESCP_PROCESS_FILE_NAME + self.exeExtension) self.dcmdumpExecutable = os.path.join(self.exeDir, 'dcmdump' + self.exeExtension)
def initializeDICOMDatabase(self): # Create alias for convenience slicer.dicomDatabase = slicer.app.dicomDatabase() # Set the dicom pre-cache tags once all plugin classes have been initialized. # Pre-caching tags is very important for fast DICOM loading because tags that are # not pre-cached during DICOM import in bulk, will be cached during Examine step one-by-one # (which takes magnitudes more time). tagsToPrecache = list(slicer.dicomDatabase.tagsToPrecache) for pluginClass in slicer.modules.dicomPlugins: plugin = slicer.modules.dicomPlugins[pluginClass]() tagsToPrecache += list(plugin.tags.values()) tagsToPrecache = sorted(set(tagsToPrecache)) # remove duplicates slicer.dicomDatabase.tagsToPrecache = tagsToPrecache # Try to initialize the database using the location stored in settings if slicer.app.commandOptions().testingEnabled: # For automatic tests (use a separate DICOM database for testing) slicer.dicomDatabaseDirectorySettingsKey = 'DatabaseDirectoryTest_'+ctk.ctkDICOMDatabase().schemaVersion() databaseDirectory = os.path.join(slicer.app.temporaryPath, 'temp'+slicer.app.applicationName+'DICOMDatabase_'+ctk.ctkDICOMDatabase().schemaVersion()) else: # For production slicer.dicomDatabaseDirectorySettingsKey = 'DatabaseDirectory_'+ctk.ctkDICOMDatabase().schemaVersion() settings = qt.QSettings() databaseDirectory = settings.value(slicer.dicomDatabaseDirectorySettingsKey) if not databaseDirectory: documentsLocation = qt.QStandardPaths.DocumentsLocation documents = qt.QStandardPaths.writableLocation(documentsLocation) databaseDirectory = os.path.join(documents, slicer.app.applicationName+"DICOMDatabase") settings.setValue(slicer.dicomDatabaseDirectorySettingsKey, databaseDirectory) # Attempt to open the database. If it fails then user will have to configure it using DICOM module. databaseFileName = databaseDirectory + "/ctkDICOM.sql" slicer.dicomDatabase.openDatabase(databaseFileName) if slicer.dicomDatabase.isOpen: # There is an existing database at the current location if slicer.dicomDatabase.schemaVersionLoaded() != slicer.dicomDatabase.schemaVersion(): # Schema does not match, do not use it slicer.dicomDatabase.closeDatabase()
def __init__(self,parent, width=50,height=100): self.widget = qt.QWidget(parent) self.widget.setMinimumHeight(height) self.widget.setMinimumWidth(width) self.width = width self.height = height self.layout = qt.QVBoxLayout() self.widget.setLayout(self.layout) self.checkBoxByPlugin = {} settings = qt.QSettings() slicerPlugins = slicer.modules.dicomPlugins for pluginClass in slicer.modules.dicomPlugins: self.checkBoxByPlugin[pluginClass] = qt.QCheckBox(pluginClass) self.layout.addWidget(self.checkBoxByPlugin[pluginClass]) if settings.contains('DICOM/disabledPlugins/size'): size = settings.beginReadArray('DICOM/disabledPlugins') disabledPlugins = [] for i in xrange(size): settings.setArrayIndex(i) disabledPlugins.append(str(settings.allKeys()[0])) settings.endArray() for pluginClass in slicer.modules.dicomPlugins: if pluginClass in disabledPlugins: self.checkBoxByPlugin[pluginClass].checked = False else: # Activate plugins for the ones who are not in the disabled list # and also plugins installed with extensions self.checkBoxByPlugin[pluginClass].checked = True else: # All DICOM plugins would be enabled by default for pluginClass in slicer.modules.dicomPlugins: self.checkBoxByPlugin[pluginClass].checked = True
def __init__(self,database,fileToBeAddedCallback=None,fileAddedCallback=None): super(DICOMListener,self).__init__() self.dicomDatabase = database self.indexer = ctk.ctkDICOMIndexer() self.fileToBeAddedCallback = fileToBeAddedCallback self.fileAddedCallback = fileAddedCallback self.lastFileAdded = None settings = qt.QSettings() dir = settings.value('DatabaseDirectory') if not dir: raise( UserWarning('Database directory not set: cannot start DICOMListener') ) if not os.path.exists(dir): os.mkdir(dir) self.incomingDir = dir + "/incoming" if not os.path.exists(self.incomingDir): os.mkdir(self.incomingDir) self.port = settings.value('StoragePort') if not self.port: settings.setValue('StoragePort', '11112') self.port = settings.value('StoragePort')
def loadParametersFromSettings(self): settings = qt.QSettings() self.ui.pixelTypeComboBox.currentText = settings.value( 'RawImageGuess/pixelType') self.ui.endiannessComboBox.currentText = settings.value( 'RawImageGuess/endianness') self.ui.imageSkipSliderWidget.value = toLong( settings.value('RawImageGuess/headerSize', 0)) self.ui.imageSizeXSliderWidget.value = toLong( settings.value('RawImageGuess/sizeX', 200)) self.ui.imageSizeYSliderWidget.value = toLong( settings.value('RawImageGuess/sizeY', 200)) self.ui.imageSizeZSliderWidget.value = toLong( settings.value('RawImageGuess/sizeZ', 1)) self.ui.skipSlicesSliderWidget.value = toLong( settings.value('RawImageGuess/skipSlices', 0)) self.ui.imageSpacingXSliderWidget.value = float( settings.value('RawImageGuess/spacingX', 1.0)) self.ui.imageSpacingYSliderWidget.value = float( settings.value('RawImageGuess/spacingY', 1.0)) self.ui.imageSpacingZSliderWidget.value = float( settings.value('RawImageGuess/spacingZ', 1.0))
def saveParametersToSettings(self): settings = qt.QSettings() settings.setValue('RawImageGuess/pixelType', self.ui.pixelTypeComboBox.currentText) settings.setValue('RawImageGuess/endianness', self.ui.endiannessComboBox.currentText) settings.setValue('RawImageGuess/headerSize', self.ui.imageSkipSliderWidget.value) settings.setValue('RawImageGuess/sizeX', self.ui.imageSizeXSliderWidget.value) settings.setValue('RawImageGuess/sizeY', self.ui.imageSizeYSliderWidget.value) settings.setValue('RawImageGuess/sizeZ', self.ui.imageSizeZSliderWidget.value) settings.setValue('RawImageGuess/skipSlices', self.ui.skipSlicesSliderWidget.value) settings.setValue('RawImageGuess/spacingX', self.ui.imageSpacingXSliderWidget.value) settings.setValue('RawImageGuess/spacingY', self.ui.imageSpacingXSliderWidget.value) settings.setValue('RawImageGuess/spacingZ', self.ui.imageSpacingXSliderWidget.value)
def closeTemporaryDatabase(originalDatabaseDir, cleanup=True): """ Close temporary DICOM database and remove its directory if requested """ if slicer.dicomDatabase.isOpen: if cleanup: slicer.dicomDatabase.initializeDatabase() # TODO: The database files cannot be deleted even if the database is closed. # Not critical, as it will be empty, so will not take measurable disk space. # import shutil # databaseDir = os.path.split(slicer.dicomDatabase.databaseFilename)[0] # shutil.rmtree(databaseDir) # if os.access(databaseDir, os.F_OK): # logging.error('Failed to delete DICOM database ' + databaseDir) slicer.dicomDatabase.closeDatabase() else: logging.error('Unable to close DICOM database ' + slicer.dicomDatabase.databaseFilename) if originalDatabaseDir is None: # Only log debug if there was no original database, as it is a valid use case, # see openTemporaryDatabase logging.debug('No original database directory was specified') return True settings = qt.QSettings() settings.setValue(slicer.dicomDatabaseDirectorySettingsKey, originalDatabaseDir) # Attempt to re-open original database only if it exists if os.access(originalDatabaseDir, os.F_OK): success = openDatabase(originalDatabaseDir) if not success: logging.error('Unable to open DICOM database ' + originalDatabaseDir) return False return True
def performPostModuleDiscoveryTasks(self): """Since dicom plugins are discovered while the application is initialized, they may be found after the DICOM module itself if initialized. This method is tied to a singleShot that will be called once the event loop is read to start. """ if slicer.mrmlScene.GetTagByClassName( "vtkMRMLScriptedModuleNode") != 'ScriptedModule': slicer.mrmlScene.RegisterNodeClass(vtkMRMLScriptedModuleNode()) # initialize the dicom infrastructure slicer.dicomDatabase = None settings = qt.QSettings() # the dicom database is a global object for slicer if settings.contains('DatabaseDirectory'): databaseDirectory = settings.value('DatabaseDirectory') if databaseDirectory: slicer.dicomDatabase = ctk.ctkDICOMDatabase() slicer.dicomDatabase.openDatabase( databaseDirectory + "/ctkDICOM.sql", "SLICER") if not slicer.dicomDatabase.isOpen: # can't open the database, so prompt the user later if they enter module slicer.dicomDatabase = None else: self.startListener() if slicer.dicomDatabase: slicer.app.setDICOMDatabase(slicer.dicomDatabase) # set the dicom pre-cache tags once all plugin classes have been initialized DICOMLib.setDatabasePrecacheTags() if not slicer.app.commandOptions().noMainWindow: # add to the main app file menu self.addMenu() # add the settings options self.settingsPanel = DICOMSettingsPanel() slicer.app.settingsDialog().addPanel("DICOM", self.settingsPanel)
def __init__(self, parent=None): ScriptedLoadableModuleWidget.__init__(self, parent) settings = qt.QSettings() self.developerMode = settings.value('Developer/DeveloperMode').lower() == 'true' if not parent: self.parent = slicer.qMRMLWidget() self.parent.setLayout(qt.QVBoxLayout()) self.parent.setMRMLScene(slicer.mrmlScene) else: self.parent = parent self.layout = self.parent.layout() if not parent: self.setup() self.parent.show() self.priority = 2 self.calcinationType = 0 self.ThresholdMin = 130.0 self.ThresholdMax = 2000.0 self.MinimumLesionSize = 1 self.MaximumLesionSize = 2000 self.croppedVolumeNode = slicer.vtkMRMLScalarVolumeNode() self.threshImage = vtk.vtkImageData() self.marchingCubes = vtk.vtkDiscreteMarchingCubes() self.transformPolyData = vtk.vtkTransformPolyDataFilter() self.selectedLableList = [] self.labelScores = [] self.selectedLables = {} self.modelNodes = [] self.voxelVolume = 1 self.selectedRGB = [1,0,0] self.observerTags = [] self.xy = [] self.totalScore = 0
def validate(self): moduleCount = len(self.selectedModules) if moduleCount == 0: self.ui.buttonBox.button(qt.QDialogButtonBox.Yes).enabled = False self.ui.addToSearchPaths.enabled = False moduleCount = len(self._moduleItems) else: self.ui.buttonBox.button(qt.QDialogButtonBox.Yes).enabled = True self.ui.addToSearchPaths.enabled = True if moduleCount == 1: self.ui.addToSearchPaths.text = "Add selected module to search paths" else: self.ui.addToSearchPaths.text = "Add selected modules to search paths" # If developer mode is already enabled then don't even show the option settings = qt.QSettings() developerModeAlreadyEnabled = slicer.util.settingsValue('Developer/DeveloperMode', False, lambda v: v.lower()=='true') if developerModeAlreadyEnabled: self.ui.enableDeveloperMode.visible = False self.ui.enableDeveloperMode.checked = False
def __init__(self, epsilon=0.01): # Get the location and initialize the DICOM DB used by Reporting logic reportingLogic = slicer.modules.reporting.logic() settings = qt.QSettings() dbFileName = settings.value("DatabaseDirectory", "") if dbFileName == "": print( "DICOMSegmentationPlugin failed to initialize DICOM db location from settings" ) else: dbFileName = dbFileName + "/ctkDICOM.sql" if reportingLogic.InitializeDICOMDatabase(dbFileName): print("DICOMSegmentationPlugin initialized DICOM db OK") else: print('Failed to initialize DICOM database at ' + dbFileName) super(DICOMSegmentationPluginClass, self).__init__() self.loadType = "DICOMSegmentation" self.tags['seriesInstanceUID'] = "0020,000E" self.tags['seriesDescription'] = "0008,103E" self.tags['seriesNumber'] = "0020,0011" self.tags['modality'] = "0008,0060" self.tags['instanceUID'] = "0008,0018"
def saveServerUrl(self): self.updateMRMLFromGUI() # Save selected server URL settings = qt.QSettings() serverUrl = self.ui.serverComboBox.currentText settings.setValue("NVIDIA-AIAA/serverUrl", serverUrl) # Save current server URL to the top of history serverUrlHistory = settings.value("NVIDIA-AIAA/serverUrlHistory") if serverUrlHistory: serverUrlHistory = serverUrlHistory.split(";") else: serverUrlHistory = [] try: serverUrlHistory.remove(serverUrl) except ValueError: pass serverUrlHistory.insert(0, serverUrl) serverUrlHistory = serverUrlHistory[:10] # keep up to first 10 elements settings.setValue("NVIDIA-AIAA/serverUrlHistory", ";".join(serverUrlHistory)) self.updateServerUrlGUIFromSettings()
def addCatheterNameToConfig(self, cathName): nameList = self.getCatheterNameListFromConfig() if cathName == None or cathName == '': return 0 if (type(nameList) == list) and (cathName in nameList): # The name already exists in the config return 0 settings = qt.QSettings() setting = settings.value(self.moduleName + '/' + 'CathList') if type(setting) == tuple: setting = list(setting) setting.append(cathName) else: setting = [cathName] settings.setValue(self.moduleName + '/' + 'CathList', setting) return 1
def savePathsInApplicationSettings(self, beamNode): if beamNode is None: return ctcreateExecFilePath = self.scriptedEngine.parameter( beamNode, "CtcreateExecFilePath") if ctcreateExecFilePath != self.ctcreateExecFilePathDefault: qt.QSettings().setValue( 'OrthovoltageDoseEngine/CtcreateExecFilePath', ctcreateExecFilePath) ctcreateOutputPath = self.scriptedEngine.parameter( beamNode, "CtcreateOutputPath") if ctcreateOutputPath != self.ctcreateOutputPathDefault: qt.QSettings().setValue( 'OrthovoltageDoseEngine/CtcreateOutputPath', ctcreateOutputPath) phaseSpaceFilePath = self.scriptedEngine.parameter( beamNode, "PhaseSpaceFilePath") if phaseSpaceFilePath != self.phaseSpaceFilePathDefault: qt.QSettings().setValue( 'OrthovoltageDoseEngine/PhaseSpaceFilePath', phaseSpaceFilePath) dosxyznrcFolderPath = self.scriptedEngine.parameter( beamNode, "DosxyznrcFolderPath") if dosxyznrcFolderPath != self.dosxyznrcFolderPathDefault: qt.QSettings().setValue( 'OrthovoltageDoseEngine/DosxyznrcFolderPath', dosxyznrcFolderPath) dosxyznrcExecFilePath = self.scriptedEngine.parameter( beamNode, "DosxyznrcExecFilePath") if dosxyznrcExecFilePath != self.dosxyznrcExecFilePathDefault: qt.QSettings().setValue( 'OrthovoltageDoseEngine/DosxyznrcExecFilePath', dosxyznrcExecFilePath) pegsFilePath = self.scriptedEngine.parameter(beamNode, "PegsFilePath") if pegsFilePath != self.pegsFilePathDefault: qt.QSettings().setValue('OrthovoltageDoseEngine/PegsFilePath', pegsFilePath)
def onCondaDirectorySelected(self): self.condaDirectoryPath = self.condaDirectoryPathSelector.directory self.logic.setPathToCondaExecutable(self.condaDirectoryPath) settings = qt.QSettings() settings.setValue("AIGT/Anaconda", self.condaDirectoryPath)
def onFontSizeSpinBox(self): self.fontSize = self.fontSizeSpinBox.value settings = qt.QSettings() settings.setValue('DataProbe/sliceViewAnnotations.fontSize', self.fontSize) self.updateSliceViewFromGUI()
def examine(self, fileLists): """ Returns a list of DICOMLoadable instances corresponding to ways of interpreting the fileLists parameter. Top-level examine() calls various individual strategies implemented in examineFiles*(). """ loadables = [] allfiles = [] for files in fileLists: loadables += self.examineFiles(files) # this strategy sorts the files into groups loadables += self.examineFilesIPPAcqTime(files) allfiles += files # here all files are lumped into one list for the situations when # individual frames should be parsed from series loadables += self.examineFilesMultiseries(allfiles) if len(allfiles) > len(files): # only examineFilesIPPAcqTime again if there are multiple file groups loadables += self.examineFilesIPPAcqTime(allfiles) # this strategy sorts the files into groups loadables += self.examineFilesIPPInstanceNumber(allfiles) # If Sequences module is available then duplicate all the loadables # for loading them as volume sequence. # A slightly higher confidence value is set for volume sequence loadables, # therefore by default data will be loaded as volume sequence. if hasattr(slicer.modules, 'sequences'): seqLoadables = [] for loadable in loadables: seqLoadable = DICOMLib.DICOMLoadable() seqLoadable.files = loadable.files seqLoadable.tooltip = loadable.tooltip.replace( ' frames MultiVolume by ', ' frames Volume Sequence by ') seqLoadable.name = loadable.name.replace( ' frames MultiVolume by ', ' frames Volume Sequence by ') seqLoadable.multivolume = loadable.multivolume seqLoadable.selected = loadable.selected seqLoadable.confidence = loadable.confidence seqLoadable.loadAsVolumeSequence = True seqLoadables.append(seqLoadable) # Among all selected loadables, the ones that are listed first will be selected by default, # therefore we need to prepend loadables if sequence format is preferred. # Determine from settings loading into sequence node should have higher confidence (selected by default). settings = qt.QSettings() sequenceFormatPreferred = (settings.value( "DICOM/PreferredMultiVolumeImportFormat", "default") == "sequence") if sequenceFormatPreferred: # prepend loadables[0:0] = seqLoadables else: # append loadables += seqLoadables return loadables
def getLoadablesFromFileLists(self, fileLists): """Take list of file lists, return loadables by plugin dictionary """ loadablesByPlugin = {} loadEnabled = False # Get selected plugins from application settings # Settings are filled in DICOMWidget using DICOMPluginSelector settings = qt.QSettings() selectedPlugins = [] if settings.contains('DICOM/disabledPlugins/size'): size = settings.beginReadArray('DICOM/disabledPlugins') disabledPlugins = [] for i in range(size): settings.setArrayIndex(i) disabledPlugins.append(str(settings.allKeys()[0])) settings.endArray() for pluginClass in slicer.modules.dicomPlugins: if pluginClass not in disabledPlugins: selectedPlugins.append(pluginClass) else: # All DICOM plugins would be enabled by default for pluginClass in slicer.modules.dicomPlugins: selectedPlugins.append(pluginClass) allFileCount = missingFileCount = 0 for fileList in fileLists: for filePath in fileList: allFileCount += 1 if not os.path.exists(filePath): missingFileCount += 1 messages = [] if missingFileCount > 0: messages.append( "Warning: %d of %d selected files listed in the database cannot be found on disk." % (missingFileCount, allFileCount)) if missingFileCount < allFileCount: progressDialog = slicer.util.createProgressDialog(parent=self, value=0, maximum=100) def progressCallback(progressDialog, progressLabel, progressValue): progressDialog.labelText = '\nChecking %s' % progressLabel slicer.app.processEvents() progressDialog.setValue(progressValue) slicer.app.processEvents() cancelled = progressDialog.wasCanceled return cancelled loadablesByPlugin, loadEnabled = DICOMLib.getLoadablesFromFileLists( fileLists, selectedPlugins, messages, lambda progressLabel, progressValue, progressDialog=progressDialog: progressCallback( progressDialog, progressLabel, progressValue), self.pluginInstances) progressDialog.close() if messages: slicer.util.warningDisplay( "Warning: %s\n\nSee python console for error message." % ' '.join(messages), windowTitle="DICOM", parent=self) return loadablesByPlugin, loadEnabled
def onRunListenerAtStart(self, toggled): settings = qt.QSettings() settings.setValue('DICOM/RunListenerAtStart', toggled)
def setup(self): ScriptedLoadableModuleWidget.setup(self) # This module is often used in developer mode, therefore # collapse reload & test section by default. if hasattr(self, "reloadCollapsibleButton"): self.reloadCollapsibleButton.collapsed = True self.dragAndDropEventFilter = DICOMLoadingByDragAndDropEventFilter() globals()['d'] = self self.testingServer = None self.browserWidget = None self.directoryButton = None # Load widget from .ui file (created by Qt Designer) uiWidget = slicer.util.loadUI(self.resourcePath('UI/DICOM.ui')) self.layout.addWidget(uiWidget) self.ui = slicer.util.childWidgetVariables(uiWidget) self.browserWidget = DICOMLib.SlicerDICOMBrowser() self.browserWidget.objectName = 'SlicerDICOMBrowser' slicer.modules.DICOMInstance.setBrowserWidgetInDICOMLayout( self.browserWidget) layoutManager = slicer.app.layoutManager() if layoutManager is not None: layoutManager.layoutChanged.connect(self.onLayoutChanged) viewArrangement = slicer.app.layoutManager().layoutLogic( ).GetLayoutNode().GetViewArrangement() self.ui.showBrowserButton.checked = ( viewArrangement == slicer.vtkMRMLLayoutNode.SlicerLayoutDicomBrowserView) # connect to the 'Show DICOM Browser' button self.ui.showBrowserButton.connect('clicked()', self.toggleBrowserWidget) self.ui.importButton.connect('clicked()', self.browserWidget.dicomBrowser, 'openImportDialog()') self.ui.subjectHierarchyTree.setMRMLScene(slicer.mrmlScene) self.ui.subjectHierarchyTree.currentItemChanged.connect( self.onCurrentItemChanged) self.ui.subjectHierarchyTree.currentItemModified.connect( self.onCurrentItemModified) self.subjectHierarchyCurrentVisibility = False self.ui.subjectHierarchyTree.setColumnHidden( self.ui.subjectHierarchyTree.model().idColumn, True) # # DICOM networking # self.ui.networkingFrame.collapsed = True self.ui.queryServerButton.connect('clicked()', self.browserWidget.dicomBrowser, "openQueryDialog()") self.ui.toggleListener.connect('toggled(bool)', self.onToggleListener) settings = qt.QSettings() self.ui.runListenerAtStart.checked = settingsValue( 'DICOM/RunListenerAtStart', False, converter=toBool) self.ui.runListenerAtStart.connect('toggled(bool)', self.onRunListenerAtStart) # Testing server - not exposed (used for development) self.toggleServer = qt.QPushButton("Start Testing Server") self.ui.networkingFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.ui.networkingFrame.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() # # Browser settings # self.ui.browserSettingsFrame.collapsed = True self.updateDatabaseDirectoryFromBrowser( self.browserWidget.dicomBrowser.databaseDirectory) # Synchronize database selection between browser and this widget self.ui.directoryButton.directoryChanged.connect( self.updateDatabaseDirectoryFromWidget) self.browserWidget.dicomBrowser.databaseDirectoryChanged.connect( self.updateDatabaseDirectoryFromBrowser) self.ui.browserAutoHideCheckBox.checked = not settingsValue( 'DICOM/BrowserPersistent', False, converter=toBool) self.ui.browserAutoHideCheckBox.stateChanged.connect( self.onBrowserAutoHideStateChanged) self.ui.repairDatabaseButton.connect('clicked()', self.browserWidget.dicomBrowser, "onRepairAction()") self.ui.clearDatabaseButton.connect('clicked()', self.onClearDatabase) # 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.onOpenBrowserWidget) except IndexError: logging.error( 'Could not connect to the main window DICOM button') self.databaseRefreshRequestTimer = qt.QTimer() self.databaseRefreshRequestTimer.setSingleShot(True) # If not receiving new file for 2 seconds then a database update is triggered. self.databaseRefreshRequestTimer.setInterval(2000) self.databaseRefreshRequestTimer.connect('timeout()', self.requestDatabaseRefresh)
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 getCustomElastixBinDir(self): settings = qt.QSettings() if settings.contains(self.customElastixBinDirSettingsKey): return slicer.util.toVTKString(settings.value(self.customElastixBinDirSettingsKey)) return ''
def onRunListenerAtStart(self): settings = qt.QSettings() settings.setValue('DICOM/RunListenerAtStart', self.runListenerAtStart.checked)
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 = self.getSavedDICOMDetailsWidgetType()() # 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.onOpenDetailsPopup) # 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.onOpenDetailsPopup) 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) self.requestUpdateRecentActivity() # Add spacer to layout self.layout.addStretch(1)
def setDICOMDetailsWidgetType(widgetType): if not widgetType in DICOMWidget.getAvailableWidgetTypes(): raise ValueError("Widget type '%s' for DICOMDetails does not exist" % widgetType) else: qt.QSettings().setValue('DICOM/BrowserWidgetType', widgetType)
def getSortedImageFiles(filePaths, epsilon=0.01): """ Sort DICOM image files in increasing slice order (IS direction) corresponding to a series Use the first file to get the ImageOrientationPatient for the series and calculate the scan direction (assumed to be perpendicular to the acquisition plane) epsilon: Maximum difference in distance between slices to consider spacing uniform """ warningText = '' if len(filePaths) == 0: return filePaths, [], warningText # Define DICOM tags used in this function tags = {} tags['position'] = "0020,0032" tags['orientation'] = "0020,0037" tags['numberOfFrames'] = "0028,0008" tags['seriesUID'] = "0020,000E" seriesUID = slicer.dicomDatabase.fileValue(filePaths[0], tags['seriesUID']) if slicer.dicomDatabase.fileValue(filePaths[0], tags['numberOfFrames']) != "": warningText += "Multi-frame image. If slice orientation or spacing is non-uniform then the image may be displayed incorrectly. Use with caution.\n" # Make sure first file contains valid geometry ref = {} for tag in [tags['position'], tags['orientation']]: value = slicer.dicomDatabase.fileValue(filePaths[0], tag) if not value or value == "": warningText += "Reference image in series does not contain geometry information. Please use caution.\n" return filePaths, [], warningText ref[tag] = value # Determine out-of-plane direction for first slice import numpy as np sliceAxes = [float(zz) for zz in ref[tags['orientation']].split('\\')] x = np.array(sliceAxes[:3]) y = np.array(sliceAxes[3:]) scanAxis = np.cross(x,y) scanOrigin = np.array([float(zz) for zz in ref[tags['position']].split('\\')]) # For each file in series, calculate the distance along the scan axis, sort files by this sortList = [] missingGeometry = False for file in filePaths: positionStr = slicer.dicomDatabase.fileValue(file,tags['position']) orientationStr = slicer.dicomDatabase.fileValue(file,tags['orientation']) if not positionStr or positionStr == "" or not orientationStr or orientationStr == "": missingGeometry = True break position = np.array([float(zz) for zz in positionStr.split('\\')]) vec = position - scanOrigin dist = vec.dot(scanAxis) sortList.append((file, dist)) if missingGeometry: warningText += "One or more images is missing geometry information in series. Please use caution.\n" return filePaths, [], warningText # Sort files names by distance from reference slice sortedFiles = sorted(sortList, key=lambda x: x[1]) files = [] distances = {} for file,dist in sortedFiles: files.append(file) distances[file] = dist # Get acquisition geometry regularization setting value settings = qt.QSettings() acquisitionGeometryRegularizationEnabled = (settings.value("DICOM/ScalarVolume/AcquisitionGeometryRegularization", "default") == "transform") # Confirm equal spacing between slices # - use variable 'epsilon' to determine the tolerance spaceWarnings = 0 if len(files) > 1: file0 = files[0] file1 = files[1] dist0 = distances[file0] dist1 = distances[file1] spacing0 = dist1 - dist0 n = 1 for fileN in files[1:]: fileNminus1 = files[n-1] distN = distances[fileN] distNminus1 = distances[fileNminus1] spacingN = distN - distNminus1 spaceError = spacingN - spacing0 if abs(spaceError) > epsilon: spaceWarnings += 1 warningText += "Images are not equally spaced (a difference of %g vs %g in spacings was detected)." % (spaceError, spacing0) if acquisitionGeometryRegularizationEnabled: warningText += " Slicer will apply a transform to this series trying to regularize the volume. Please use caution.\n" else: warningText += (" If loaded image appears distorted, enable 'Acquisition geometry regularization'" " in Application settings / DICOM / DICOMScalarVolumePlugin. Please use caution.\n") break n += 1 if spaceWarnings != 0: logging.warning("Geometric issues were found with %d of the series. Please use caution.\n" % spaceWarnings) return files, distances, warningText
def allowLoadingByTime(self): settings = qt.QSettings() return (int( settings.value("DICOM/ScalarVolume/AllowLoadingByTime", "0")) != 0)