def enter(self): # switch to Two-over-Two layout lm = slicer.app.layoutManager() if lm != None: lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutTwoOverTwoView) # 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 events') hasObserver = self.__logic.HasObserver(self.__logic.ErrorEvent) if hasObserver == 0: tag = self.__logic.AddObserver(self.__logic.ErrorEvent, self.respondToErrorMessage) # print '\tobserver tag = ',tag else: Helper.Debug('Logic already has an observer on the ErrorEvent') # respond to logic annotation added events hasObserver = self.__logic.HasObserver(self.__logic.AnnotationAdded) if hasObserver == 0: tag = self.__logic.AddObserver(self.__logic.AnnotationAdded, self.respondToAnnotationAdded) vnode = self.volumeSelector.currentNode() if vnode != None: # print "Enter: updating the tree view" self.updateTreeView() self.__editorWidget.enter()
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 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 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() self.updateTreeView()
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 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)
def setHeader(self,reportNode): """Load the table widget with annotations for the report """ Helper.Debug('setHeader() called') self.widget.clearContents() self.widget.setColumnCount(2) self.widget.setHorizontalHeaderLabels(['Markup','Visibility']) self.widget.horizontalHeader().setResizeMode(0, qt.QHeaderView.Stretch) self.widget.horizontalHeader().setResizeMode(1, qt.QHeaderView.Fixed) self.widget.setEditTriggers(qt.QAbstractItemView.NoEditTriggers) if not reportNode: return # get the volume node associated with this report volumeID = reportNode.GetVolumeNodeID() Helper.Debug('volumeID = '+volumeID) if volumeID == "": return volumeNode = slicer.mrmlScene.GetNodeByID(volumeID) if not volumeNode: return # get the annotations associated with this report # find displayble nodes that are associated with this volume numDisplayableNodes = slicer.mrmlScene.GetNumberOfNodesByClass("vtkMRMLDisplayableNode") nodeIDList = [volumeID] for n in range(numDisplayableNodes): displayableNode = slicer.mrmlScene.GetNthNodeByClass(n, "vtkMRMLDisplayableNode") # how best to get at module widget logic? Helper.Debug('Will check if in report here for '+displayableNode.GetID()) inReport = slicer.modules.reporting.logic().IsInReport(displayableNode) Helper.Debug('Checked for report') if inReport: Helper.Debug('Found node associated with the report node: ' + displayableNode.GetName()) nodeIDList.append(displayableNode.GetID()) else: Helper.Debug('Failed to found associated node') self.widget.setRowCount(len(nodeIDList)) row = 0 for nodeID in nodeIDList: node = Helper.getNodeByID(nodeID) item = qt.QTableWidgetItem(node.GetName()) self.widget.setItem(row,0,item) self.items.append(item) if node.IsA("vtkMRMLAnnotationNode"): item = qt.QTableWidgetItem() # save the id for toggling visibility on the node # TODO: make this more elegant instead of overloading the what's this role with the id item.setData(qt.Qt.WhatsThisRole, node.GetID()) if node.GetDisplayVisibility() == 1: item.setData(qt.Qt.DecorationRole,qt.QPixmap(":/Icons/Small/SlicerVisible.png")) else: item.setData(qt.Qt.DecorationRole,qt.QPixmap(":/Icons/Small/SlicerInvisible.png")) self.widget.setItem(row,1,item) self.items.append(item) row += 1
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 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()
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()))
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 + "'")
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 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 onItemClicked(self, item): # column 0 is the name, do jump to annotation if item.column() == 0: # get the id from the item in column 1 idItem = self.widget.item(item.row(), 1) if idItem == None: # volumes don't have anything in this column return nodeID = idItem.data(qt.Qt.WhatsThisRole) if nodeID == None: return controlPointsNode = slicer.mrmlScene.GetNodeByID(nodeID) if controlPointsNode == None: return # is it an annotation? if controlPointsNode.IsA('vtkMRMLAnnotationControlPointsNode') == 0: return # get the coordinates of this annotation rasCoordinates = [0,0,0] if controlPointsNode.IsA('vtkMRMLAnnotationFiducialNode'): controlPointsNode.GetFiducialCoordinates(rasCoordinates) if controlPointsNode.IsA('vtkMRMLAnnotationRulerNode'): controlPointsNode.GetPosition1(rasCoordinates) sliceNode = slicer.mrmlScene.GetNthNodeByClass(0, 'vtkMRMLSliceNode') if sliceNode != None: Helper.Debug("Jumping to first point in annotation") # jump the other slices and then this slice sliceNode.JumpAllSlices(rasCoordinates[0],rasCoordinates[1],rasCoordinates[2]) sliceNode.JumpSlice(rasCoordinates[0],rasCoordinates[1],rasCoordinates[2]) # column 1 is the visibility column if item.column() == 1: # is it a displayable? nodeID = item.data(qt.Qt.WhatsThisRole) if id == None: return node = slicer.mrmlScene.GetNodeByID(nodeID) if node == None: return # update the visibility and the eye icon if node.GetDisplayVisibility() == 0: node.SetDisplayVisibility(1) item.setData(qt.Qt.DecorationRole,qt.QPixmap(":/Icons/Small/SlicerVisible.png")) else: node.SetDisplayVisibility(0) item.setData(qt.Qt.DecorationRole,qt.QPixmap(":/Icons/Small/SlicerInvisible.png"))
def populateTreeView(self): Helper.Debug('populateTreeView') self.__markupTreeView.setHeader(self.__rNode) return Helper.Debug('populateTreeView') if self.__rNode == None: Helper.Debug('populateTreeView: no report node set!') return volumeID = self.__rNode.GetVolumeNodeID() if volumeID == "": Helper.Debug('populateTreeView: no active volume node id on report ' + self.__rNode.GetName()) return volumeNode = Helper.getNodeByID(volumeID) Helper.Debug('volumeID = ' + volumeID) # find displayble nodes that are associated with this volume numDisplayableNodes = slicer.mrmlScene.GetNumberOfNodesByClass("vtkMRMLDisplayableNode") nodeIDList = [] for n in range(numDisplayableNodes): displayableNode = slicer.mrmlScene.GetNthNodeByClass(n, "vtkMRMLDisplayableNode") inReport = self.__logic.IsInReport(displayableNode) if inReport: Helper.Debug('Found node associated with the volume node: ' + displayableNode.GetName()) nodeIDList.append(displayableNode.GetID()) print "Report: " + self.__rNode.GetName() print "Volume: " + volumeNode.GetName() row = 0 # volume name item = qt.QStandardItem() item.setEditable(False) item.setText(volumeNode.GetName()) self.__markupsModel.setItem(row,0,item) self.items.append(item) row += 1 # get the associated markups for nodeID in nodeIDList: node = Helper.getNodeByID(nodeID) if node != None and node.IsA("vtkMRMLVolumeNode") and node.GetLabelMap() == 1: Helper.Debug('Segmentation: ' + node.GetName()) item = qt.QStandardItem() item.setEditable(False) item.setText(node.GetName()) self.__markupsModel.setItem(row,0,item) self.items.append(item) row += 1 if node != None and node.IsA("vtkMRMLAnnotationNode"): Helper.Debug('Annotation: ' + node.GetName()) # annotation name item = qt.QStandardItem() item.setEditable(False) item.setText(node.GetName()) self.__markupsModel.setItem(row,0,item) self.items.append(item) # annotation visibility item = qt.QStandardItem() # todo: allow click to toggle visib item.setEditable(False) if node.GetDisplayVisibility() == 1: item.setData(qt.QPixmap(":/Icons/Small/SlicerVisible.png"),qt.Qt.DecorationRole) else: item.setData(qt.QPixmap(":/Icons/Small/SlicerInvisible.png"),qt.Qt.DecorationRole) self.__markupsModel.setItem(row,1,item) self.items.append(item) row += 1
def respondToAnnotationAdded(self, caller, event): Helper.Debug("respondToAnnotationAdded: updating the tree view") self.updateTreeView()
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 updateTreeView(self): Helper.Debug('updateTreeView') self.__markupTreeView.setHeader(self.__rNode)
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 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 volumesLogic = slicer.modules.volumes.logic() geometryCheckString = volumesLogic.CheckForLabelVolumeValidity(self.__vNode, sNode) if geometryCheckString != "": newNodeAnswer = Helper.QuestionPopup('The geometry of the segmentation label you selected does not match the geometry of the volume being annotated!\nDo you want to create a new label to match the geometry, resampling data to fit?\n' + geometryCheckString) if newNodeAnswer == True: # create a new resampled label node from the input image resampledSegmentationNode = volumesLogic.ResampleVolumeToReferenceVolume(sNode, self.__vNode) # reselect it self.segmentationSelector.setCurrentNode(resampledSegmentationNode) # reset vars sNode = self.segmentationSelector.currentNode() Helper.InfoPopup('Created a new segmentation label named ' + sNode.GetName() + ' resampled from the input label map') image = sNode.GetImageData() else: 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() self.updateTreeView()
def test_RoundTrip(self): """ Test fiducial round trip to and from AIM XML file on disk """ print("ctest, please don't truncate my output: CTEST_FULL_OUTPUT") # enter the module mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('Reporting') # l = slicer.modulelogic.vtkSlicerReportingModuleLogic() l = slicer.modules.reporting.logic() l.GUIHiddenOff() # testDataPath = os.path.normpath(os.path.join(os.path.realpath(__file__), "..", "..", "Prototype/TestData/DICOM.CT/") print("Reporting round trip test, current working directory = " + os.getcwd()) testDataPath = os.path.join(os.getcwd(), "../../Testing/Temporary/DICOM.CT") # testDataPath = "/projects/birn/nicole/Slicer4/Reporting/Prototype/TestData/DICOM.CT" print("test data path = " + testDataPath) # set up a new DICOM database print("Creating a dicomDatabase!") ddb = ctk.ctkDICOMDatabase() if not ddb: print("ERROR: failed to create a new dicom database!") return dbpath = slicer.app.slicerHome + '/Testing/Temporary/TestingDCMDB/ctkDICOM.sql' print('database path set to ' + dbpath) if not os.path.exists(os.path.dirname(dbpath)): print('Creating dir ' + os.path.dirname(dbpath)) os.makedirs(os.path.dirname(dbpath)) ddb.openDatabase(dbpath, "ReportingTesting") if not ddb.isOpen: print("ERROR: failed to open a new dicom database at path " + dbpath) return retval = ddb.initializeDatabase() if not retval: print("ERROR: failed to init database") return l.InitializeDICOMDatabase(dbpath) testFileNames = [] for n in [487, 488, 489]: filename = os.path.join(testDataPath, "instance_" + str(n) + ".dcm") print("Adding file " + filename) testFileNames.append(filename) # check to see if the test data is already in it patients = ddb.patients() if len(patients) == 0: # add the files for filename in testFileNames: print("Inserting file " + filename) retval = ddb.insert(filename) patients = ddb.patients() if len(patients) == 0: print("ERROR: unable to add test files to database!") print(str(testFileNames)) return # get the UID for the series study = ddb.studiesForPatient(patients[0]) series = ddb.seriesForStudy(study[0]) seriesUID = series[0] # seriesUID = "1.2.392.200103.20080913.113635.2.2009.6.22.21.43.10.23432.1" # seriesUID = "2.16.840.1.114362.1.759508.1251415878280.192" # seriesUID = "1.3.12.2.1107.5.1.4.53031.30000011032906120157800000219" print("For test, using the AIM sample volume with series UID of " + seriesUID) fileList = ddb.filesForSeries(seriesUID) print("fileList = " + str(fileList)) if not fileList: print("ERROR: sample series with id " + seriesUID + " not found in database!") return # add a parameter node parameterNode = slicer.vtkMRMLScriptedModuleNode() parameterNode.SetModuleName('Reporting') slicer.mrmlScene.AddNode(parameterNode) # set it to be the active parameter node l.SetActiveParameterNodeID(parameterNode.GetID()) # # create a new report, make it the report in the parameter node, set up hierarchy # reportNode = slicer.mrmlScene.CreateNodeByClass( "vtkMRMLReportingReportNode") reportNode.SetReferenceCount(reportNode.GetReferenceCount() - 1) # set the color id colorID = 'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt' reportNode.SetColorNodeID(colorID) reportNode.SetDICOMDatabaseFileName(dbpath) slicer.mrmlScene.AddNode(reportNode) parameterNode.SetParameter("reportID", reportNode.GetID()) print( "Init hierarchy for report node, set parameter node to report id of " + reportNode.GetID()) l.InitializeHierarchyForReport(reportNode) # # get some sample data from the database # volId = 1 volumeNode = None volName = 'AIM volume ' + str(volId) print("Dicom data base = " + ddb) slicer.dicomDatabase = ddb scalarVolumePlugin = slicer.modules.dicomPlugins[ 'DICOMScalarVolumePlugin']() scalarVolumeLoadables = scalarVolumePlugin.examine([filelist]) volumeNode = scalarVolumePlugin.load(scalarVolumeLoadables[0]) volumeNode.SetName(volName) # print "volumeNode = ",volumeNode print("Initialize Hierarchy For Volume with id " + volumeNode.GetID()) l.InitializeHierarchyForVolume(volumeNode) print("---Now active mark up is " + l.GetActiveMarkupHierarchyID()) print("adding a fiducial") # # define a fiducial # fidNode = slicer.vtkMRMLAnnotationFiducialNode() fidName = "AIM Round Trip Test Fiducial" fidNode.SetName(fidName) fidNode.SetSelected(1) fidNode.SetVisible(1) fidNode.SetLocked(0) print("Calling set fid coords") startCoords = [15.8, 70.8, -126.7] fidNode.SetFiducialCoordinates(startCoords[0], startCoords[1], startCoords[2]) print("Starting fiducial coordinates: " + str(startCoords)) # point it to the volume fidNode.SetAttribute("AssociatedNodeID", volumeNode.GetID()) fidNode.SetScene(slicer.mrmlScene) print("Adding text disp node") fidNode.CreateAnnotationTextDisplayNode() print("Adding point display node") fidNode.CreateAnnotationPointDisplayNode() print("add node:") # slicer.mrmlScene.DebugOn() # l.DebugOn() slicer.mrmlScene.AddNode(fidNode) print("getting slice uid") uid = l.GetSliceUIDFromMarkUp(fidNode) print("fidNode uid = " + uid) # # create a label volume # volumesLogic = slicer.modules.volumes.logic() labelNode = volumesLogic.CreateLabelVolume(slicer.mrmlScene, volumeNode, "Segmentation") labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt') l.AddNodeToReport(labelNode) # initialize image content labelImage = labelNode.GetImageData() extent = labelImage.GetExtent() pixelCounter = 0 for k in range(extent[5]): for j in range(extent[3]): for i in range(extent[1]): if pixelCounter in initializedSegmentationVoxels: labelImage.SetScalarComponentFromFloat(i, j, k, 0, 1) else: labelImage.SetScalarComponentFromFloat(i, j, k, 0, 0) pixelCounter = pixelCounter + 1 # test save to mrml # slicer.mrmlScene.SetURL('/spl/tmp/nicole/Testing/aim/RoundTripTest.mrml') # slicer.mrmlScene.Commit() # # output AIM XML # dirName = slicer.app.slicerHome + '/Testing/Temporary/' reportNode.SetStorageDirectoryName(dirName) print("Saving report to " + dirName) retval = l.SaveReportToAIM(reportNode) if (retval != 0): print("ERROR: unable to save report to aim file " + dirName + ", retval=" + retval) else: print("Saved report to " + dirName) self.assertEqual(retval, 0) print("\n\n\nReloading aim file...") # # now clear the scene so can read in # # TBD: this causes a crash # slicer.mrmlScene.Clear(0) # # load in the aim file # newReport = slicer.mrmlScene.CreateNodeByClass( "vtkMRMLReportingReportNode") newReport.SetReferenceCount(newReport.GetReferenceCount() - 1) # set the default color map newReport.SetColorNodeID(colorID) newReport.SetDICOMDatabaseFileName(dbpath) slicer.mrmlScene.AddNode(newReport) parameterNode.SetParameter("reportID", newReport.GetID()) Helper.LoadAIMFile(newReport.GetID(), aimFileName) # check the fiducial endCoords = [0, 0, 0] col = slicer.mrmlScene.GetNodesByClass("vtkMRMLAnnotationFiducialNode") col.SetReferenceCount(col.GetReferenceCount() - 1) # if the scene is not cleared, we should have 2 fiducials nFiducials = col.GetNumberOfItems() if nFiducials != 2: print("Failed to read fiducial form the saved report!") self.assertTrue(False) f = col.GetItemAsObject(1) f.GetFiducialCoordinates(endCoords) print("Start Coords = " + str(startCoords[0]) + "," + str(startCoords[1]) + "," + str(startCoords[2])) print("End Coords = " + str(endCoords)) xdiff = endCoords[0] - startCoords[0] ydiff = endCoords[1] - startCoords[1] zdiff = endCoords[2] - startCoords[2] diffTotal = xdiff + ydiff + zdiff print( "Difference between coordinates after loaded the aim file and value from before stored the aim file: " + str(xdiff) + "," + str(ydiff) + "," + str(zdiff) + ". Total difference = " + str(diffTotal)) if diffTotal > 0.1: print("Fiducial coordinates error exceeds the allowed bounds") self.assertTrue(False) # check the label node sceneVolumes = slicer.mrmlScene.GetNodesByClass( "vtkMRMLScalarVolumeNode") sceneVolumes.SetReferenceCount(sceneVolumes.GetReferenceCount() - 1) sceneLabels = [] for i in range(sceneVolumes.GetNumberOfItems()): vol = sceneVolumes.GetItemAsObject(i) if vol.GetLabelMap(): sceneLabels.append(vol) if len(sceneLabels) != 2: print( "Scene does not have two label nodes after reloading from AIM!" ) self.assertTrue(False) newLabelNode = sceneLabels[1] newLabelImage = newLabelNode.GetImageData() extent = newLabelImage.GetExtent() pixelCounter = 0 for k in range(extent[5]): for j in range(extent[3]): for i in range(extent[1]): pixel = newLabelImage.GetScalarComponentAsFloat(i, j, k, 0) if ((pixelCounter in initializedSegmentationVoxels) and pixel != 1) or (not ( pixelCounter in initializedSegmentationVoxels) and pixel != 0): print("Segmentation content not recovered correctly!") print("Pixel counter " + str(pixelCounter) + " is set to " + str(pixel)) self.assertTrue(False) pixelCounter = pixelCounter + 1 self.assertTrue(True)