import os import unittest import vtk, qt, ctk, slicer from slicer.ScriptedLoadableModule import * import logging import slicerNetwork import json import loadPatient # # dataExplorer # class dataExplorer(ScriptedLoadableModule): """Uses ScriptedLoadableModule base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ def __init__(self, parent): ScriptedLoadableModule.__init__(self, parent) self.parent.title = "dataExplorer" # TODO make this more human readable by adding spaces self.parent.categories = ["EMBRACE"] self.parent.dependencies = [] self.parent.contributors = ["Andrej Studen (University of Ljubljana)"] # replace with "Firstname Lastname (Organization)" self.parent.helpText = """ This is an example of scripted loadable module bundled in an extension. It performs a simple thresholding on the input volume and optionally captures a screenshot. """ self.parent.helpText += self.getDefaultModuleDocumentationLink() self.parent.acknowledgementText = """ This extension developed within Medical Physics research programe of ARRS """ # replace with organization, grant and thanks. # # dataExplorerWidget # class dataExplorerWidget(ScriptedLoadableModuleWidget): """Uses ScriptedLoadableModuleWidget base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ def setup(self): ScriptedLoadableModuleWidget.setup(self) self.loadPatientLogic=loadPatient.loadPatientLogic(self) # Instantiate and connect widgets ... # # Parameters Area # basePath=os.path.join(os.path.expanduser('~'),'.EMBRACE') #'D:\\Sw\\src\\EMBRACE' netConfig=os.path.join(basePath,'onko-nix.json') self.sNet=slicerNetwork.labkeyURIHandler() self.sNet.parseConfig(netConfig) self.sNet.initRemote() self.loadPatientLogic.setURIHandler(self.sNet) self.project="EMBRACE/Studija" self.dataset="ImagingVisitsManaged" datasetCollapsibleButton = ctk.ctkCollapsibleButton() datasetCollapsibleButton.text = "Dataset" self.layout.addWidget(datasetCollapsibleButton) # Layout within the dummy collapsible button datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton) self.datasetButton=qt.QPushButton("Load") self.datasetButton.connect('clicked(bool)',self.onDatasetLoadButtonClicked) datasetFormLayout.addRow("Data:",self.datasetButton) self.patientId=qt.QLineEdit("LJU004") datasetFormLayout.addRow("Patient ID:",self.patientId) loadCTButton=qt.QPushButton("CT") loadCTButton.clicked.connect(self.onLoadCTButtonClicked) datasetFormLayout.addRow("Load:",loadCTButton) loadCTRSButton=qt.QPushButton("CT-RS") loadCTRSButton.clicked.connect(self.onLoadCTRSButtonClicked) datasetFormLayout.addRow("Load:",loadCTRSButton) loadDMRButton=qt.QPushButton("DMR") loadDMRButton.clicked.connect(self.onLoadDMRButtonClicked) datasetFormLayout.addRow("Load:",loadDMRButton) dataCollapsibleButton = ctk.ctkCollapsibleButton() dataCollapsibleButton.text = "Data" self.layout.addWidget(dataCollapsibleButton) # Layout within the dummy collapsible button dataFormLayout = qt.QVBoxLayout(dataCollapsibleButton) self.data=qt.QTableWidget(3,3) dataFormLayout.addWidget(self.data) patientsCollapsibleButton = ctk.ctkCollapsibleButton() patientsCollapsibleButton.text = "Patients" self.layout.addWidget(patientsCollapsibleButton) # Layout within the dummy collapsible button self.patientsFormLayout = qt.QVBoxLayout(patientsCollapsibleButton) self.signalMapper=qt.QSignalMapper() self.signalMapper.connect(self.signalMapper, qt.SIGNAL("mapped(const QString &)"), self.onPatientButtonClicked) def cleanup(self): pass def onDatasetLoadButtonClicked(self): ds=self.sNet.loadDataset(self.project,self.dataset) #loaded a JSON object -> convert to suitable display columns=ds['columnModel'] self.data.setColumnCount(len(columns)) self.data.setRowCount(len(ds['rows'])) columnNames=[f['header'] for f in columns] self.data.setHorizontalHeaderLabels(columnNames) columnID=[f['dataIndex'] for f in columns] insertRowID=0 for row in ds['rows']: insertColumnID=0 for c in columnID: item=qt.QTableWidgetItem(str(row[c])) self.data.setItem(insertRowID,insertColumnID,item) insertColumnID+=1 insertRowID+=1 #clear patient list while self.patientsFormLayout.count(): child=self.patientsFormLayout.takeAt(0) if child.widget(): self.signalMapper.removeMapping(child.widget()) child.widget().deleteLater() #populate patient list patientList=[f['EMBRACE_ID'] for f in ds['rows']] #remove duplicates patientSet=set(patientList) for p in patientSet: patientButton=qt.QPushButton(p) patientButton.connect(patientButton, qt.SIGNAL("clicked()"), self.signalMapper, qt.SLOT("map()")) self.patientsFormLayout.addWidget(patientButton) self.signalMapper.setMapping(patientButton,p) def onPatientButtonClicked(self, label): print "onPatientButtonClicked() for {}".format(label) self.loadPatientLogic.load(label) def onLoadCTButtonClicked(self): self.CT=self.loadPatientLogic.loadCT(self.patientId.text) self.CT[0]['node'].SetName(self.patientId.text+"_CT") def onLoadCTRSButtonClicked(self): self.CTRS=self.loadPatientLogic.loadCTRS(self.patientId.text) def onLoadDMRButtonClicked(self): self.DMR=self.loadPatientLogic.loadDMR(self.patientId.text) self.DMR[0]['node'].SetName(self.patientId.text+"_DMR") # # dataExplorerLogic # class dataExplorerLogic(ScriptedLoadableModuleLogic): """This class should implement all the actual computation done by your module. The interface should be such that other python code can import this class and make use of the functionality without requiring an instance of the Widget. Uses ScriptedLoadableModuleLogic base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ def hasImageData(self,volumeNode): """This is an example logic method that returns true if the passed in volume node has valid image data """ if not volumeNode: logging.debug('hasImageData failed: no volume node') return False if volumeNode.GetImageData() is None: logging.debug('hasImageData failed: no image data in volume node') return False return True def isValidInputOutputData(self, inputVolumeNode, outputVolumeNode): """Validates if the output is not the same as input """ if not inputVolumeNode: logging.debug('isValidInputOutputData failed: no input volume node defined') return False if not outputVolumeNode: logging.debug('isValidInputOutputData failed: no output volume node defined') return False if inputVolumeNode.GetID()==outputVolumeNode.GetID(): logging.debug('isValidInputOutputData failed: input and output volume is the same. Create a new volume for output to avoid this error.') return False return True def takeScreenshot(self,name,description,type=-1): # show the message even if not taking a screen shot slicer.util.delayDisplay('Take screenshot: '+description+'.\nResult is available in the Annotations module.', 3000) lm = slicer.app.layoutManager() # switch on the type to get the requested window widget = 0 if type == slicer.qMRMLScreenShotDialog.FullLayout: # full layout widget = lm.viewport() elif type == slicer.qMRMLScreenShotDialog.ThreeD: # just the 3D window widget = lm.threeDWidget(0).threeDView() elif type == slicer.qMRMLScreenShotDialog.Red: # red slice window widget = lm.sliceWidget("Red") elif type == slicer.qMRMLScreenShotDialog.Yellow: # yellow slice window widget = lm.sliceWidget("Yellow") elif type == slicer.qMRMLScreenShotDialog.Green: # green slice window widget = lm.sliceWidget("Green") else: # default to using the full window widget = slicer.util.mainWindow() # reset the type so that the node is set correctly type = slicer.qMRMLScreenShotDialog.FullLayout # grab and convert to vtk image data qimage = ctk.ctkWidgetsUtils.grabWidget(widget) imageData = vtk.vtkImageData() slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData) annotationLogic = slicer.modules.annotations.logic() annotationLogic.CreateSnapShot(name, description, type, 1, imageData) def run(self, inputVolume, outputVolume, imageThreshold, enableScreenshots=0): """ Run the actual algorithm """ if not self.isValidInputOutputData(inputVolume, outputVolume): slicer.util.errorDisplay('Input volume is the same as output volume. Choose a different output volume.') return False logging.info('Processing started') # Compute the thresholded output volume using the Threshold Scalar Volume CLI module cliParams = {'InputVolume': inputVolume.GetID(), 'OutputVolume': outputVolume.GetID(), 'ThresholdValue' : imageThreshold, 'ThresholdType' : 'Above'} cliNode = slicer.cli.run(slicer.modules.thresholdscalarvolume, None, cliParams, wait_for_completion=True) # Capture screenshot if enableScreenshots: self.takeScreenshot('dataExplorerTest-Start','MyScreenshot',-1) logging.info('Processing completed') return True class dataExplorerTest(ScriptedLoadableModuleTest): """ This is the test case for your scripted module. Uses ScriptedLoadableModuleTest base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ def setUp(self): """ Do whatever is needed to reset the state - typically a scene clear will be enough. """ slicer.mrmlScene.Clear(0) def runTest(self): """Run as few or as many tests as needed here. """ self.setUp() self.test_dataExplorer1() def test_dataExplorer1(self): """ Ideally you should have several levels of tests. At the lowest level tests should exercise the functionality of the logic with different inputs (both valid and invalid). At higher levels your tests should emulate the way the user would interact with your code and confirm that it still works the way you intended. One of the most important features of the tests is that it should alert other developers when their changes will have an impact on the behavior of your module. For example, if a developer removes a feature that you depend on, your test should break so they know that the feature is needed. """ self.delayDisplay("Starting the test") # # first, get some data # import urllib downloads = ( ('http://slicer.kitware.com/midas3/download?items=5767', 'FA.nrrd', slicer.util.loadVolume), ) for url,name,loader in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: logging.info('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) if loader: logging.info('Loading %s...' % (name,)) loader(filePath) self.delayDisplay('Finished with download and loading') volumeNode = slicer.util.getNode(pattern="FA") logic = dataExplorerLogic() self.assertIsNotNone( logic.hasImageData(volumeNode) ) self.delayDisplay('Test passed!')