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)
Example #2
0
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 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) 
Example #4
0
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 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) 
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')
Example #7
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')