def createUserInterface(self): """ This step is mostly empty. A volume rendering threshold is added to be useful. """ self.__layout = self.__parent.createUserInterface() self.__threshRange = slicer.qMRMLRangeWidget() self.__threshRange.decimals = 0 self.__threshRange.singleStep = 1 self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) qt.QTimer.singleShot(0, self.killButton) ThreshGroupBox = qt.QGroupBox() ThreshGroupBox.setTitle('3D Visualization Intensity Threshold') ThreshGroupBoxLayout = qt.QFormLayout(ThreshGroupBox) ThreshGroupBoxLayout.addRow(self.__threshRange) self.__layout.addRow(ThreshGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent) self.__editorWidget.setup() self.__layout.addRow(editorWidgetParent) self.hideUnwantedEditorUIElements() RestartGroupBox = qt.QGroupBox() RestartGroupBox.setTitle('Restart') RestartGroupBoxLayout = qt.QFormLayout(RestartGroupBox) self.__RestartButton = qt.QPushButton('Return to Step 1') RestartGroupBoxLayout.addRow(self.__RestartButton) self.__RemoveCroppedSubtractionMap = qt.QCheckBox() self.__RemoveCroppedSubtractionMap.checked = True self.__RemoveCroppedSubtractionMap.setToolTip( "Delete the cropped version of your subtaction map.") RestartGroupBoxLayout.addRow("Delete cropped subtraction map: ", self.__RemoveCroppedSubtractionMap) self.__RemoveFullSubtracitonMap = qt.QCheckBox() self.__RemoveFullSubtracitonMap.checked = True self.__RemoveFullSubtracitonMap.setToolTip( "Delete the full version of your subtaction map.") RestartGroupBoxLayout.addRow("Delete full subtraction map: ", self.__RemoveFullSubtracitonMap) self.__RemoveROI = qt.QCheckBox() self.__RemoveROI.checked = False self.__RemoveROI.setToolTip( "Delete the ROI resulting from your subtaction map.") RestartGroupBoxLayout.addRow("Delete ROI: ", self.__RemoveROI) # self.__RestartButton.setEnabled(0) self.__RestartButton.connect('clicked()', self.Restart) self.__RestartActivated = True self.__layout.addRow(RestartGroupBox)
def setupEditorWidget(self): editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent) self.editorWidget.setup() self.segmentationGroupBoxLayout.addWidget(self.editorWidget.parent)
def __init__(self, parent=None, showVolumesFrame=False, activeTools=("DefaultTool", "DrawEffect", "RectangleEffect", "EraseLabel", "PreviousCheckPoint", "NextCheckPoint")): """Constructor. Just invokes the parent's constructor""" self.activeTools = activeTools # self.__masterVolume__ = scalarNode # self.__labelmapVolume__ = labelmapNode EditorWidget.__init__(self, parent, showVolumesFrame)
def __init__(self, parent=None, showVolumesFrame=True, activeTools=("DefaultTool", "PaintEffect", "DrawEffect", "LevelTracingEffect", "RectangleEffect", "EraseLabel", "PreviousCheckPoint", "NextCheckPoint")): """Constructor. Just invokes the parent's constructor""" self.activeTools = activeTools EditorWidget.__init__(self, parent, showVolumesFrame)
def createUserInterface(self): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #endoSegmentButton = qt.QPushButton('Open Slicer Editor tool') #self.__layout.addRow(endoSegmentButton) #endoSegmentButton.connect('clicked()', self.startEditor) #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) self.updateParameters(self.parameterNode()) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True) self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() ##TODO: How to set the required merge node? wallCleanupSegLabel = qt.QLabel('Final Wall Segmentation Image:') self.__wallCleanupSegSelector = slicer.qMRMLNodeComboBox() self.__wallCleanupSegSelector.toolTip = "Choose the final wall segmentation label image." self.__wallCleanupSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__wallCleanupSegSelector.setMRMLScene(slicer.mrmlScene) self.__wallCleanupSegSelector.addEnabled = 0 self.__layout.addRow(wallCleanupSegLabel, self.__wallCleanupSegSelector) # Hide unnecessary button findChildren(text='AutomaticLeft*')[0].hide()
def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter()
def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() endoSegLabel = qt.QLabel( 'Endo Segmentation Image:' ) self.__endoSegSelector = slicer.qMRMLNodeComboBox() self.__endoSegSelector.toolTip = "Choose the endo segmentation label image." self.__endoSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__endoSegSelector.setMRMLScene(slicer.mrmlScene) self.__endoSegSelector.addEnabled = 0 self.__layout.addRow( endoSegLabel, self.__endoSegSelector )
def test_ThresholdThreading(self): """ Test for issue 2329, using the slicer EditorLib components in outside of the editor. """ # # first, get some sample data # import SampleData sampleDataLogic = SampleData.SampleDataLogic() head = sampleDataLogic.downloadMRHead() # # create a label map and set it for editing # volumesLogic = slicer.modules.volumes.logic() # AF: intentionally use the naming convention that does not match the one # used by Editor headLabel = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetActiveVolumeID( head.GetID() ) selectionNode.SetActiveLabelVolumeID( headLabel.GetID() ) slicer.app.applicationLogic().PropagateVolumeSelection(0) # # Instantiate a new widget, and add Editor widget to it # from Editor import EditorWidget editorWidget = EditorWidget(showVolumesFrame=False)
def setupEditorWidget(self): self.editorWidgetParent = slicer.qMRMLWidget() self.editorWidgetParent.setLayout(qt.QVBoxLayout()) self.editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editUtil = EditorLib.EditUtil.EditUtil() self.editorWidget = EditorWidget(parent=self.editorWidgetParent, showVolumesFrame=False) self.editorWidget.setup() self.editorParameterNode = self.editUtil.getParameterNode()
def createUserInterface(self): """ This UI takes advantage of a pre-built slicer thresholding widget. """ self.__layout = self.__parent.createUserInterface() step_label = qt.QLabel( """Automatic segmentation with deep learning is not available in this demo, due to lack of computing resources. You may however create a segmentation using 3D Slicer's editor module in this step.""" ) step_label.setWordWrap(True) self.__informationGroupBox = qt.QGroupBox() self.__informationGroupBox.setTitle('Information') self.__informationGroupBoxLayout = qt.QFormLayout( self.__informationGroupBox) self.__informationGroupBoxLayout.addRow(step_label) self.__layout.addRow(self.__informationGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.EditorWidget = EditorWidget(parent=editorWidgetParent) self.EditorWidget.setup() self.__layout.addRow(editorWidgetParent) # self.__thresholdGroupBox = qt.QGroupBox() # self.__thresholdGroupBox.setTitle('Threshold Range') # self.__thresholdGroupBoxLayout = qt.QFormLayout(self.__thresholdGroupBox) # threshLabel = qt.QLabel('Select Intensity Range:') # threshLabel.alignment = 4 # self.__threshRange = slicer.qMRMLRangeWidget() # self.__threshRange.decimals = 0 # self.__threshRange.singleStep = 1 # self.__thresholdGroupBoxLayout.addRow(threshLabel) # self.__thresholdGroupBoxLayout.addRow(self.__threshRange) # self.__layout.addRow(self.__thresholdGroupBox) # self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) qt.QTimer.singleShot(0, self.killButton)
def createUserInterface(self): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True) self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() endoSegLabel = qt.QLabel('Endo Segmentation Image:') self.__endoSegSelector = slicer.qMRMLNodeComboBox() self.__endoSegSelector.toolTip = "Choose the endo segmentation label image." self.__endoSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__endoSegSelector.setMRMLScene(slicer.mrmlScene) self.__endoSegSelector.addEnabled = 0 self.__layout.addRow(endoSegLabel, self.__endoSegSelector)
def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #endoSegmentButton = qt.QPushButton('Open Slicer Editor tool') #self.__layout.addRow(endoSegmentButton) #endoSegmentButton.connect('clicked()', self.startEditor) #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) self.updateParameters(self.parameterNode()) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() ##TODO: How to set the required merge node? wallCleanupSegLabel = qt.QLabel( 'Final Wall Segmentation Image:' ) self.__wallCleanupSegSelector = slicer.qMRMLNodeComboBox() self.__wallCleanupSegSelector.toolTip = "Choose the final wall segmentation label image." self.__wallCleanupSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__wallCleanupSegSelector.setMRMLScene(slicer.mrmlScene) self.__wallCleanupSegSelector.addEnabled = 0 self.__layout.addRow( wallCleanupSegLabel, self.__wallCleanupSegSelector ) # Hide unnecessary button findChildren( text = 'AutomaticLeft*')[0].hide()
class LASegmentationWorkflowEndoSegmentationStep( LASegmentationWorkflowStep ) : def __init__( self, stepid ): self.initialize( stepid ) self.setName( '3. Segmentation of endocardium' ) self.setDescription( 'Segment the endocardium using the Editor tools.' ) self.__parent = super( LASegmentationWorkflowEndoSegmentationStep, self ) def killButton(self): # Hide unneccesary button bl = slicer.util.findChildren(text='LAEndo*') if len(bl): bl[0].hide() def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() #def startEditor(self): # from Editor import EditorWidget # editorWidget = EditorWidget(showVolumesFrame=True) def validate( self, desiredBranchId ): self.__parent.validate( desiredBranchId ) if endoSegVolume != None : self.__parent.validationSucceeded(desiredBranchId) else: self.__parent.validationFailed(desiredBranchId, 'Error','Endo segmentation step did not complete successfully.') def onEntry(self, comingFrom, transitionType): super(LASegmentationWorkflowEndoSegmentationStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def onExit(self, goingTo, transitionType): pNode = self.parameterNode() self.editorWidget.exit() super(LASegmentationWorkflowEndoSegmentationStep, self).onExit(goingTo, transitionType)
def setup(self): EditorWidget.setup(self) self.infoIconLabel.setVisible(False) self.segmentEditorLabel.setVisible(False)
class CMRToolkitWizardWallCleanupStep(CMRToolkitWizardStep): def __init__(self, stepid): self.initialize(stepid) self.setName('5. Edit Wall Segmentation') self.setDescription( 'Set the wall segmentation image from the previous step as the merge volume in the Slicer Editor. Remove the pulmonary veins from the wall segmentation using the Slicer Editor erase tool and select the final label image to proceed.' ) self.__parent = super(CMRToolkitWizardWallCleanupStep, self) def killButton(self): # Hide unneccesary button bl = slicer.util.findChildren(text='AutomaticLeft*') if len(bl): bl[0].hide() def createUserInterface(self): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #endoSegmentButton = qt.QPushButton('Open Slicer Editor tool') #self.__layout.addRow(endoSegmentButton) #endoSegmentButton.connect('clicked()', self.startEditor) #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) self.updateParameters(self.parameterNode()) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True) self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() ##TODO: How to set the required merge node? wallCleanupSegLabel = qt.QLabel('Final Wall Segmentation Image:') self.__wallCleanupSegSelector = slicer.qMRMLNodeComboBox() self.__wallCleanupSegSelector.toolTip = "Choose the final wall segmentation label image." self.__wallCleanupSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__wallCleanupSegSelector.setMRMLScene(slicer.mrmlScene) self.__wallCleanupSegSelector.addEnabled = 0 self.__layout.addRow(wallCleanupSegLabel, self.__wallCleanupSegSelector) # Hide unnecessary button findChildren(text='AutomaticLeft*')[0].hide() def validate(self, desiredBranchId): self.__parent.validate(desiredBranchId) wallCleanupSegVolume = self.__wallCleanupSegSelector.currentNode() if wallCleanupSegVolume != None: wallCleanupSegID = wallCleanupSegVolume.GetID() pNode = self.parameterNode() pNode.SetParameter('wallCleanupSegID', wallCleanupSegID) Helper.SetLabelVolume(wallCleanupSegVolume.GetID()) self.__parent.validationSucceeded(desiredBranchId) else: self.__parent.validationFailed( desiredBranchId, 'Error', 'Please select the wall segmentation image to proceed.') def onEntry(self, comingFrom, transitionType): super(CMRToolkitWizardWallCleanupStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) ## TODO: Why does editor effect continue to the next workflow step? def onExit(self, goingTo, transitionType): pNode = self.parameterNode() #if goingTo.id() != 'PVAntrumCut': # return self.editorWidget.exit() super(CMRToolkitWizardWallCleanupStep, self).onExit(goingTo, transitionType) def updateParameters(self, parameterNode): global wallSegVolume wallSegVolume = parameterNode.GetParameter('wallSegVolumeID')
class SegmentationStep(GBMWizardStep): def __init__(self, stepid): self.initialize(stepid) self.setName('5. Threshold') self.__vrDisplayNode = None self.__threshold = [-1, -1] # Initialize volume rendering. self.__vrLogic = slicer.modules.volumerendering.logic() self.__vrOpacityMap = None self.__vrColorMap = None self.__thresholdedLabelNode = None self.__roiVolume = None self.__visualizedVolume = None self.__parent = super(SegmentationStep, self) def createUserInterface(self): """ This UI takes advantage of a pre-built slicer thresholding widget. """ self.__layout = self.__parent.createUserInterface() step_label = qt.QLabel( """Automatic segmentation with deep learning is not available in this demo, due to lack of computing resources. You may however create a segmentation using 3D Slicer's editor module in this step.""" ) step_label.setWordWrap(True) self.__informationGroupBox = qt.QGroupBox() self.__informationGroupBox.setTitle('Information') self.__informationGroupBoxLayout = qt.QFormLayout( self.__informationGroupBox) self.__informationGroupBoxLayout.addRow(step_label) self.__layout.addRow(self.__informationGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.EditorWidget = EditorWidget(parent=editorWidgetParent) self.EditorWidget.setup() self.__layout.addRow(editorWidgetParent) # self.__thresholdGroupBox = qt.QGroupBox() # self.__thresholdGroupBox.setTitle('Threshold Range') # self.__thresholdGroupBoxLayout = qt.QFormLayout(self.__thresholdGroupBox) # threshLabel = qt.QLabel('Select Intensity Range:') # threshLabel.alignment = 4 # self.__threshRange = slicer.qMRMLRangeWidget() # self.__threshRange.decimals = 0 # self.__threshRange.singleStep = 1 # self.__thresholdGroupBoxLayout.addRow(threshLabel) # self.__thresholdGroupBoxLayout.addRow(self.__threshRange) # self.__layout.addRow(self.__thresholdGroupBox) # self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) qt.QTimer.singleShot(0, self.killButton) def onThresholdChanged(self): """ Upon changing the slider (or intializing this step), this method updates the volume rendering node and label volume accordingly. """ range0 = self.__threshRange.minimumValue range1 = self.__threshRange.maximumValue # Use vtk to threshold the label volume. if self.__roiVolume != None: thresh = vtk.vtkImageThreshold() if vtk.VTK_MAJOR_VERSION <= 5: thresh.SetInput(self.__roiVolume.GetImageData()) else: thresh.SetInputData(self.__roiVolume.GetImageData()) thresh.ThresholdBetween(range0, range1) thresh.SetInValue(1) thresh.SetOutValue(0) thresh.ReplaceOutOn() thresh.ReplaceInOn() thresh.Update() self.__thresholdedLabelNode.SetAndObserveImageData( thresh.GetOutput()) def killButton(self): # ctk creates a useless final page button. This method gets rid of it. bl = slicer.util.findChildren(text='ReviewStep') if len(bl): bl[0].hide() def validate(self, desiredBranchId): # For now, no validation required. self.__parent.validationSucceeded(desiredBranchId) def onEntry(self, comingFrom, transitionType): """ This method removes and adds nodes necessary to for a segementation display, intializes color and opacity maps, and calls the main thresholding function for the first time. """ super(SegmentationStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() self.EditorWidget.setMergeNode(self.__thresholdedLabelNode) self.EditorWidget.volumes.collapsed = True self.EditorWidget.editLabelMapsFrame.collapsed = False try: self.EditorWidget.segmentEditorLabel.hide() self.EditorWidget.infoIconLabel.hide() except: pass # pNode = self.parameterNode() # # What if someone goes to the Volume Rendering module, creates a new VRNode, # # and then returns? Need some way to check if self.__vrNode is currently # # being viewed. # self.__vrDisplayNode = Helper.getNodeByID(pNode.GetParameter('vrDisplayNodeID')) # self.updateWidgetFromParameters(pNode) # # Retrieves necessary nodes. # self.__roiVolume = Helper.getNodeByID(pNode.GetParameter('croppedVolumeID')) # self.__thresholdedLabelNode = Helper.getNodeByID(pNode.GetParameter('thresholdedLabelID')) # self.__nonThresholdedLabelNode = Helper.getNodeByID(pNode.GetParameter('nonThresholdedLabelID')) # # self.InitVRDisplayNode() # # Adds segementation label volume. # Helper.SetLabelVolume(self.__thresholdedLabelNode.GetID()) # threshRange = [self.__threshRange.minimumValue, self.__threshRange.maximumValue] # # Segments the entire vtk model. I assume there's a more concise way # # to do it than thresholding over its entire intensity range, so TODO # range0 = self.__threshRange.minimum # range1 = self.__threshRange.maximum # thresh = vtk.vtkImageThreshold() # if vtk.VTK_MAJOR_VERSION <= 5: # thresh.SetInput(self.__roiVolume.GetImageData()) # else: # thresh.SetInputData(self.__roiVolume.GetImageData()) # thresh.ThresholdBetween(range0, range1) # thresh.SetInValue(1) # thresh.SetOutValue(0) # thresh.ReplaceOutOn() # thresh.ReplaceInOn() # thresh.Update() # self.__nonThresholdedLabelNode.SetAndObserveImageData(thresh.GetOutput()) # # Adjusts threshold information. # self.onThresholdChanged() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def updateWidgetFromParameters(self, pNode): """ Intializes the threshold and label volume established in the previous step. """ pass # if pNode.GetParameter('followupVolumeID') == None or pNode.GetParameter('followupVolumeID') == '': # Helper.SetBgFgVolumes(pNode.GetParameter('baselineVolumeID'), '') # self.__visualizedVolume = Helper.getNodeByID(pNode.GetParameter('baselineVolumeID')) # else: # Helper.SetBgFgVolumes(pNode.GetParameter('subtractVolumeID'), pNode.GetParameter('followupVolumeID')) # self.__visualizedVolume = Helper.getNodeByID(pNode.GetParameter('subtractVolumeID')) # thresholdRange = [float(pNode.GetParameter('intensityThreshRangeMin')), float(pNode.GetParameter('intensityThreshRangeMax'))] # if thresholdRange != '': # self.__threshRange.maximum = thresholdRange[1] # self.__threshRange.minimum = thresholdRange[0] # self.__threshRange.maximumValue = thresholdRange[1] # self.__threshRange.minimumValue = thresholdRange[0] # else: # Helper.Error('Unexpected parameter values! Error code CT-S03-TNA. Please report') # labelID = pNode.GetParameter('thresholdedLabelID') # self.__thresholdedLabelNode = Helper.getNodeByID(labelID) def onExit(self, goingTo, transitionType): pNode = self.parameterNode() # if self.__vrDisplayNode != None: # # self.__vrDisplayNode.VisibilityOff() # pNode.SetParameter('vrDisplayNodeID', self.__vrDisplayNode.GetID()) # roiRange = self.__threshRange # pNode.SetParameter('intensityThreshRangeMin', str(roiRange.minimumValue)) # pNode.SetParameter('intensityThreshRangeMax', str(roiRange.maximumValue)) # pNode.SetParameter('vrThreshRangeMin', str(roiRange.minimumValue)) # pNode.SetParameter('vrThreshRangeMax', str(roiRange.maximumValue)) super(GBMWizardStep, self).onExit(goingTo, transitionType) def InitVRDisplayNode(self): """ This method calls a series of steps necessary to initailizing a volume rendering node with an ROI. """ if self.__vrDisplayNode == None or self.__vrDisplayNode == '': pNode = self.parameterNode() self.__vrDisplayNode = self.__vrLogic.CreateVolumeRenderingDisplayNode( ) slicer.mrmlScene.AddNode(self.__vrDisplayNode) # Documentation on UnRegister is scant so far. self.__vrDisplayNode.UnRegister(self.__vrLogic) Helper.InitVRDisplayNode(self.__vrDisplayNode, self.__roiVolume.GetID(), '') self.__roiVolume.AddAndObserveDisplayNodeID( self.__vrDisplayNode.GetID()) else: self.__vrDisplayNode.SetAndObserveVolumeNodeID( self.__roiVolume.GetID()) # This is a bit messy. viewNode = slicer.util.getNode('vtkMRMLViewNode1') self.__vrDisplayNode.AddViewNodeID(viewNode.GetID()) self.__vrLogic.CopyDisplayToVolumeRenderingDisplayNode( self.__vrDisplayNode) self.__vrOpacityMap = self.__vrDisplayNode.GetVolumePropertyNode( ).GetVolumeProperty().GetScalarOpacity() self.__vrColorMap = self.__vrDisplayNode.GetVolumePropertyNode( ).GetVolumeProperty().GetRGBTransferFunction() vrRange = self.__visualizedVolume.GetImageData().GetScalarRange() # Renders in yellow, like the label map in the next steps. self.__vrColorMap.RemoveAllPoints() self.__vrColorMap.AddRGBPoint(vrRange[0], 0.8, 0.8, 0) self.__vrColorMap.AddRGBPoint(vrRange[1], 0.8, 0.8, 0)
class CMRToolkitWizardWallCleanupStep( CMRToolkitWizardStep ) : def __init__( self, stepid ): self.initialize( stepid ) self.setName( '5. Edit Wall Segmentation' ) self.setDescription( 'Set the wall segmentation image from the previous step as the merge volume in the Slicer Editor. Remove the pulmonary veins from the wall segmentation using the Slicer Editor erase tool and select the final label image to proceed.' ) self.__parent = super( CMRToolkitWizardWallCleanupStep, self ) def killButton(self): # Hide unneccesary button bl = slicer.util.findChildren(text='AutomaticLeft*') if len(bl): bl[0].hide() def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #endoSegmentButton = qt.QPushButton('Open Slicer Editor tool') #self.__layout.addRow(endoSegmentButton) #endoSegmentButton.connect('clicked()', self.startEditor) #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) self.updateParameters(self.parameterNode()) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() ##TODO: How to set the required merge node? wallCleanupSegLabel = qt.QLabel( 'Final Wall Segmentation Image:' ) self.__wallCleanupSegSelector = slicer.qMRMLNodeComboBox() self.__wallCleanupSegSelector.toolTip = "Choose the final wall segmentation label image." self.__wallCleanupSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__wallCleanupSegSelector.setMRMLScene(slicer.mrmlScene) self.__wallCleanupSegSelector.addEnabled = 0 self.__layout.addRow( wallCleanupSegLabel, self.__wallCleanupSegSelector ) # Hide unnecessary button findChildren( text = 'AutomaticLeft*')[0].hide() def validate( self, desiredBranchId ): self.__parent.validate( desiredBranchId ) wallCleanupSegVolume = self.__wallCleanupSegSelector.currentNode() if wallCleanupSegVolume != None : wallCleanupSegID = wallCleanupSegVolume.GetID() pNode = self.parameterNode() pNode.SetParameter('wallCleanupSegID', wallCleanupSegID) Helper.SetLabelVolume(wallCleanupSegVolume.GetID()) self.__parent.validationSucceeded(desiredBranchId) else: self.__parent.validationFailed(desiredBranchId, 'Error','Please select the wall segmentation image to proceed.') def onEntry(self, comingFrom, transitionType): super(CMRToolkitWizardWallCleanupStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) ## TODO: Why does editor effect continue to the next workflow step? def onExit(self, goingTo, transitionType): pNode = self.parameterNode() #if goingTo.id() != 'PVAntrumCut': # return self.editorWidget.exit() super(CMRToolkitWizardWallCleanupStep, self).onExit(goingTo, transitionType) def updateParameters(self, parameterNode): global wallSegVolume wallSegVolume = parameterNode.GetParameter('wallSegVolumeID')
class CMRToolkitWizardEndoSegmentationStep( CMRToolkitWizardStep ) : def __init__( self, stepid ): self.initialize( stepid ) self.setName( '2. Segmentation of endocardium' ) self.setDescription( 'Segment the endocardium using the Editor paint tool and select the final label image to proceed.' ) self.__parent = super( CMRToolkitWizardEndoSegmentationStep, self ) def killButton(self): # Hide unneccesary button bl = slicer.util.findChildren(text='AutomaticLeft*') if len(bl): bl[0].hide() def createUserInterface( self ): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True); self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() endoSegLabel = qt.QLabel( 'Endo Segmentation Image:' ) self.__endoSegSelector = slicer.qMRMLNodeComboBox() self.__endoSegSelector.toolTip = "Choose the endo segmentation label image." self.__endoSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__endoSegSelector.setMRMLScene(slicer.mrmlScene) self.__endoSegSelector.addEnabled = 0 self.__layout.addRow( endoSegLabel, self.__endoSegSelector ) #def startEditor(self): # from Editor import EditorWidget # editorWidget = EditorWidget(showVolumesFrame=True) def validate( self, desiredBranchId ): self.__parent.validate( desiredBranchId ) endoSegVolume = self.__endoSegSelector.currentNode() if endoSegVolume != None : endoSegID = endoSegVolume.GetID() pNode = self.parameterNode() pNode.SetParameter('endoSegVolumeID', endoSegID) Helper.SetLabelVolume(endoSegVolume.GetID()) self.__parent.validationSucceeded(desiredBranchId) else: self.__parent.validationFailed(desiredBranchId, 'Error','Please select an endo segmentation image to proceed.') def onEntry(self, comingFrom, transitionType): super(CMRToolkitWizardEndoSegmentationStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def onExit(self, goingTo, transitionType): pNode = self.parameterNode() #if goingTo.id() != 'AxialDilate': # return self.editorWidget.exit() super(CMRToolkitWizardEndoSegmentationStep, self).onExit(goingTo, transitionType)
class SlicerPathologyWidget(ScriptedLoadableModuleWidget, ModuleWidgetMixin): def __init__(self, parent = None): ScriptedLoadableModuleWidget.__init__(self, parent) self.resourcesPath = os.path.join(slicer.modules.slicerpathology.path.replace(self.moduleName+".py",""), 'Resources') self.modulePath = os.path.dirname(slicer.util.modulePath(self.moduleName)) self.currentStep = 1 def setup(self): ScriptedLoadableModuleWidget.setup(self) # this section is for custom color box infoGroupBox = qt.QWidget() hbox = qt.QHBoxLayout() hbox.setMargin(0) self.studySelectionGroupBoxLayout = qt.QGridLayout() infoGroupBox.setLayout(hbox) self.studySelectionGroupBoxLayout.addWidget(infoGroupBox, 0, 3, 1, 1) infoIcon = qt.QPixmap(os.path.join(self.resourcesPath, 'Icons', 'icon-infoBox.png')) self.customLUTInfoIcon = qt.QLabel() self.customLUTInfoIcon.setPixmap(infoIcon) self.customLUTInfoIcon.setSizePolicy(PythonQt.QtGui.QSizePolicy()) hbox.addWidget(self.customLUTInfoIcon) self.customLUTLabel = qt.QLabel() hbox.addWidget(self.customLUTLabel) # end of custom color box section self.setupIcons() self.setupTabBarNavigation() self.setupsetupUI() self.setupimageSelectionUI() self.setupsegmentationUI() self.setupsubmissionUI() self.setupEditorWidget() editUtil = EditorLib.EditUtil.EditUtil() self.parameterNode = editUtil.getParameterNode() # Instantiate and connect widgets ... # # Parameters Area # parametersCollapsibleButton = ctk.ctkCollapsibleButton() parametersCollapsibleButton.text = "Parameters" self.layout.addWidget(parametersCollapsibleButton) # Layout within the dummy collapsible button parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton) # # check box to trigger taking screen shots for later use in tutorials # self.enableScreenshotsFlagCheckBox = qt.QCheckBox() self.enableScreenshotsFlagCheckBox.checked = 0 self.enableScreenshotsFlagCheckBox.setToolTip("If checked, take screen shots for tutorials. Use Save Data to write them to disk.") parametersFormLayout.addRow("Enable Screenshots", self.enableScreenshotsFlagCheckBox) # # scale factor for screen shots # self.screenshotScaleFactorSliderWidget = ctk.ctkSliderWidget() self.screenshotScaleFactorSliderWidget.singleStep = 1.0 self.screenshotScaleFactorSliderWidget.minimum = 1.0 self.screenshotScaleFactorSliderWidget.maximum = 50.0 self.screenshotScaleFactorSliderWidget.value = 1.0 self.screenshotScaleFactorSliderWidget.setToolTip("Set scale factor for the screen shots.") parametersFormLayout.addRow("Screenshot scale factor", self.screenshotScaleFactorSliderWidget) # # Apply Button # #self.applyButton = qt.QPushButton("Apply") #self.applyButton.toolTip = "Run the algorithm." #self.applyButton.enabled = False #parametersFormLayout.addRow(self.applyButton) # Add vertical spacer self.layout.addStretch(1) self.j = {} def cleanup(self): pass def onSelect(self): self.applyButton.enabled = self.inputSelector.currentNode() and self.outputSelector.currentNode() def onApplyButton(self): logic = SlicerPathologyLogic() enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value) print("Run the algorithm") logic.run(self.inputSelector.currentNode(), self.outputSelector.currentNode(), enableScreenshotsFlag,screenshotScaleFactor) def setupIcons(self): self.setupIcon = self.createIcon('icon-setup.png') self.imageSelectionIcon = self.createIcon('icon-imageselection.png') self.segmentationIcon = self.createIcon('icon-segmentation.png') self.submissionIcon = self.createIcon('icon-submission.png') def setupTabBarNavigation(self): self.tabWidget = qt.QTabWidget() self.layout.addWidget(self.tabWidget) setupGroupBox = qt.QGroupBox() imageSelectionGroupBox = qt.QGroupBox() segmentationGroupBox = qt.QGroupBox() submissionGroupBox = qt.QGroupBox() self.setupGroupBoxLayout = qt.QFormLayout() self.imageSelectionGroupBoxLayout = qt.QFormLayout() self.segmentationGroupBoxLayout = qt.QGridLayout() self.submissionGroupBoxLayout = qt.QFormLayout() setupGroupBox.setLayout(self.setupGroupBoxLayout) imageSelectionGroupBox.setLayout(self.imageSelectionGroupBoxLayout) segmentationGroupBox.setLayout(self.segmentationGroupBoxLayout) submissionGroupBox.setLayout(self.submissionGroupBoxLayout) self.tabWidget.setIconSize(qt.QSize(85, 30)) self.tabWidget.addTab(setupGroupBox, self.setupIcon, '') self.tabWidget.addTab(imageSelectionGroupBox, self.imageSelectionIcon, '') self.tabWidget.addTab(segmentationGroupBox, self.segmentationIcon, '') self.tabWidget.addTab(submissionGroupBox, self.submissionIcon, '') self.tabWidget.connect('currentChanged(int)',self.onTabWidgetClicked) self.setTabsEnabled([1,2,3,4], True) def onTabWidgetClicked(self, currentIndex): if currentIndex == 0: self.onStep1Selected() if currentIndex == 1: print "" if currentIndex == 2: print "" if currentIndex == 3: print "" def setTabsEnabled(self, indexes, enabled): for index in indexes: self.tabWidget.childAt(1, 1).setTabEnabled(index, enabled) def onStep1Selected(self): #if self.checkStep3or4Leave() is True: #return if self.currentStep == 1: return self.currentStep = 1 self.setTabsEnabled([0],True) self.setTabsEnabled([1,2,3], False) def setupsetupUI(self): self.setupUserName = qt.QLineEdit() self.setupGroupBoxLayout.addRow("Username:"******"Password:"******"Data directory:") ) self.setupGroupBoxLayout.addWidget(self.dataDirButton) self.setupExecutionID = qt.QLineEdit() self.setupGroupBoxLayout.addRow("Execution ID:", self.setupExecutionID) def setupimageSelectionUI(self): self.loadDataButton = qt.QPushButton("Load Image from disk") self.imageSelectionGroupBoxLayout.addWidget(self.loadDataButton) self.loadDataButton.connect('clicked()', self.loadTCGAData) self.WIP2 = qt.QPushButton("Select image from web") self.WIP2.connect('clicked()', self.onWIP2ButtonClicked) self.imageSelectionGroupBoxLayout.addWidget(self.WIP2) self.WIP3 = qt.QPushButton("Load image from web") self.WIP3.connect('clicked()', self.onWIP3ButtonClicked) self.imageSelectionGroupBoxLayout.addWidget(self.WIP3) self.RestoreButton = qt.QPushButton("Restore Session") self.RestoreButton.connect('clicked()', self.RestoreSession) self.imageSelectionGroupBoxLayout.addWidget(self.RestoreButton) # def ebCenter(self): # r = slicer.app.layoutManager().sliceWidget("Red").sliceController() # r.fitSliceToBackground() def onWIP2ButtonClicked(self): self.openTargetImage0() def onWIP3ButtonClicked(self): self.openTargetImage() r = slicer.app.layoutManager().sliceWidget("Red").sliceController() r.fitSliceToBackground() def setupsegmentationUI(self): print "" def setupsubmissionUI(self): self.SaveButton = qt.QPushButton("Save") self.submissionGroupBoxLayout.addWidget(self.SaveButton) self.SaveButton.connect('clicked()', self.onSaveButtonClicked) self.SaveButton.setEnabled(0) #self.WebSaveButton = qt.QPushButton("Submit to web") #self.submissionGroupBoxLayout.addWidget(self.WebSaveButton) #self.WebSaveButton.connect('clicked()', self.onWebSaveButtonClicked) def QImage2vtkImage(self, image): i = vtk.vtkImageData().NewInstance() i.SetDimensions(image.width(),image.height(),1) i.AllocateScalars(vtk.VTK_UNSIGNED_CHAR,3) for x in range(0,image.width()): for y in range(0,image.height()): c = qt.QColor(image.pixel(x,y)) i.SetScalarComponentFromDouble(x,y,0,0,c.red()) i.SetScalarComponentFromDouble(x,y,0,1,c.green()) i.SetScalarComponentFromDouble(x,y,0,2,c.blue()) return i def onWIPButtonClicked(self): import urllib2 reply = urllib2.urlopen('http://www.osa.sunysb.edu/erich.png') byte_array = reply.read() image = qt.QImage(qt.QImage.Format_RGB888) image.loadFromData(byte_array) imageData = self.QImage2vtkImage(image) volumeNode = slicer.vtkMRMLVectorVolumeNode() volumeNode.SetName("WEB") volumeNode.SetAndObserveImageData(imageData) displayNode = slicer.vtkMRMLVectorVolumeDisplayNode() slicer.mrmlScene.AddNode(volumeNode) slicer.mrmlScene.AddNode(displayNode) volumeNode.SetAndObserveDisplayNodeID(displayNode.GetID()) displayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeGrey') self.mutate() def RestoreSession(self): import zipfile import os.path f = qt.QFileDialog.getOpenFileName() zf = zipfile.ZipFile(f) for filename in zf.namelist(): try: data = zf.read(filename) except KeyError: print 'ERROR: Did not find %s in zip file' % filename else: print filename def onSaveButtonClicked(self): import zipfile import os.path import uuid bundle = EditUtil.EditUtil().getParameterNode().GetParameter('QuickTCGAEffect,erich') tran = json.loads(bundle) layers = [] for key in tran: nn = tran[key] nn["file"] = key + '.tif' layers.append(tran[key]) self.j['layers'] = layers self.j['username'] = self.setupUserName.text self.j['sourcetile'] = self.tilename self.j['generator'] = slicer.app.applicationVersion self.j['timestamp'] = datetime.datetime.now().strftime("%Y%m%d%H%M%S") self.j['execution_id'] = self.setupExecutionID.text + "-"+ uuid.uuid4().get_urn() labelNodes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') savedMessage = 'Segmentations for the following series were saved:\n\n' zfname = os.path.join(self.dataDirButton.directory, self.tilename + "_" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '.zip') print "zipfile name" print zfname zf = zipfile.ZipFile(zfname, mode='w') red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() fg = red_cn.GetForegroundVolumeID() ff = slicer.util.getNode(fg) sNode = slicer.vtkMRMLVolumeArchetypeStorageNode() sNode.SetFileName("original.tif") sNode.SetWriteFileFormat('tif') sNode.SetURI(None) success = sNode.WriteData(ff) zf.write("original.tif") os.remove("original.tif") for label in labelNodes.values(): labelName = label.GetName() labelFileName = os.path.join(self.dataDirButton.directory, labelName + '.tif') compFileName = os.path.join(self.dataDirButton.directory, labelName + '-comp.tif') sNode.SetFileName(labelFileName) success = sNode.WriteData(label) if success: print "adding "+labelFileName+" to zipfile" zf.write(labelFileName,os.path.basename(labelFileName)) os.remove(labelFileName) else: print "failed writing "+labelFileName comp = self.WriteLonI(label.GetImageData(),ff.GetImageData()) volumeNode = slicer.vtkMRMLVectorVolumeNode() volumeNode.SetName("COMP") volumeNode.SetAndObserveImageData(comp) sNode.SetFileName(compFileName) success = sNode.WriteData(volumeNode) if success: print "adding "+compFileName+" to zipfile" zf.write(compFileName,os.path.basename(compFileName)) os.remove(compFileName) else: print "failed writing "+compFileName jstr = json.dumps(self.j,sort_keys=True, indent=4, separators=(',', ': ')) mfname = os.path.join(self.dataDirButton.directory, 'manifest.json') f = open(mfname,'w') f.write(jstr) f.close() zf.write(mfname,os.path.basename(mfname)) zf.close() os.remove(mfname) #import sys #reload(sys) #sys.setdefaultencoding('utf8') #opener = urllib2.build_opener(MultipartPostHandler) #params = { "ss" : "0", # show source # "doctype" : "Inline", # "uploaded_file" : open(zfname, "rb") } #print params #print opener.open('http://quip1.bmi.stonybrook.edu:4000/upload', params).read() def WriteLonI(self, src, dest): dim = src.GetDimensions() i = vtk.vtkImageData().NewInstance() i.SetDimensions(dim[0],dim[1],1) i.AllocateScalars(vtk.VTK_UNSIGNED_CHAR,3) for x in range(0,dim[0]): for y in range(0,dim[1]): if (src.GetScalarComponentAsDouble(x,y,0,0)==0): for c in range(0,3): i.SetScalarComponentFromDouble(x,y,0,c,dest.GetScalarComponentAsDouble(x,y,0,c)) else: if ( (src.GetScalarComponentAsDouble(x+1,y-1,0,0)==1) and (src.GetScalarComponentAsDouble(x+1,y,0,0)==1) and (src.GetScalarComponentAsDouble(x+1,y+1,0,0)==1) and (src.GetScalarComponentAsDouble(x,y+1,0,0)==1) and (src.GetScalarComponentAsDouble(x,y-1,0,0)==1) and (src.GetScalarComponentAsDouble(x-1,y+1,0,0)==1) and (src.GetScalarComponentAsDouble(x-1,y,0,0)==1) and (src.GetScalarComponentAsDouble(x-1,y-1,0,0)==1)): for c in range(0,3): i.SetScalarComponentFromDouble(x,y,0,c,dest.GetScalarComponentAsDouble(x,y,0,c)) else: i.SetScalarComponentFromDouble(x,y,0,0,0) i.SetScalarComponentFromDouble(x,y,0,1,250) i.SetScalarComponentFromDouble(x,y,0,2,0) i.Modified() return i def onWebSaveButtonClicked(self): print "Web Save to be implemented...." def checkAndSetLUT(self): # Default to module color table self.resourcesPath = os.path.join(slicer.modules.slicerpathology.path.replace(self.moduleName+".py",""), 'Resources') self.colorFile = os.path.join(self.resourcesPath, "Colors/SlicerPathology.csv") self.customLUTLabel.setText('Using Default LUT') try: self.editorWidget.helper.structureListWidget.merge = None except AttributeError: pass # setup the color table, make sure SlicerPathology LUT is a singleton allColorTableNodes = slicer.util.getNodes('vtkMRMLColorTableNode*').values() for ctn in allColorTableNodes: #print "color: "+ctn.GetName() if ctn.GetName() == 'SlicerPathologyColor': slicer.mrmlScene.RemoveNode(ctn) break self.SlicerPathologyColorNode = slicer.vtkMRMLColorTableNode() colorNode = self.SlicerPathologyColorNode colorNode.SetName('SlicerPathologyColor') slicer.mrmlScene.AddNode(colorNode) colorNode.SetTypeToUser() with open(self.colorFile) as f: n = sum(1 for line in f) colorNode.SetNumberOfColors(n-1) colorNode.NamesInitialisedOn() import csv self.structureNames = [] with open(self.colorFile, 'rb') as csvfile: reader = csv.DictReader(csvfile, delimiter=',') for index,row in enumerate(reader): success = colorNode.SetColor(index ,row['Label'],float(row['R'])/255,float(row['G'])/255,float(row['B'])/255,float(row['A'])) if not success: print "color %s could not be set" % row['Label'] self.structureNames.append(row['Label']) def setupEditorWidget(self): editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent) self.editorWidget.setup() self.segmentationGroupBoxLayout.addWidget(self.editorWidget.parent) def mutate(self): red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() fgrdVolID = red_cn.GetBackgroundVolumeID() fgrdNode = slicer.util.getNode("WEB") fgrdVolID = fgrdNode.GetID() fMat=vtk.vtkMatrix4x4() fgrdNode.GetIJKToRASDirectionMatrix(fMat) bgrdName = fgrdNode.GetName() + '_gray' magnitude = vtk.vtkImageMagnitude() magnitude.SetInputData(fgrdNode.GetImageData()) magnitude.Update() bgrdNode = slicer.vtkMRMLScalarVolumeNode() bgrdNode.SetImageDataConnection(magnitude.GetOutputPort()) bgrdNode.SetName(bgrdName) bgrdNode.SetIJKToRASDirectionMatrix(fMat) slicer.mrmlScene.AddNode(bgrdNode) bgrdVolID = bgrdNode.GetID() red_cn.SetForegroundVolumeID(fgrdVolID) red_cn.SetBackgroundVolumeID(bgrdVolID) red_cn.SetForegroundOpacity(1) resourcesPath = os.path.join(slicer.modules.slicerpathology.path.replace("SlicerPathology.py",""), 'Resources') colorFile = os.path.join(resourcesPath, "Colors/SlicerPathology.csv") try: slicer.modules.EditorWidget.helper.structureListWidget.merge = None except AttributeError: pass allColorTableNodes = slicer.util.getNodes('vtkMRMLColorTableNode*').values() for ctn in allColorTableNodes: if ctn.GetName() == 'SlicerPathologyColor': slicer.mrmlScene.RemoveNode(ctn) break SlicerPathologyColorNode = slicer.vtkMRMLColorTableNode() colorNode = SlicerPathologyColorNode colorNode.SetName('SlicerPathologyColor') slicer.mrmlScene.AddNode(colorNode) colorNode.SetTypeToUser() with open(colorFile) as f: n = sum(1 for line in f) colorNode.SetNumberOfColors(n-1) colorNode.NamesInitialisedOn() import csv structureNames = [] with open(colorFile, 'rb') as csvfile: reader = csv.DictReader(csvfile, delimiter=',') for index,row in enumerate(reader): success = colorNode.SetColor(index ,row['Label'],float(row['R'])/255,float(row['G'])/255,float(row['B'])/255,float(row['A'])) if not success: print "color %s could not be set" % row['Label'] structureNames.append(row['Label']) volumesLogic = slicer.modules.volumes.logic() labelName = bgrdName+'-label' refLabel = volumesLogic.CreateAndAddLabelVolume(slicer.mrmlScene,bgrdNode,labelName) refLabel.GetDisplayNode().SetAndObserveColorNodeID(SlicerPathologyColorNode.GetID()) self.editorWidget.helper.setMasterVolume(bgrdNode) def openTargetImage0(self): self.v = qt.QWebView() weburl='http://quip1.bmi.stonybrook.edu:4000/' self.v.setUrl(qt.QUrl(weburl)) self.v.show() def openTargetImage(self): import string p = self.v.page() m = p.mainFrame() imageBound=m.evaluateJavaScript('viewer.viewport.viewportToImageRectangle(viewer.viewport.getBounds().x, viewer.viewport.getBounds().y, viewer.viewport.getBounds().width, viewer.viewport.getBounds().height)') x=imageBound[u'x'] y=imageBound[u'y'] width=imageBound[u'width'] height=imageBound[u'height'] self.j['x'] = x self.j['y'] = y self.j['width'] = width self.j['height'] = height imagedata = m.evaluateJavaScript('imagedata') tmpfilename= imagedata[u'metaData'][1] imageFileName=string.rstrip(tmpfilename,'.dzi') self.tilename = imagedata[u'imageId'] print self.tilename self.parameterNode.SetParameter("SlicerPathology,tilename", self.tilename) current_weburl ='http://quip1.uhmc.sunysb.edu/fcgi-bin/iipsrv.fcgi?IIIF=' + imageFileName +'/' + str(x) + ','+ str(y) + ',' + str(width) + ',' + str(height) + '/full/0/default.jpg' print current_weburl self.v.setUrl(qt.QUrl(current_weburl)) self.v.show() reply = urllib2.urlopen(current_weburl) byte_array = reply.read() image = qt.QImage(qt.QImage.Format_RGB888) image.loadFromData(byte_array) imageData = self.QImage2vtkImage(image) volumeNode = slicer.vtkMRMLVectorVolumeNode() volumeNode.SetName("WEB") volumeNode.SetAndObserveImageData(imageData) displayNode = slicer.vtkMRMLVectorVolumeDisplayNode() slicer.mrmlScene.AddNode(volumeNode) slicer.mrmlScene.AddNode(displayNode) volumeNode.SetAndObserveDisplayNodeID(displayNode.GetID()) displayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeGrey') self.mutate() def Four2ThreeChannel(self, image): dim = image.GetDimensions() i = vtk.vtkImageData().NewInstance() i.SetDimensions(dim[0],dim[1],1) i.AllocateScalars(vtk.VTK_UNSIGNED_CHAR,3) for x in range(0,dim[0]): for y in range(0,dim[1]): for c in range(0,3): i.SetScalarComponentFromDouble(x,y,0,c,image.GetScalarComponentAsDouble(x,y,0,c)) i.Modified() return i def loadTCGAData(self): slicer.util.openAddVolumeDialog() import EditorLib editUtil = EditorLib.EditUtil.EditUtil() imsainode = editUtil.getBackgroundVolume() imsai = imsainode.GetImageData() print imsai.GetNumberOfScalarComponents() if imsai.GetNumberOfScalarComponents() > 3: lala = self.Four2ThreeChannel(imsai) print lala.GetNumberOfScalarComponents() imsainode.SetAndObserveImageData(lala) imsainode.Modified() red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() fgrdVolID = red_cn.GetBackgroundVolumeID() fgrdNode = slicer.util.getNode(fgrdVolID) fMat=vtk.vtkMatrix4x4() fgrdNode.GetIJKToRASDirectionMatrix(fMat) bgrdName = fgrdNode.GetName() + '_gray' self.tilename = fgrdNode.GetName() + '_gray' self.parameterNode.SetParameter("SlicerPathology,tilename", self.tilename) # Create dummy grayscale image magnitude = vtk.vtkImageMagnitude() magnitude.SetInputData(fgrdNode.GetImageData()) magnitude.Update() bgrdNode = slicer.vtkMRMLScalarVolumeNode() bgrdNode.SetImageDataConnection(magnitude.GetOutputPort()) bgrdNode.SetName(bgrdName) bgrdNode.SetIJKToRASDirectionMatrix(fMat) slicer.mrmlScene.AddNode(bgrdNode) bgrdVolID = bgrdNode.GetID() red_cn.SetForegroundVolumeID(fgrdVolID) red_cn.SetBackgroundVolumeID(bgrdVolID) red_cn.SetForegroundOpacity(1) self.checkAndSetLUT() print bgrdName cv = slicer.util.getNode(bgrdName) self.volumesLogic = slicer.modules.volumes.logic() labelName = bgrdName+'-label' refLabel = self.volumesLogic.CreateAndAddLabelVolume(slicer.mrmlScene,cv,labelName) refLabel.GetDisplayNode().SetAndObserveColorNodeID(self.SlicerPathologyColorNode.GetID()) self.editorWidget.helper.setMasterVolume(cv)
class SlicerPathologyWidget(ScriptedLoadableModuleWidget, ModuleWidgetMixin): def __init__(self, parent = None): ScriptedLoadableModuleWidget.__init__(self, parent) self.resourcesPath = os.path.join(slicer.modules.slicerpathology.path.replace(self.moduleName+".py",""), 'Resources') self.modulePath = os.path.dirname(slicer.util.modulePath(self.moduleName)) self.currentStep = 1 def setup(self): ScriptedLoadableModuleWidget.setup(self) # this section is for custom color box infoGroupBox = qt.QWidget() hbox = qt.QHBoxLayout() hbox.setMargin(0) self.studySelectionGroupBoxLayout = qt.QGridLayout() infoGroupBox.setLayout(hbox) self.studySelectionGroupBoxLayout.addWidget(infoGroupBox, 0, 3, 1, 1) infoIcon = qt.QPixmap(os.path.join(self.resourcesPath, 'Icons', 'icon-infoBox.png')) self.customLUTInfoIcon = qt.QLabel() self.customLUTInfoIcon.setPixmap(infoIcon) self.customLUTInfoIcon.setSizePolicy(PythonQt.QtGui.QSizePolicy()) hbox.addWidget(self.customLUTInfoIcon) self.customLUTLabel = qt.QLabel() hbox.addWidget(self.customLUTLabel) # end of custom color box section self.setupIcons() self.setupTabBarNavigation() self.setupsetupUI() self.setupimageSelectionUI() self.setupsegmentationUI() self.setupsubmissionUI() self.setupEditorWidget() editUtil = EditorLib.EditUtil.EditUtil() self.parameterNode = editUtil.getParameterNode() # Instantiate and connect widgets ... # # Parameters Area # parametersCollapsibleButton = ctk.ctkCollapsibleButton() parametersCollapsibleButton.text = "Parameters" self.layout.addWidget(parametersCollapsibleButton) # Layout within the dummy collapsible button parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton) # # check box to trigger taking screen shots for later use in tutorials # self.enableScreenshotsFlagCheckBox = qt.QCheckBox() self.enableScreenshotsFlagCheckBox.checked = 0 self.enableScreenshotsFlagCheckBox.setToolTip("If checked, take screen shots for tutorials. Use Save Data to write them to disk.") parametersFormLayout.addRow("Enable Screenshots", self.enableScreenshotsFlagCheckBox) # # scale factor for screen shots # self.screenshotScaleFactorSliderWidget = ctk.ctkSliderWidget() self.screenshotScaleFactorSliderWidget.singleStep = 1.0 self.screenshotScaleFactorSliderWidget.minimum = 1.0 self.screenshotScaleFactorSliderWidget.maximum = 50.0 self.screenshotScaleFactorSliderWidget.value = 1.0 self.screenshotScaleFactorSliderWidget.setToolTip("Set scale factor for the screen shots.") parametersFormLayout.addRow("Screenshot scale factor", self.screenshotScaleFactorSliderWidget) # # Apply Button # #self.applyButton = qt.QPushButton("Apply") #self.applyButton.toolTip = "Run the algorithm." #self.applyButton.enabled = False #parametersFormLayout.addRow(self.applyButton) # Add vertical spacer self.layout.addStretch(1) def cleanup(self): pass def onSelect(self): self.applyButton.enabled = self.inputSelector.currentNode() and self.outputSelector.currentNode() def onApplyButton(self): logic = SlicerPathologyLogic() enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value) print("Run the algorithm") logic.run(self.inputSelector.currentNode(), self.outputSelector.currentNode(), enableScreenshotsFlag,screenshotScaleFactor) def setupIcons(self): self.setupIcon = self.createIcon('icon-setup.png') self.imageSelectionIcon = self.createIcon('icon-imageselection.png') self.segmentationIcon = self.createIcon('icon-segmentation.png') self.submissionIcon = self.createIcon('icon-submission.png') def setupTabBarNavigation(self): self.tabWidget = qt.QTabWidget() self.layout.addWidget(self.tabWidget) setupGroupBox = qt.QGroupBox() imageSelectionGroupBox = qt.QGroupBox() segmentationGroupBox = qt.QGroupBox() submissionGroupBox = qt.QGroupBox() self.setupGroupBoxLayout = qt.QFormLayout() self.imageSelectionGroupBoxLayout = qt.QFormLayout() self.segmentationGroupBoxLayout = qt.QGridLayout() self.submissionGroupBoxLayout = qt.QFormLayout() setupGroupBox.setLayout(self.setupGroupBoxLayout) imageSelectionGroupBox.setLayout(self.imageSelectionGroupBoxLayout) segmentationGroupBox.setLayout(self.segmentationGroupBoxLayout) submissionGroupBox.setLayout(self.submissionGroupBoxLayout) self.tabWidget.setIconSize(qt.QSize(85, 30)) self.tabWidget.addTab(setupGroupBox, self.setupIcon, '') self.tabWidget.addTab(imageSelectionGroupBox, self.imageSelectionIcon, '') self.tabWidget.addTab(segmentationGroupBox, self.segmentationIcon, '') self.tabWidget.addTab(submissionGroupBox, self.submissionIcon, '') self.tabWidget.connect('currentChanged(int)',self.onTabWidgetClicked) self.setTabsEnabled([1,2,3,4], True) def onTabWidgetClicked(self, currentIndex): if currentIndex == 0: self.onStep1Selected() if currentIndex == 1: print "to be implemented..." if currentIndex == 2: print "to be implemented..." if currentIndex == 3: print "to be implemented..." def setTabsEnabled(self, indexes, enabled): for index in indexes: self.tabWidget.childAt(1, 1).setTabEnabled(index, enabled) def onStep1Selected(self): #if self.checkStep3or4Leave() is True: #return if self.currentStep == 1: return self.currentStep = 1 self.setTabsEnabled([0],True) self.setTabsEnabled([1,2,3], False) def setupsetupUI(self): self.setupUserName = qt.QLineEdit() self.setupGroupBoxLayout.addRow("Username:"******"Password:"******"Load Data") self.imageSelectionGroupBoxLayout.addWidget(self.loadDataButton) self.loadDataButton.connect('clicked()', self.loadTCGAData) print "Adding WIP Button!" self.WIP = qt.QPushButton("WIP") self.WIP.connect('clicked()', self.onWIPButtonClicked) self.imageSelectionGroupBoxLayout.addWidget(self.WIP) def setupsegmentationUI(self): print "adding this for now..." def setupsubmissionUI(self): self.dataDirButton = ctk.ctkDirectoryButton() self.submissionGroupBoxLayout.addWidget(qt.QLabel("Data directory:") ) self.submissionGroupBoxLayout.addWidget(self.dataDirButton) self.SaveButton = qt.QPushButton("Save") self.submissionGroupBoxLayout.addWidget(self.SaveButton) self.SaveButton.connect('clicked()', self.onSaveButtonClicked) self.WebSaveButton = qt.QPushButton("Submit to web") self.submissionGroupBoxLayout.addWidget(self.WebSaveButton) self.WebSaveButton.connect('clicked()', self.onWebSaveButtonClicked) def onWIPButtonClicked(self): print "WIP ME!" from EditorLib import EditUtil self.editUtil = EditorLib.EditUtil.EditUtil() self.labelNode = self.editUtil.getLabelVolume() print self.labelNode.GetImageData() self.labelNode.GetImageData().Modified() self.labelNode.Modified() self.currentMessage = "WIP Code is done being executed..." slicer.util.showStatusMessage(self.currentMessage) def onSaveButtonClicked(self): bundle = EditUtil.EditUtil().getParameterNode().GetParameter('QuickTCGAEffect,erich') tran = json.loads(bundle) layers = [] for key in tran: nn = tran[key] nn["file"] = key + '.tif' layers.append(tran[key]) j = {} j['layers'] = layers j['username'] = self.setupUserName.text j['sourcetile'] = self.tilename j['generator'] = "3DSlicer-4.5.0 with SlicerPathology v1.0a" j['timestamp'] = datetime.datetime.now().strftime("%Y%m%d%H%M%S") labelNodes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') savedMessage = 'Segmentations for the following series were saved:\n\n' for label in labelNodes.values(): labelName = label.GetName() labelFileName = os.path.join(self.dataDirButton.directory, labelName + '.tif') print "labelFileName : "+labelFileName sNode = slicer.vtkMRMLVolumeArchetypeStorageNode() sNode.SetFileName(labelFileName) sNode.SetWriteFileFormat('tif') sNode.SetURI(None) success = sNode.WriteData(label) if success: print "successful writing "+labelFileName else: print "failed writing "+labelFileName jstr = json.dumps(j,sort_keys=True, indent=4, separators=(',', ': ')) f = open(os.path.join(self.dataDirButton.directory, self.tilename + '.json'),'w') f.write(jstr) f.close() def onWebSaveButtonClicked(self): print "Web Save to be implemented...." def checkAndSetLUT(self): # Default to module color table self.resourcesPath = os.path.join(slicer.modules.slicerpathology.path.replace(self.moduleName+".py",""), 'Resources') self.colorFile = os.path.join(self.resourcesPath, "Colors/SlicerPathology.csv") self.customLUTLabel.setText('Using Default LUT') try: self.editorWidget.helper.structureListWidget.merge = None except AttributeError: pass # setup the color table, make sure SlicerPathology LUT is a singleton allColorTableNodes = slicer.util.getNodes('vtkMRMLColorTableNode*').values() for ctn in allColorTableNodes: #print "color: "+ctn.GetName() if ctn.GetName() == 'SlicerPathologyColor': slicer.mrmlScene.RemoveNode(ctn) break self.SlicerPathologyColorNode = slicer.vtkMRMLColorTableNode() colorNode = self.SlicerPathologyColorNode colorNode.SetName('SlicerPathologyColor') slicer.mrmlScene.AddNode(colorNode) colorNode.SetTypeToUser() with open(self.colorFile) as f: n = sum(1 for line in f) colorNode.SetNumberOfColors(n-1) colorNode.NamesInitialisedOn() import csv self.structureNames = [] with open(self.colorFile, 'rb') as csvfile: reader = csv.DictReader(csvfile, delimiter=',') for index,row in enumerate(reader): success = colorNode.SetColor(index ,row['Label'],float(row['R'])/255,float(row['G'])/255,float(row['B'])/255,float(row['A'])) if not success: print "color %s could not be set" % row['Label'] self.structureNames.append(row['Label']) def setupEditorWidget(self): editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent) self.editorWidget.setup() self.segmentationGroupBoxLayout.addWidget(self.editorWidget.parent) def loadTCGAData(self): slicer.util.openAddVolumeDialog() red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() fgrdVolID = red_cn.GetBackgroundVolumeID() fgrdNode = slicer.util.getNode(fgrdVolID) fMat=vtk.vtkMatrix4x4() fgrdNode.GetIJKToRASDirectionMatrix(fMat) bgrdName = fgrdNode.GetName() + '_gray' self.tilename = fgrdNode.GetName() + '_gray' self.parameterNode.SetParameter("SlicerPathology,tilename", self.tilename) # Get dummy grayscale image magnitude = vtk.vtkImageMagnitude() magnitude.SetInputData(fgrdNode.GetImageData()) magnitude.Update() bgrdNode = slicer.vtkMRMLScalarVolumeNode() bgrdNode.SetImageDataConnection(magnitude.GetOutputPort()) bgrdNode.SetName(bgrdName) bgrdNode.SetIJKToRASDirectionMatrix(fMat) slicer.mrmlScene.AddNode(bgrdNode) bgrdVolID = bgrdNode.GetID() # Reset slice configuration red_cn.SetForegroundVolumeID(fgrdVolID) red_cn.SetBackgroundVolumeID(bgrdVolID) red_cn.SetForegroundOpacity(1) self.checkAndSetLUT() print bgrdName cv = slicer.util.getNode(bgrdName) self.volumesLogic = slicer.modules.volumes.logic() labelName = bgrdName+'-label' refLabel = self.volumesLogic.CreateAndAddLabelVolume(slicer.mrmlScene,cv,labelName) refLabel.GetDisplayNode().SetAndObserveColorNodeID(self.SlicerPathologyColorNode.GetID()) self.editorWidget.helper.setMasterVolume(cv)
def createUserInterface(self): self.__layout = self.__parent.createUserInterface() step_label = qt.QLabel( """Review your segmentation. Use the 3D Visualization slider to see your segmentation in context with your image. Use the Editor panel to apply spot edits to your segmentation. If you would like to start over, see the Restart box below""" ) step_label.setWordWrap(True) self.__primaryGroupBox = qt.QGroupBox() self.__primaryGroupBox.setTitle('Information') self.__primaryGroupBoxLayout = qt.QFormLayout(self.__primaryGroupBox) self.__primaryGroupBoxLayout.addRow(step_label) self.__layout.addRow(self.__primaryGroupBox) # self.__threshRange = slicer.qMRMLRangeWidget() # self.__threshRange.decimals = 0 # self.__threshRange.singleStep = 1 # self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) # qt.QTimer.singleShot(0, self.killButton) # ThreshGroupBox = qt.QGroupBox() # ThreshGroupBox.setTitle('3D Visualization Intensity Threshold') # ThreshGroupBoxLayout = qt.QFormLayout(ThreshGroupBox) # ThreshGroupBoxLayout.addRow(self.__threshRange) # self.__layout.addRow(ThreshGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.EditorWidget = EditorWidget(parent=editorWidgetParent) self.EditorWidget.setup() self.__layout.addRow(editorWidgetParent) RestartGroupBox = qt.QGroupBox() RestartGroupBox.setTitle('Restart') RestartGroupBoxLayout = qt.QFormLayout(RestartGroupBox) self.__RestartButton = qt.QPushButton('Return to Step 1') RestartGroupBoxLayout.addRow(self.__RestartButton) self.__RemoveRegisteredImage = qt.QCheckBox() self.__RemoveRegisteredImage.checked = True self.__RemoveRegisteredImage.setToolTip( "Delete your registered images.") RestartGroupBoxLayout.addRow("Delete Registered images: ", self.__RemoveRegisteredImage) self.__RemoveNormalizedImages = qt.QCheckBox() self.__RemoveNormalizedImages.checked = True self.__RemoveNormalizedImages.setToolTip( "Delete your normalized images.") RestartGroupBoxLayout.addRow("Delete Normalized images: ", self.__RemoveNormalizedImages) self.__RemoveSubtractionMap = qt.QCheckBox() self.__RemoveSubtractionMap.checked = True self.__RemoveSubtractionMap.setToolTip("Delete your subtraction map.") RestartGroupBoxLayout.addRow("Delete Subtraction map: ", self.__RemoveSubtractionMap) self.__RemoveCroppedMap = qt.QCheckBox() self.__RemoveCroppedMap.checked = True self.__RemoveCroppedMap.setToolTip( "Delete the cropped version of your input volume.") RestartGroupBoxLayout.addRow("Delete Cropped Volume: ", self.__RemoveCroppedMap) self.__RemoveROI = qt.QCheckBox() self.__RemoveROI.checked = False self.__RemoveROI.setToolTip( "Delete the ROI you made with your markup points.") RestartGroupBoxLayout.addRow("Delete Full ROI: ", self.__RemoveROI) self.__RemoveThresholdedROI = qt.QCheckBox() self.__RemoveThresholdedROI.checked = False self.__RemoveThresholdedROI.setToolTip( "Delete the intensity-thresholded version of your ROI.") RestartGroupBoxLayout.addRow("Delete Thresholded ROI: ", self.__RemoveThresholdedROI) self.__RemoveMarkups = qt.QCheckBox() self.__RemoveMarkups.checked = True self.__RemoveMarkups.setToolTip( "Delete the markup points you used to create your 3D ROI.") RestartGroupBoxLayout.addRow("Delete Markup Points: ", self.__RemoveMarkups) self.__RemoveModels = qt.QCheckBox() self.__RemoveModels.checked = True self.__RemoveModels.setToolTip( "Delete the 3D model you created from your markup points.") RestartGroupBoxLayout.addRow("Delete 3D Model: ", self.__RemoveModels) self.__RestartButton.connect('clicked()', self.Restart) self.__RestartActivated = True self.__layout.addRow(RestartGroupBox)
class SlicerPathologyWidget(ScriptedLoadableModuleWidget, ModuleWidgetMixin): def __init__(self, parent=None): ScriptedLoadableModuleWidget.__init__(self, parent) self.resourcesPath = os.path.join( slicer.modules.slicerpathology.path.replace( self.moduleName + ".py", ""), 'Resources') self.modulePath = os.path.dirname( slicer.util.modulePath(self.moduleName)) self.currentStep = 1 def setup(self): ScriptedLoadableModuleWidget.setup(self) # this section is for custom color box infoGroupBox = qt.QWidget() hbox = qt.QHBoxLayout() hbox.setMargin(0) self.studySelectionGroupBoxLayout = qt.QGridLayout() infoGroupBox.setLayout(hbox) self.studySelectionGroupBoxLayout.addWidget(infoGroupBox, 0, 3, 1, 1) infoIcon = qt.QPixmap( os.path.join(self.resourcesPath, 'Icons', 'icon-infoBox.png')) self.customLUTInfoIcon = qt.QLabel() self.customLUTInfoIcon.setPixmap(infoIcon) self.customLUTInfoIcon.setSizePolicy(PythonQt.QtGui.QSizePolicy()) hbox.addWidget(self.customLUTInfoIcon) self.customLUTLabel = qt.QLabel() hbox.addWidget(self.customLUTLabel) # end of custom color box section self.setupIcons() self.setupTabBarNavigation() self.setupsetupUI() self.setupimageSelectionUI() self.setupsegmentationUI() self.setupsubmissionUI() self.setupEditorWidget() editUtil = EditorLib.EditUtil.EditUtil() self.parameterNode = editUtil.getParameterNode() # Instantiate and connect widgets ... # # Parameters Area # parametersCollapsibleButton = ctk.ctkCollapsibleButton() parametersCollapsibleButton.text = "Parameters" self.layout.addWidget(parametersCollapsibleButton) # Layout within the dummy collapsible button parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton) # # check box to trigger taking screen shots for later use in tutorials # self.enableScreenshotsFlagCheckBox = qt.QCheckBox() self.enableScreenshotsFlagCheckBox.checked = 0 self.enableScreenshotsFlagCheckBox.setToolTip( "If checked, take screen shots for tutorials. Use Save Data to write them to disk." ) parametersFormLayout.addRow("Enable Screenshots", self.enableScreenshotsFlagCheckBox) # # scale factor for screen shots # self.screenshotScaleFactorSliderWidget = ctk.ctkSliderWidget() self.screenshotScaleFactorSliderWidget.singleStep = 1.0 self.screenshotScaleFactorSliderWidget.minimum = 1.0 self.screenshotScaleFactorSliderWidget.maximum = 50.0 self.screenshotScaleFactorSliderWidget.value = 1.0 self.screenshotScaleFactorSliderWidget.setToolTip( "Set scale factor for the screen shots.") parametersFormLayout.addRow("Screenshot scale factor", self.screenshotScaleFactorSliderWidget) # # Apply Button # #self.applyButton = qt.QPushButton("Apply") #self.applyButton.toolTip = "Run the algorithm." #self.applyButton.enabled = False #parametersFormLayout.addRow(self.applyButton) # Add vertical spacer self.layout.addStretch(1) def cleanup(self): pass def onSelect(self): self.applyButton.enabled = self.inputSelector.currentNode( ) and self.outputSelector.currentNode() def onApplyButton(self): logic = SlicerPathologyLogic() enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked screenshotScaleFactor = int( self.screenshotScaleFactorSliderWidget.value) print("Run the algorithm") logic.run(self.inputSelector.currentNode(), self.outputSelector.currentNode(), enableScreenshotsFlag, screenshotScaleFactor) def setupIcons(self): self.setupIcon = self.createIcon('icon-setup.png') self.imageSelectionIcon = self.createIcon('icon-imageselection.png') self.segmentationIcon = self.createIcon('icon-segmentation.png') self.submissionIcon = self.createIcon('icon-submission.png') def setupTabBarNavigation(self): self.tabWidget = qt.QTabWidget() self.layout.addWidget(self.tabWidget) setupGroupBox = qt.QGroupBox() imageSelectionGroupBox = qt.QGroupBox() segmentationGroupBox = qt.QGroupBox() submissionGroupBox = qt.QGroupBox() self.setupGroupBoxLayout = qt.QFormLayout() self.imageSelectionGroupBoxLayout = qt.QFormLayout() self.segmentationGroupBoxLayout = qt.QGridLayout() self.submissionGroupBoxLayout = qt.QFormLayout() setupGroupBox.setLayout(self.setupGroupBoxLayout) imageSelectionGroupBox.setLayout(self.imageSelectionGroupBoxLayout) segmentationGroupBox.setLayout(self.segmentationGroupBoxLayout) submissionGroupBox.setLayout(self.submissionGroupBoxLayout) self.tabWidget.setIconSize(qt.QSize(85, 30)) self.tabWidget.addTab(setupGroupBox, self.setupIcon, '') self.tabWidget.addTab(imageSelectionGroupBox, self.imageSelectionIcon, '') self.tabWidget.addTab(segmentationGroupBox, self.segmentationIcon, '') self.tabWidget.addTab(submissionGroupBox, self.submissionIcon, '') self.tabWidget.connect('currentChanged(int)', self.onTabWidgetClicked) self.setTabsEnabled([1, 2, 3, 4], True) def onTabWidgetClicked(self, currentIndex): if currentIndex == 0: self.onStep1Selected() if currentIndex == 1: print "to be implemented..." if currentIndex == 2: print "to be implemented..." if currentIndex == 3: print "to be implemented..." def setTabsEnabled(self, indexes, enabled): for index in indexes: self.tabWidget.childAt(1, 1).setTabEnabled(index, enabled) def onStep1Selected(self): #if self.checkStep3or4Leave() is True: #return if self.currentStep == 1: return self.currentStep = 1 self.setTabsEnabled([0], True) self.setTabsEnabled([1, 2, 3], False) def setupsetupUI(self): self.setupUserName = qt.QLineEdit() self.setupGroupBoxLayout.addRow("Username:"******"Password:"******"Load Data") self.imageSelectionGroupBoxLayout.addWidget(self.loadDataButton) self.loadDataButton.connect('clicked()', self.loadTCGAData) print "Adding WIP Button!" self.WIP = qt.QPushButton("WIP") self.WIP.connect('clicked()', self.onWIPButtonClicked) self.imageSelectionGroupBoxLayout.addWidget(self.WIP) def setupsegmentationUI(self): print "adding this for now..." def setupsubmissionUI(self): self.dataDirButton = ctk.ctkDirectoryButton() self.submissionGroupBoxLayout.addWidget(qt.QLabel("Data directory:")) self.submissionGroupBoxLayout.addWidget(self.dataDirButton) self.SaveButton = qt.QPushButton("Save") self.submissionGroupBoxLayout.addWidget(self.SaveButton) self.SaveButton.connect('clicked()', self.onSaveButtonClicked) self.WebSaveButton = qt.QPushButton("Submit to web") self.submissionGroupBoxLayout.addWidget(self.WebSaveButton) self.WebSaveButton.connect('clicked()', self.onWebSaveButtonClicked) def onWIPButtonClicked(self): print "WIP ME!" from EditorLib import EditUtil self.editUtil = EditorLib.EditUtil.EditUtil() self.labelNode = self.editUtil.getLabelVolume() print self.labelNode.GetImageData() self.labelNode.GetImageData().Modified() self.labelNode.Modified() self.currentMessage = "WIP Code is done being executed..." slicer.util.showStatusMessage(self.currentMessage) def onSaveButtonClicked(self): bundle = EditUtil.EditUtil().getParameterNode().GetParameter( 'QuickTCGAEffect,erich') tran = json.loads(bundle) layers = [] for key in tran: nn = tran[key] nn["file"] = key + '.tif' layers.append(tran[key]) j = {} j['layers'] = layers j['username'] = self.setupUserName.text j['sourcetile'] = self.tilename j['generator'] = "3DSlicer-4.5.0 with SlicerPathology v1.0a" j['timestamp'] = datetime.datetime.now().strftime("%Y%m%d%H%M%S") labelNodes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') savedMessage = 'Segmentations for the following series were saved:\n\n' for label in labelNodes.values(): labelName = label.GetName() labelFileName = os.path.join(self.dataDirButton.directory, labelName + '.tif') print "labelFileName : " + labelFileName sNode = slicer.vtkMRMLVolumeArchetypeStorageNode() sNode.SetFileName(labelFileName) sNode.SetWriteFileFormat('tif') sNode.SetURI(None) success = sNode.WriteData(label) if success: print "successful writing " + labelFileName else: print "failed writing " + labelFileName jstr = json.dumps(j, sort_keys=True, indent=4, separators=(',', ': ')) f = open( os.path.join(self.dataDirButton.directory, self.tilename + '.json'), 'w') f.write(jstr) f.close() def onWebSaveButtonClicked(self): print "Web Save to be implemented...." def checkAndSetLUT(self): # Default to module color table self.resourcesPath = os.path.join( slicer.modules.slicerpathology.path.replace( self.moduleName + ".py", ""), 'Resources') self.colorFile = os.path.join(self.resourcesPath, "Colors/SlicerPathology.csv") self.customLUTLabel.setText('Using Default LUT') try: self.editorWidget.helper.structureListWidget.merge = None except AttributeError: pass # setup the color table, make sure SlicerPathology LUT is a singleton allColorTableNodes = slicer.util.getNodes( 'vtkMRMLColorTableNode*').values() for ctn in allColorTableNodes: #print "color: "+ctn.GetName() if ctn.GetName() == 'SlicerPathologyColor': slicer.mrmlScene.RemoveNode(ctn) break self.SlicerPathologyColorNode = slicer.vtkMRMLColorTableNode() colorNode = self.SlicerPathologyColorNode colorNode.SetName('SlicerPathologyColor') slicer.mrmlScene.AddNode(colorNode) colorNode.SetTypeToUser() with open(self.colorFile) as f: n = sum(1 for line in f) colorNode.SetNumberOfColors(n - 1) colorNode.NamesInitialisedOn() import csv self.structureNames = [] with open(self.colorFile, 'rb') as csvfile: reader = csv.DictReader(csvfile, delimiter=',') for index, row in enumerate(reader): success = colorNode.SetColor(index, row['Label'], float(row['R']) / 255, float(row['G']) / 255, float(row['B']) / 255, float(row['A'])) if not success: print "color %s could not be set" % row['Label'] self.structureNames.append(row['Label']) def setupEditorWidget(self): editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent) self.editorWidget.setup() self.segmentationGroupBoxLayout.addWidget(self.editorWidget.parent) def loadTCGAData(self): slicer.util.openAddVolumeDialog() red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() fgrdVolID = red_cn.GetBackgroundVolumeID() fgrdNode = slicer.util.getNode(fgrdVolID) fMat = vtk.vtkMatrix4x4() fgrdNode.GetIJKToRASDirectionMatrix(fMat) bgrdName = fgrdNode.GetName() + '_gray' self.tilename = fgrdNode.GetName() + '_gray' self.parameterNode.SetParameter("SlicerPathology,tilename", self.tilename) # Get dummy grayscale image magnitude = vtk.vtkImageMagnitude() magnitude.SetInputData(fgrdNode.GetImageData()) magnitude.Update() bgrdNode = slicer.vtkMRMLScalarVolumeNode() bgrdNode.SetImageDataConnection(magnitude.GetOutputPort()) bgrdNode.SetName(bgrdName) bgrdNode.SetIJKToRASDirectionMatrix(fMat) slicer.mrmlScene.AddNode(bgrdNode) bgrdVolID = bgrdNode.GetID() # Reset slice configuration red_cn.SetForegroundVolumeID(fgrdVolID) red_cn.SetBackgroundVolumeID(bgrdVolID) red_cn.SetForegroundOpacity(1) self.checkAndSetLUT() print bgrdName cv = slicer.util.getNode(bgrdName) self.volumesLogic = slicer.modules.volumes.logic() labelName = bgrdName + '-label' refLabel = self.volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, cv, labelName) refLabel.GetDisplayNode().SetAndObserveColorNodeID( self.SlicerPathologyColorNode.GetID()) self.editorWidget.helper.setMasterVolume(cv)
class ReviewStep(BeersSingleStep): def __init__(self, stepid): """ This method creates a drop-down menu that includes the whole step. The description also acts as a tooltip for the button. There may be some way to override this. The initialize method is inherited from ctk. """ self.initialize(stepid) self.setName('6. Review') self.setDescription( 'The segment from the subtraction map is now overlaid on your post-contrast image. Use the threshold bar below to edit the volume rendering node. Use the official Volume Rendering module for more specific visualization.' ) self.__pNode = None self.__vrDisplayNode = None self.__threshold = [-1, -1] # initialize VR stuff self.__vrLogic = slicer.modules.volumerendering.logic() self.__vrOpacityMap = None self.__roiSegmentationNode = None self.__roiVolume = None self.__parent = super(ReviewStep, self) self.__RestartActivated = False def createUserInterface(self): """ This step is mostly empty. A volume rendering threshold is added to be useful. """ self.__layout = self.__parent.createUserInterface() self.__threshRange = slicer.qMRMLRangeWidget() self.__threshRange.decimals = 0 self.__threshRange.singleStep = 1 self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) qt.QTimer.singleShot(0, self.killButton) ThreshGroupBox = qt.QGroupBox() ThreshGroupBox.setTitle('3D Visualization Intensity Threshold') ThreshGroupBoxLayout = qt.QFormLayout(ThreshGroupBox) ThreshGroupBoxLayout.addRow(self.__threshRange) self.__layout.addRow(ThreshGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent) self.__editorWidget.setup() self.__layout.addRow(editorWidgetParent) self.hideUnwantedEditorUIElements() RestartGroupBox = qt.QGroupBox() RestartGroupBox.setTitle('Restart') RestartGroupBoxLayout = qt.QFormLayout(RestartGroupBox) self.__RestartButton = qt.QPushButton('Return to Step 1') RestartGroupBoxLayout.addRow(self.__RestartButton) self.__RemoveCroppedSubtractionMap = qt.QCheckBox() self.__RemoveCroppedSubtractionMap.checked = True self.__RemoveCroppedSubtractionMap.setToolTip( "Delete the cropped version of your subtaction map.") RestartGroupBoxLayout.addRow("Delete cropped subtraction map: ", self.__RemoveCroppedSubtractionMap) self.__RemoveFullSubtracitonMap = qt.QCheckBox() self.__RemoveFullSubtracitonMap.checked = True self.__RemoveFullSubtracitonMap.setToolTip( "Delete the full version of your subtaction map.") RestartGroupBoxLayout.addRow("Delete full subtraction map: ", self.__RemoveFullSubtracitonMap) self.__RemoveROI = qt.QCheckBox() self.__RemoveROI.checked = False self.__RemoveROI.setToolTip( "Delete the ROI resulting from your subtaction map.") RestartGroupBoxLayout.addRow("Delete ROI: ", self.__RemoveROI) # self.__RestartButton.setEnabled(0) self.__RestartButton.connect('clicked()', self.Restart) self.__RestartActivated = True self.__layout.addRow(RestartGroupBox) def hideUnwantedEditorUIElements(self): print self.__editorWidget.editBoxFrame self.__editorWidget.volumes.hide() print dir(self.__editorWidget) print slicer.util.findChildren() # for widgetName in slicer.util.findChildren(self.__editorWidget.editBoxFrame): for widgetName in slicer.util.findChildren(self.__editorWidget.helper): # widget = slicer.util.findChildren(self.__editorWidget.editBoxFrame) print widgetName.objectName # print widgetName.parent.name # widgetName.hide() for widget in [ 'DrawEffectToolButton', 'RectangleEffectToolButton', 'IdentifyIslandsEffectToolButton', 'RemoveIslandsEffectToolButton', 'SaveIslandEffectToolButton', 'RowFrame2' ]: slicer.util.findChildren(self.__editorWidget.editBoxFrame, widget)[0].hide() print slicer.util.findChildren('', 'EditColorFrame') def Restart(self): print self.__pNode slicer.mrmlScene.RemoveNode(testVolume) self.__pNode.SetParameter('baselineVolumeID', None) self.__pNode.SetParameter('croppedSubtractVolumeID', None) self.__pNode.SetParameter('croppedSubtractVolumeSegmentationID', None) self.__pNode.SetParameter('followupVolumeID', None) self.__pNode.SetParameter('roiNodeID', None) self.__pNode.SetParameter('roiTransformID', None) self.__pNode.SetParameter('subtractVolumeID', None) self.__pNode.SetParameter('vrDisplayNodeID', None) self.__pNode.SetParameter('thresholdRange', None) if self.__RestartActivated: self.workflow().goForward() def onThresholdChanged(self): if self.__vrOpacityMap == None: return range0 = self.__threshRange.minimumValue range1 = self.__threshRange.maximumValue self.__vrOpacityMap.RemoveAllPoints() self.__vrOpacityMap.AddPoint(range0 - 75, 0) self.__vrOpacityMap.AddPoint(range0, 1) self.__vrOpacityMap.AddPoint(range1, 1) self.__vrOpacityMap.AddPoint(range1 + 75, 0) def killButton(self): # ctk creates a useless final page button. This method gets rid of it. bl = slicer.util.findChildren(text='ReviewStep') ex = slicer.util.findChildren('', 'EditColorFrame') if len(bl): bl[0].hide() if len(ex): ex[0].hide() print 'success' else: print 'fail' self.__editLabelMapsFrame = slicer.util.findChildren( '', 'EditLabelMapsFrame')[0] self.__toolsColor = EditorLib.EditColor(self.__editLabelMapsFrame) def validate(self, desiredBranchId): # For now, no validation required. self.__parent.validationSucceeded(desiredBranchId) def onEntry(self, comingFrom, transitionType): super(ReviewStep, self).onEntry(comingFrom, transitionType) # self.__RestartButton.setEnabled(1) self.__RestartActivated = True pNode = self.parameterNode() self.__pNode = pNode self.__clippingModelNode = Helper.getNodeByID( pNode.GetParameter('clippingModelNodeID')) print self.__clippingModelNode print pNode.GetParameter('clippingModelNodeID') self.__baselineVolumeID = pNode.GetParameter('baselineVolumeID') self.__followupVolumeID = pNode.GetParameter('followupVolumeID') self.__subtractVolumeID = pNode.GetParameter('subtractVolumeID') self.__roiNodeID = pNode.GetParameter('roiNodeID') self.__followupVolumeNode = Helper.getNodeByID(self.__followupVolumeID) self.__subtractVolumeNode = Helper.getNodeByID(self.__subtractVolumeID) self.__vrDisplayNodeID = pNode.GetParameter('vrDisplayNodeID') self.__roiSegmentationNode = Helper.getNodeByID( pNode.GetParameter('croppedSubtractVolumeSegmentationID')) self.__roiVolumeNode = Helper.getNodeByID( pNode.GetParameter('croppedSubtractVolumeID')) self.__editorWidget.setMergeNode(self.__roiSegmentationNode) self.__clippingModelNode.GetDisplayNode().VisibilityOn() followupRange = self.__followupVolumeNode.GetImageData( ).GetScalarRange() ROIRange = self.__roiSegmentationNode.GetImageData().GetScalarRange() if self.__vrDisplayNode == None: if self.__vrDisplayNodeID != '': self.__vrDisplayNode = slicer.mrmlScene.GetNodeByID( self.__vrDisplayNodeID) self.__vrDisplayNode.SetCroppingEnabled(1) self.__followupVolumeNode.AddAndObserveDisplayNodeID( self.__vrDisplayNode.GetID()) Helper.InitVRDisplayNode(self.__vrDisplayNode, self.__followupVolumeID, self.__roiNodeID) self.__threshRange.minimum = followupRange[0] self.__threshRange.maximum = followupRange[1] self.__threshRange.setValues(followupRange[1] / 3, 2 * followupRange[1] / 3) self.__vrOpacityMap = self.__vrDisplayNode.GetVolumePropertyNode( ).GetVolumeProperty().GetScalarOpacity() vrColorMap = self.__vrDisplayNode.GetVolumePropertyNode( ).GetVolumeProperty().GetRGBTransferFunction() vrColorMap.RemoveAllPoints() vrColorMap.AddRGBPoint(followupRange[0], 0.8, 0.8, 0) vrColorMap.AddRGBPoint(followupRange[1], 0.8, 0.8, 0) self.__vrDisplayNode.VisibilityOn() # threshRange = [self.__threshRange.minimumValue, self.__threshRange.maximumValue] # self.__vrOpacityMap.RemoveAllPoints() # self.__vrOpacityMap.AddPoint(threshRange[1]/2-75,0) # self.__vrOpacityMap.AddPoint(threshRange[1]/2,1) # self.__vrOpacityMap.AddPoint(threshRange[1]/2+200,1) # self.__vrOpacityMap.AddPoint(threshRange[1]/2+250,0) Helper.SetBgFgVolumes(self.__baselineVolumeID, self.__followupVolumeID) Helper.SetLabelVolume(self.__roiSegmentationNode.GetID()) self.onThresholdChanged() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def onExit(self, goingTo, transitionType): # extra error checking, in case the user manages to click ReportROI button super(BeersSingleStep, self).onExit(goingTo, transitionType)
class qSlicerReportingModuleWidget: def __init__(self, parent=None): if not parent: self.parent = slicer.qMRMLWidget() self.parent.setLayout(qt.QVBoxLayout()) self.parent.setMRMLScene(slicer.mrmlScene) self.layout = self.parent.layout() self.setup() self.parent.show() else: self.parent = parent self.layout = parent.layout() # Reference to the logic that Slicer instantiated self.__logic = slicer.modules.reporting.logic() if not self.__logic: # create a new instance self.__logic = slicer.modulelogic.vtkSlicerReportingModuleLogic() # Get the location and initialize the DICOM DB settings = qt.QSettings() self.__dbFileName = settings.value("DatabaseDirectory", "") if self.__dbFileName == "": Helper.Warning("DICOM Database is not accessible.") else: self.__dbFileName = self.__dbFileName + "/ctkDICOM.sql" if self.__logic.InitializeDICOMDatabase(self.__dbFileName): Helper.Info('DICOM database initialized correctly!') else: Helper.Error('Failed to initialize DICOM database at ' + self.__dbFileName) if not self.__logic.GetMRMLScene(): # set the logic's mrml scene self.__logic.SetMRMLScene(slicer.mrmlScene) # for export self.exportFileName = None self.exportFileDialog = None # initialize parameter node self.__parameterNode = None nNodes = slicer.mrmlScene.GetNumberOfNodesByClass( 'vtkMRMLScriptedModuleNode') for n in xrange(nNodes): compNode = slicer.mrmlScene.GetNthNodeByClass( n, 'vtkMRMLScriptedModuleNode') compNode.SetReferenceCount(compNode.GetReferenceCount() - 1) nodeid = None if compNode.GetModuleName() == 'Reporting': self.__parameterNode = compNode 'Found existing Reporting parameter node' break if self.__parameterNode == None: self.__parameterNode = slicer.vtkMRMLScriptedModuleNode() self.__parameterNode.SetModuleName('Reporting') self.__parameterNode.SetSingletonTag('Reporting') slicer.mrmlScene.AddNode(self.__parameterNode) # keep active report and volume self.__rNode = None self.__vNode = None if self.__parameterNode != None: paramID = self.__parameterNode.GetID() self.__logic.SetActiveParameterNodeID(paramID) else: Helper.Error('Unable to set logic active parameter node') # TODO: figure out why module/class hierarchy is different # between developer builds ans packages try: # for developer build... self.editUtil = EditorLib.EditUtil.EditUtil() except AttributeError: # for release package... self.editUtil = EditorLib.EditUtil() def setup(self): # # Input frame # self.__inputFrame = ctk.ctkCollapsibleButton() self.__inputFrame.text = "Input" self.__inputFrame.collapsed = 0 inputFrameLayout = qt.QFormLayout(self.__inputFrame) self.layout.addWidget(self.__inputFrame) # Active report node label = qt.QLabel('Report: ') self.__reportSelector = slicer.qMRMLNodeComboBox() self.__reportSelector.nodeTypes = ['vtkMRMLReportingReportNode'] self.__reportSelector.setMRMLScene(slicer.mrmlScene) self.__reportSelector.addEnabled = 1 self.__reportSelector.removeEnabled = 0 inputFrameLayout.addRow(label, self.__reportSelector) self.__reportSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onReportNodeChanged) # Volume being annotated (only one is allowed for the report) label = qt.QLabel( 'NOTE: Only volumes loaded from DICOM can be annotated!') inputFrameLayout.addRow(label) label = qt.QLabel('Annotated volume: ') self.__volumeSelector = slicer.qMRMLNodeComboBox() self.__volumeSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] # only allow volumes with the attribute DICOM.instanceUIDs self.__volumeSelector.addAttribute('vtkMRMLScalarVolumeNode', 'DICOM.instanceUIDs') self.__volumeSelector.setMRMLScene(slicer.mrmlScene) self.__volumeSelector.addEnabled = False inputFrameLayout.addRow(label, self.__volumeSelector) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onAnnotatedVolumeNodeChanged) self.__volumeSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # # Annotation frame -- vocabulary-based description of what is # being annotated/marked up in this report # self.__annotationsFrame = ctk.ctkCollapsibleButton() self.__annotationsFrame.text = "Annotation" self.__annotationsFrame.collapsed = 0 annotationsFrameLayout = qt.QFormLayout(self.__annotationsFrame) self.layout.addWidget(self.__annotationsFrame) self.__defaultColorNode = self.__logic.GetDefaultColorNode() self.__toolsColor = EditColor(self.__annotationsFrame, colorNode=self.__defaultColorNode) # # Markup frame -- summary of all the markup elements contained in the # report # self.__markupFrame = ctk.ctkCollapsibleButton() self.__markupFrame.text = "Markup" self.__markupFrame.collapsed = 0 markupFrameLayout = qt.QFormLayout(self.__markupFrame) self.layout.addWidget(self.__markupFrame) # Add a flag to switch between different tree view models self.__useNewTreeView = 1 # Add the tree widget if self.__useNewTreeView == 1: self.__markupTreeView = slicer.modulewidget.qMRMLReportingTreeView( ) self.__markupTreeView.sceneModelType = "DisplayableHierarchy" else: self.__markupTreeView = slicer.qMRMLTreeView() self.__markupTreeView.sceneModelType = "Displayable" self.__markupTreeView.setMRMLScene(self.__logic.GetMRMLScene()) self.__markupSliceText = qt.QLabel() markupFrameLayout.addRow(self.__markupSliceText) markupFrameLayout.addRow(self.__markupTreeView) # Editor frame self.__editorFrame = ctk.ctkCollapsibleButton() self.__editorFrame.text = 'Segmentation' self.__editorFrame.collapsed = 0 editorFrameLayout = qt.QFormLayout(self.__editorFrame) label = qt.QLabel('Segmentation volume: ') self.__segmentationSelector = slicer.qMRMLNodeComboBox() self.__segmentationSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__segmentationSelector.setMRMLScene(slicer.mrmlScene) self.__segmentationSelector.addEnabled = 1 self.__segmentationSelector.noneEnabled = 1 self.__segmentationSelector.removeEnabled = 0 self.__segmentationSelector.showHidden = 0 self.__segmentationSelector.showChildNodeTypes = 0 self.__segmentationSelector.selectNodeUponCreation = 1 self.__segmentationSelector.addAttribute('vtkMRMLScalarVolumeNode', 'LabelMap', 1) editorFrameLayout.addRow(label, self.__segmentationSelector) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent, showVolumesFrame=False) self.__editorWidget.setup() self.__editorWidget.toolsColor.frame.setVisible(False) editorFrameLayout.addRow(editorWidgetParent) markupFrameLayout.addRow(self.__editorFrame) self.__segmentationSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSegmentationNodeChanged) self.__segmentationSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # IO frame self.__ioFrame = ctk.ctkCollapsibleButton() self.__ioFrame.text = 'Import/Export' self.__ioFrame.collapsed = 1 ioFrameLayout = qt.QGridLayout(self.__ioFrame) self.layout.addWidget(self.__ioFrame) # Buttons to save/load report using AIM XML serialization label = qt.QLabel('Export folder') self.__exportFolderPicker = ctk.ctkDirectoryButton() exportButton = qt.QPushButton('Export') exportButton.connect('clicked()', self.onReportExport) ioFrameLayout.addWidget(label, 0, 0) ioFrameLayout.addWidget(self.__exportFolderPicker, 0, 1) ioFrameLayout.addWidget(exportButton, 0, 2) label = qt.QLabel('AIM file to import') self.__aimFilePicker = qt.QPushButton('N/A') self.__aimFilePicker.connect('clicked()', self.onSelectAIMFile) button = qt.QPushButton('Import') button.connect('clicked()', self.onReportImport) ioFrameLayout.addWidget(label, 1, 0) ioFrameLayout.addWidget(self.__aimFilePicker, 1, 1) ioFrameLayout.addWidget(button, 1, 2) self.__importAIMFile = None self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.updateWidgets() self.layout.addStretch(1) self.__editorParameterNode = self.editUtil.getParameterNode() self.__editorParameterNode.AddObserver( vtk.vtkCommand.ModifiedEvent, self.onEditorParameterNodeChanged) def onEditorParameterNodeChanged(self, caller, event): print('Editor parameter node changed') label = self.__editorParameterNode.GetParameter('label') if self.__rNode: print("Updating report label") self.__rNode.SetFindingLabel(int(label)) def enter(self): # switch to Two-over-Two layout lm = slicer.app.layoutManager() lm.setLayout(26) # two over two # update the logic to know that the module has been entered self.__logic.GUIHiddenOff() self.updateWidgetFromParameters() # respond to error events Helper.Info( 'Enter: Setting up connection to respond to logic error events') hasObserver = self.__logic.HasObserver(vtk.vtkCommand.ErrorEvent) if hasObserver == 0: tag = self.__logic.AddObserver(vtk.vtkCommand.ErrorEvent, self.respondToErrorMessage) # print '\tobserver tag = ',tag else: Helper.Debug('Logic already has an observer on the ErrorEvent') vnode = self.__volumeSelector.currentNode() if vnode != None: # print "Enter: setting active hierarchy from node ",vnode.GetID() # update the logic active markup self.__logic.SetActiveMarkupHierarchyIDFromNode(vnode) self.updateTreeView() self.__editorWidget.enter() def exit(self): self.updateParametersFromWidget() Helper.Debug( "Reporting Exit. Letting logic know that module has been exited") # let the module logic know that the GUI is hidden, so that fiducials can go elsewehre self.__logic.GUIHiddenOn() # disconnect observation self.__logic.RemoveObservers(vtk.vtkCommand.ErrorEvent) self.__editorWidget.exit() # respond to error events from the logic def respondToErrorMessage(self, caller, event): errorMessage = self.__logic.GetErrorMessage() # inform user only if the message is not empty since vtkErrorMacro invokes this event as well if errorMessage != None: Helper.Debug('respondToErrorMessage, event = ' + str(event) + ', message =\n\t' + str(errorMessage)) errorDialog = qt.QErrorMessage(self.parent) errorDialog.showMessage(errorMessage) def onMRMLSceneChanged(self, mrmlScene): if mrmlScene != self.__logic.GetMRMLScene(): self.__logic.SetMRMLScene(mrmlScene) self.__logic.RegisterNodes() self.__reportSelector.setMRMLScene(slicer.mrmlScene) def updateTreeView(self): # make the tree view update if self.__useNewTreeView == 1: self.__markupTreeView.updateTreeView() else: self.__markupTreeView.sceneModelType = "Displayable" nodeTypes = [ 'vtkMRMLDisplayableHierarchyNode', 'vtkMRMLAnnotationHierarchyNode', 'vtkMRMLAnnotationNode', 'vtkMRMLVolumeNode', 'vtkMRMLReportingReportNode' ] self.__markupTreeView.nodeTypes = nodeTypes self.__markupTreeView.listenNodeModifiedEvent = 1 self.__markupTreeView.sceneModelType = "Displayable" # show these nodes even if they're hidden by being children of hidden hierarchy nodes showHiddenNodeTypes = [ 'vtkMRMLAnnotationNode', 'vtkMRMLVolumeNode', 'vtkMRMLDisplayableHierarchyNode' ] self.__markupTreeView.model( ).showHiddenForTypes = showHiddenNodeTypes # set the root to be the current report hierarchy root if self.__rNode == None: Helper.Error("updateTreeView: report node is not initialized!") self.__markupTreeView.setRootNode(None) return else: # the tree root node has to be a hierarchy node, so get the associated hierarchy node for the active report node rootNode = slicer.vtkMRMLHierarchyNode( ).GetAssociatedHierarchyNode(self.__rNode.GetScene(), self.__rNode.GetID()) if rootNode: self.__markupTreeView.setRootNode(rootNode) Helper.Debug("Setting tree view root to be " + rootNode.GetID()) self.__markupTreeView.expandAll() else: Helper.Debug("Setting tree view root to be None") self.__markupTreeView.setRootNode(None) def onAnnotatedVolumeNodeChanged(self): Helper.Debug("onAnnotatedVolumeNodeChanged()") # get the current volume node selectedVolume = self.__volumeSelector.currentNode() # do the error checks if selectedVolume == None or self.__rNode == None: self.__volumeSelector.setCurrentNode(None) return uids = selectedVolume.GetAttribute('DICOM.instanceUIDs') if uids == None: Helper.ErrorPopup( "Volume \"" + selectedVolume.GetName() + "\" was not loaded from DICOM. Only volumes loaded from DICOM data can be annotated in the Reporting module." ) self.__volumeSelector.setCurrentNode(None) return nSlices = selectedVolume.GetImageData().GetExtent()[-1] + 1 if nSlices != len(string.split(uids)): Helper.ErrorPopup( "Volume \"" + selectedVolume.GetName() + "\" was loaded from multi-frame DICOM. Multi-frame DICOM is currently not supported by the Reporting module" ) self.__volumeSelector.setCurrentNode(None) return # volume node is valid! self.__vNode = selectedVolume # update the report node if self.__rNode != None: self.__rNode.SetVolumeNodeID(self.__vNode.GetID()) self.__rNode.SetName('Report for Volume ' + self.__vNode.GetName()) Helper.SetBgFgVolumes(self.__vNode.GetID(), '') Helper.RotateToVolumePlanes() # go over all label nodes in the scene # if there is a label that has the selected volume as associated node, # initialize label selector to show that label volumeNodes = slicer.mrmlScene.GetNodesByClass( 'vtkMRMLScalarVolumeNode') volumeNodes.SetReferenceCount(volumeNodes.GetReferenceCount() - 1) associatedLabelFound = False for i in range(volumeNodes.GetNumberOfItems()): vol = volumeNodes.GetItemAsObject(i) associatedNodeID = vol.GetAttribute('AssociatedNodeID') label = vol.GetAttribute('LabelMap') if associatedNodeID == self.__vNode.GetID() and label == '1': Helper.SetLabelVolume(vol.GetID()) associatedLabelFound = True # if there is no associated label node, set the selector to none if associatedLabelFound == False: Helper.SetLabelVolume("") orientation = Helper.GetScanOrderSliceName(self.__vNode) message = "Slice viewers to be used for markup: " for sliceViewer in orientation: message = message + sliceViewer if orientation.index(sliceViewer) < (len(orientation) - 1): message = message + ", " Helper.Debug(message) self.__markupSliceText.text = message # take the first one self.__parameterNode.SetParameter('acquisitionSliceViewer', orientation[0]) # print "Calling logic to set up hierarchy" self.__logic.InitializeHierarchyForVolume(self.__vNode) self.updateTreeView() def onSegmentationNodeChanged(self): Helper.Debug('onSegmentationNodeChanged()') if self.__vNode == None: Helper.Error( 'Should not be possible to select segmentation unless annotated volume is initialized!' ) return # get the current segmentation (label) node sNode = self.__segmentationSelector.currentNode() if sNode == None: self.updateWidgets() return # if it's a new label, it should have/will be added to the report # automatically image = sNode.GetImageData() if image == None: Helper.initializeNewLabel(sNode, self.__vNode) else: # if it's an existing label, we need to check that the geometry matches # the annotated label geometry, and if so, add it to the hierarchy if Helper.GeometriesMatch(sNode, self.__vNode) == False: Helper.ErrorPopup( 'The geometry of the segmentation label you attempted to select does not match the geometry of the volume being annotated! Please select a different label or create a new one.' ) self.__segmentationSelector.setCurrentNode(None) self.updateWidgets() return # assign the color LUT we use dNode = sNode.GetDisplayNode() dNode.SetAndObserveColorNodeID(self.__defaultColorNode.GetID()) sNode.SetAttribute('AssociatedNodeID', self.__vNode.GetID()) self.__logic.AddNodeToReport(sNode) # assign the volume and the selected color to the editor parameter node Helper.SetLabelVolume(sNode.GetID()) # TODO: disable adding new label node to the hierarchy if it was added # outside the reporting module self.__segmentationSelector.setCurrentNode(sNode) self.__editorWidget.setMasterNode(self.__vNode) self.__editorWidget.setMergeNode(sNode) self.__editorParameterNode.Modified() self.updateWidgets() def onReportNodeChanged(self): Helper.Debug("onReportNodeChanged()") # cancel the active effect in Editor if self.__editorWidget: self.__editorWidget.toolsBox.defaultEffect() # TODO # -- initialize annotations and markup frames based on the report node # content self.__rNode = self.__reportSelector.currentNode() self.__segmentationSelector.setCurrentNode(None) # disable editor frame self.onSegmentationNodeChanged() if self.__rNode != None: Helper.Debug("Selected report has changed to " + self.__rNode.GetID()) if self.__rNode.GetDICOMDatabaseFileName() == "": self.__rNode.SetDICOMDatabaseFileName(self.__dbFileName) self.__parameterNode.SetParameter('reportID', self.__rNode.GetID()) # setup the default color node, if not initialized if self.__rNode.GetColorNodeID() == "": self.__rNode.SetColorNodeID(self.__defaultColorNode.GetID()) Helper.Debug('Set color node id to ' + self.__defaultColorNode.GetID()) else: Helper.Debug('Color node has already been set to ' + self.__rNode.GetColorNodeID()) self.__logic.InitializeHierarchyForReport(self.__rNode) self.updateTreeView() vID = self.__rNode.GetVolumeNodeID() if vID: Helper.Debug('Have a volume node id in the report ' + vID + ', setting current volume node selector') self.__vNode = slicer.mrmlScene.GetNodeByID(vID) self.__volumeSelector.setCurrentNode(self.__vNode) else: Helper.Debug( 'Do not have a volume id in the report, setting current volume node selector to none' ) # set the volume to be none self.__vNode = None self.__volumeSelector.setCurrentNode(None) # hide the markups that go with other report nodes self.__logic.HideAnnotationsForOtherReports(self.__rNode) # update the GUI annotation name/label/color self.updateWidgets() # initialize the label used by the EditorWidget if self.__editorParameterNode: self.__editorParameterNode.SetParameter( 'label', str(self.__rNode.GetFindingLabel())) ''' Load report and initialize GUI based on .xml report file content ''' def onReportImport(self): print('onReportImport here!!!') # TODO # -- popup file open dialog to choose the .xml AIM file # -- warn the user if the selected report node is not empty # -- populate the selected report node, initializing annotation template, # content, markup hierarchy and content if not self.__importAIMFile: Helper.Debug('onReportImport: import file name not specified') return Helper.Debug('onReportImport') # For now, always create a new report node newReport = slicer.modulemrml.vtkMRMLReportingReportNode() # always use the default color map newReport.SetColorNodeID(self.__defaultColorNode.GetID()) slicer.mrmlScene.AddNode(newReport) self.__reportSelector.setCurrentNode(newReport) self.onReportNodeChanged() # initialize the report hierarchy # -- assume that report node has been created and is in the selector Helper.LoadAIMFile(newReport.GetID(), self.__importAIMFile) # update the GUI Helper.Debug('onReportImport --> calling onReportNodeChanged()') self.onReportNodeChanged() def onSelectAIMFile(self): # -- popup file dialog prompting output file if not self.__importAIMFile: fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose AIM report", "/", "XML Files (*.xml)") else: lastDir = self.__importAIMFile[0:string. rfind(self.__importAIMFile, '/')] fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose AIM report", lastDir, "XML Files (*.xml)") if fileName == '': return self.__importAIMFile = fileName try: label = string.split(fileName, '/')[-1] except: label = fileName self.__aimFilePicker.text = label ''' Save report to an xml file ''' def onReportExport(self): if self.__rNode == None: return Helper.Debug('onReportExport') exportDirectory = self.__exportFolderPicker.directory self.__rNode.SetStorageDirectoryName(exportDirectory) Helper.Debug('Will export to ' + exportDirectory) # use the currently selected report self.__rNode = self.__reportSelector.currentNode() if self.__rNode == None: return # -- traverse markup hierarchy and translate retval = self.__logic.SaveReportToAIM(self.__rNode) if retval == EXIT_FAILURE: Helper.Error("Failed to save report to '" + exportDirectory + "'") else: Helper.Debug("Saved report to '" + exportDirectory + "'") # This code is commented out because repeated insertion into the database # leads to database lockout (illustrated in Testing/RepeatInsertTest.py). # For now, the solution will be to import the created DICOM objects # manually. # attempt to insert each of the saved items into the database #filesToInsert = glob.glob(exportDirectory+'/*') #for f in filesToInsert: # Helper.Debug('Inserting '+f+' into DICOM database...') # slicer.dicomDatabase.insert(f) # Helper.Debug('Done') def updateWidgetFromParameters(self): pn = self.__parameterNode if pn == None: return reportID = pn.GetParameter('reportID') if reportID != None: self.__rNode = Helper.getNodeByID(reportID) # AF: looks like this does not trigger event, why? self.__reportSelector.setCurrentNode(self.__rNode) def updateParametersFromWidget(self): pn = self.__parameterNode if pn == None: return report = self.__reportSelector.currentNode() if report != None: pn.SetParameter('reportID', report.GetID()) def updateWidgets(self): Helper.Debug("updateWidgets()") report = self.__rNode volume = None label = self.__segmentationSelector.currentNode() if report != None: self.__reportSelector.setCurrentNode(self.__rNode) volume = slicer.mrmlScene.GetNodeByID(report.GetVolumeNodeID()) # TODO: get the label node from volume hieararchy if volume != None: self.__volumeSelector.setCurrentNode(volume) self.__markupFrame.enabled = 1 self.__annotationsFrame.enabled = 1 self.__volumeSelector.enabled = 0 else: self.__volumeSelector.enabled = 1 self.__markupFrame.enabled = 0 self.__annotationsFrame.enabled = 0 else: self.__volumeSelector.enabled = 0 self.__markupFrame.enabled = 0 self.__annotationsFrame.enabled = 0 if not label: self.__editorWidget.editLabelMapsFrame.collapsed = True self.__editorWidget.editLabelMapsFrame.setEnabled(False) else: self.__editorWidget.editLabelMapsFrame.collapsed = False self.__editorWidget.editLabelMapsFrame.setEnabled(True)
class qSlicerReportingModuleWidget: def __init__( self, parent=None ): if not parent: self.parent = slicer.qMRMLWidget() self.parent.setLayout( qt.QVBoxLayout()) self.parent.setMRMLScene(slicer.mrmlScene) self.layout = self.parent.layout() self.setup() self.parent.show() else: self.parent = parent self.layout = parent.layout() # Reference to the logic that Slicer instantiated self.__logic = slicer.modules.reporting.logic() if not self.__logic: # create a new instance self.__logic = slicer.modulelogic.vtkSlicerReportingModuleLogic() # Get the location and initialize the DICOM DB settings = qt.QSettings() self.__dbFileName = settings.value("DatabaseDirectory","") if self.__dbFileName == "": Helper.Warning("DICOM Database is not accessible.") else: self.__dbFileName = self.__dbFileName+"/ctkDICOM.sql" if self.__logic.InitializeDICOMDatabase(self.__dbFileName): Helper.Info('DICOM database initialized correctly!') else: Helper.Error('Failed to initialize DICOM database at '+self.__dbFileName) if not self.__logic.GetMRMLScene(): # set the logic's mrml scene self.__logic.SetMRMLScene(slicer.mrmlScene) # for export self.exportFileName = None self.exportFileDialog = None # initialize parameter node self.__parameterNode = None nNodes = slicer.mrmlScene.GetNumberOfNodesByClass('vtkMRMLScriptedModuleNode') for n in xrange(nNodes): compNode = slicer.mrmlScene.GetNthNodeByClass(n, 'vtkMRMLScriptedModuleNode') compNode.SetReferenceCount(compNode.GetReferenceCount() - 1) nodeid = None if compNode.GetModuleName() == 'Reporting': self.__parameterNode = compNode 'Found existing Reporting parameter node' break if self.__parameterNode == None: self.__parameterNode = slicer.vtkMRMLScriptedModuleNode() self.__parameterNode.SetModuleName('Reporting') self.__parameterNode.SetSingletonTag('Reporting') slicer.mrmlScene.AddNode(self.__parameterNode) # keep active report and volume self.__rNode = None self.__vNode = None if self.__parameterNode != None: paramID = self.__parameterNode.GetID() self.__logic.SetActiveParameterNodeID(paramID) else: Helper.Error('Unable to set logic active parameter node') # TODO: figure out why module/class hierarchy is different # between developer builds ans packages try: # for developer build... self.editUtil = EditorLib.EditUtil.EditUtil() except AttributeError: # for release package... self.editUtil = EditorLib.EditUtil() def setup( self ): # # Input frame # self.__inputFrame = ctk.ctkCollapsibleButton() self.__inputFrame.text = "Input" self.__inputFrame.collapsed = 0 inputFrameLayout = qt.QFormLayout(self.__inputFrame) self.layout.addWidget(self.__inputFrame) # Active report node label = qt.QLabel('Report: ') self.__reportSelector = slicer.qMRMLNodeComboBox() self.__reportSelector.nodeTypes = ['vtkMRMLReportingReportNode'] self.__reportSelector.setMRMLScene(slicer.mrmlScene) self.__reportSelector.addEnabled = 1 self.__reportSelector.removeEnabled = 0 inputFrameLayout.addRow(label, self.__reportSelector) self.__reportSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onReportNodeChanged) # Volume being annotated (only one is allowed for the report) label = qt.QLabel('NOTE: Only volumes loaded from DICOM can be annotated!') inputFrameLayout.addRow(label) label = qt.QLabel('Annotated volume: ') self.__volumeSelector = slicer.qMRMLNodeComboBox() self.__volumeSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] # only allow volumes with the attribute DICOM.instanceUIDs self.__volumeSelector.addAttribute('vtkMRMLScalarVolumeNode','DICOM.instanceUIDs') self.__volumeSelector.setMRMLScene(slicer.mrmlScene) self.__volumeSelector.addEnabled = False inputFrameLayout.addRow(label, self.__volumeSelector) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onAnnotatedVolumeNodeChanged) self.__volumeSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # # Annotation frame -- vocabulary-based description of what is # being annotated/marked up in this report # self.__annotationsFrame = ctk.ctkCollapsibleButton() self.__annotationsFrame.text = "Annotation" self.__annotationsFrame.collapsed = 0 annotationsFrameLayout = qt.QFormLayout(self.__annotationsFrame) self.layout.addWidget(self.__annotationsFrame) self.__defaultColorNode = self.__logic.GetDefaultColorNode() self.__toolsColor = EditColor(self.__annotationsFrame,colorNode=self.__defaultColorNode) # # Markup frame -- summary of all the markup elements contained in the # report # self.__markupFrame = ctk.ctkCollapsibleButton() self.__markupFrame.text = "Markup" self.__markupFrame.collapsed = 0 markupFrameLayout = qt.QFormLayout(self.__markupFrame) self.layout.addWidget(self.__markupFrame) # Add a flag to switch between different tree view models self.__useNewTreeView = 1 # Add the tree widget if self.__useNewTreeView == 1: self.__markupTreeView = slicer.modulewidget.qMRMLReportingTreeView() self.__markupTreeView.sceneModelType = "DisplayableHierarchy" else: self.__markupTreeView = slicer.qMRMLTreeView() self.__markupTreeView.sceneModelType = "Displayable" self.__markupTreeView.setMRMLScene(self.__logic.GetMRMLScene()) self.__markupSliceText = qt.QLabel() markupFrameLayout.addRow(self.__markupSliceText) markupFrameLayout.addRow(self.__markupTreeView) # Editor frame self.__editorFrame = ctk.ctkCollapsibleButton() self.__editorFrame.text = 'Segmentation' self.__editorFrame.collapsed = 0 editorFrameLayout = qt.QFormLayout(self.__editorFrame) label = qt.QLabel('Segmentation volume: ') self.__segmentationSelector = slicer.qMRMLNodeComboBox() self.__segmentationSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__segmentationSelector.setMRMLScene(slicer.mrmlScene) self.__segmentationSelector.addEnabled = 1 self.__segmentationSelector.noneEnabled = 1 self.__segmentationSelector.removeEnabled = 0 self.__segmentationSelector.showHidden = 0 self.__segmentationSelector.showChildNodeTypes = 0 self.__segmentationSelector.selectNodeUponCreation = 1 self.__segmentationSelector.addAttribute('vtkMRMLScalarVolumeNode','LabelMap',1) editorFrameLayout.addRow(label, self.__segmentationSelector) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent,showVolumesFrame=False) self.__editorWidget.setup() self.__editorWidget.toolsColor.frame.setVisible(False) editorFrameLayout.addRow(editorWidgetParent) markupFrameLayout.addRow(self.__editorFrame) self.__segmentationSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSegmentationNodeChanged) self.__segmentationSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # IO frame self.__ioFrame = ctk.ctkCollapsibleButton() self.__ioFrame.text = 'Import/Export' self.__ioFrame.collapsed = 1 ioFrameLayout = qt.QGridLayout(self.__ioFrame) self.layout.addWidget(self.__ioFrame) # Buttons to save/load report using AIM XML serialization label = qt.QLabel('Export folder') self.__exportFolderPicker = ctk.ctkDirectoryButton() exportButton = qt.QPushButton('Export') exportButton.connect('clicked()', self.onReportExport) ioFrameLayout.addWidget(label,0,0) ioFrameLayout.addWidget(self.__exportFolderPicker,0,1) ioFrameLayout.addWidget(exportButton,0,2) label = qt.QLabel('AIM file to import') self.__aimFilePicker = qt.QPushButton('N/A') self.__aimFilePicker.connect('clicked()',self.onSelectAIMFile) button = qt.QPushButton('Import') button.connect('clicked()', self.onReportImport) ioFrameLayout.addWidget(label,1,0) ioFrameLayout.addWidget(self.__aimFilePicker,1,1) ioFrameLayout.addWidget(button,1,2) self.__importAIMFile = None self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.updateWidgets() self.layout.addStretch(1) self.__editorParameterNode = self.editUtil.getParameterNode() self.__editorParameterNode.AddObserver(vtk.vtkCommand.ModifiedEvent, self.onEditorParameterNodeChanged) def onEditorParameterNodeChanged(self,caller,event): print('Editor parameter node changed') label = self.__editorParameterNode.GetParameter('label') if self.__rNode: print("Updating report label") self.__rNode.SetFindingLabel(int(label)) def enter(self): # switch to Two-over-Two layout lm = slicer.app.layoutManager() lm.setLayout(26) # two over two # update the logic to know that the module has been entered self.__logic.GUIHiddenOff() self.updateWidgetFromParameters() # respond to error events Helper.Info('Enter: Setting up connection to respond to logic error events') hasObserver = self.__logic.HasObserver(vtk.vtkCommand.ErrorEvent) if hasObserver == 0: tag = self.__logic.AddObserver(vtk.vtkCommand.ErrorEvent, self.respondToErrorMessage) # print '\tobserver tag = ',tag else: Helper.Debug('Logic already has an observer on the ErrorEvent') vnode = self.__volumeSelector.currentNode() if vnode != None: # print "Enter: setting active hierarchy from node ",vnode.GetID() # update the logic active markup self.__logic.SetActiveMarkupHierarchyIDFromNode(vnode) self.updateTreeView() self.__editorWidget.enter() def exit(self): self.updateParametersFromWidget() Helper.Debug("Reporting Exit. Letting logic know that module has been exited") # let the module logic know that the GUI is hidden, so that fiducials can go elsewehre self.__logic.GUIHiddenOn() # disconnect observation self.__logic.RemoveObservers(vtk.vtkCommand.ErrorEvent) self.__editorWidget.exit() # respond to error events from the logic def respondToErrorMessage(self, caller, event): errorMessage = self.__logic.GetErrorMessage() # inform user only if the message is not empty since vtkErrorMacro invokes this event as well if errorMessage != None: Helper.Debug('respondToErrorMessage, event = '+str(event)+', message =\n\t'+str(errorMessage)) errorDialog = qt.QErrorMessage(self.parent) errorDialog.showMessage(errorMessage) def onMRMLSceneChanged(self, mrmlScene): if mrmlScene != self.__logic.GetMRMLScene(): self.__logic.SetMRMLScene(mrmlScene) self.__logic.RegisterNodes() self.__reportSelector.setMRMLScene(slicer.mrmlScene) def updateTreeView(self): # make the tree view update if self.__useNewTreeView == 1: self.__markupTreeView.updateTreeView() else: self.__markupTreeView.sceneModelType = "Displayable" nodeTypes = ['vtkMRMLDisplayableHierarchyNode', 'vtkMRMLAnnotationHierarchyNode', 'vtkMRMLAnnotationNode', 'vtkMRMLVolumeNode', 'vtkMRMLReportingReportNode'] self.__markupTreeView.nodeTypes = nodeTypes self.__markupTreeView.listenNodeModifiedEvent = 1 self.__markupTreeView.sceneModelType = "Displayable" # show these nodes even if they're hidden by being children of hidden hierarchy nodes showHiddenNodeTypes = ['vtkMRMLAnnotationNode', 'vtkMRMLVolumeNode', 'vtkMRMLDisplayableHierarchyNode'] self.__markupTreeView.model().showHiddenForTypes = showHiddenNodeTypes # set the root to be the current report hierarchy root if self.__rNode == None: Helper.Error("updateTreeView: report node is not initialized!") self.__markupTreeView.setRootNode(None) return else: # the tree root node has to be a hierarchy node, so get the associated hierarchy node for the active report node rootNode = slicer.vtkMRMLHierarchyNode().GetAssociatedHierarchyNode(self.__rNode.GetScene(), self.__rNode.GetID()) if rootNode: self.__markupTreeView.setRootNode(rootNode) Helper.Debug("Setting tree view root to be " + rootNode.GetID()) self.__markupTreeView.expandAll() else: Helper.Debug("Setting tree view root to be None") self.__markupTreeView.setRootNode(None) def onAnnotatedVolumeNodeChanged(self): Helper.Debug("onAnnotatedVolumeNodeChanged()") # get the current volume node selectedVolume = self.__volumeSelector.currentNode() # do the error checks if selectedVolume == None or self.__rNode == None: self.__volumeSelector.setCurrentNode(None) return uids = selectedVolume.GetAttribute('DICOM.instanceUIDs') if uids == None: Helper.ErrorPopup("Volume \""+selectedVolume.GetName()+"\" was not loaded from DICOM. Only volumes loaded from DICOM data can be annotated in the Reporting module.") self.__volumeSelector.setCurrentNode(None) return nSlices = selectedVolume.GetImageData().GetExtent()[-1]+1 if nSlices != len(string.split(uids)): Helper.ErrorPopup("Volume \""+selectedVolume.GetName()+"\" was loaded from multi-frame DICOM. Multi-frame DICOM is currently not supported by the Reporting module") self.__volumeSelector.setCurrentNode(None) return # volume node is valid! self.__vNode = selectedVolume # update the report node if self.__rNode != None: self.__rNode.SetVolumeNodeID(self.__vNode.GetID()) self.__rNode.SetName('Report for Volume '+self.__vNode.GetName()) Helper.SetBgFgVolumes(self.__vNode.GetID(), '') Helper.RotateToVolumePlanes() # go over all label nodes in the scene # if there is a label that has the selected volume as associated node, # initialize label selector to show that label volumeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode') volumeNodes.SetReferenceCount(volumeNodes.GetReferenceCount()-1) associatedLabelFound = False for i in range(volumeNodes.GetNumberOfItems()): vol = volumeNodes.GetItemAsObject(i) associatedNodeID = vol.GetAttribute('AssociatedNodeID') label = vol.GetAttribute('LabelMap') if associatedNodeID == self.__vNode.GetID() and label == '1': Helper.SetLabelVolume(vol.GetID()) associatedLabelFound = True # if there is no associated label node, set the selector to none if associatedLabelFound == False: Helper.SetLabelVolume("") orientation = Helper.GetScanOrderSliceName(self.__vNode) message = "Slice viewers to be used for markup: " for sliceViewer in orientation: message = message + sliceViewer if orientation.index(sliceViewer) < (len(orientation) - 1 ): message = message + ", " Helper.Debug(message) self.__markupSliceText.text = message # take the first one self.__parameterNode.SetParameter('acquisitionSliceViewer',orientation[0]) # print "Calling logic to set up hierarchy" self.__logic.InitializeHierarchyForVolume(self.__vNode) self.updateTreeView() def onSegmentationNodeChanged(self): Helper.Debug('onSegmentationNodeChanged()') if self.__vNode == None: Helper.Error('Should not be possible to select segmentation unless annotated volume is initialized!') return # get the current segmentation (label) node sNode = self.__segmentationSelector.currentNode() if sNode == None: self.updateWidgets() return # if it's a new label, it should have/will be added to the report # automatically image = sNode.GetImageData() if image == None: Helper.initializeNewLabel(sNode, self.__vNode) else: # if it's an existing label, we need to check that the geometry matches # the annotated label geometry, and if so, add it to the hierarchy if Helper.GeometriesMatch(sNode, self.__vNode) == False: Helper.ErrorPopup('The geometry of the segmentation label you attempted to select does not match the geometry of the volume being annotated! Please select a different label or create a new one.') self.__segmentationSelector.setCurrentNode(None) self.updateWidgets() return # assign the color LUT we use dNode = sNode.GetDisplayNode() dNode.SetAndObserveColorNodeID(self.__defaultColorNode.GetID()) sNode.SetAttribute('AssociatedNodeID',self.__vNode.GetID()) self.__logic.AddNodeToReport(sNode) # assign the volume and the selected color to the editor parameter node Helper.SetLabelVolume(sNode.GetID()) # TODO: disable adding new label node to the hierarchy if it was added # outside the reporting module self.__segmentationSelector.setCurrentNode(sNode) self.__editorWidget.setMasterNode(self.__vNode) self.__editorWidget.setMergeNode(sNode) self.__editorParameterNode.Modified() self.updateWidgets() def onReportNodeChanged(self): Helper.Debug("onReportNodeChanged()") # cancel the active effect in Editor if self.__editorWidget: self.__editorWidget.toolsBox.defaultEffect() # TODO # -- initialize annotations and markup frames based on the report node # content self.__rNode = self.__reportSelector.currentNode() self.__segmentationSelector.setCurrentNode(None) # disable editor frame self.onSegmentationNodeChanged() if self.__rNode != None: Helper.Debug("Selected report has changed to " + self.__rNode.GetID()) if self.__rNode.GetDICOMDatabaseFileName() == "": self.__rNode.SetDICOMDatabaseFileName(self.__dbFileName) self.__parameterNode.SetParameter('reportID', self.__rNode.GetID()) # setup the default color node, if not initialized if self.__rNode.GetColorNodeID() == "": self.__rNode.SetColorNodeID(self.__defaultColorNode.GetID()) Helper.Debug('Set color node id to '+self.__defaultColorNode.GetID()) else: Helper.Debug('Color node has already been set to '+self.__rNode.GetColorNodeID()) self.__logic.InitializeHierarchyForReport(self.__rNode) self.updateTreeView() vID = self.__rNode.GetVolumeNodeID() if vID: Helper.Debug('Have a volume node id in the report ' + vID + ', setting current volume node selector') self.__vNode = slicer.mrmlScene.GetNodeByID(vID) self.__volumeSelector.setCurrentNode(self.__vNode) else: Helper.Debug('Do not have a volume id in the report, setting current volume node selector to none') # set the volume to be none self.__vNode = None self.__volumeSelector.setCurrentNode(None) # hide the markups that go with other report nodes self.__logic.HideAnnotationsForOtherReports(self.__rNode) # update the GUI annotation name/label/color self.updateWidgets() # initialize the label used by the EditorWidget if self.__editorParameterNode: self.__editorParameterNode.SetParameter('label',str(self.__rNode.GetFindingLabel())) ''' Load report and initialize GUI based on .xml report file content ''' def onReportImport(self): print('onReportImport here!!!') # TODO # -- popup file open dialog to choose the .xml AIM file # -- warn the user if the selected report node is not empty # -- populate the selected report node, initializing annotation template, # content, markup hierarchy and content if not self.__importAIMFile: Helper.Debug('onReportImport: import file name not specified') return Helper.Debug('onReportImport') # For now, always create a new report node newReport = slicer.modulemrml.vtkMRMLReportingReportNode() # always use the default color map newReport.SetColorNodeID(self.__defaultColorNode.GetID()) slicer.mrmlScene.AddNode(newReport) self.__reportSelector.setCurrentNode(newReport) self.onReportNodeChanged() # initialize the report hierarchy # -- assume that report node has been created and is in the selector Helper.LoadAIMFile(newReport.GetID(),self.__importAIMFile) # update the GUI Helper.Debug('onReportImport --> calling onReportNodeChanged()') self.onReportNodeChanged() def onSelectAIMFile(self): # -- popup file dialog prompting output file if not self.__importAIMFile: fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose AIM report","/","XML Files (*.xml)") else: lastDir = self.__importAIMFile[0:string.rfind(self.__importAIMFile,'/')] fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose AIM report",lastDir,"XML Files (*.xml)") if fileName == '': return self.__importAIMFile = fileName try: label = string.split(fileName,'/')[-1] except: label = fileName self.__aimFilePicker.text = label ''' Save report to an xml file ''' def onReportExport(self): if self.__rNode == None: return Helper.Debug('onReportExport') exportDirectory = self.__exportFolderPicker.directory self.__rNode.SetStorageDirectoryName(exportDirectory) Helper.Debug('Will export to '+exportDirectory) # use the currently selected report self.__rNode = self.__reportSelector.currentNode() if self.__rNode == None: return # -- traverse markup hierarchy and translate retval = self.__logic.SaveReportToAIM(self.__rNode) if retval == EXIT_FAILURE: Helper.Error("Failed to save report to '"+exportDirectory+"'") else: Helper.Debug("Saved report to '"+exportDirectory+"'") # This code is commented out because repeated insertion into the database # leads to database lockout (illustrated in Testing/RepeatInsertTest.py). # For now, the solution will be to import the created DICOM objects # manually. # attempt to insert each of the saved items into the database #filesToInsert = glob.glob(exportDirectory+'/*') #for f in filesToInsert: # Helper.Debug('Inserting '+f+' into DICOM database...') # slicer.dicomDatabase.insert(f) # Helper.Debug('Done') def updateWidgetFromParameters(self): pn = self.__parameterNode if pn == None: return reportID = pn.GetParameter('reportID') if reportID != None: self.__rNode = Helper.getNodeByID(reportID) # AF: looks like this does not trigger event, why? self.__reportSelector.setCurrentNode(self.__rNode) def updateParametersFromWidget(self): pn = self.__parameterNode if pn == None: return report = self.__reportSelector.currentNode() if report != None: pn.SetParameter('reportID', report.GetID()) def updateWidgets(self): Helper.Debug("updateWidgets()") report = self.__rNode volume = None label = self.__segmentationSelector.currentNode() if report != None: self.__reportSelector.setCurrentNode(self.__rNode) volume = slicer.mrmlScene.GetNodeByID(report.GetVolumeNodeID()) # TODO: get the label node from volume hieararchy if volume != None: self.__volumeSelector.setCurrentNode(volume) self.__markupFrame.enabled = 1 self.__annotationsFrame.enabled = 1 self.__volumeSelector.enabled = 0 else: self.__volumeSelector.enabled = 1 self.__markupFrame.enabled = 0 self.__annotationsFrame.enabled = 0 else: self.__volumeSelector.enabled = 0 self.__markupFrame.enabled = 0 self.__annotationsFrame.enabled = 0 if not label: self.__editorWidget.editLabelMapsFrame.collapsed = True self.__editorWidget.editLabelMapsFrame.setEnabled(False) else: self.__editorWidget.editLabelMapsFrame.collapsed = False self.__editorWidget.editLabelMapsFrame.setEnabled(True)
class VisAIReWidget: def __init__(self, parent = None): 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() # TODO: figure out why module/class hierarchy is different # between developer builds ans packages try: # for developer build... self.editUtil = EditorLib.EditUtil.EditUtil() except AttributeError: # for release package... self.editUtil = EditorLib.EditUtil() def setup(self): self.viewMode = 'compare' self.compare0 = None self.compare1 = None self.sidebyside0 = None self.sidebyside1 = None # Instantiate and connect widgets ... # reload button # (use this during development, but remove it when delivering # your module to users) self.reloadButton = qt.QPushButton("Reload") self.reloadButton.toolTip = "Reload this module." self.reloadButton.name = "VisAIRe Reload" self.layout.addWidget(self.reloadButton) self.reloadButton.connect('clicked()', self.onReload) # reload and test button # (use this during development, but remove it when delivering # your module to users) #self.reloadAndTestButton = qt.QPushButton("Reload and Test") #self.reloadAndTestButton.toolTip = "Reload this module and then run the self tests." #self.layout.addWidget(self.reloadAndTestButton) #self.reloadAndTestButton.connect('clicked()', self.onReloadAndTest) # entry for the rater name label = qt.QLabel('Rater name:') self.raterName = qt.QLineEdit() self.layout.addWidget(label) self.layout.addWidget(self.raterName) # Configuration file picker label = qt.QLabel('Configuration file:') self.configFilePicker = qt.QPushButton('N/A') self.configFilePicker.connect('clicked()',self.onConfigFileSelected) self.layout.addWidget(label) self.layout.addWidget(self.configFilePicker) self.makeSnapshots = qt.QPushButton('Make snapshots') self.layout.addWidget(self.makeSnapshots) self.makeSnapshots.connect('clicked()', self.onMakeSnapshots) # Opacity control label = qt.QLabel('Foreground/Background opacity:') self.opacitySlider = ctk.ctkSliderWidget() self.opacitySlider.connect('valueChanged(double)',self.onOpacityChangeRequested) self.opacitySlider.minimum = 0. self.opacitySlider.maximum = 1. self.opacitySlider.decimals = 1 self.opacitySlider.singleStep = 0.1 self.layout.addWidget(label) self.layout.addWidget(self.opacitySlider) # Button to switch between showing background/foreground volumes self.bgfgButton = qt.QPushButton("Switch Background/Foreground") self.layout.addWidget(self.bgfgButton) self.bgfgButton.connect('clicked()', self.onbgfgButtonPressed) # Select between compare view and editing layouts groupLabel = qt.QLabel('Review mode:') self.viewGroup = qt.QButtonGroup() self.compareSelector = qt.QRadioButton('Compare view') self.sideBySideSelector = qt.QRadioButton('Side by side') self.compareSelector.setChecked(1) self.viewGroup.addButton(self.compareSelector,1) self.viewGroup.addButton(self.sideBySideSelector,2) self.groupWidget = qt.QWidget() self.groupLayout = qt.QFormLayout(self.groupWidget) self.groupLayout.addRow(self.compareSelector, self.sideBySideSelector) self.layout.addWidget(self.groupWidget) # step4Layout.addRow(groupLabel, self.viewGroup) self.viewGroup.connect('buttonClicked(int)', self.onViewUpdateRequested) # setup Editor widget editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent,showVolumesFrame=False) self.editorWidget.setup() self.editorParameterNode = self.editUtil.getParameterNode() self.layout.addWidget(editorWidgetParent) # Slice control #label = qt.QLabel('Slice selector:') #self.sliceSlider = ctk.ctkSliderWidget() #self.sliceSlider.connect('valueChanged(double)',self.onSliceChangeRequested) #self.sliceSlider.minimum = 0. #self.sliceSlider.maximum = 1. #self.sliceSlider.decimals = 1 #self.sliceSlider.singleStep = 0.1 #self.layout.addWidget(label) #self.layout.addWidget(self.opacitySlider) # Collapsible button to keep the content of the form self.evaluationFrame = ctk.ctkCollapsibleButton() self.evaluationFrame.text = "Assessment Form" self.evaluationFrame.collapsed = 0 self.evaluationFrameLayout = qt.QFormLayout(self.evaluationFrame) self.layout.addWidget(self.evaluationFrame) self.formEntries = [] self.questions = {'Improved compared to non-registered?':'binary','Diagnostic quality?':'binary','Error quantification (if available)':'numeric'} self.formEntryMapper = qt.QSignalMapper() self.formEntryMapper.connect('mapped(const QString&)', self.entrySelected) self.maxFormEntries = 30 for i in range(self.maxFormEntries): # populate the assessment form # create a new sub-frame with the questions cb = ctk.ctkCollapsibleButton() cb.visible = False cb.collapsed = True self.formEntries.append(cb) self.formEntryMapper.setMapping(cb, str(i)) cb.connect('contentsCollapsed(bool)', self.formEntryMapper, 'map()') layout = qt.QFormLayout(cb) self.evaluationFrameLayout.addRow(cb) for (q,c) in self.questions.items(): if c == 'binary': self.addBinaryEntry(q, layout) elif c == 'numeric': self.addNumericEntry(q, layout) # Save button self.doneButton = qt.QPushButton("Save") self.doneButton.toolTip = "Click this when done." self.layout.addWidget(self.doneButton) self.doneButton.connect('clicked(bool)', self.onDoneButtonClicked) # Add vertical spacer self.layout.addStretch(1) # Initialize internal persistent variables self.configFile = None self.movingVolume = None self.perVolumeForms = [] self.fixedVolumes = [] self.registeredVolumes = [] self.transforms = [] self.caseName = None # add custom layout for comparing two pairs of volumes compareViewTwoRows ="<layout type=\"vertical\">" for i in range(2): compareViewTwoRows = compareViewTwoRows+" <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"Compare"+str(i)+"\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">"+str(i)+"</property>\ <property name=\"viewcolor\" action=\"default\">#E17012</property>\ <property name=\"lightboxrows\" action=\"default\">1</property>\ <property name=\"lightboxcolumns\" action=\"default\">6</property>\ <property name=\"lightboxrows\" action=\"relayout\">1</property>\ <property name=\"lightboxcolumns\" action=\"relayout\">6</property>\ </view>\ </item>" compareViewTwoRows = compareViewTwoRows+"</layout>" sideBySide = "<layout type=\"horizontal\">\ <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"SideBySide0\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">Moving</property>\ <property name=\"viewcolor\" action=\"default\">#F34A33</property>\ </view>\ </item>\ <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"SideBySide1\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">Reference</property>\ <property name=\"viewcolor\" action=\"default\">#EDD54C</property>\ </view>\ </item>\ </layout>" print(sideBySide) layoutNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLLayoutNode') layoutNodes.SetReferenceCount(layoutNodes.GetReferenceCount()-1) self.layoutNode = layoutNodes.GetItemAsObject(0) self.CompareLayout = 123 self.ContouringLayout = 124 self.layoutNode.AddLayoutDescription(self.CompareLayout,compareViewTwoRows) self.layoutNode.AddLayoutDescription(self.ContouringLayout,sideBySide) self.layoutNode.SetViewArrangement(self.ContouringLayout) self.layoutNode.SetViewArrangement(self.CompareLayout) sliceCompositeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode') sliceCompositeNodes.SetReferenceCount(sliceCompositeNodes.GetReferenceCount()-1) sliceNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceNode') sliceNodes.SetReferenceCount(sliceNodes.GetReferenceCount()-1) for i in range(sliceCompositeNodes.GetNumberOfItems()): scn = sliceCompositeNodes.GetItemAsObject(i) sn = sliceNodes.GetItemAsObject(i) sn.SetUseLabelOutline(1) if sn.GetName() == 'Compare0': self.compare0 = scn if sn.GetName() == 'Compare1': self.compare1 = scn if sn.GetName() == 'SideBySide0': self.sidebyside0 = scn if sn.GetName() == 'SideBySide1': self.sidebyside1 = scn def compositeNodeForWidget(self,name): sliceCompositeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode') sliceCompositeNodes.SetReferenceCount(sliceCompositeNodes.GetReferenceCount()-1) sliceNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceNode') sliceNodes.SetReferenceCount(sliceNodes.GetReferenceCount()-1) for i in range(sliceCompositeNodes.GetNumberOfItems()): scn = sliceCompositeNodes.GetItemAsObject(i) sn = sliceNodes.GetItemAsObject(i) sn.SetUseLabelOutline(1) if sn.GetName() == name: return scn return None def onViewUpdateRequested(self,id): if id == 1: self.viewMode = 'compare' if id == 2: self.viewMode = 'sidebyside' self.entrySelected('0') def onOpacityChangeRequested(self,value): if self.viewMode == 'compare': viewer0 = self.compare0 viewer1 = self.compare1 else: viewer0 = self.sidebyside0 viewer1 = self.sidebyside1 if viewer0: viewer0.SetForegroundOpacity(value) if viewer1: viewer1.SetForegroundOpacity(value) def onbgfgButtonPressed(self): if self.viewMode == 'compare': viewer0 = self.compare0 viewer1 = self.compare1 else: viewer0 = self.sidebyside0 viewer1 = self.sidebyside1 if viewer0.GetForegroundOpacity() == 1: viewer0.SetForegroundOpacity(0) viewer1.SetForegroundOpacity(0) else: viewer0.SetForegroundOpacity(1) viewer1.SetForegroundOpacity(1) def onMakeSnapshots(self): for key in range(len(self.transforms)): print('Key :'+str(key)) if not self.transforms[key]: continue self.makeSnapshotsForKey(key) def makeSnapshotsForKey(self,name,snapshots=True): movingID = self.movingVolume.GetID() registeredID = self.registeredVolumes[int(name)].GetID() fixedID = self.fixedVolumes[int(name)].GetID() snapshotsDir = '/Users/fedorov/Temp/RegistrationSnapshots' # assume this is full path, and the file name has the format # <CaseID>_<junk> caseId = os.path.split(self.configFileName)[-1].split('_')[0] redSliceCompositeNode = self.compositeNodeForWidget('Red') redSliceCompositeNode.SetForegroundOpacity(0) self.setupLightbox(fixedID,movingID) if snapshots: snapshotName = os.path.join(snapshotsDir,caseId+'_'+str(name)+'_fixed.png') self.makeSnapshot('Red',snapshotName) self.setupLightbox(registeredID,movingID) if snapshots: snapshotName = os.path.join(snapshotsDir,caseId+'_'+str(name)+'_registered.png') self.makeSnapshot('Red',snapshotName) # make snapshots of the moving image for the first one # (same for all keys) if str(name) == '0': redSliceCompositeNode.SetForegroundOpacity(1) if snapshots: snapshotName = os.path.join(snapshotsDir,caseId+'_moving.png') self.makeSnapshot('Red',snapshotName) def makeSnapshot(self, widgetName, snapshotName): w = slicer.app.layoutManager().sliceWidget(widgetName) if w: qt.QPixmap().grabWidget(w).toImage().save(snapshotName) def setupSliceWidget(self,swName): w = slicer.app.layoutManager().sliceWidget(swName) w.fitSliceToBackground() sn = self.compositeNodeForWidget(swName) sn.SetForegroundOpacity(0) n = w.sliceLogic().GetSliceNode() fov = n.GetFieldOfView() n.SetFieldOfView(fov[0]/2.,fov[1]/2.,fov[2]/2.) def entrySelected(self, name): # prepare fixed volume label, if it is not available if self.movingVolumeSeg and not self.fixedVolumesSegmentations[int(name)] and self.transforms[int(name)]: tfm = self.transforms[int(name)] print('Resampled segmentation is missing, but will resample with '+tfm.GetID()) resample = slicer.modules.brainsresample volumesLogic = slicer.modules.volumes.logic() labelName = self.fixedVolumes[int(name)].GetName()+'-label' self.fixedVolumesSegmentations[int(name)] = volumesLogic.CreateAndAddLabelVolume(slicer.mrmlScene, self.fixedVolumes[int(name)], labelName) parameters = {} parameters['inputVolume'] = self.movingVolumeSeg.GetID() parameters['referenceVolume'] = self.fixedVolumes[int(name)].GetID() parameters['warpTransform'] = self.transforms[int(name)].GetID() parameters['outputVolume'] = self.fixedVolumesSegmentations[int(name)] parameters['pixelType'] = 'short' parameters['interpolationMode'] = 'NearestNeighbor' slicer.cli.run(resample, None, parameters, wait_for_completion = True) # initialize the file name for the segmentation storageNode = self.fixedVolumesSegmentations[int(name)].GetStorageNode() fixedVolumeIdStr = str(self.fixedVolumeIds[int(name)]) segFileName = os.path.join(os.path.split(self.config.get('MovingData','Segmentation'))[0],fixedVolumeIdStr+'-label.nrrd') storageNode.SetFileName(segFileName) storageNode.SetWriteFileFormat('.nrrd') # update the config file to keep reference to the newly created # segmentation label self.config.set('FixedData','Segmentation'+fixedVolumeIdStr,segFileName) # setup the Editor self.editorWidget.setMasterNode(self.fixedVolumes[int(name)]) self.editorWidget.setMergeNode(self.fixedVolumesSegmentations[int(name)]) self.editorParameterNode.Modified() if self.viewMode == 'compare': self.layoutNode.SetViewArrangement(self.CompareLayout) viewer0 = self.compare0 viewer1 = self.compare1 else: self.layoutNode.SetViewArrangement(self.ContouringLayout) viewer0 = self.sidebyside0 viewer1 = self.sidebyside1 entry = self.formEntries[int(name)] if entry.collapsed == True: return print 'Entry selected: ',name for i in range(len(self.fixedVolumes)): #self.formEntries[i].blockSignals(True) if i == int(name): self.formEntries[i].collapsed = False continue else: self.formEntries[i].collapsed = True #self.formEntries[i].blockSignals(False) viewer0.SetLinkedControl(False) viewer1.SetLinkedControl(False) v0fgID = self.movingVolume.GetID() v1fgID = self.registeredVolumes[int(name)].GetID() bgID = self.fixedVolumes[int(name)].GetID() print('Will be setting ids: '+v0fgID+','+v1fgID+','+bgID) viewer0.SetForegroundVolumeID(v0fgID) if self.movingVolumeSeg: viewer0.SetLabelVolumeID(self.movingVolumeSeg.GetID()) viewer1.SetForegroundVolumeID(v1fgID) # if segmentation is available for the registered node, display it if self.fixedVolumesSegmentations[int(name)]: viewer1.SetLabelVolumeID(self.fixedVolumesSegmentations[int(name)].GetID()) # otherwise, if the transform is available, resample the moving volume # segmentation, populate the entry in the list of segmentations and # initialize the view viewer0.SetLinkedControl(True) viewer1.SetLinkedControl(True) viewer0.SetBackgroundVolumeID(bgID) viewer1.SetBackgroundVolumeID(bgID) def setupLightbox(self,fixedID,movingID): lm = slicer.app.layoutManager() lm.setLayout(6) # Red w = lm.sliceWidget('Red') cn = self.compositeNodeForWidget('Red') cn.SetForegroundVolumeID(movingID) cn.SetBackgroundVolumeID(fixedID) slicer.app.processEvents() sc = w.sliceController() sc.setLightbox(3,6) w.fitSliceToBackground() slider = sc.children()[1].children()[-1] n = w.sliceLogic().GetSliceNode() fov = n.GetFieldOfView() # zoom into the prostate region n.SetFieldOfView(fov[0]/2.5,fov[1]/2.5,fov[2]) sc.setSliceOffsetValue(slider.minimum) def initializeViews(self, name): if self.viewMode == 'compare': self.layoutNode.SetViewArrangement(self.CompareLayout) viewer0 = self.compare0 viewer1 = self.compare1 else: self.layoutNode.SetViewArrangement(self.ContouringLayout) viewer0 = self.sidebyside0 viewer1 = self.sidebyside1 viewer0.SetLinkedControl(False) viewer1.SetLinkedControl(False) v0fgID = self.movingVolume.GetID() v1fgID = self.registeredVolumes[int(name)].GetID() bgID = self.fixedVolumes[int(name)].GetID() print('Will be setting ids: '+v0fgID+','+v1fgID+','+bgID) viewer0.SetForegroundVolumeID(v0fgID) if self.movingVolumeSeg: viewer0.SetLabelVolumeID(self.movingVolumeSeg.GetID()) viewer1.SetForegroundVolumeID(v1fgID) # if segmentation is available for the registered node, display it if self.fixedVolumesSegmentations[int(name)]: viewer1.SetLabelVolumeID(self.fixedVolumesSegmentations[int(name)].GetID()) # otherwise, if the transform is available, resample the moving volume # segmentation, populate the entry in the list of segmentations and # initialize the view viewer0.SetLinkedControl(True) viewer1.SetLinkedControl(True) viewer0.SetBackgroundVolumeID(bgID) viewer1.SetBackgroundVolumeID(bgID) def onConfigFileSelected(self): if not self.configFile: fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose assessment for configuration file","/","Conf Files (*.ini)") else: lastDir = self.configFile[0:string.rfind(self.configFile,'/')] fileName = qt.QFileDialog.getOpenFileName(self.parent, "Choose assessment for configuration file",lastDir,"Conf Files (*.ini)") if fileName == '': return self.configFile = fileName try: label = string.split(fileName,'/')[-1] except: label = fileName self.configFilePicker.text = label self.initFromFile(fileName) def initFromFile(self, fileName): # parse config file and load all of the volumes referenced slicer.mrmlScene.Clear(0) self.clearForm() self.fixedVolumes = [] self.fixedVolumeIds = [] self.registeredVolumes = [] self.movingVolume = None self.movingVolumeSeg = None self.config = config.SafeConfigParser() cf = self.config cf.optionxform = str cf.read(fileName) self.configFileName = fileName assert cf.has_section('Info') assert cf.has_section('MovingData') assert cf.has_section('FixedData') assert cf.has_section('RegisteredData') # there should only be one moving image self.movingVolume = slicer.util.loadVolume(cf.get('MovingData','Image'),returnNode=True)[1] dn = self.movingVolume.GetDisplayNode() mwl = [dn.GetWindow(), dn.GetLevel()] # (optional) segmentation of the moving image print('Set moving volume seg') try: self.movingVolumeSeg = slicer.util.loadLabelVolume(cf.get('MovingData','Segmentation'),{},returnNode=True)[1] self.movingVolumeSeg.SetAttribute('LabelMap','1') self.movingVolumeSeg.GetDisplayNode().SetAndObserveColorNodeID('vtkMRMLColorTableNodeFileGenericAnatomyColors.txt') print('Setup color node: '+self.movingVolumeSeg.GetDisplayNode().GetColorNodeID()) except: print(' ... to None') self.movingVolumeSeg = None # fixedVolumes: Slicer volume nodes corresponding to fixed images from the # config file # fixedVolumeIds: Each volume appears as "Image<id>" in the config file; # this list stores the ids assigned to the volumes # registeredVolumes: results of registration # transforms: transformations mapping moving volume to the corresponding # fixed volume # fixedVolumesSegmentations: masks of a structure contoured in each of the # fixed volumes # movingFiducials: fiducial points corresponding to image landmarks # fixedFiducials: fiducial points corresponding to the same landmarks in # the fixed images # and an arbitrary number of fixed images fixedImageFiles = cf.options('FixedData') print(str(fixedImageFiles)) for fi in fixedImageFiles: if re.match('Image\d+',fi): self.fixedVolumes.append(slicer.util.loadVolume(cf.get('FixedData',fi),returnNode=True)[1]) fixedDisplNode = self.fixedVolumes[-1].GetDisplayNode() fixedDisplNode.SetAutoWindowLevel(0) fixedDisplNode.SetWindow(mwl[0]) fixedDisplNode.SetLevel(mwl[1]) self.fixedVolumeIds.append(fi.split('Image')[1]) print('Fixed volumes: '+str(self.fixedVolumes)) # (optional) segmentations of the structure in the fixed images self.registeredVolumes = [None] * len(self.fixedVolumes) self.fixedVolumesSegmentations = [None] * len(self.fixedVolumes) self.transforms = [None] * len(self.fixedVolumes) for fvId in range(len(self.fixedVolumes)): imageId = 'Image'+self.fixedVolumeIds[fvId] segId = 'Segmentation'+self.fixedVolumeIds[fvId] tfmId = 'Transform'+self.fixedVolumeIds[fvId] try: self.registeredVolumes[fvId] = slicer.util.loadVolume(cf.get('RegisteredData',imageId),{},returnNode=True)[1] registeredDisplNode = self.registeredVolumes[fvId].GetDisplayNode() registeredDisplNode.SetAutoWindowLevel(0) registeredDisplNode.SetWindow(mwl[0]) registeredDisplNode.SetLevel(mwl[1]) except: print('Failed to read RegisteredData/'+imageId) pass try: self.fixedVolumesSegmentations[fvId] = slicer.util.loadLabelVolume(cf.get('FixedData',segId),{},returnNode=True)[1] except: print('Failed to read FixedData/'+segId) pass #try: self.transforms[fvId] = slicer.util.loadTransform(cf.get('RegisteredData',tfmId),returnNode=True)[1] #except: # print('Failed to read RegisteredData/'+tfmId) # pass print('Number of fixed images: '+str(self.registeredVolumes)) print('Number of transformations: '+str(self.transforms)) print('Number of fixed image segmentations: '+str(self.fixedVolumesSegmentations)) assert len(self.fixedVolumes) == len(self.registeredVolumes) self.caseName = cf.get('Info','CaseName') self.evaluationFrame.text = "Assessment Form for "+self.caseName # populate the assessment form for fv in range(len(self.fixedVolumes)): self.formEntries[fv].visible = True self.formEntries[fv].text = self.fixedVolumes[fv].GetName() # self.entrySelected('0') def clearForm(self): for i in range(self.maxFormEntries): self.formEntries[i].visible = False def onDoneButtonClicked(self): # save the config file self.config.write(open(self.configFileName,'w')) return path = self.configFile[0:string.rfind(self.configFile,'/')] reportName = path+'/'+self.caseName+'-'+self.raterName.text+'-report.log' report = open(reportName,'w') report.write(self.configFile+'\n') for i in range(len(self.fixedVolumes)): report.write(self.fixedVolumes[i].GetName()+';') item = 2 print 'num children: ',len(self.formEntries[i].children()) for (q,c) in self.questions.items(): report.write(q+';') widget = self.formEntries[i].children()[item] print widget if c == 'binary': checked = str(int(widget.checked)) report.write(str(checked)+';') elif c == 'numeric': error = str(widget.text) report.write(error+';') item = item+2 report.write('\n') report.close() def addBinaryEntry(self,question,layout): self.questions[question] = 'binary' label = qt.QLabel(question) item = qt.QCheckBox() layout.addRow(label,item) #self.formEntries.append(item) def addNumericEntry(self,question,layout): self.questions[question] = 'numeric' label = qt.QLabel(question) item = qt.QLineEdit() layout.addRow(label,item) #self.formEntries.append(item) def onReload(self,moduleName="VisAIRe"): """Generic reload method for any scripted module. ModuleWizard will subsitute correct default moduleName. """ import imp, sys, os, slicer widgetName = moduleName + "Widget" # reload the source code # - set source file path # - load the module to the global space filePath = eval('slicer.modules.%s.path' % moduleName.lower()) p = os.path.dirname(filePath) if not sys.path.__contains__(p): sys.path.insert(0,p) fp = open(filePath, "r") globals()[moduleName] = imp.load_module( moduleName, fp, filePath, ('.py', 'r', imp.PY_SOURCE)) fp.close() # rebuild the widget # - find and hide the existing widget # - create a new widget in the existing parent parent = slicer.util.findChildren(name='%s Reload' % moduleName)[0].parent() for child in parent.children(): try: child.hide() except AttributeError: pass # Remove spacer items item = parent.layout().itemAt(0) while item: parent.layout().removeItem(item) item = parent.layout().itemAt(0) # delete the old widget instance ''' widgetName = moduleName+'Widget' if hasattr(globals()['slicer'].modules, widgetName): getattr(globals()['slicer'].modules, widgetName).cleanup() ''' # create new widget inside existing parent globals()[widgetName.lower()] = eval( 'globals()["%s"].%s(parent)' % (moduleName, widgetName)) globals()[widgetName.lower()].setup() def onReloadAndTest(self,moduleName="VisAIRe"): self.onReload() evalString = 'globals()["%s"].%sTest()' % (moduleName, moduleName) tester = eval(evalString) tester.runTest()
class SliceTrackerZFrameRegistrationWidget(ScriptedLoadableModuleWidget, ModuleWidgetMixin): ZFRAME_MODEL_PATH = 'Resources/zframe/zframe-model.vtk' ZFRAME_MODEL_NAME = 'ZFrameModel' def __init__(self, parent=None): ScriptedLoadableModuleWidget.__init__(self, parent) self.logic = SliceTrackerZFrameRegistrationLogic() def setup(self): ScriptedLoadableModuleWidget.setup(self) self.setupSliceWidget() self.tag = None self.zFrameModelNode = None self.registrationGroupBox = qt.QGroupBox() self.registrationGroupBoxLayout = qt.QFormLayout() self.registrationGroupBox.setLayout(self.registrationGroupBoxLayout) self.zFrameTemplateVolumeSelector = self.createComboBox(nodeTypes=["vtkMRMLScalarVolumeNode", ""], showChildNodeTypes=False, selectNodeUponCreation=True, toolTip="Pick algorithm input.") self.applyRegistrationButton = self.createButton("Run Registration") self.registrationGroupBoxLayout.addRow("ZFrame template: ", self.zFrameTemplateVolumeSelector) self.registrationGroupBoxLayout.addRow(self.applyRegistrationButton) self.layout.addWidget(self.registrationGroupBox) self.layout.addStretch() self.setupConnections() self.setupEditorWidget() self.loadZFrameModel() def loadZFrameModel(self): collection = slicer.mrmlScene.GetNodesByName(self.ZFRAME_MODEL_NAME) for index in range(collection.GetNumberOfItems()): slicer.mrmlScene.RemoveNode(collection.GetItemAsObject(index)) self.modulePath = os.path.dirname(slicer.util.modulePath(self.moduleName)) zFrameModelPath = os.path.join(self.modulePath, self.ZFRAME_MODEL_PATH) if not self.zFrameModelNode: _, self.zFrameModelNode = slicer.util.loadModel(zFrameModelPath, returnNode=True) self.zFrameModelNode.SetName(self.ZFRAME_MODEL_NAME) slicer.mrmlScene.AddNode(self.zFrameModelNode) modelDisplayNode = self.zFrameModelNode.GetDisplayNode() modelDisplayNode.SetColor(1, 1, 0) def setupSliceWidget(self, name): self.redWidget = self.layoutManager.sliceWidget("Red") self.redCompositeNode = self.redWidget.mrmlSliceCompositeNode() self.redSliceView = self.redWidget.sliceView() self.redSliceLogic = self.redWidget.sliceLogic() self.redSliceNode = self.redSliceLogic.GetSliceNode() def setupEditorWidget(self): self.editorWidgetParent = slicer.qMRMLWidget() self.editorWidgetParent.setLayout(qt.QVBoxLayout()) self.editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editUtil = EditorLib.EditUtil.EditUtil() self.editorWidget = EditorWidget(parent=self.editorWidgetParent, showVolumesFrame=False) self.editorWidget.setup() self.editorParameterNode = self.editUtil.getParameterNode() def setupConnections(self): self.applyRegistrationButton.clicked.connect(self.runRegistration) self.zFrameTemplateVolumeSelector.connect('currentNodeChanged(bool)', self.loadVolumeAndEnableEditor) def processEvent(self, caller=None, event=None): if event == "LeftButtonReleaseEvent": interactor = self.redSliceView.interactorStyle().GetInteractor() xy = interactor.GetEventPosition() interactor.RemoveObserver(self.tag) self.tag = None self.onCenterPointSet(xy) def loadVolumeAndEnableEditor(self): zFrameTemplateVolume = self.zFrameTemplateVolumeSelector.currentNode() self.redCompositeNode.SetBackgroundVolumeID(zFrameTemplateVolume.GetID()) interactor = self.redSliceView.interactorStyle().GetInteractor() if self.tag: interactor.RemoveObserver(self.tag) self.tag = interactor.AddObserver(vtk.vtkCommand.LeftButtonReleaseEvent, self.processEvent, 1.0) def onCenterPointSet(self, xy): import vtk.util.numpy_support as vnp zFrameTemplateVolume = self.zFrameTemplateVolumeSelector.currentNode() volumesLogic = slicer.modules.volumes.logic() zFrameTemplateMask = volumesLogic.CreateLabelVolume(slicer.mrmlScene, zFrameTemplateVolume, 'zFrameTemplateMask') imageDataWorkingCopy = vtk.vtkImageData() imageDataWorkingCopy.DeepCopy(zFrameTemplateMask.GetImageData()) newLabelNodeImage=vtk.vtkImageData() newLabelNodeImage.AllocateScalars(vtk.VTK_SHORT, 1) newLabelNodeImage.SetExtent(zFrameTemplateMask.GetImageData().GetExtent()) numpyArray = vnp.vtk_to_numpy(imageDataWorkingCopy.GetPointData().GetScalars()).reshape(imageDataWorkingCopy.GetDimensions()).transpose(2,1,0) numpyArray[6:10,70:185,65:190].fill(1) spacing = imageDataWorkingCopy.GetSpacing() vtk_data = vnp.numpy_to_vtk(num_array=numpyArray.ravel(), deep=True, array_type=vtk.VTK_SHORT) newLabelNodeImage.GetPointData().SetScalars(vtk_data) dims = numpyArray.shape newLabelNodeImage.SetDimensions(dims[2], dims[1], dims[0]) newLabelNodeImage.SetOrigin(0,0,0) newLabelNodeImage.SetSpacing(spacing[0], spacing[1], spacing[2]) zFrameTemplateMask.SetAndObserveImageData(newLabelNodeImage) # update the default label map to the generic anatomy colors labelDisplayNode = zFrameTemplateMask.GetDisplayNode() labelColorTable = slicer.util.getNode('GenericAnatomyColors') labelDisplayNode.SetAndObserveColorNodeID(labelColorTable.GetID()) self.redCompositeNode.SetBackgroundVolumeID(zFrameTemplateVolume.GetID()) self.redCompositeNode.SetLabelVolumeID(zFrameTemplateMask.GetID()) self.redCompositeNode.SetLabelOpacity(0.6) self.maskedVolume = self.createMaskedVolume(zFrameTemplateVolume, zFrameTemplateMask) def createMaskedVolume(self, inputVolume, labelVolume): maskedVolume = slicer.vtkMRMLScalarVolumeNode() maskedVolume.SetName("maskedTemplateVolume") slicer.mrmlScene.AddNode(maskedVolume) params = {'InputVolume': inputVolume, 'MaskVolume': labelVolume, 'OutputVolume': maskedVolume} slicer.cli.run(slicer.modules.maskscalarvolume, None, params, wait_for_completion=True) return maskedVolume def isRegistrationPossible(self): return False def runRegistration(self): registration = OpenSourceZFrameRegistration(self.maskedVolume) registration.runRegistration() self.zFrameModelNode.SetAndObserveTransformNodeID(registration.outputTransform.GetID())
def setup(self): self.viewMode = 'compare' self.compare0 = None self.compare1 = None self.sidebyside0 = None self.sidebyside1 = None # Instantiate and connect widgets ... # reload button # (use this during development, but remove it when delivering # your module to users) self.reloadButton = qt.QPushButton("Reload") self.reloadButton.toolTip = "Reload this module." self.reloadButton.name = "VisAIRe Reload" self.layout.addWidget(self.reloadButton) self.reloadButton.connect('clicked()', self.onReload) # reload and test button # (use this during development, but remove it when delivering # your module to users) #self.reloadAndTestButton = qt.QPushButton("Reload and Test") #self.reloadAndTestButton.toolTip = "Reload this module and then run the self tests." #self.layout.addWidget(self.reloadAndTestButton) #self.reloadAndTestButton.connect('clicked()', self.onReloadAndTest) # entry for the rater name label = qt.QLabel('Rater name:') self.raterName = qt.QLineEdit() self.layout.addWidget(label) self.layout.addWidget(self.raterName) # Configuration file picker label = qt.QLabel('Configuration file:') self.configFilePicker = qt.QPushButton('N/A') self.configFilePicker.connect('clicked()',self.onConfigFileSelected) self.layout.addWidget(label) self.layout.addWidget(self.configFilePicker) self.makeSnapshots = qt.QPushButton('Make snapshots') self.layout.addWidget(self.makeSnapshots) self.makeSnapshots.connect('clicked()', self.onMakeSnapshots) # Opacity control label = qt.QLabel('Foreground/Background opacity:') self.opacitySlider = ctk.ctkSliderWidget() self.opacitySlider.connect('valueChanged(double)',self.onOpacityChangeRequested) self.opacitySlider.minimum = 0. self.opacitySlider.maximum = 1. self.opacitySlider.decimals = 1 self.opacitySlider.singleStep = 0.1 self.layout.addWidget(label) self.layout.addWidget(self.opacitySlider) # Button to switch between showing background/foreground volumes self.bgfgButton = qt.QPushButton("Switch Background/Foreground") self.layout.addWidget(self.bgfgButton) self.bgfgButton.connect('clicked()', self.onbgfgButtonPressed) # Select between compare view and editing layouts groupLabel = qt.QLabel('Review mode:') self.viewGroup = qt.QButtonGroup() self.compareSelector = qt.QRadioButton('Compare view') self.sideBySideSelector = qt.QRadioButton('Side by side') self.compareSelector.setChecked(1) self.viewGroup.addButton(self.compareSelector,1) self.viewGroup.addButton(self.sideBySideSelector,2) self.groupWidget = qt.QWidget() self.groupLayout = qt.QFormLayout(self.groupWidget) self.groupLayout.addRow(self.compareSelector, self.sideBySideSelector) self.layout.addWidget(self.groupWidget) # step4Layout.addRow(groupLabel, self.viewGroup) self.viewGroup.connect('buttonClicked(int)', self.onViewUpdateRequested) # setup Editor widget editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.editorWidget = EditorWidget(parent=editorWidgetParent,showVolumesFrame=False) self.editorWidget.setup() self.editorParameterNode = self.editUtil.getParameterNode() self.layout.addWidget(editorWidgetParent) # Slice control #label = qt.QLabel('Slice selector:') #self.sliceSlider = ctk.ctkSliderWidget() #self.sliceSlider.connect('valueChanged(double)',self.onSliceChangeRequested) #self.sliceSlider.minimum = 0. #self.sliceSlider.maximum = 1. #self.sliceSlider.decimals = 1 #self.sliceSlider.singleStep = 0.1 #self.layout.addWidget(label) #self.layout.addWidget(self.opacitySlider) # Collapsible button to keep the content of the form self.evaluationFrame = ctk.ctkCollapsibleButton() self.evaluationFrame.text = "Assessment Form" self.evaluationFrame.collapsed = 0 self.evaluationFrameLayout = qt.QFormLayout(self.evaluationFrame) self.layout.addWidget(self.evaluationFrame) self.formEntries = [] self.questions = {'Improved compared to non-registered?':'binary','Diagnostic quality?':'binary','Error quantification (if available)':'numeric'} self.formEntryMapper = qt.QSignalMapper() self.formEntryMapper.connect('mapped(const QString&)', self.entrySelected) self.maxFormEntries = 30 for i in range(self.maxFormEntries): # populate the assessment form # create a new sub-frame with the questions cb = ctk.ctkCollapsibleButton() cb.visible = False cb.collapsed = True self.formEntries.append(cb) self.formEntryMapper.setMapping(cb, str(i)) cb.connect('contentsCollapsed(bool)', self.formEntryMapper, 'map()') layout = qt.QFormLayout(cb) self.evaluationFrameLayout.addRow(cb) for (q,c) in self.questions.items(): if c == 'binary': self.addBinaryEntry(q, layout) elif c == 'numeric': self.addNumericEntry(q, layout) # Save button self.doneButton = qt.QPushButton("Save") self.doneButton.toolTip = "Click this when done." self.layout.addWidget(self.doneButton) self.doneButton.connect('clicked(bool)', self.onDoneButtonClicked) # Add vertical spacer self.layout.addStretch(1) # Initialize internal persistent variables self.configFile = None self.movingVolume = None self.perVolumeForms = [] self.fixedVolumes = [] self.registeredVolumes = [] self.transforms = [] self.caseName = None # add custom layout for comparing two pairs of volumes compareViewTwoRows ="<layout type=\"vertical\">" for i in range(2): compareViewTwoRows = compareViewTwoRows+" <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"Compare"+str(i)+"\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">"+str(i)+"</property>\ <property name=\"viewcolor\" action=\"default\">#E17012</property>\ <property name=\"lightboxrows\" action=\"default\">1</property>\ <property name=\"lightboxcolumns\" action=\"default\">6</property>\ <property name=\"lightboxrows\" action=\"relayout\">1</property>\ <property name=\"lightboxcolumns\" action=\"relayout\">6</property>\ </view>\ </item>" compareViewTwoRows = compareViewTwoRows+"</layout>" sideBySide = "<layout type=\"horizontal\">\ <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"SideBySide0\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">Moving</property>\ <property name=\"viewcolor\" action=\"default\">#F34A33</property>\ </view>\ </item>\ <item>\ <view class=\"vtkMRMLSliceNode\" singletontag=\"SideBySide1\">\ <property name=\"orientation\" action=\"default\">Axial</property>\ <property name=\"viewlabel\" action=\"default\">Reference</property>\ <property name=\"viewcolor\" action=\"default\">#EDD54C</property>\ </view>\ </item>\ </layout>" print(sideBySide) layoutNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLLayoutNode') layoutNodes.SetReferenceCount(layoutNodes.GetReferenceCount()-1) self.layoutNode = layoutNodes.GetItemAsObject(0) self.CompareLayout = 123 self.ContouringLayout = 124 self.layoutNode.AddLayoutDescription(self.CompareLayout,compareViewTwoRows) self.layoutNode.AddLayoutDescription(self.ContouringLayout,sideBySide) self.layoutNode.SetViewArrangement(self.ContouringLayout) self.layoutNode.SetViewArrangement(self.CompareLayout) sliceCompositeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode') sliceCompositeNodes.SetReferenceCount(sliceCompositeNodes.GetReferenceCount()-1) sliceNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceNode') sliceNodes.SetReferenceCount(sliceNodes.GetReferenceCount()-1) for i in range(sliceCompositeNodes.GetNumberOfItems()): scn = sliceCompositeNodes.GetItemAsObject(i) sn = sliceNodes.GetItemAsObject(i) sn.SetUseLabelOutline(1) if sn.GetName() == 'Compare0': self.compare0 = scn if sn.GetName() == 'Compare1': self.compare1 = scn if sn.GetName() == 'SideBySide0': self.sidebyside0 = scn if sn.GetName() == 'SideBySide1': self.sidebyside1 = scn
class CMRToolkitWizardEndoSegmentationStep(CMRToolkitWizardStep): def __init__(self, stepid): self.initialize(stepid) self.setName('2. Segmentation of endocardium') self.setDescription( 'Segment the endocardium using the Editor paint tool and select the final label image to proceed.' ) self.__parent = super(CMRToolkitWizardEndoSegmentationStep, self) def killButton(self): # Hide unneccesary button bl = slicer.util.findChildren(text='AutomaticLeft*') if len(bl): bl[0].hide() def createUserInterface(self): from Editor import EditorWidget self.__layout = self.__parent.createUserInterface() #TODO: Create label map and set it for editing by user? #volumesLogic = slicer.modules.volumes.logic() #headLabel = volumesLogic.CreateLabelVolume( slicer.mrmlScene, head, head.GetName() + '-segmentation' ) #selectionNode = slicer.app.applicationLogic().GetSelectionNode() #selectionNode.SetReferenceActiveVolumeID( head.GetID() ) #selectionNode.SetReferenceActiveLabelVolumeID( headLabel.GetID() ) #slicer.app.applicationLogic().PropagateVolumeSelection(0) editorFrame = qt.QFrame() editorFrame.setLayout(qt.QVBoxLayout()) palette = editorFrame.palette bgColor = 230 palette.setColor(qt.QPalette.Background, qt.QColor(bgColor, bgColor, bgColor)) editorFrame.setPalette(palette) editorFrame.setAutoFillBackground(True) self.__layout.addRow(editorFrame) self.editorFrame = editorFrame global editorWidget self.editorWidget = EditorWidget(parent=self.editorFrame, showVolumesFrame=True) self.editorWidget.setup() self.editorWidget.enter() endoSegLabel = qt.QLabel('Endo Segmentation Image:') self.__endoSegSelector = slicer.qMRMLNodeComboBox() self.__endoSegSelector.toolTip = "Choose the endo segmentation label image." self.__endoSegSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__endoSegSelector.setMRMLScene(slicer.mrmlScene) self.__endoSegSelector.addEnabled = 0 self.__layout.addRow(endoSegLabel, self.__endoSegSelector) #def startEditor(self): # from Editor import EditorWidget # editorWidget = EditorWidget(showVolumesFrame=True) def validate(self, desiredBranchId): self.__parent.validate(desiredBranchId) endoSegVolume = self.__endoSegSelector.currentNode() if endoSegVolume != None: endoSegID = endoSegVolume.GetID() pNode = self.parameterNode() pNode.SetParameter('endoSegVolumeID', endoSegID) Helper.SetLabelVolume(endoSegVolume.GetID()) self.__parent.validationSucceeded(desiredBranchId) else: self.__parent.validationFailed( desiredBranchId, 'Error', 'Please select an endo segmentation image to proceed.') def onEntry(self, comingFrom, transitionType): super(CMRToolkitWizardEndoSegmentationStep, self).onEntry(comingFrom, transitionType) pNode = self.parameterNode() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def onExit(self, goingTo, transitionType): pNode = self.parameterNode() #if goingTo.id() != 'AxialDilate': # return self.editorWidget.exit() super(CMRToolkitWizardEndoSegmentationStep, self).onExit(goingTo, transitionType)
def setup( self ): # # Input frame # self.__inputFrame = ctk.ctkCollapsibleButton() self.__inputFrame.text = "Input" self.__inputFrame.collapsed = 0 inputFrameLayout = qt.QFormLayout(self.__inputFrame) self.layout.addWidget(self.__inputFrame) # Active report node label = qt.QLabel('Report: ') self.__reportSelector = slicer.qMRMLNodeComboBox() self.__reportSelector.nodeTypes = ['vtkMRMLReportingReportNode'] self.__reportSelector.setMRMLScene(slicer.mrmlScene) self.__reportSelector.addEnabled = 1 self.__reportSelector.removeEnabled = 0 inputFrameLayout.addRow(label, self.__reportSelector) self.__reportSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onReportNodeChanged) # Volume being annotated (only one is allowed for the report) label = qt.QLabel('NOTE: Only volumes loaded from DICOM can be annotated!') inputFrameLayout.addRow(label) label = qt.QLabel('Annotated volume: ') self.__volumeSelector = slicer.qMRMLNodeComboBox() self.__volumeSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] # only allow volumes with the attribute DICOM.instanceUIDs self.__volumeSelector.addAttribute('vtkMRMLScalarVolumeNode','DICOM.instanceUIDs') self.__volumeSelector.setMRMLScene(slicer.mrmlScene) self.__volumeSelector.addEnabled = False inputFrameLayout.addRow(label, self.__volumeSelector) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onAnnotatedVolumeNodeChanged) self.__volumeSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # # Annotation frame -- vocabulary-based description of what is # being annotated/marked up in this report # self.__annotationsFrame = ctk.ctkCollapsibleButton() self.__annotationsFrame.text = "Annotation" self.__annotationsFrame.collapsed = 0 annotationsFrameLayout = qt.QFormLayout(self.__annotationsFrame) self.layout.addWidget(self.__annotationsFrame) self.__defaultColorNode = self.__logic.GetDefaultColorNode() self.__toolsColor = EditColor(self.__annotationsFrame,colorNode=self.__defaultColorNode) # # Markup frame -- summary of all the markup elements contained in the # report # self.__markupFrame = ctk.ctkCollapsibleButton() self.__markupFrame.text = "Markup" self.__markupFrame.collapsed = 0 markupFrameLayout = qt.QFormLayout(self.__markupFrame) self.layout.addWidget(self.__markupFrame) # Add a flag to switch between different tree view models self.__useNewTreeView = 1 # Add the tree widget if self.__useNewTreeView == 1: self.__markupTreeView = slicer.modulewidget.qMRMLReportingTreeView() self.__markupTreeView.sceneModelType = "DisplayableHierarchy" else: self.__markupTreeView = slicer.qMRMLTreeView() self.__markupTreeView.sceneModelType = "Displayable" self.__markupTreeView.setMRMLScene(self.__logic.GetMRMLScene()) self.__markupSliceText = qt.QLabel() markupFrameLayout.addRow(self.__markupSliceText) markupFrameLayout.addRow(self.__markupTreeView) # Editor frame self.__editorFrame = ctk.ctkCollapsibleButton() self.__editorFrame.text = 'Segmentation' self.__editorFrame.collapsed = 0 editorFrameLayout = qt.QFormLayout(self.__editorFrame) label = qt.QLabel('Segmentation volume: ') self.__segmentationSelector = slicer.qMRMLNodeComboBox() self.__segmentationSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__segmentationSelector.setMRMLScene(slicer.mrmlScene) self.__segmentationSelector.addEnabled = 1 self.__segmentationSelector.noneEnabled = 1 self.__segmentationSelector.removeEnabled = 0 self.__segmentationSelector.showHidden = 0 self.__segmentationSelector.showChildNodeTypes = 0 self.__segmentationSelector.selectNodeUponCreation = 1 self.__segmentationSelector.addAttribute('vtkMRMLScalarVolumeNode','LabelMap',1) editorFrameLayout.addRow(label, self.__segmentationSelector) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent,showVolumesFrame=False) self.__editorWidget.setup() self.__editorWidget.toolsColor.frame.setVisible(False) editorFrameLayout.addRow(editorWidgetParent) markupFrameLayout.addRow(self.__editorFrame) self.__segmentationSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSegmentationNodeChanged) self.__segmentationSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # IO frame self.__ioFrame = ctk.ctkCollapsibleButton() self.__ioFrame.text = 'Import/Export' self.__ioFrame.collapsed = 1 ioFrameLayout = qt.QGridLayout(self.__ioFrame) self.layout.addWidget(self.__ioFrame) # Buttons to save/load report using AIM XML serialization label = qt.QLabel('Export folder') self.__exportFolderPicker = ctk.ctkDirectoryButton() exportButton = qt.QPushButton('Export') exportButton.connect('clicked()', self.onReportExport) ioFrameLayout.addWidget(label,0,0) ioFrameLayout.addWidget(self.__exportFolderPicker,0,1) ioFrameLayout.addWidget(exportButton,0,2) label = qt.QLabel('AIM file to import') self.__aimFilePicker = qt.QPushButton('N/A') self.__aimFilePicker.connect('clicked()',self.onSelectAIMFile) button = qt.QPushButton('Import') button.connect('clicked()', self.onReportImport) ioFrameLayout.addWidget(label,1,0) ioFrameLayout.addWidget(self.__aimFilePicker,1,1) ioFrameLayout.addWidget(button,1,2) self.__importAIMFile = None self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.updateWidgets() self.layout.addStretch(1) self.__editorParameterNode = self.editUtil.getParameterNode() self.__editorParameterNode.AddObserver(vtk.vtkCommand.ModifiedEvent, self.onEditorParameterNodeChanged)
class ReviewStep(GBMWizardStep): def __init__(self, stepid): self.initialize(stepid) self.setName('6. Review') self.__vrDisplayNode = None self.__threshold = [-1, -1] # initialize VR stuff self.__vrLogic = slicer.modules.volumerendering.logic() self.__vrOpacityMap = None self.__vrColorMap = None self.__thresholdedLabelNode = None self.__roiVolume = None self.__parent = super(ReviewStep, self) self.__RestartActivated = False def createUserInterface(self): self.__layout = self.__parent.createUserInterface() step_label = qt.QLabel( """Review your segmentation. Use the 3D Visualization slider to see your segmentation in context with your image. Use the Editor panel to apply spot edits to your segmentation. If you would like to start over, see the Restart box below""" ) step_label.setWordWrap(True) self.__primaryGroupBox = qt.QGroupBox() self.__primaryGroupBox.setTitle('Information') self.__primaryGroupBoxLayout = qt.QFormLayout(self.__primaryGroupBox) self.__primaryGroupBoxLayout.addRow(step_label) self.__layout.addRow(self.__primaryGroupBox) # self.__threshRange = slicer.qMRMLRangeWidget() # self.__threshRange.decimals = 0 # self.__threshRange.singleStep = 1 # self.__threshRange.connect('valuesChanged(double,double)', self.onThresholdChanged) # qt.QTimer.singleShot(0, self.killButton) # ThreshGroupBox = qt.QGroupBox() # ThreshGroupBox.setTitle('3D Visualization Intensity Threshold') # ThreshGroupBoxLayout = qt.QFormLayout(ThreshGroupBox) # ThreshGroupBoxLayout.addRow(self.__threshRange) # self.__layout.addRow(ThreshGroupBox) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.EditorWidget = EditorWidget(parent=editorWidgetParent) self.EditorWidget.setup() self.__layout.addRow(editorWidgetParent) RestartGroupBox = qt.QGroupBox() RestartGroupBox.setTitle('Restart') RestartGroupBoxLayout = qt.QFormLayout(RestartGroupBox) self.__RestartButton = qt.QPushButton('Return to Step 1') RestartGroupBoxLayout.addRow(self.__RestartButton) self.__RemoveRegisteredImage = qt.QCheckBox() self.__RemoveRegisteredImage.checked = True self.__RemoveRegisteredImage.setToolTip( "Delete your registered images.") RestartGroupBoxLayout.addRow("Delete Registered images: ", self.__RemoveRegisteredImage) self.__RemoveNormalizedImages = qt.QCheckBox() self.__RemoveNormalizedImages.checked = True self.__RemoveNormalizedImages.setToolTip( "Delete your normalized images.") RestartGroupBoxLayout.addRow("Delete Normalized images: ", self.__RemoveNormalizedImages) self.__RemoveSubtractionMap = qt.QCheckBox() self.__RemoveSubtractionMap.checked = True self.__RemoveSubtractionMap.setToolTip("Delete your subtraction map.") RestartGroupBoxLayout.addRow("Delete Subtraction map: ", self.__RemoveSubtractionMap) self.__RemoveCroppedMap = qt.QCheckBox() self.__RemoveCroppedMap.checked = True self.__RemoveCroppedMap.setToolTip( "Delete the cropped version of your input volume.") RestartGroupBoxLayout.addRow("Delete Cropped Volume: ", self.__RemoveCroppedMap) self.__RemoveROI = qt.QCheckBox() self.__RemoveROI.checked = False self.__RemoveROI.setToolTip( "Delete the ROI you made with your markup points.") RestartGroupBoxLayout.addRow("Delete Full ROI: ", self.__RemoveROI) self.__RemoveThresholdedROI = qt.QCheckBox() self.__RemoveThresholdedROI.checked = False self.__RemoveThresholdedROI.setToolTip( "Delete the intensity-thresholded version of your ROI.") RestartGroupBoxLayout.addRow("Delete Thresholded ROI: ", self.__RemoveThresholdedROI) self.__RemoveMarkups = qt.QCheckBox() self.__RemoveMarkups.checked = True self.__RemoveMarkups.setToolTip( "Delete the markup points you used to create your 3D ROI.") RestartGroupBoxLayout.addRow("Delete Markup Points: ", self.__RemoveMarkups) self.__RemoveModels = qt.QCheckBox() self.__RemoveModels.checked = True self.__RemoveModels.setToolTip( "Delete the 3D model you created from your markup points.") RestartGroupBoxLayout.addRow("Delete 3D Model: ", self.__RemoveModels) self.__RestartButton.connect('clicked()', self.Restart) self.__RestartActivated = True self.__layout.addRow(RestartGroupBox) def hideUnwantedEditorUIElements(self): """ We import the Editor module wholesale, which is useful, but it means we have to manually hide parts we don't want after the fact.. If we could somehow import the segmentations module instead, that might be better. On the other hand, first-time users often don't know how to use the segmentations module. """ self.EditorWidget.setMergeNode(self.__thresholdedLabelNode) self.EditorWidget.volumes.collapsed = True self.EditorWidget.editLabelMapsFrame.collapsed = False try: self.EditorWidget.segmentEditorLabel.hide() self.EditorWidget.infoIconLabel.hide() except: pass def Restart(self): # Unclick any selected editor tools.. self.__DefaultToolButton.click() pNode = self.parameterNode() slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('clippingModelNodeID'))) slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('clippingMarkupNodeID'))) slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('vrDisplayNodeID'))) if self.__RemoveRegisteredImage.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('registrationVolumeID'))) if self.__RemoveNormalizedImages.checked: for node in [ pNode.GetParameter('baselineNormalizeVolumeID'), pNode.GetParameter('followupNormalizeVolumeID') ]: if node != pNode.GetParameter( 'baselineVolumeID') and node != pNode.GetParameter( 'followupVolumeID'): slicer.mrmlScene.RemoveNode(Helper.getNodeByID(node)) if self.__RemoveSubtractionMap.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('subtractVolumeID'))) if self.__RemoveCroppedMap.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('croppedVolumeID'))) if self.__RemoveROI.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID( pNode.GetParameter('nonThresholdedLabelID'))) if self.__RemoveThresholdedROI.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('thresholdedLabelID'))) if self.__RemoveMarkups.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('clippingMarkupNodeID'))) if self.__RemoveModels.checked: slicer.mrmlScene.RemoveNode( Helper.getNodeByID(pNode.GetParameter('clippingModelNodeID'))) pNode.SetParameter('baselineVolumeID', '') pNode.SetParameter('followupVolumeID', '') pNode.SetParameter('originalBaselineVolumeID', '') pNode.SetParameter('originalFollowupVolumeID', '') pNode.SetParameter('registrationVolumeID', '') pNode.SetParameter('baselineNormalizeVolumeID', '') pNode.SetParameter('followupNormalizeVolumeID', '') pNode.SetParameter('subtractVolumeID', '') pNode.SetParameter('clippingMarkupNodeID', '') pNode.SetParameter('clippingModelNodeID', '') pNode.SetParameter('outputList', '') pNode.SetParameter('markupList', '') pNode.SetParameter('modelList', '') pNode.SetParameter('thresholdedLabelID', '') pNode.SetParameter('croppedVolumeID', '') pNode.SetParameter('nonThresholdedLabelID', '') pNode.SetParameter('roiNodeID', '') pNode.SetParameter('roiTransformID', '') pNode.SetParameter('vrDisplayNodeID', '') pNode.SetParameter('intensityThreshRangeMin', '') pNode.SetParameter('intensityThreshRangeMax', '') pNode.SetParameter('vrThreshRange', '') Helper.SetLabelVolume('') self.EditorWidget.exit() if self.__RestartActivated: self.workflow().goForward() def onThresholdChanged(self): if self.__vrOpacityMap == None: return range0 = self.__threshRange.minimumValue range1 = self.__threshRange.maximumValue self.__vrOpacityMap.RemoveAllPoints() self.__vrOpacityMap.AddPoint(range0 - 75, 0) self.__vrOpacityMap.AddPoint(range0, .02) self.__vrOpacityMap.AddPoint(range1, .04) self.__vrOpacityMap.AddPoint(range1 + 75, .1) def killButton(self): stepButtons = slicer.util.findChildren(className='ctkPushButton') backButton = '' nextButton = '' for stepButton in stepButtons: if stepButton.text == 'Next': nextButton = stepButton if stepButton.text == 'Back': backButton = stepButton nextButton.hide() # ctk creates a useless final page button. This method gets rid of it. bl = slicer.util.findChildren(text='ReviewStep') ex = slicer.util.findChildren('', 'EditColorFrame') if len(bl): bl[0].hide() if len(ex): ex[0].hide() self.__editLabelMapsFrame = slicer.util.findChildren( '', 'EditLabelMapsFrame')[0] self.__toolsColor = EditorLib.EditColor(self.__editLabelMapsFrame) def validate(self, desiredBranchId): # For now, no validation required. self.__parent.validationSucceeded(desiredBranchId) def onEntry(self, comingFrom, transitionType): super(ReviewStep, self).onEntry(comingFrom, transitionType) self.__RestartActivated = True self.__DefaultToolButton = slicer.util.findChildren( name='DefaultToolToolButton')[0] pNode = self.parameterNode() self.updateWidgetFromParameters(pNode) Helper.SetBgFgVolumes(self.__visualizedID, '') Helper.SetLabelVolume(self.__thresholdedLabelNode.GetID()) self.onThresholdChanged() pNode.SetParameter('currentStep', self.stepid) qt.QTimer.singleShot(0, self.killButton) def updateWidgetFromParameters(self, pNode): self.__clippingModelNode = Helper.getNodeByID( pNode.GetParameter('clippingModelNodeID')) self.__baselineVolumeID = pNode.GetParameter('baselineVolumeID') self.__followupVolumeID = pNode.GetParameter('followupVolumeID') self.__subtractVolumeID = pNode.GetParameter('subtractVolumeID') self.__croppedVolumeID = pNode.GetParameter('cropedVolumeID') self.__baselineVolumeNode = Helper.getNodeByID(self.__baselineVolumeID) self.__followupVolumeNode = Helper.getNodeByID(self.__followupVolumeID) self.__subtractVolumeNode = Helper.getNodeByID(self.__subtractVolumeID) self.__vrDisplayNodeID = pNode.GetParameter('vrDisplayNodeID') self.__thresholdedLabelNode = Helper.getNodeByID( pNode.GetParameter('thresholdedLabelID')) self.__clippingModelNode.GetDisplayNode().VisibilityOn() if self.__followupVolumeID == None or self.__followupVolumeID == '': self.__visualizedNode = self.__baselineVolumeNode self.__visualizedID = self.__baselineVolumeID else: self.__visualizedID = self.__followupVolumeID self.__visualizedNode = self.__followupVolumeNode # vrRange = self.__visualizedNode.GetImageData().GetScalarRange() # if self.__vrDisplayNode == None: # if self.__vrDisplayNodeID != '': # self.__vrDisplayNode = slicer.mrmlScene.GetNodeByID(self.__vrDisplayNodeID) # Replace this, most likely. # self.__visualizedNode.AddAndObserveDisplayNodeID(self.__vrDisplayNode.GetID()) # Helper.InitVRDisplayNode(self.__vrDisplayNode, self.__visualizedID, self.__croppedVolumeID) # self.__threshRange.minimum = vrRange[0] # self.__threshRange.maximum = vrRange[1] # if pNode.GetParameter('vrThreshRangeMin') == '' or pNode.GetParameter('vrThreshRangeMin') == None: # self.__threshRange.setValues(vrRange[1]/3, 2*vrRange[1]/3) # else: # self.__threshRange.setValues(float(pNode.GetParameter('vrThreshRangeMin')), float(pNode.GetParameter('vrThreshRangeMax'))) # self.__vrOpacityMap = self.__vrDisplayNode.GetVolumePropertyNode().GetVolumeProperty().GetScalarOpacity() # self.__vrColorMap = self.__vrDisplayNode.GetVolumePropertyNode().GetVolumeProperty().GetRGBTransferFunction() # self.__vrColorMap.RemoveAllPoints() # self.__vrColorMap.AddRGBPoint(vrRange[0], 0.8, 0.8, 0) # self.__vrColorMap.AddRGBPoint(vrRange[1], 0.8, 0.8, 0) self.hideUnwantedEditorUIElements() def onExit(self, goingTo, transitionType): self.__DefaultToolButton.click() super(GBMWizardStep, self).onExit(goingTo, transitionType)
def setup(self): # # Input frame # self.__inputFrame = ctk.ctkCollapsibleButton() self.__inputFrame.text = "Input" self.__inputFrame.collapsed = 0 inputFrameLayout = qt.QFormLayout(self.__inputFrame) self.layout.addWidget(self.__inputFrame) # Active report node label = qt.QLabel('Report: ') self.__reportSelector = slicer.qMRMLNodeComboBox() self.__reportSelector.nodeTypes = ['vtkMRMLReportingReportNode'] self.__reportSelector.setMRMLScene(slicer.mrmlScene) self.__reportSelector.addEnabled = 1 self.__reportSelector.removeEnabled = 0 inputFrameLayout.addRow(label, self.__reportSelector) self.__reportSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onReportNodeChanged) # Volume being annotated (only one is allowed for the report) label = qt.QLabel( 'NOTE: Only volumes loaded from DICOM can be annotated!') inputFrameLayout.addRow(label) label = qt.QLabel('Annotated volume: ') self.__volumeSelector = slicer.qMRMLNodeComboBox() self.__volumeSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] # only allow volumes with the attribute DICOM.instanceUIDs self.__volumeSelector.addAttribute('vtkMRMLScalarVolumeNode', 'DICOM.instanceUIDs') self.__volumeSelector.setMRMLScene(slicer.mrmlScene) self.__volumeSelector.addEnabled = False inputFrameLayout.addRow(label, self.__volumeSelector) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onAnnotatedVolumeNodeChanged) self.__volumeSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # # Annotation frame -- vocabulary-based description of what is # being annotated/marked up in this report # self.__annotationsFrame = ctk.ctkCollapsibleButton() self.__annotationsFrame.text = "Annotation" self.__annotationsFrame.collapsed = 0 annotationsFrameLayout = qt.QFormLayout(self.__annotationsFrame) self.layout.addWidget(self.__annotationsFrame) self.__defaultColorNode = self.__logic.GetDefaultColorNode() self.__toolsColor = EditColor(self.__annotationsFrame, colorNode=self.__defaultColorNode) # # Markup frame -- summary of all the markup elements contained in the # report # self.__markupFrame = ctk.ctkCollapsibleButton() self.__markupFrame.text = "Markup" self.__markupFrame.collapsed = 0 markupFrameLayout = qt.QFormLayout(self.__markupFrame) self.layout.addWidget(self.__markupFrame) # Add a flag to switch between different tree view models self.__useNewTreeView = 1 # Add the tree widget if self.__useNewTreeView == 1: self.__markupTreeView = slicer.modulewidget.qMRMLReportingTreeView( ) self.__markupTreeView.sceneModelType = "DisplayableHierarchy" else: self.__markupTreeView = slicer.qMRMLTreeView() self.__markupTreeView.sceneModelType = "Displayable" self.__markupTreeView.setMRMLScene(self.__logic.GetMRMLScene()) self.__markupSliceText = qt.QLabel() markupFrameLayout.addRow(self.__markupSliceText) markupFrameLayout.addRow(self.__markupTreeView) # Editor frame self.__editorFrame = ctk.ctkCollapsibleButton() self.__editorFrame.text = 'Segmentation' self.__editorFrame.collapsed = 0 editorFrameLayout = qt.QFormLayout(self.__editorFrame) label = qt.QLabel('Segmentation volume: ') self.__segmentationSelector = slicer.qMRMLNodeComboBox() self.__segmentationSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.__segmentationSelector.setMRMLScene(slicer.mrmlScene) self.__segmentationSelector.addEnabled = 1 self.__segmentationSelector.noneEnabled = 1 self.__segmentationSelector.removeEnabled = 0 self.__segmentationSelector.showHidden = 0 self.__segmentationSelector.showChildNodeTypes = 0 self.__segmentationSelector.selectNodeUponCreation = 1 self.__segmentationSelector.addAttribute('vtkMRMLScalarVolumeNode', 'LabelMap', 1) editorFrameLayout.addRow(label, self.__segmentationSelector) editorWidgetParent = slicer.qMRMLWidget() editorWidgetParent.setLayout(qt.QVBoxLayout()) editorWidgetParent.setMRMLScene(slicer.mrmlScene) self.__editorWidget = EditorWidget(parent=editorWidgetParent, showVolumesFrame=False) self.__editorWidget.setup() self.__editorWidget.toolsColor.frame.setVisible(False) editorFrameLayout.addRow(editorWidgetParent) markupFrameLayout.addRow(self.__editorFrame) self.__segmentationSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSegmentationNodeChanged) self.__segmentationSelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onMRMLSceneChanged) # IO frame self.__ioFrame = ctk.ctkCollapsibleButton() self.__ioFrame.text = 'Import/Export' self.__ioFrame.collapsed = 1 ioFrameLayout = qt.QGridLayout(self.__ioFrame) self.layout.addWidget(self.__ioFrame) # Buttons to save/load report using AIM XML serialization label = qt.QLabel('Export folder') self.__exportFolderPicker = ctk.ctkDirectoryButton() exportButton = qt.QPushButton('Export') exportButton.connect('clicked()', self.onReportExport) ioFrameLayout.addWidget(label, 0, 0) ioFrameLayout.addWidget(self.__exportFolderPicker, 0, 1) ioFrameLayout.addWidget(exportButton, 0, 2) label = qt.QLabel('AIM file to import') self.__aimFilePicker = qt.QPushButton('N/A') self.__aimFilePicker.connect('clicked()', self.onSelectAIMFile) button = qt.QPushButton('Import') button.connect('clicked()', self.onReportImport) ioFrameLayout.addWidget(label, 1, 0) ioFrameLayout.addWidget(self.__aimFilePicker, 1, 1) ioFrameLayout.addWidget(button, 1, 2) self.__importAIMFile = None self.__reportSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.__volumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.updateWidgets) self.updateWidgets() self.layout.addStretch(1) self.__editorParameterNode = self.editUtil.getParameterNode() self.__editorParameterNode.AddObserver( vtk.vtkCommand.ModifiedEvent, self.onEditorParameterNodeChanged)