Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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 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 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()
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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()))
Ejemplo n.º 8
0
    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()
Ejemplo n.º 9
0
    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 + "'")
Ejemplo n.º 10
0
 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 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 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"))
Ejemplo n.º 13
0
    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
      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 updateTreeView(self):
   Helper.Debug('updateTreeView')
   self.__markupTreeView.setHeader(self.__rNode)
 def respondToAnnotationAdded(self, caller, event):
   Helper.Debug("respondToAnnotationAdded: updating the tree view")
   self.updateTreeView()