class Window(QWidget):
    def __init__(self):

        QWidget.__init__(self)

        self.textBrowser = QTextBrowser(self)
        self.textBrowser.setTextFormat(QTextBrowser.LogText)
        self.lineEdit = QLineEdit(self)
        self.startButton = QPushButton(self.tr("Start"), self)
        self.stopButton = QPushButton(self.tr("Stop"), self)
        self.stopButton.setEnabled(False)

        self.connect(self.lineEdit, SIGNAL("returnPressed()"), self.startCommand)
        self.connect(self.startButton, SIGNAL("clicked()"), self.startCommand)
        self.connect(self.stopButton, SIGNAL("clicked()"), self.stopCommand)

 
        layout = QGridLayout(self, 2, 3)
        layout.setSpacing(8)
        layout.addMultiCellWidget(self.textBrowser, 0, 0, 0, 2)
        layout.addWidget(self.lineEdit, 1, 0)
        layout.addWidget(self.startButton, 1, 1)
        layout.addWidget(self.stopButton, 1, 2)
 
        self.process = QProcess()
        self.connect(self.process, SIGNAL("readyReadStdout()"), self.readOutput)
        self.connect(self.process, SIGNAL("readyReadStderr()"), self.readErrors)
        self.connect(self.process, SIGNAL("processExited()"), self.resetButtons)

    def startCommand(self):
        self.process.setArguments(QStringList.split(" ", self.lineEdit.text()))
        self.process.closeStdin()

        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.textBrowser.clear()
        if not self.process.start():
            self.textBrowser.setText(
                QString("*** Failed to run %1 ***").arg(self.lineEdit.text())
                )
            self.resetButtons()
            return

 
    def stopCommand(self):
        self.resetButtons()
        self.process.tryTerminate()
        QTimer.singleShot(5000, self.process, SLOT("kill()"))

    def readOutput(self):

        self.textBrowser.append(QString(self.process.readStdout()))

    def readErrors(self):

        self.textBrowser.append("error: " + QString(self.process.readLineStderr()))

    def resetButtons(self):
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
Exemple #2
0
class ParamAdvancedWidget(QWidget):
    def __init__(self, phl_obj=None, parent=None):
        super(ParamAdvancedWidget, self).__init__()

        self.scrollable_widget = PhilWidget(phl_obj, parent=self)
        scrollArea = QScrollArea()
        scrollArea.setWidget(self.scrollable_widget)
        vbox = QVBoxLayout()

        search_label = QLabel("Search:")
        search_edit = QLineEdit()
        search_edit.setPlaceholderText("Type search here")
        search_edit.textChanged.connect(self.scrollable_widget.user_searching)
        self.search_next_button = QPushButton("Find next")
        self.search_next_button.setEnabled(False)

        hbox = QHBoxLayout()
        hbox.addWidget(search_label)
        hbox.addWidget(search_edit)
        hbox.addWidget(self.search_next_button)
        self.search_next_button.clicked.connect(self.scrollable_widget.find_next)
        vbox.addLayout(hbox)

        vbox.addWidget(scrollArea)
        self.setLayout(vbox)
class qSlicerMultiVolumeExplorerModuleWidget(
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget):
    def __init__(self, parent=None):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.__init__(self, parent)

    def setupAdditionalFrames(self):
        self.setupPlotSettingsFrame()

    def setupInputFrame(self, parent=None):
        self.inputFrame = ctk.ctkCollapsibleButton()
        self.inputFrame.text = "Input"
        self.inputFrame.collapsed = 0
        inputFrameCollapsibleLayout = QFormLayout(self.inputFrame)
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupInputFrame(
            self, parent=inputFrameCollapsibleLayout)
        self.layout.addWidget(self.inputFrame)

        self.fgMultiVolumeSelector = slicer.qMRMLNodeComboBox()
        self.fgMultiVolumeSelector.nodeTypes = ['vtkMRMLMultiVolumeNode']
        self.fgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
        self.fgMultiVolumeSelector.addEnabled = 0
        self.fgMultiVolumeSelector.noneEnabled = 1
        self.fgMultiVolumeSelector.toolTip = "Secondary multivolume will be used for the secondary \
      plot in interactive charting. As an example, this can be used to overlay the \
      curve obtained by fitting a model to the data"

        self.inputFrameLayout.addRow(QLabel('Input secondary multivolume'),
                                     self.fgMultiVolumeSelector)

    def setupFrameControlFrame(self):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupFrameControlFrame(
            self)

        self.frameCopySelector = slicer.qMRMLNodeComboBox()
        self.frameCopySelector.setSizePolicy(QSizePolicy.Ignored,
                                             QSizePolicy.Preferred)
        self.frameCopySelector.nodeTypes = ['vtkMRMLScalarVolumeNode']
        self.frameCopySelector.setMRMLScene(slicer.mrmlScene)
        self.frameCopySelector.addEnabled = 1
        self.frameCopySelector.enabled = 0
        # do not show "children" of vtkMRMLScalarVolumeNode
        self.frameCopySelector.hideChildNodeTypes = [
            "vtkMRMLDiffusionWeightedVolumeNode",
            "vtkMRMLDiffusionTensorVolumeNode", "vtkMRMLVectorVolumeNode"
        ]
        self.extractFrameCopy = False
        self.extractFrameCheckBox = QCheckBox('Enable copying')
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Current frame copy'))
        hbox.addWidget(self.frameCopySelector)
        hbox.addWidget(self.extractFrameCheckBox)
        self.inputFrameLayout.addRow(hbox)

    def setupPlotSettingsFrame(self):
        self.plotSettingsFrame = ctk.ctkCollapsibleButton()
        self.plotSettingsFrame.text = "Plotting Settings"
        self.plotSettingsFrame.collapsed = 1
        plotSettingsFrameLayout = QFormLayout(self.plotSettingsFrame)
        self.layout.addWidget(self.plotSettingsFrame)

        # label map for probing
        self.labelMapSelector = slicer.qMRMLNodeComboBox()
        self.labelMapSelector.nodeTypes = ['vtkMRMLLabelMapVolumeNode']
        self.labelMapSelector.toolTip = 'Label map to be probed'
        self.labelMapSelector.setMRMLScene(slicer.mrmlScene)
        self.labelMapSelector.addEnabled = 0
        self.chartButton = QPushButton('Chart')
        self.chartButton.setEnabled(False)

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Probed label volume'))
        hbox.addWidget(self.labelMapSelector)
        hbox.addWidget(self.chartButton)
        plotSettingsFrameLayout.addRow(hbox)

        self.iCharting = QCheckBox('Interactive charting')
        self.iCharting.setChecked(True)
        plotSettingsFrameLayout.addRow(self.iCharting)

        self.iChartingMode = QButtonGroup()
        self.iChartingIntensity = QRadioButton('Signal intensity')
        self.iChartingIntensityFixedAxes = QRadioButton(
            'Fixed range intensity')
        self.iChartingPercent = QRadioButton('Percentage change')
        self.iChartingIntensity.setChecked(1)
        self.iChartingMode.addButton(self.iChartingIntensity)
        self.iChartingMode.addButton(self.iChartingIntensityFixedAxes)
        self.iChartingMode.addButton(self.iChartingPercent)

        hbox = QHBoxLayout()
        self.plottingModeGroupBox = QGroupBox('Plotting mode:')
        plottingModeLayout = QVBoxLayout()
        self.plottingModeGroupBox.setLayout(plottingModeLayout)
        plottingModeLayout.addWidget(self.iChartingIntensity)
        plottingModeLayout.addWidget(self.iChartingIntensityFixedAxes)
        plottingModeLayout.addWidget(self.iChartingPercent)
        hbox.addWidget(self.plottingModeGroupBox)

        self.showLegendCheckBox = QCheckBox('Display legend')
        self.showLegendCheckBox.setChecked(0)
        self.xLogScaleCheckBox = QCheckBox('Use log scale for X axis')
        self.xLogScaleCheckBox.setChecked(0)
        self.yLogScaleCheckBox = QCheckBox('Use log scale for Y axis')
        self.yLogScaleCheckBox.setChecked(0)

        self.plotGeneralSettingsGroupBox = QGroupBox('General Plot options:')
        plotGeneralSettingsLayout = QVBoxLayout()
        self.plotGeneralSettingsGroupBox.setLayout(plotGeneralSettingsLayout)
        plotGeneralSettingsLayout.addWidget(self.showLegendCheckBox)
        plotGeneralSettingsLayout.addWidget(self.xLogScaleCheckBox)
        plotGeneralSettingsLayout.addWidget(self.yLogScaleCheckBox)
        hbox.addWidget(self.plotGeneralSettingsGroupBox)
        plotSettingsFrameLayout.addRow(hbox)

        self.nFramesBaselineCalculation = QSpinBox()
        self.nFramesBaselineCalculation.minimum = 1
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Frame count(baseline calculation):'))
        hbox.addWidget(self.nFramesBaselineCalculation)
        plotSettingsFrameLayout.addRow(hbox)

    def setupPlottingFrame(self, parent=None):
        self.plotFrame = ctk.ctkCollapsibleButton()
        self.plotFrame.text = "Plotting"
        self.plotFrame.collapsed = 0
        plotFrameLayout = QGridLayout(self.plotFrame)
        self.layout.addWidget(self.plotFrame)
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupPlottingFrame(
            self, parent=plotFrameLayout)

    def onDockChartViewToggled(self, checked):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onDockChartViewToggled(
            self, checked)
        if checked:
            self.layout.removeWidget(self.plotFrame)
            self.plotFrame.hide()

    def dockChartView(self):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.dockChartView(self)
        self.layout.addWidget(self.plotFrame)
        self.plotFrame.show()

    def setFramesEnabled(self, enabled):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setFramesEnabled(
            self, enabled)
        self.plotSettingsFrame.setEnabled(enabled)
        self.plotFrame.setEnabled(enabled)
        self.plotFrame.collapsed = 0 if enabled else 1

    def setupConnections(self):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupConnections(self)
        self.labelMapSelector.connect('currentNodeChanged(vtkMRMLNode*)',
                                      self.onLabelNodeChanged)
        self.chartButton.connect('clicked()', self.onLabeledChartRequested)
        self.xLogScaleCheckBox.connect('stateChanged(int)',
                                       self.onXLogScaleRequested)
        self.yLogScaleCheckBox.connect('stateChanged(int)',
                                       self.onYLogScaleRequested)
        self.nFramesBaselineCalculation.valueChanged.connect(
            self.onFrameCountBaselineCalculationChanged)
        self.iChartingMode.buttonClicked.connect(self.onChartingModeChanged)
        self.showLegendCheckBox.connect('stateChanged(int)',
                                        self.onShowLegendChanged)
        self.fgMultiVolumeSelector.connect('currentNodeChanged(vtkMRMLNode*)',
                                           self.onForegroundInputChanged)
        self.extractFrameCheckBox.connect('stateChanged(int)',
                                          self.onExtractFrameChanged)
        self.frameCopySelector.connect('mrmlSceneChanged(vtkMRMLScene*)',
                                       self.onVFMRMLSceneChanged)

    def onFrameCountBaselineCalculationChanged(self, value):
        self._multiVolumeIntensityChart.nFramesForBaselineCalculation = value

    def onChartingModeChanged(self, button):
        if button is self.iChartingIntensity:
            self._multiVolumeIntensityChart.activateSignalIntensityMode()
        elif button is self.iChartingIntensityFixedAxes:
            self._multiVolumeIntensityChart.activateFixedRangeIntensityMode()
        elif button is self.iChartingPercent:
            self._multiVolumeIntensityChart.activatePercentageChangeMode()

    def onShowLegendChanged(self, checked):
        self._multiVolumeIntensityChart.showLegend = True if checked == 2 else False

    def onXLogScaleRequested(self, checked):
        self._multiVolumeIntensityChart.showXLogScale = True if checked == 2 else False

    def onYLogScaleRequested(self, checked):
        self._multiVolumeIntensityChart.showYLogScale = True if checked == 2 else False

    def onLVMRMLSceneChanged(self, mrmlScene):
        self.labelMapSelector.setMRMLScene(slicer.mrmlScene)

    def onVFMRMLSceneChanged(self, mrmlScene):
        self.frameCopySelector.setMRMLScene(slicer.mrmlScene)

    def onLabelNodeChanged(self):
        labelNode = self.labelMapSelector.currentNode()
        self.chartButton.setEnabled(labelNode is not None
                                    and self._bgMultiVolumeNode is not None)

    def onForegroundInputChanged(self):
        logging.info(
            "qSlicerMultiVolumeExplorerModuleWidget:ForegroundInputChanged")
        self._fgMultiVolumeNode = self.fgMultiVolumeSelector.currentNode()
        self._multiVolumeIntensityChart.fgMultiVolumeNode = self.fgMultiVolumeSelector.currentNode(
        )
        self.refreshGUIForNewBackgroundImage()

    def onBackgroundInputChanged(self):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onBackgroundInputChanged(
            self)

        if self._bgMultiVolumeNode is not None:
            self.frameCopySelector.setCurrentNode(None)
            self.nFramesBaselineCalculation.maximum = self._bgMultiVolumeNode.GetNumberOfFrames(
            )
        self.onLabelNodeChanged()

    '''
  If extract button is checked, will copy the current frame to the
  selected volume node on each event from frame slider
  '''

    def onExtractFrameChanged(self, checked):
        if checked:
            self.extractFrameCopy = True
            self.onSliderChanged(self.frameSlider.value)
        else:
            self.extractFrameCopy = False

    def onSliderChanged(self, frameId):
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onSliderChanged(
            self, frameId)
        frameId = int(frameId)
        if self.extractFrameCopy:
            frameVolume = self.frameCopySelector.currentNode()
            frameVolumeCopy = Helper.extractFrame(frameVolume,
                                                  self._bgMultiVolumeNode,
                                                  frameId)
            if not frameVolume:
                self.frameCopySelector.setCurrentNode(frameVolumeCopy)
            frameName = '%s frame %d' % (self._bgMultiVolumeNode.GetName(),
                                         frameId)
            frameVolumeCopy.SetName(frameName)

    def onLabeledChartRequested(self):
        labelNode = self.labelMapSelector.currentNode()
        mvNode = self._bgMultiVolumeNode

        mvLabels = MultiVolumeIntensityChartView.getMultiVolumeLabels(
            self._bgMultiVolumeNode)

        chartViewNode = LabeledImageChartView(
            labelNode=labelNode,
            multiVolumeNode=mvNode,
            multiVolumeLabels=mvLabels,
            baselineFrames=self.nFramesBaselineCalculation,
            displayPercentageChange=self.iChartingPercent.checked)
        chartViewNode.requestChartCreation()

    def processEvent(self, observee, event):
        if not self.iCharting.checked:
            return
        qSlicerMultiVolumeExplorerSimplifiedModuleWidget.processEvent(
            self, observee, event)
class qSlicerMultiVolumeExplorerModuleWidget(qSlicerMultiVolumeExplorerSimplifiedModuleWidget):

  def __init__(self, parent=None):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.__init__(self, parent)

  def setupAdditionalFrames(self):
    self.setupPlotSettingsFrame()

  def setupInputFrame(self, parent=None):
    self.inputFrame = ctk.ctkCollapsibleButton()
    self.inputFrame.text = "Input"
    self.inputFrame.collapsed = 0
    inputFrameCollapsibleLayout = QFormLayout(self.inputFrame)
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupInputFrame(self, parent=inputFrameCollapsibleLayout)
    self.layout.addWidget(self.inputFrame)

    self.fgMultiVolumeSelector = slicer.qMRMLNodeComboBox()
    self.fgMultiVolumeSelector.nodeTypes = ['vtkMRMLMultiVolumeNode']
    self.fgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
    self.fgMultiVolumeSelector.addEnabled = 0
    self.fgMultiVolumeSelector.noneEnabled = 1
    self.fgMultiVolumeSelector.toolTip = "Secondary multivolume will be used for the secondary \
      plot in interactive charting. As an example, this can be used to overlay the \
      curve obtained by fitting a model to the data"
    self.inputFrameLayout.addRow(QLabel('Input secondary multivolume'), self.fgMultiVolumeSelector)

  def setupFrameControlFrame(self):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupFrameControlFrame(self)

    self.frameCopySelector = slicer.qMRMLNodeComboBox()
    self.frameCopySelector.nodeTypes = ['vtkMRMLScalarVolumeNode']
    self.frameCopySelector.setMRMLScene(slicer.mrmlScene)
    self.frameCopySelector.addEnabled = 1
    self.frameCopySelector.enabled = 0
    # do not show "children" of vtkMRMLScalarVolumeNode
    self.frameCopySelector.hideChildNodeTypes = ["vtkMRMLDiffusionWeightedVolumeNode",
                                                  "vtkMRMLDiffusionTensorVolumeNode",
                                                  "vtkMRMLVectorVolumeNode"]
    self.extractFrameCopy = False
    self.extractFrameCheckBox = QCheckBox('Enable copying')
    hbox = QHBoxLayout()
    hbox.addWidget(QLabel('Current frame copy'))
    hbox.addWidget(self.frameCopySelector)
    hbox.addWidget(self.extractFrameCheckBox)
    self.inputFrameLayout.addRow(hbox)

  def setupPlotSettingsFrame(self):
    self.plotSettingsFrame = ctk.ctkCollapsibleButton()
    self.plotSettingsFrame.text = "Plotting Settings"
    self.plotSettingsFrame.collapsed = 1
    plotSettingsFrameLayout = QFormLayout(self.plotSettingsFrame)
    self.layout.addWidget(self.plotSettingsFrame)

    # label map for probing
    self.labelMapSelector = slicer.qMRMLNodeComboBox()
    self.labelMapSelector.nodeTypes = ['vtkMRMLLabelMapVolumeNode']
    self.labelMapSelector.toolTip = 'Label map to be probed'
    self.labelMapSelector.setMRMLScene(slicer.mrmlScene)
    self.labelMapSelector.addEnabled = 0
    self.chartButton = QPushButton('Chart')
    self.chartButton.setEnabled(False)

    hbox = QHBoxLayout()
    hbox.addWidget(QLabel('Probed label volume'))
    hbox.addWidget(self.labelMapSelector)
    hbox.addWidget(self.chartButton)
    plotSettingsFrameLayout.addRow(hbox)

    self.iCharting = QCheckBox('Interactive charting')
    self.iCharting.setChecked(True)
    plotSettingsFrameLayout.addRow(self.iCharting)

    self.iChartingMode = QButtonGroup()
    self.iChartingIntensity = QRadioButton('Signal intensity')
    self.iChartingIntensityFixedAxes = QRadioButton('Fixed range intensity')
    self.iChartingPercent = QRadioButton('Percentage change')
    self.iChartingIntensity.setChecked(1)
    self.iChartingMode.addButton(self.iChartingIntensity)
    self.iChartingMode.addButton(self.iChartingIntensityFixedAxes)
    self.iChartingMode.addButton(self.iChartingPercent)

    hbox = QHBoxLayout()
    self.plottingModeGroupBox = QGroupBox('Plotting mode:')
    plottingModeLayout = QVBoxLayout()
    self.plottingModeGroupBox.setLayout(plottingModeLayout)
    plottingModeLayout.addWidget(self.iChartingIntensity)
    plottingModeLayout.addWidget(self.iChartingIntensityFixedAxes)
    plottingModeLayout.addWidget(self.iChartingPercent)
    hbox.addWidget(self.plottingModeGroupBox)

    self.showLegendCheckBox = QCheckBox('Display legend')
    self.showLegendCheckBox.setChecked(0)
    self.xLogScaleCheckBox = QCheckBox('Use log scale for X axis')
    self.xLogScaleCheckBox.setChecked(0)
    self.yLogScaleCheckBox = QCheckBox('Use log scale for Y axis')
    self.yLogScaleCheckBox.setChecked(0)

    self.plotGeneralSettingsGroupBox = QGroupBox('General Plot options:')
    plotGeneralSettingsLayout = QVBoxLayout()
    self.plotGeneralSettingsGroupBox.setLayout(plotGeneralSettingsLayout)
    plotGeneralSettingsLayout.addWidget(self.showLegendCheckBox)
    plotGeneralSettingsLayout.addWidget(self.xLogScaleCheckBox)
    plotGeneralSettingsLayout.addWidget(self.yLogScaleCheckBox)
    hbox.addWidget(self.plotGeneralSettingsGroupBox)
    plotSettingsFrameLayout.addRow(hbox)

    self.nFramesBaselineCalculation = QSpinBox()
    self.nFramesBaselineCalculation.minimum = 1
    hbox = QHBoxLayout()
    hbox.addWidget(QLabel('Frame count(baseline calculation):'))
    hbox.addWidget(self.nFramesBaselineCalculation)
    plotSettingsFrameLayout.addRow(hbox)

  def setupPlottingFrame(self, parent=None):
    self.plotFrame = ctk.ctkCollapsibleButton()
    self.plotFrame.text = "Plotting"
    self.plotFrame.collapsed = 0
    plotFrameLayout = QGridLayout(self.plotFrame)
    self.layout.addWidget(self.plotFrame)
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupPlottingFrame(self, parent=plotFrameLayout)

  def onDockChartViewToggled(self, checked):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onDockChartViewToggled(self, checked)
    if checked:
      self.layout.removeWidget(self.plotFrame)
      self.plotFrame.hide()

  def dockChartView(self):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.dockChartView(self)
    self.layout.addWidget(self.plotFrame)
    self.plotFrame.show()

  def setFramesEnabled(self, enabled):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setFramesEnabled(self, enabled)
    self.plotSettingsFrame.setEnabled(enabled)
    self.plotFrame.setEnabled(enabled)
    self.plotFrame.collapsed = 0 if enabled else 1

  def setupConnections(self):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.setupConnections(self)
    self.labelMapSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onLabelNodeChanged)
    self.chartButton.connect('clicked()', self.onLabeledChartRequested)
    self.xLogScaleCheckBox.connect('stateChanged(int)', self.onXLogScaleRequested)
    self.yLogScaleCheckBox.connect('stateChanged(int)', self.onYLogScaleRequested)
    self.nFramesBaselineCalculation.valueChanged.connect(self.onFrameCountBaselineCalculationChanged)
    self.iChartingMode.buttonClicked.connect(self.onChartingModeChanged)
    self.showLegendCheckBox.connect('stateChanged(int)', self.onShowLegendChanged)
    self.fgMultiVolumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onForegroundInputChanged)
    self.extractFrameCheckBox.connect('stateChanged(int)', self.onExtractFrameChanged)
    self.frameCopySelector.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onVFMRMLSceneChanged)

  def onFrameCountBaselineCalculationChanged(self, value):
    self._multiVolumeIntensityChart.nFramesForBaselineCalculation = value

  def onChartingModeChanged(self, button):
    if button is self.iChartingIntensity:
      self._multiVolumeIntensityChart.activateSignalIntensityMode()
    elif button is self.iChartingIntensityFixedAxes:
      self._multiVolumeIntensityChart.activateFixedRangeIntensityMode()
    elif button is self.iChartingPercent:
      self._multiVolumeIntensityChart.activatePercentageChangeMode()

  def onShowLegendChanged(self, checked):
    self._multiVolumeIntensityChart.showLegend = True if checked == 2 else False

  def onXLogScaleRequested(self, checked):
    self._multiVolumeIntensityChart.showXLogScale = True if checked == 2 else False

  def onYLogScaleRequested(self, checked):
    self._multiVolumeIntensityChart.showYLogScale = True if checked == 2 else False

  def onLVMRMLSceneChanged(self, mrmlScene):
    self.labelMapSelector.setMRMLScene(slicer.mrmlScene)

  def onVFMRMLSceneChanged(self, mrmlScene):
    self.frameCopySelector.setMRMLScene(slicer.mrmlScene)

  def onLabelNodeChanged(self):
    labelNode = self.labelMapSelector.currentNode()
    self.chartButton.setEnabled(labelNode is not None and self._bgMultiVolumeNode is not None)

  def onForegroundInputChanged(self):
    logging.info("qSlicerMultiVolumeExplorerModuleWidget:ForegroundInputChanged")
    self._fgMultiVolumeNode = self.fgMultiVolumeSelector.currentNode()
    self._multiVolumeIntensityChart.fgMultiVolumeNode = self.fgMultiVolumeSelector.currentNode()
    self.refreshGUIForNewBackgroundImage()

  def onBackgroundInputChanged(self):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onBackgroundInputChanged(self)

    if self._bgMultiVolumeNode is not None:
      self.frameCopySelector.setCurrentNode(None)
      self.nFramesBaselineCalculation.maximum = self._bgMultiVolumeNode.GetNumberOfFrames()
    self.onLabelNodeChanged()

  '''
  If extract button is checked, will copy the current frame to the
  selected volume node on each event from frame slider
  '''
  def onExtractFrameChanged(self, checked):
    if checked:
      self.extractFrameCopy = True
      self.onSliderChanged(self.frameSlider.value)
    else:
      self.extractFrameCopy = False

  def onSliderChanged(self, frameId):
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.onSliderChanged(self, frameId)
    frameId = int(frameId)
    if self.extractFrameCopy:
      frameVolume = self.frameCopySelector.currentNode()
      frameVolumeCopy = Helper.extractFrame(frameVolume, self._bgMultiVolumeNode, frameId)
      if not frameVolume:
        self.frameCopySelector.setCurrentNode(frameVolumeCopy)
      frameName = '%s frame %d' % (self._bgMultiVolumeNode.GetName(), frameId)
      frameVolumeCopy.SetName(frameName)

  def onLabeledChartRequested(self):
    labelNode = self.labelMapSelector.currentNode()
    mvNode = self._bgMultiVolumeNode

    mvLabels = MultiVolumeIntensityChartView.getMultiVolumeLabels(self._bgMultiVolumeNode)

    chartViewNode = LabeledImageChartView(labelNode=labelNode,
                                           multiVolumeNode=mvNode,
                                           multiVolumeLabels=mvLabels,
                                           baselineFrames=self.nFramesBaselineCalculation,
                                           displayPercentageChange=self.iChartingPercent.checked)
    chartViewNode.requestChartCreation()

  def processEvent(self, observee, event):
    if not self.iCharting.checked:
      return
    qSlicerMultiVolumeExplorerSimplifiedModuleWidget.processEvent(self, observee, event)
Exemple #5
0
class ScoringDialog(QWidget):

    """a dialog for entering the scores"""

    # pylint: disable=too-many-instance-attributes

    def __init__(self, scene):
        QWidget.__init__(self)
        self.scene = scene
        decorateWindow(self, m18n("Scoring for this Hand"))
        self.nameLabels = [None] * 4
        self.spValues = [None] * 4
        self.windLabels = [None] * 4
        self.wonBoxes = [None] * 4
        self.detailsLayout = [None] * 4
        self.details = [None] * 4
        self.__tilePixMaps = []
        self.__meldPixMaps = []
        grid = QGridLayout(self)
        pGrid = QGridLayout()
        grid.addLayout(pGrid, 0, 0, 2, 1)
        pGrid.addWidget(QLabel(m18nc("kajongg", "Player")), 0, 0)
        pGrid.addWidget(QLabel(m18nc("kajongg", "Wind")), 0, 1)
        pGrid.addWidget(QLabel(m18nc("kajongg", "Score")), 0, 2)
        pGrid.addWidget(QLabel(m18n("Winner")), 0, 3)
        self.detailTabs = QTabWidget()
        self.detailTabs.setDocumentMode(True)
        pGrid.addWidget(self.detailTabs, 0, 4, 8, 1)
        for idx in range(4):
            self.setupUiForPlayer(pGrid, idx)
        self.draw = QCheckBox(m18nc("kajongg", "Draw"))
        self.draw.clicked.connect(self.wonChanged)
        btnPenalties = QPushButton(m18n("&Penalties"))
        btnPenalties.clicked.connect(self.penalty)
        self.btnSave = QPushButton(m18n("&Save Hand"))
        self.btnSave.clicked.connect(self.game.nextScoringHand)
        self.btnSave.setEnabled(False)
        self.setupUILastTileMeld(pGrid)
        pGrid.setRowStretch(87, 10)
        pGrid.addWidget(self.draw, 7, 3)
        self.cbLastTile.currentIndexChanged.connect(self.slotLastTile)
        self.cbLastMeld.currentIndexChanged.connect(self.slotInputChanged)
        btnBox = QHBoxLayout()
        btnBox.addWidget(btnPenalties)
        btnBox.addWidget(self.btnSave)
        pGrid.addLayout(btnBox, 8, 4)
        StateSaver(self)
        self.refresh()

    @property
    def game(self):
        """proxy"""
        return self.scene.game

    def setupUILastTileMeld(self, pGrid):
        """setup UI elements for last tile and last meld"""
        self.lblLastTile = QLabel(m18n("&Last Tile:"))
        self.cbLastTile = QComboBox()
        self.cbLastTile.setMinimumContentsLength(1)
        vpol = QSizePolicy()
        vpol.setHorizontalPolicy(QSizePolicy.Fixed)
        self.cbLastTile.setSizePolicy(vpol)
        self.cbLastTile.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.lblLastTile.setBuddy(self.cbLastTile)
        self.lblLastMeld = QLabel(m18n("L&ast Meld:"))
        self.prevLastTile = None
        self.cbLastMeld = QComboBox()
        self.cbLastMeld.setMinimumContentsLength(1)
        self.cbLastMeld.setSizePolicy(vpol)
        self.cbLastMeld.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.lblLastMeld.setBuddy(self.cbLastMeld)
        self.comboTilePairs = set()
        pGrid.setRowStretch(6, 5)
        pGrid.addWidget(self.lblLastTile, 7, 0, 1, 2)
        pGrid.addWidget(self.cbLastTile, 7, 2, 1, 1)
        pGrid.addWidget(self.lblLastMeld, 8, 0, 1, 2)
        pGrid.addWidget(self.cbLastMeld, 8, 2, 1, 2)

    def setupUiForPlayer(self, pGrid, idx):
        """setup UI elements for a player"""
        self.spValues[idx] = QSpinBox()
        self.nameLabels[idx] = QLabel()
        self.nameLabels[idx].setBuddy(self.spValues[idx])
        self.windLabels[idx] = WindLabel()
        pGrid.addWidget(self.nameLabels[idx], idx + 2, 0)
        pGrid.addWidget(self.windLabels[idx], idx + 2, 1)
        pGrid.addWidget(self.spValues[idx], idx + 2, 2)
        self.wonBoxes[idx] = QCheckBox("")
        pGrid.addWidget(self.wonBoxes[idx], idx + 2, 3)
        self.wonBoxes[idx].clicked.connect(self.wonChanged)
        self.spValues[idx].valueChanged.connect(self.slotInputChanged)
        detailTab = QWidget()
        self.detailTabs.addTab(detailTab, "")
        self.details[idx] = QWidget()
        detailTabLayout = QVBoxLayout(detailTab)
        detailTabLayout.addWidget(self.details[idx])
        detailTabLayout.addStretch()
        self.detailsLayout[idx] = QVBoxLayout(self.details[idx])

    def refresh(self):
        """reload game"""
        self.clear()
        game = self.game
        self.setVisible(game is not None and not game.finished())
        if game:
            for idx, player in enumerate(game.players):
                for child in self.details[idx].children():
                    if isinstance(child, RuleBox):
                        child.hide()
                        self.detailsLayout[idx].removeWidget(child)
                        del child
                if game:
                    self.spValues[idx].setRange(0, game.ruleset.limit or 99999)
                    self.nameLabels[idx].setText(player.localName)
                    self.windLabels[idx].wind = player.wind
                    self.windLabels[idx].roundsFinished = game.roundsFinished
                    self.detailTabs.setTabText(idx, player.localName)
                    player.manualRuleBoxes = [RuleBox(x) for x in game.ruleset.allRules if x.hasSelectable]
                    for ruleBox in player.manualRuleBoxes:
                        self.detailsLayout[idx].addWidget(ruleBox)
                        ruleBox.clicked.connect(self.slotInputChanged)
                player.refreshManualRules()

    def show(self):
        """only now compute content"""
        if self.game and not self.game.finished():
            self.slotInputChanged()
            QWidget.show(self)

    def penalty(self):
        """penalty button clicked"""
        dlg = PenaltyDialog(self.game)
        dlg.exec_()

    def slotLastTile(self):
        """called when the last tile changes"""
        newLastTile = self.computeLastTile()
        if not newLastTile:
            return
        if self.prevLastTile and self.prevLastTile.isExposed != newLastTile.isExposed:
            # state of last tile (concealed/exposed) changed:
            # for all checked boxes check if they still are applicable
            winner = self.game.winner
            if winner:
                for box in winner.manualRuleBoxes:
                    if box.isChecked():
                        box.setChecked(False)
                        if winner.hand.manualRuleMayApply(box.rule):
                            box.setChecked(True)
        self.prevLastTile = newLastTile
        self.fillLastMeldCombo()
        self.slotInputChanged()

    def computeLastTile(self):
        """returns the currently selected last tile"""
        idx = self.cbLastTile.currentIndex()
        if idx >= 0:
            return variantValue(self.cbLastTile.itemData(idx))

    def clickedPlayerIdx(self, checkbox):
        """the player whose box has been clicked"""
        for idx in range(4):
            if checkbox == self.wonBoxes[idx]:
                return idx
        assert False

    def wonChanged(self):
        """if a new winner has been defined, uncheck any previous winner"""
        newWinner = None
        if self.sender() != self.draw:
            clicked = self.clickedPlayerIdx(self.sender())
            if self.wonBoxes[clicked].isChecked():
                newWinner = self.game.players[clicked]
            else:
                newWinner = None
        self.game.winner = newWinner
        for idx in range(4):
            if newWinner != self.game.players[idx]:
                self.wonBoxes[idx].setChecked(False)
        if newWinner:
            self.draw.setChecked(False)
        self.fillLastTileCombo()
        self.slotInputChanged()

    def updateManualRules(self):
        """enable/disable them"""
        # if an exclusive rule has been activated, deactivate it for
        # all other players
        ruleBox = self.sender()
        if isinstance(ruleBox, RuleBox) and ruleBox.isChecked() and ruleBox.rule.exclusive():
            for idx, player in enumerate(self.game.players):
                if ruleBox.parentWidget() != self.details[idx]:
                    for pBox in player.manualRuleBoxes:
                        if pBox.rule.name == ruleBox.rule.name:
                            pBox.setChecked(False)
        try:
            newState = bool(self.game.winner.handBoard.uiTiles)
        except AttributeError:
            newState = False
        self.lblLastTile.setEnabled(newState)
        self.cbLastTile.setEnabled(newState)
        self.lblLastMeld.setEnabled(newState)
        self.cbLastMeld.setEnabled(newState)
        if self.game:
            for player in self.game.players:
                player.refreshManualRules(self.sender())

    def clear(self):
        """prepare for next hand"""
        if self.game:
            for idx, player in enumerate(self.game.players):
                self.spValues[idx].clear()
                self.spValues[idx].setValue(0)
                self.wonBoxes[idx].setChecked(False)
                player.payment = 0
                player.invalidateHand()
        for box in self.wonBoxes:
            box.setVisible(False)
        self.draw.setChecked(False)
        self.updateManualRules()

        if self.game is None:
            self.hide()
        else:
            for idx, player in enumerate(self.game.players):
                self.windLabels[idx].setPixmap(WINDPIXMAPS[(player.wind, player.wind == self.game.roundWind)])
            self.computeScores()
            self.spValues[0].setFocus()
            self.spValues[0].selectAll()

    def computeScores(self):
        """if tiles have been selected, compute their value"""
        # pylint: disable=too-many-branches
        # too many branches
        if not self.game:
            return
        if self.game.finished():
            self.hide()
            return
        for nameLabel, wonBox, spValue, player in zip(self.nameLabels, self.wonBoxes, self.spValues, self.game.players):
            with BlockSignals([spValue, wonBox]):
                # we do not want that change to call computeScores again
                if player.handBoard and player.handBoard.uiTiles:
                    spValue.setEnabled(False)
                    nameLabel.setBuddy(wonBox)
                    for _ in range(10):
                        prevTotal = player.handTotal
                        player.invalidateHand()
                        wonBox.setVisible(player.hand.won)
                        if not wonBox.isVisibleTo(self) and wonBox.isChecked():
                            wonBox.setChecked(False)
                            self.game.winner = None
                        elif prevTotal == player.handTotal:
                            break
                        player.refreshManualRules()
                    spValue.setValue(player.handTotal)
                else:
                    if not spValue.isEnabled():
                        spValue.clear()
                        spValue.setValue(0)
                        spValue.setEnabled(True)
                        nameLabel.setBuddy(spValue)
                    wonBox.setVisible(player.handTotal >= self.game.ruleset.minMJTotal())
                    if not wonBox.isVisibleTo(self) and wonBox.isChecked():
                        wonBox.setChecked(False)
                if not wonBox.isVisibleTo(self) and player is self.game.winner:
                    self.game.winner = None
        if Internal.scene.explainView:
            Internal.scene.explainView.refresh()

    def __lastMeldContent(self):
        """prepare content for lastmeld combo"""
        lastTiles = set()
        winnerTiles = []
        if self.game.winner and self.game.winner.handBoard:
            winnerTiles = self.game.winner.handBoard.uiTiles
            pairs = []
            for meld in self.game.winner.hand.melds:
                if len(meld) < 4:
                    pairs.extend(meld)
            for tile in winnerTiles:
                if tile.tile in pairs and not tile.isBonus:
                    lastTiles.add(tile.tile)
        return lastTiles, winnerTiles

    def __fillLastTileComboWith(self, lastTiles, winnerTiles):
        """fill last meld combo with prepared content"""
        self.comboTilePairs = lastTiles
        idx = self.cbLastTile.currentIndex()
        if idx < 0:
            idx = 0
        indexedTile = variantValue(self.cbLastTile.itemData(idx))
        restoredIdx = None
        self.cbLastTile.clear()
        if not winnerTiles:
            return
        pmSize = winnerTiles[0].board.tileset.faceSize
        pmSize = QSize(pmSize.width() * 0.5, pmSize.height() * 0.5)
        self.cbLastTile.setIconSize(pmSize)
        QPixmapCache.clear()
        self.__tilePixMaps = []
        shownTiles = set()
        for tile in winnerTiles:
            if tile.tile in lastTiles and tile.tile not in shownTiles:
                shownTiles.add(tile.tile)
                self.cbLastTile.addItem(QIcon(tile.pixmapFromSvg(pmSize, withBorders=False)), "", toQVariant(tile.tile))
                if indexedTile is tile.tile:
                    restoredIdx = self.cbLastTile.count() - 1
        if not restoredIdx and indexedTile:
            # try again, maybe the tile changed between concealed and exposed
            indexedTile = indexedTile.exposed
            for idx in range(self.cbLastTile.count()):
                if indexedTile is variantValue(self.cbLastTile.itemData(idx)).exposed:
                    restoredIdx = idx
                    break
        if not restoredIdx:
            restoredIdx = 0
        self.cbLastTile.setCurrentIndex(restoredIdx)
        self.prevLastTile = self.computeLastTile()

    def clearLastTileCombo(self):
        """as the name says"""
        self.comboTilePairs = None
        self.cbLastTile.clear()

    def fillLastTileCombo(self):
        """fill the drop down list with all possible tiles.
        If the drop down had content before try to preserve the
        current index. Even if the tile changed state meanwhile."""
        if self.game is None:
            return
        lastTiles, winnerTiles = self.__lastMeldContent()
        if self.comboTilePairs == lastTiles:
            return
        with BlockSignals(self.cbLastTile):
            # we only want to emit the changed signal once
            self.__fillLastTileComboWith(lastTiles, winnerTiles)
        self.cbLastTile.currentIndexChanged.emit(0)

    def __fillLastMeldComboWith(self, winnerMelds, indexedMeld, lastTile):
        """fill last meld combo with prepared content"""
        winner = self.game.winner
        faceWidth = winner.handBoard.tileset.faceSize.width() * 0.5
        faceHeight = winner.handBoard.tileset.faceSize.height() * 0.5
        restoredIdx = None
        for meld in winnerMelds:
            pixMap = QPixmap(faceWidth * len(meld), faceHeight)
            pixMap.fill(Qt.transparent)
            self.__meldPixMaps.append(pixMap)
            painter = QPainter(pixMap)
            for element in meld:
                painter.drawPixmap(
                    0,
                    0,
                    winner.handBoard.tilesByElement(element)[0].pixmapFromSvg(
                        QSize(faceWidth, faceHeight), withBorders=False
                    ),
                )
                painter.translate(QPointF(faceWidth, 0.0))
            self.cbLastMeld.addItem(QIcon(pixMap), "", toQVariant(str(meld)))
            if indexedMeld == str(meld):
                restoredIdx = self.cbLastMeld.count() - 1
        if not restoredIdx and indexedMeld:
            # try again, maybe the meld changed between concealed and exposed
            indexedMeld = indexedMeld.lower()
            for idx in range(self.cbLastMeld.count()):
                meldContent = str(variantValue(self.cbLastMeld.itemData(idx)))
                if indexedMeld == meldContent.lower():
                    restoredIdx = idx
                    if lastTile not in meldContent:
                        lastTile = lastTile.swapped
                        assert lastTile in meldContent
                        with BlockSignals(self.cbLastTile):  # we want to continue right here
                            idx = self.cbLastTile.findData(toQVariant(lastTile))
                            self.cbLastTile.setCurrentIndex(idx)
                    break
        if not restoredIdx:
            restoredIdx = 0
        self.cbLastMeld.setCurrentIndex(restoredIdx)
        self.cbLastMeld.setIconSize(QSize(faceWidth * 3, faceHeight))

    def fillLastMeldCombo(self):
        """fill the drop down list with all possible melds.
        If the drop down had content before try to preserve the
        current index. Even if the meld changed state meanwhile."""
        with BlockSignals(self.cbLastMeld):  # we only want to emit the changed signal once
            showCombo = False
            idx = self.cbLastMeld.currentIndex()
            if idx < 0:
                idx = 0
            indexedMeld = str(variantValue(self.cbLastMeld.itemData(idx)))
            self.cbLastMeld.clear()
            self.__meldPixMaps = []
            if not self.game.winner:
                return
            if self.cbLastTile.count() == 0:
                return
            lastTile = Internal.scene.computeLastTile()
            winnerMelds = [m for m in self.game.winner.hand.melds if len(m) < 4 and lastTile in m]
            assert len(winnerMelds), "lastTile %s missing in %s" % (lastTile, self.game.winner.hand.melds)
            if len(winnerMelds) == 1:
                self.cbLastMeld.addItem(QIcon(), "", toQVariant(str(winnerMelds[0])))
                self.cbLastMeld.setCurrentIndex(0)
                return
            showCombo = True
            self.__fillLastMeldComboWith(winnerMelds, indexedMeld, lastTile)
            self.lblLastMeld.setVisible(showCombo)
            self.cbLastMeld.setVisible(showCombo)
        self.cbLastMeld.currentIndexChanged.emit(0)

    def slotInputChanged(self):
        """some input fields changed: update"""
        for player in self.game.players:
            player.invalidateHand()
        self.updateManualRules()
        self.computeScores()
        self.validate()
        for player in self.game.players:
            self.game.wall.decoratePlayer(player)
        Internal.mainWindow.updateGUI()

    def validate(self):
        """update the status of the OK button"""
        game = self.game
        if game:
            valid = True
            if game.winner and game.winner.handTotal < game.ruleset.minMJTotal():
                valid = False
            elif not game.winner and not self.draw.isChecked():
                valid = False
            self.btnSave.setEnabled(valid)
class ProstateTRUSNavUltrasound(UltraSound):

    OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha"
    SCOUT_VOLUME_FILENAME = "ScoutScan.mha"
    LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha"

    RECORDING_FILENAME = "Recording.mha"
    SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha"
    LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME

    SCOUT_VOLUME_NODE_NAME = "ScoutScan"
    OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference"
    LIVE_VOLUME_NODE_NAME = "liveReconstruction"

    APPLY_HOLE_FILLING_FOR_SNAPSHOT = False
    SNAPSHOT_INTERVAL = 3
    OUTPUT_VOLUME_SPACING = 3
    LIVE_OUTPUT_VOLUME_SPACING = 1

    @property
    def roiNode(self):
        return self._roiNode

    @roiNode.setter
    def roiNode(self, node):
        self._roiNode = node
        if node is not None:
            self.startStopLiveReconstructionButton.setEnabled(True)
        else:
            self.startStopLiveReconstructionButton.setEnabled(False)

    def __init__(self, guideletParent):
        UltraSound.__init__(self, guideletParent)

        self.parameterNode = guideletParent.parameterNode
        self.parameterNodeObserver = None
        self._roiNode = None
        self.liveOutputSpacingValue = [
            self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING,
            self.LIVE_OUTPUT_VOLUME_SPACING
        ]
        self.outputSpacing = [
            self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING,
            self.OUTPUT_VOLUME_SPACING
        ]
        self.roiOrigin = None
        self.roiExtent = None
        self.defaultParameterNode = None
        self.logic = ProstateTRUSNavUltrasoundLogic()

    def setupPanel(self, parentWidget):
        logging.debug('ProstateTRUSNavUltrasound.setupPanel')

        self.connectorNode = self.guideletParent.connectorNode
        self.connectorNodeConnected = False

        collapsibleButton = ctkCollapsibleButton()
        collapsibleButton.setProperty('collapsedHeight', 20)
        setButtonStyle(collapsibleButton, 2.0)
        collapsibleButton.text = "Ultrasound"
        parentWidget.addWidget(collapsibleButton)

        ultrasoundLayout = QFormLayout(collapsibleButton)
        ultrasoundLayout.setContentsMargins(12, 4, 4, 4)
        ultrasoundLayout.setSpacing(4)

        self.connectDisconnectButton = QPushButton("Connect")
        self.connectDisconnectButton.setToolTip(
            "If clicked, connection OpenIGTLink")

        hbox = QHBoxLayout()
        hbox.addWidget(self.connectDisconnectButton)
        ultrasoundLayout.addRow(hbox)

        self.setupIcons()

        self.captureIDSelector = QComboBox()
        self.captureIDSelector.setToolTip("Pick capture device ID")
        self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Expanding)

        self.volumeReconstructorIDSelector = QComboBox()
        self.volumeReconstructorIDSelector.setToolTip(
            "Pick volume reconstructor device ID")
        self.volumeReconstructorIDSelector.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.startStopRecordingButton = QPushButton("  Start Recording")
        self.startStopRecordingButton.setCheckable(True)
        self.startStopRecordingButton.setIcon(self.recordIcon)
        self.startStopRecordingButton.setEnabled(False)
        self.startStopRecordingButton.setToolTip("If clicked, start recording")
        self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding,
                                                    QSizePolicy.Expanding)

        recordParametersControlsLayout = QGridLayout()

        self.filenameLabel = self.createLabel("Filename:", visible=False)
        recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0)

        # Offline Reconstruction
        self.offlineReconstructButton = QPushButton("  Offline Reconstruction")
        self.offlineReconstructButton.setCheckable(True)
        self.offlineReconstructButton.setIcon(self.recordIcon)
        self.offlineReconstructButton.setEnabled(False)
        self.offlineReconstructButton.setToolTip(
            "If clicked, reconstruct recorded volume")
        self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding,
                                                    QSizePolicy.Expanding)

        self.offlineVolumeToReconstructSelector = QComboBox()
        self.offlineVolumeToReconstructSelector.setEditable(True)
        self.offlineVolumeToReconstructSelector.setToolTip(
            "Pick/set volume to reconstruct")
        self.offlineVolumeToReconstructSelector.visible = False

        hbox = QHBoxLayout()
        hbox.addWidget(self.startStopRecordingButton)
        hbox.addWidget(self.offlineReconstructButton)
        ultrasoundLayout.addRow(hbox)

        # Scout scan (record and low resolution reconstruction) and live reconstruction
        # Scout scan part

        self.startStopScoutScanButton = QPushButton(
            "  Scout scan\n  Start recording")
        self.startStopScoutScanButton.setCheckable(True)
        self.startStopScoutScanButton.setIcon(self.recordIcon)
        self.startStopScoutScanButton.setToolTip("If clicked, start recording")
        self.startStopScoutScanButton.setEnabled(False)
        self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding,
                                                    QSizePolicy.Expanding)

        self.startStopLiveReconstructionButton = QPushButton(
            "  Start live reconstruction")
        self.startStopLiveReconstructionButton.setCheckable(True)
        self.startStopLiveReconstructionButton.setIcon(self.recordIcon)
        self.startStopLiveReconstructionButton.setToolTip(
            "If clicked, start live reconstruction")
        self.startStopLiveReconstructionButton.setEnabled(False)
        self.startStopLiveReconstructionButton.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.displayRoiButton = QToolButton()
        self.displayRoiButton.setCheckable(True)
        self.displayRoiButton.setIcon(self.visibleOffIcon)
        self.displayRoiButton.setToolTip("If clicked, display ROI")

        hbox = QHBoxLayout()
        hbox.addWidget(self.startStopScoutScanButton)
        hbox.addWidget(self.startStopLiveReconstructionButton)
        # hbox.addWidget(self.displayRoiButton)
        ultrasoundLayout.addRow(hbox)

        self.snapshotTimer = QTimer()
        self.snapshotTimer.setSingleShot(True)

        self.onParameterSetSelected()

        return collapsibleButton

    def setupResliceDriver(self):
        layoutManager = slicer.app.layoutManager()
        # Show ultrasound in red view.
        redSlice = layoutManager.sliceWidget('Red')
        redSliceLogic = redSlice.sliceLogic()
        redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID(
            self.liveUltrasoundNode_Reference.GetID())

        resliceLogic = slicer.modules.volumereslicedriver.logic()
        if resliceLogic:
            redNode = slicer.util.getNode('vtkMRMLSliceNodeRed')
            redNode.SetSliceResolutionMode(
                slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes)
            resliceLogic.SetDriverForSlice(
                self.liveUltrasoundNode_Reference.GetID(), redNode)
            resliceLogic.SetModeForSlice(
                6, redNode)  # Transverse mode, default for PLUS ultrasound.
        else:
            logging.warning('Logic not found for Volume Reslice Driver')

    def createCollapsibleButton(self, text, collapsed=False):
        collapsibleButton = ctkCollapsibleButton()
        collapsibleButton.text = text
        collapsibleButton.collapsed = collapsed
        return collapsibleButton

    def createLabel(self, text, visible=True):
        label = QLabel()
        label.setText(text)
        label.visible = visible
        return label

    def setupConnections(self):
        self.startStopRecordingButton.connect(
            'clicked(bool)', self.onStartStopRecordingButtonClicked)
        self.offlineReconstructButton.connect('clicked(bool)',
                                              self.onReconstVolume)
        self.startStopScoutScanButton.connect(
            'clicked(bool)', self.onStartStopScoutScanButtonClicked)
        self.startStopLiveReconstructionButton.connect(
            'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked)
        self.displayRoiButton.connect('clicked(bool)',
                                      self.onDisplayRoiButtonClicked)
        self.captureIDSelector.connect('currentIndexChanged(QString)',
                                       self.updateParameterNodeFromGui)
        self.volumeReconstructorIDSelector.connect(
            'currentIndexChanged(QString)', self.updateParameterNodeFromGui)
        self.offlineVolumeToReconstructSelector.connect(
            'currentIndexChanged(int)', self.updateParameterNodeFromGui)
        self.displayRoiButton.connect('clicked(bool)',
                                      self.updateParameterNodeFromGui)
        self.snapshotTimer.timeout.connect(
            self.onRequestVolumeReconstructionSnapshot)
        self.connectDisconnectButton.connect(
            'clicked(bool)', self.onConnectDisconnectButtonClicked)

    def disconnect(self):
        self.startStopRecordingButton.disconnect(
            'clicked(bool)', self.onStartStopRecordingButtonClicked)
        self.offlineReconstructButton.disconnect('clicked(bool)',
                                                 self.onReconstVolume)
        self.startStopScoutScanButton.disconnect(
            'clicked(bool)', self.onStartStopScoutScanButtonClicked)
        self.startStopLiveReconstructionButton.disconnect(
            'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked)
        self.displayRoiButton.disconnect('clicked(bool)',
                                         self.onDisplayRoiButtonClicked)
        self.captureIDSelector.disconnect('currentIndexChanged(QString)',
                                          self.updateParameterNodeFromGui)
        self.volumeReconstructorIDSelector.disconnect(
            'currentIndexChanged(QString)', self.updateParameterNodeFromGui)
        self.offlineVolumeToReconstructSelector.disconnect(
            'currentIndexChanged(int)', self.updateParameterNodeFromGui)
        self.displayRoiButton.disconnect('clicked(bool)',
                                         self.updateParameterNodeFromGui)
        self.snapshotTimer.timeout.disconnect(
            self.onRequestVolumeReconstructionSnapshot)
        self.connectDisconnectButton.disconnect(
            'clicked(bool)', self.onConnectDisconnectButtonClicked)

    def setupIcons(self):
        self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace(
            "PlusRemote.py", "")
        self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath +
                                '/Resources/Icons/icon_Record.png')
        self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath +
                              '/Resources/Icons/icon_Stop.png')
        self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath +
                              '/Resources/Icons/icon_Wait.png')
        self.visibleOffIcon = QIcon(":Icons\VisibleOff.png")
        self.visibleOnIcon = QIcon(":Icons\VisibleOn.png")

    def onParameterSetSelected(self):
        # Set up default values for new nodes
        if self.parameterNode:
            self.plusRemoteLogic.setDefaultParameters(self.parameterNode)
        self.updateGuiFromParameterNode()

    def updateGuiFromParameterNode(self):

        self.parameterVolumeList = {
            'OfflineVolumeToReconstruct':
            self.offlineVolumeToReconstructSelector
        }
        for parameter in self.parameterVolumeList:
            if self.parameterNode.GetParameter(parameter):
                self.parameterVolumeList[parameter].blockSignals(True)
            self.parameterVolumeList[parameter].blockSignals(False)

        if self.parameterNode.GetParameter('CaptureID'):
            self.captureIDSelector.blockSignals(True)
            for i in range(0, self.captureIDSelector.count):
                if self.parameterNode.GetParameter(
                        'CaptureID') == self.captureIDSelector.itemText(i):
                    self.captureIDSelector.setCurrentIndex(
                        int(self.parameterNode.GetParameter('CaptureIdIndex')))
            self.captureIDSelector.blockSignals(False)

        if self.parameterNode.GetParameter('VolumeReconstructor'):
            self.volumeReconstructorIDSelector.blockSignals(True)
            for i in range(0, self.volumeReconstructorIDSelector.count):
                if self.parameterNode.GetParameter(
                        'VolumeReconstructor'
                ) == self.volumeReconstructorIDSelector.itemText(i):
                    self.volumeReconstructorIDSelector.setCurrentIndex(
                        int(
                            self.parameterNode.GetParameter(
                                'VolumeReconstructorIndex')))
            self.volumeReconstructorIDSelector.blockSignals(False)

            self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0)

    def updateParameterNodeFromGui(self):
        #Update parameter node value to save when user change value in the interface
        if not self.parameterNode:
            return
        self.parametersList = {
            'CaptureID':
            self.captureIDSelector.currentText,
            'CaptureIdIndex':
            self.captureIDSelector.currentIndex,
            'VolumeReconstructor':
            self.volumeReconstructorIDSelector.currentText,
            'VolumeReconstructorIndex':
            self.volumeReconstructorIDSelector.currentIndex,
            'OfflineVolumeToReconstruct':
            self.offlineVolumeToReconstructSelector.currentIndex
        }
        for parameter in self.parametersList:
            self.parameterNode.SetParameter(
                parameter, str(self.parametersList[parameter]))
        if self.roiNode:
            roiNodeID = self.roiNode.GetID()
            self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID)


#
# Connector observation and actions
#

    def onConnectorNodeConnected(self):
        logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected")
        self.connectorNodeConnected = True
        self.captureIDSelector.setDisabled(False)
        self.volumeReconstructorIDSelector.setDisabled(False)
        self.plusRemoteLogic.getCaptureDeviceIds(
            self.connectorNode.GetID(),
            self.onGetCaptureDeviceCommandResponseReceived)
        self.plusRemoteLogic.getVolumeReconstructorDeviceIds(
            self.connectorNode.GetID(),
            self.onGetVolumeReconstructorDeviceCommandResponseReceived)
        self.connectDisconnectButton.setText("Disconnect")

    def onConnectorNodeDisconnected(self):
        logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected")
        self.connectorNodeConnected = False
        self.startStopRecordingButton.setEnabled(False)
        self.startStopScoutScanButton.setEnabled(False)
        self.startStopLiveReconstructionButton.setEnabled(False)
        self.offlineReconstructButton.setEnabled(False)
        self.captureIDSelector.setDisabled(True)
        self.volumeReconstructorIDSelector.setDisabled(True)
        self.connectDisconnectButton.setText("Connect")

    def getLiveVolumeRecNode(self):
        liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME)
        return liveVolumeRecNode

    def getOfflineVolumeRecNode(self):
        offlineVolumeRecNode = slicer.util.getNode(
            self.OFFLINE_VOLUME_NODE_NAME)
        return offlineVolumeRecNode

    def getScoutVolumeNode(self):
        scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME)
        return scoutScanVolumeNode

    def onConnectDisconnectButtonClicked(self):
        if self.connectorNode.GetState(
        ) == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED:
            self.connectorNode.Stop()
        else:
            self.connectorNode.Start()

    def onStartStopRecordingButtonClicked(self):
        if self.startStopRecordingButton.isChecked():
            self.startStopRecordingButton.setText("  Stop Recording")
            self.startStopRecordingButton.setIcon(self.stopIcon)
            self.startStopRecordingButton.setToolTip(
                "If clicked, stop recording")
            self.onStartRecording(self.generateRecordingOutputFilename())
        else:
            self.startStopRecordingButton.setText("  Start Recording")
            self.startStopRecordingButton.setIcon(self.recordIcon)
            self.startStopRecordingButton.setToolTip(
                "If clicked, start recording")
            self.onStopRecording(self.onVolumeRecorded)

    def onStartStopScoutScanButtonClicked(self):
        if self.startStopScoutScanButton.isChecked():
            self.startStopScoutScanButton.setText(
                "  Scout Scan\n  Stop Recording and Reconstruct Recorded Volume"
            )
            self.startStopScoutScanButton.setIcon(self.stopIcon)
            self.startStopScoutScanButton.setToolTip(
                "If clicked, stop recording and reconstruct recorded volume")
            self.onStartRecording(self.generateScoutRecordingOutputFilename())
        else:
            self.onStopRecording(self.onScoutVolumeRecorded)

    def onStartStopLiveReconstructionButtonClicked(self):
        if self.startStopLiveReconstructionButton.isChecked():
            if self.roiNode:
                self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(
                    self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode)
            self.startStopLiveReconstructionButton.setText(
                "  Stop Live Reconstruction")
            self.startStopLiveReconstructionButton.setIcon(self.stopIcon)
            self.startStopLiveReconstructionButton.setToolTip(
                "If clicked, stop live reconstruction")
            self.onStartRecording(self.getLiveRecordingOutputFilename())
            self.onStartReconstruction()
        else:
            self.startStopLiveReconstructionButton.setText(
                "  Start Live Reconstruction")
            self.startStopLiveReconstructionButton.setIcon(self.recordIcon)
            self.startStopLiveReconstructionButton.setToolTip(
                "If clicked, start live reconstruction")
            self.onStopRecording(self.printCommandResponse)
            self.onStopReconstruction()

    def onDisplayRoiButtonClicked(self):
        if self.displayRoiButton.isChecked():
            self.displayRoiButton.setIcon(self.visibleOnIcon)
            self.displayRoiButton.setToolTip("If clicked, hide ROI")
            if self.roiNode:
                self.roiNode.SetDisplayVisibility(1)
        else:
            self.displayRoiButton.setIcon(self.visibleOffIcon)
            self.displayRoiButton.setToolTip("If clicked, display ROI")
            if self.roiNode:
                self.roiNode.SetDisplayVisibility(0)

    def generateRecordingOutputFilename(self):
        return self.plusRemoteLogic.addTimestampToFilename(
            self.RECORDING_FILENAME)

    def generateScoutRecordingOutputFilename(self):
        return self.plusRemoteLogic.addTimestampToFilename(
            self.SCOUT_RECORDING_FILENAME)

    def getLiveRecordingOutputFilename(self):
        return self.plusRemoteLogic.addTimestampToFilename(
            self.LIVE_RECORDING_FILENAME)

    def getLiveReconstructionOutputFilename(self):
        return self.plusRemoteLogic.addTimestampToFilename(
            self.LIVE_VOLUME_FILENAME)

    def onStartRecording(self, filename):
        self.plusRemoteLogic.startRecording(self.connectorNode.GetID(),
                                            self.captureIDSelector.currentText,
                                            filename,
                                            self.printCommandResponse)

    def onStopRecording(self, callback):
        self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(),
                                           self.captureIDSelector.currentText,
                                           callback)

    def onStartReconstruction(self):
        if self.roiNode:
            self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(
                self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode)
        self.plusRemoteLogic.startVolumeReconstuction(
            self.connectorNode.GetID(),
            self.volumeReconstructorIDSelector.currentText,
            self.liveOutputSpacingValue, self.roiOrigin, self.roiExtent,
            self.printCommandResponse,
            self.getLiveReconstructionOutputFilename(),
            self.LIVE_VOLUME_NODE_NAME)
        # Set up timer for requesting snapshot
        self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000)

    def onStopReconstruction(self):
        self.snapshotTimer.stop()
        self.plusRemoteLogic.stopVolumeReconstruction(
            self.connectorNode.GetID(),
            self.volumeReconstructorIDSelector.currentText,
            self.onVolumeLiveReconstructed,
            self.getLiveReconstructionOutputFilename(),
            self.LIVE_VOLUME_NODE_NAME)

    def onReconstVolume(self):
        self.offlineReconstructButton.setIcon(self.waitIcon)
        self.offlineReconstructButton.setText(
            "  Offline Reconstruction in progress ...")
        self.offlineReconstructButton.setEnabled(False)
        self.plusRemoteLogic.reconstructRecorded(
            self.connectorNode.GetID(),
            self.volumeReconstructorIDSelector.currentText,
            self.offlineVolumeToReconstructSelector.currentText,
            self.outputSpacing, self.onVolumeReconstructed,
            self.OFFLINE_VOLUME_FILENAME, self.OFFLINE_VOLUME_NODE_NAME)

    def onScoutScanReconstVolume(self):
        self.startStopScoutScanButton.setIcon(self.waitIcon)
        self.startStopScoutScanButton.setText(
            "  Scout Scan\n  Reconstruction in progress ...")
        self.startStopScoutScanButton.setEnabled(False)
        self.plusRemoteLogic.reconstructRecorded(
            self.connectorNode.GetID(),
            self.volumeReconstructorIDSelector.currentText,
            self.lastScoutRecordingOutputFilename, self.outputSpacing,
            self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME,
            self.SCOUT_VOLUME_NODE_NAME)

    def onRequestVolumeReconstructionSnapshot(self, stopFlag=""):
        self.plusRemoteLogic.getVolumeReconstructionSnapshot(
            self.connectorNode.GetID(),
            self.volumeReconstructorIDSelector.currentText,
            self.LIVE_VOLUME_FILENAME, self.LIVE_VOLUME_NODE_NAME,
            self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired)

    def executeCommandDelayed(self, method, delay=100):
        # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same
        # therefore we wait a bit to make sure the image message is processed as well
        QTimer.singleShot(delay, method)

    def printCommandResponse(self, command, q):
        statusText = "Command {0} [{1}]: {2}\n".format(
            command.GetCommandName(), command.GetID(),
            command.StatusToString(command.GetStatus()))
        if command.GetResponseMessage():
            statusText = statusText + command.GetResponseMessage()
        elif command.GetResponseText():
            statusText = statusText + command.GetResponseText()
        logging.debug(statusText)

    def onGetCaptureDeviceCommandResponseReceived(self, command, q):
        self.printCommandResponse(command, q)
        if command.GetStatus() != command.CommandSuccess:
            return

        captureDeviceIdsListString = command.GetResponseMessage()
        if captureDeviceIdsListString:
            captureDevicesIdsList = captureDeviceIdsListString.split(",")
        else:
            captureDevicesIdsList = []

        for i in range(0, len(captureDevicesIdsList)):
            if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1:
                self.captureIDSelector.addItem(captureDevicesIdsList[i])

    def onGetVolumeReconstructorDeviceCommandResponseReceived(
            self, command, q):
        self.printCommandResponse(command, q)
        if command.GetStatus() != command.CommandSuccess:
            return

        volumeReconstructorDeviceIdsListString = command.GetResponseMessage()
        if volumeReconstructorDeviceIdsListString:
            volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split(
                ",")
        else:
            volumeReconstructorDeviceIdsList = []

        self.volumeReconstructorIDSelector.clear()
        self.volumeReconstructorIDSelector.addItems(
            volumeReconstructorDeviceIdsList)
        self.startStopRecordingButton.setEnabled(True)
        self.offlineReconstructButton.setEnabled(True)
        self.startStopScoutScanButton.setEnabled(True)
        if self.roiNode:
            self.startStopLiveReconstructionButton.setEnabled(True)

    def onVolumeRecorded(self, command, q):
        self.printCommandResponse(command, q)
        self.offlineReconstructButton.setEnabled(True)

        volumeToReconstructFileName = os.path.basename(
            command.GetResponseMessage())
        self.offlineVolumeToReconstructSelector.insertItem(
            0, volumeToReconstructFileName)
        self.offlineVolumeToReconstructSelector.setCurrentIndex(0)

    def onScoutVolumeRecorded(self, command, q):
        self.printCommandResponse(command, q)
        self.offlineReconstructButton.setEnabled(True)

        if command.GetStatus() == command.CommandExpired:
            logging.fatal(
                "Scout Volume Recording: Timeout while waiting for volume reconstruction result"
            )
            return

        if command.GetStatus() == command.CommandSuccess:
            self.lastScoutRecordingOutputFilename = os.path.basename(
                command.GetResponseMessage())
            self.onScoutScanReconstVolume()

    def onVolumeReconstructed(self, command, q):
        self.printCommandResponse(command, q)

        self.offlineReconstructButton.setIcon(self.recordIcon)
        self.offlineReconstructButton.setText("Offline Reconstruction")
        self.offlineReconstructButton.setEnabled(True)
        self.offlineReconstructButton.setChecked(False)

        if command.GetStatus() == command.CommandExpired:
            # volume reconstruction command timed out
            logging.fatal(
                "Volume Reconstruction: Timeout while waiting for volume reconstruction result"
            )
            return

        if command.GetStatus() != command.CommandSuccess:
            logging.debug("Volume Reconstruction: " +
                          command.GetResponseMessage())
            return

        self.executeCommandDelayed(self.onVolumeReconstructedFinalize)

    def onVolumeReconstructedFinalize(self):
        applicationLogic = slicer.app.applicationLogic()
        applicationLogic.FitSliceToAll()
        self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode())

    def onScoutVolumeReconstructed(self, command, q):
        self.printCommandResponse(command, q)

        if command.GetStatus() == command.CommandExpired:
            logging.fatal(
                "Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result"
            )
            return

        self.startStopScoutScanButton.setIcon(self.recordIcon)
        self.startStopScoutScanButton.setText(
            "  Scout Scan\n  Start Recording")
        self.startStopScoutScanButton.setEnabled(True)

        if command.GetStatus() != command.CommandSuccess:
            logging.debug("Scout Volume Reconstruction: " +
                          command.GetResponseMessage())
            return

        self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize)

    def onScoutVolumeReconstructedFinalize(self):
        # Create and initialize ROI after scout scan because low resolution scout scan is used to set
        # a smaller ROI for the live high resolution reconstruction
        self.roiNode = self.logic.onRoiInitialization(
            self.SCOUT_VOLUME_NODE_NAME, self.roiNode)
        self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(
            self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode)
        scoutScanVolumeNode = self.getScoutVolumeNode()

        applicationLogic = slicer.app.applicationLogic()
        applicationLogic.FitSliceToAll()

        self.guideletParent.showVolumeRendering(scoutScanVolumeNode)

    def onSnapshotAcquired(self, command, q):
        self.printCommandResponse(command, q)

        if not self.startStopLiveReconstructionButton.isChecked():
            # live volume reconstruction is not active
            return

        self.executeCommandDelayed(self.onSnapshotAcquiredFinalize)

    def onSnapshotAcquiredFinalize(self):
        self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())
        self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000)

    def onVolumeLiveReconstructed(self, command, q):
        self.printCommandResponse(command, q)

        if command.GetStatus() == command.CommandExpired:
            logging.fatal(
                "LIVE Volume Reconstruction: Failed to stop volume reconstruction"
            )
            return

        if command.GetStatus() != command.CommandSuccess:
            logging.debug("LIVE Volume Reconstruction " +
                          command.GetResponseMessage())
            return

        self.executeCommandDelayed(self.getLiveVolumeRecNode)

    def onVolumeLiveReconstructedFinalize(self):
        self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())
class ProstateTRUSNavWidget(GuideletWidget):

    DEFAULT_PLUSSERVER_CHOOSER_TEXT = "Choose PlusServer.exe"
    DEFAULT_CONFIGURATION_CHOOSER_TEXT = "Select Configuration"

    def __init__(self, parent=None):
        GuideletWidget.__init__(self, parent)
        self.plusServerProcess = None
        self.configurationFile = self.getSetting(
            'ConfigurationFile', self.DEFAULT_CONFIGURATION_CHOOSER_TEXT)
        self.serverExecutable = self.getSetting(
            'PlusServer', self.DEFAULT_PLUSSERVER_CHOOSER_TEXT)

    def cleanup(self):
        GuideletWidget.cleanup(self)
        if self.plusServerProcess:
            self.plusServerProcess.terminate()

    def setup(self):
        showPlusServerWidget = True
        if _platform == "linux" or _platform == "linux2" or _platform == "darwin":  #linux or linux or OS X
            message = "Attention: You are running Slicer on Linux or OS X. Do you have PlusServer installed on the current OS?"
            result = QMessageBox.question(slicer.util.mainWindow(),
                                          'ProstateTRUSNav', message,
                                          QMessageBox.Yes | QMessageBox.No)
            showPlusServerWidget = result == QMessageBox.Yes

        if _platform == "win32" or showPlusServerWidget:
            # Windows...
            plusServerCollapsibleButton = ctkCollapsibleButton()
            plusServerCollapsibleButton.text = "PlusServer"
            self.layout.addWidget(plusServerCollapsibleButton)
            self.configurationFileChooserButton = QPushButton(
                self.configurationFile)
            self.configurationFileChooserButton.connect(
                'clicked()', self.onConfigFileSelected)
            self.runPlusServerButton = QPushButton("Run PlusServer")
            self.runPlusServerButton.setCheckable(True)
            self.runPlusServerButton.connect('clicked()',
                                             self.onRunPlusServerButtonClicked)

            self.serverFormLayout = QFormLayout(plusServerCollapsibleButton)

            self.serverExecutableChooserButton = QPushButton(
                self.serverExecutable)
            self.serverExecutableChooserButton.connect(
                'clicked()', self.onServerExecutableSelected)

            hbox = QHBoxLayout()
            hbox.addWidget(self.serverExecutableChooserButton)
            self.serverFormLayout.addRow(hbox)

            hbox = QHBoxLayout()
            hbox.addWidget(self.configurationFileChooserButton)
            hbox.addWidget(self.runPlusServerButton)
            self.serverFormLayout.addRow(hbox)

        GuideletWidget.setup(self)

        # do specific setup here
        if _platform == "win32" or showPlusServerWidget:
            self.launchGuideletButton.setEnabled(False)
            self.checkExecutableAndArgument()

    def checkExecutableAndArgument(self):
        if os.path.exists(self.serverExecutable) and os.path.exists(
                self.configurationFile):
            self.runPlusServerButton.setEnabled(True)
        else:
            self.runPlusServerButton.setEnabled(False)

    def getSetting(self, settingName, defaultValue=""):
        settings = QSettings()
        value = settings.value(self.moduleName + '/' + settingName,
                               defaultValue)
        return value if value is not None and value != "" else defaultValue

    def setSetting(self, settingName, value):
        settings = QSettings()
        settings.setValue(self.moduleName + '/' + settingName, value)

    def addLauncherWidgets(self):
        GuideletWidget.addLauncherWidgets(self)
        # add launcher widget here

    def onServerExecutableSelected(self):
        executable = QFileDialog.getOpenFileName(self.parent,
                                                 "PlusServer Executable",
                                                 self.serverExecutable,
                                                 "*.exe")
        if executable != "" and executable.find("PlusServer.exe"):
            self.serverExecutable = executable
            self.serverExecutableChooserButton.setText(executable)
            self.setSetting("PlusServer", executable)
        self.checkExecutableAndArgument()

    def onConfigFileSelected(self):
        self.configurationFile = QFileDialog.getOpenFileName(
            self.parent, "Choose Configuration File", self.configurationFile,
            "*.xml")
        if self.configurationFile != "":
            self.configurationFileChooserButton.setText(
                os.path.split(self.configurationFile)[1])
            self.setSetting("ConfigurationFile", self.configurationFile)
        self.checkExecutableAndArgument()

    def onRunPlusServerButtonClicked(self):
        if self.runPlusServerButton.isChecked():
            command = [
                self.serverExecutable,
                "--config-file=" + self.configurationFile
            ]
            logging.info("Executing %s %s" % tuple(command))
            self.plusServerProcess = Popen([
                self.serverExecutable,
                "--config-file=" + self.configurationFile
            ])
            if self.plusServerProcess:
                self.runPlusServerButton.setText("Quit Plus Server")
                self.launchGuideletButton.setEnabled(True)
        else:
            if self.plusServerProcess:
                self.plusServerProcess.terminate()
                self.runPlusServerButton.setText("Run PlusServer")
                self.launchGuideletButton.setEnabled(False)

    def collectParameterList(self):
        parameterList = GuideletWidget.collectParameterList(self)
        if not parameterList:
            parameterList = dict()
        parameterList['OfflineVolumeToReconstruct'] = 0,
        return parameterList

    def createGuideletInstance(self, parameterList=None):
        return ProstateTRUSNavGuidelet(None, self.guideletLogic, parameterList)

    def createGuideletLogic(self):
        return ProstateTRUSNavLogic()
Exemple #8
0
class ImportPage(QWidget):

    update_command_lst_low_level = Signal(list)

    """
    This stacked widget basically helps the user to browse the input images
    path, there is no auto-generated GUI form Phil parameters in use withing
    this widget.
    """

    def __init__(self, parent=None):
        super(ImportPage, self).__init__(parent=None)

        main_v_box = QVBoxLayout()

        label_font = QFont()
        sys_font_point_size = label_font.pointSize()
        label_font.setPointSize(sys_font_point_size + 2)
        step_label = QLabel(str("Import"))
        step_label.setFont(label_font)

        self.simple_lin = QLineEdit(self)
        self.simple_lin.textChanged.connect(self.update_command)

        self.x_spn_bx = QSpinBox()
        self.x_spn_bx.setMaximum(99999)
        self.x_spn_bx.setSpecialValueText(" ")
        self.y_spn_bx = QSpinBox()
        self.y_spn_bx.setMaximum(99999)
        self.y_spn_bx.setSpecialValueText(" ")

        self.x_spn_bx.valueChanged.connect(self.x_beam_changed)
        self.y_spn_bx.valueChanged.connect(self.y_beam_changed)

        self.chk_invert = QCheckBox("Invert rotation axis")
        self.chk_invert.stateChanged.connect(self.inv_rota_changed)

        self.opn_fil_btn = QPushButton(" \n Select file(s) \n ")

        main_path = get_main_path()

        self.opn_fil_btn.setIcon(QIcon(main_path + "/resources/import.png"))
        self.opn_fil_btn.setIconSize(QSize(80, 48))

        main_v_box.addWidget(step_label)
        main_v_box.addWidget(self.opn_fil_btn)
        main_v_box.addWidget(self.simple_lin)
        self.b_cetre_label = QLabel("\n\n Beam centre")
        main_v_box.addWidget(self.b_cetre_label)
        cent_hbox = QHBoxLayout()
        self.x_label = QLabel("    X: ")
        cent_hbox.addWidget(self.x_label)
        cent_hbox.addWidget(self.x_spn_bx)
        self.y_label = QLabel("    Y: ")
        cent_hbox.addWidget(self.y_label)
        cent_hbox.addWidget(self.y_spn_bx)
        #    cent_hbox.addWidget(QLabel(" \n "))
        cent_hbox.addStretch()
        main_v_box.addLayout(cent_hbox)
        main_v_box.addWidget(self.chk_invert)
        main_v_box.addStretch()

        self.opn_fil_btn.clicked.connect(self.open_files)

        self.defa_dir = str(os.getcwd())
        self.setLayout(main_v_box)
        # self.show()
        self.reset_par()

    def reset_par(self):
        logger.info("reset_par(ImportPage)")
        self.cmd_list = []
        self.simple_lin.setText(" ? ")
        self.x_spn_bx.setValue(0.0)
        self.y_spn_bx.setValue(0.0)
        self.chk_invert.setChecked(False)

        self.x_beam, self.y_beam = 0.0, 0.0
        self.path_file_str = ""
        self.second_half = ""
        self.third_half = ""

    def update_param_w_lst(self, lst_in):
        self.reset_par()
        logger.info("update_param_w_lst(ImportPage) \n lst: \n", lst_in)
        for singl_com in lst_in:
            if singl_com[0:1] == "/":
                self.path_file_str = str(singl_com)
                self.put_str_lin()

            if singl_com[0:12] == "image_range=":
                self.path_file_str += " "
                self.path_file_str += str(singl_com)
                self.put_str_lin()

            if singl_com == "invert_rotation_axis=True":
                self.chk_invert.setChecked(True)

            if singl_com[0:22] == "slow_fast_beam_centre=":
                yb_xb_str = singl_com[22:]
                yb_str, xb_str = yb_xb_str.split(",")
                yb = float(yb_str)
                xb = float(xb_str)
                self.y_spn_bx.setValue(yb)
                self.x_spn_bx.setValue(xb)

    def inv_rota_changed(self):
        if self.chk_invert.checkState():
            self.third_half = "invert_rotation_axis=True"

        else:
            self.third_half = ""

        self.put_str_lin()

    def x_beam_changed(self, value):
        self.x_beam = value
        self.build_second_half()

    def y_beam_changed(self, value):
        self.y_beam = value
        self.build_second_half()

    def build_second_half(self):
        if self.x_beam != 0.0 and self.y_beam != 0.0:
            self.second_half = (
                "slow_fast_beam_centre=" + str(self.y_beam) + "," + str(self.x_beam)
            )

        else:
            self.second_half = ""

        self.put_str_lin()

    def open_files(self):

        lst_file_path = QFileDialog.getOpenFileNames(
            self, "Open File(s)", self.defa_dir, "All Files (*.*)"
        )

        if len(lst_file_path) > 0:
            new_dir, new_command = get_import_run_string(lst_file_path)
            # logger.info("\n new_dir=", new_dir, ">>")
            # logger.info("\n new_command =", new_command, ">>")
            self.path_file_str = new_command
            self.defa_dir = new_dir
            self.put_str_lin()

    def put_str_lin(self):
        # logger.info("self.path_file_str =", self.path_file_str, ">>")
        self.cmd_list = [
            self.path_file_str,
            self.second_half.lstrip(),
            self.third_half.lstrip(),
        ]
        txt_lin = " ".join(self.cmd_list).rstrip()
        while "  " in txt_lin:
            txt_lin = txt_lin.replace("  ", " ")

        self.simple_lin.setText(txt_lin)

    def set_arg_obj(self, sys_arg_in):
        """Pass the system argument object to handle launch arguments."""
        if sys_arg_in.template is not None:
            str_arg = str(sys_arg_in.template)
            self.simple_lin.setText(str_arg)

    def update_command(self):
        self.command_lst = [["import"]]
        param_com = str(self.simple_lin.text())

        cmd_lst = param_com.split(" ")

        for single_com in cmd_lst:
            self.command_lst[0].append(single_com)

        self.update_command_lst_low_level.emit(self.command_lst[0])

    def gray_me_out(self):
        self.simple_lin.setEnabled(False)
        self.opn_fil_btn.setEnabled(False)
        self.x_spn_bx.setEnabled(False)
        self.y_spn_bx.setEnabled(False)
        self.x_label.setEnabled(False)
        self.y_label.setEnabled(False)
        self.b_cetre_label.setEnabled(False)
        self.chk_invert.setEnabled(False)

    def activate_me(self, cur_nod=None):
        self.simple_lin.setEnabled(True)
        self.opn_fil_btn.setEnabled(True)
        self.y_spn_bx.setEnabled(True)
        self.x_spn_bx.setEnabled(True)
        self.x_label.setEnabled(True)
        self.y_label.setEnabled(True)
        self.b_cetre_label.setEnabled(True)
        self.chk_invert.setEnabled(True)
Exemple #9
0
class ScoringDialog(QWidget):
    """a dialog for entering the scores"""

    # pylint: disable=too-many-instance-attributes

    def __init__(self, scene):
        QWidget.__init__(self)
        self.scene = scene
        decorateWindow(self, i18n('Scoring for this Hand'))
        self.nameLabels = [None] * 4
        self.spValues = [None] * 4
        self.windLabels = [None] * 4
        self.wonBoxes = [None] * 4
        self.detailsLayout = [None] * 4
        self.details = [None] * 4
        self.__tilePixMaps = []
        self.__meldPixMaps = []
        grid = QGridLayout(self)
        pGrid = QGridLayout()
        grid.addLayout(pGrid, 0, 0, 2, 1)
        pGrid.addWidget(QLabel(i18nc('kajongg', "Player")), 0, 0)
        pGrid.addWidget(QLabel(i18nc('kajongg', "Wind")), 0, 1)
        pGrid.addWidget(QLabel(i18nc('kajongg', 'Score')), 0, 2)
        pGrid.addWidget(QLabel(i18n("Winner")), 0, 3)
        self.detailTabs = QTabWidget()
        self.detailTabs.setDocumentMode(True)
        pGrid.addWidget(self.detailTabs, 0, 4, 8, 1)
        for idx in range(4):
            self.setupUiForPlayer(pGrid, idx)
        self.draw = QCheckBox(i18nc('kajongg', 'Draw'))
        self.draw.clicked.connect(self.wonChanged)
        btnPenalties = QPushButton(i18n("&Penalties"))
        btnPenalties.clicked.connect(self.penalty)
        self.btnSave = QPushButton(i18n('&Save Hand'))
        self.btnSave.clicked.connect(self.game.nextScoringHand)
        self.btnSave.setEnabled(False)
        self.setupUILastTileMeld(pGrid)
        pGrid.setRowStretch(87, 10)
        pGrid.addWidget(self.draw, 7, 3)
        self.cbLastTile.currentIndexChanged.connect(self.slotLastTile)
        self.cbLastMeld.currentIndexChanged.connect(self.slotInputChanged)
        btnBox = QHBoxLayout()
        btnBox.addWidget(btnPenalties)
        btnBox.addWidget(self.btnSave)
        pGrid.addLayout(btnBox, 8, 4)
        StateSaver(self)
        self.refresh()

    @property
    def game(self):
        """proxy"""
        return self.scene.game

    def setupUILastTileMeld(self, pGrid):
        """setup UI elements for last tile and last meld"""
        self.lblLastTile = QLabel(i18n('&Last Tile:'))
        self.cbLastTile = QComboBox()
        self.cbLastTile.setMinimumContentsLength(1)
        vpol = QSizePolicy()
        vpol.setHorizontalPolicy(QSizePolicy.Fixed)
        self.cbLastTile.setSizePolicy(vpol)
        self.cbLastTile.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.lblLastTile.setBuddy(self.cbLastTile)
        self.lblLastMeld = QLabel(i18n('L&ast Meld:'))
        self.prevLastTile = None
        self.cbLastMeld = QComboBox()
        self.cbLastMeld.setMinimumContentsLength(1)
        self.cbLastMeld.setSizePolicy(vpol)
        self.cbLastMeld.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.lblLastMeld.setBuddy(self.cbLastMeld)
        self.comboTilePairs = set()
        pGrid.setRowStretch(6, 5)
        pGrid.addWidget(self.lblLastTile, 7, 0, 1, 2)
        pGrid.addWidget(self.cbLastTile, 7, 2, 1, 1)
        pGrid.addWidget(self.lblLastMeld, 8, 0, 1, 2)
        pGrid.addWidget(self.cbLastMeld, 8, 2, 1, 2)

    def setupUiForPlayer(self, pGrid, idx):
        """setup UI elements for a player"""
        self.spValues[idx] = QSpinBox()
        self.nameLabels[idx] = QLabel()
        self.nameLabels[idx].setBuddy(self.spValues[idx])
        self.windLabels[idx] = WindLabel()
        pGrid.addWidget(self.nameLabels[idx], idx + 2, 0)
        pGrid.addWidget(self.windLabels[idx], idx + 2, 1)
        pGrid.addWidget(self.spValues[idx], idx + 2, 2)
        self.wonBoxes[idx] = QCheckBox("")
        pGrid.addWidget(self.wonBoxes[idx], idx + 2, 3)
        self.wonBoxes[idx].clicked.connect(self.wonChanged)
        self.spValues[idx].valueChanged.connect(self.slotInputChanged)
        detailTab = QWidget()
        self.detailTabs.addTab(detailTab, '')
        self.details[idx] = QWidget()
        detailTabLayout = QVBoxLayout(detailTab)
        detailTabLayout.addWidget(self.details[idx])
        detailTabLayout.addStretch()
        self.detailsLayout[idx] = QVBoxLayout(self.details[idx])

    def refresh(self):
        """reload game"""
        self.clear()
        game = self.game
        self.setVisible(game is not None and not game.finished())
        if game:
            for idx, player in enumerate(game.players):
                for child in self.details[idx].children():
                    if isinstance(child, RuleBox):
                        child.hide()
                        self.detailsLayout[idx].removeWidget(child)
                        del child
                if game:
                    self.spValues[idx].setRange(0, game.ruleset.limit or 99999)
                    self.nameLabels[idx].setText(player.localName)
                    self.refreshWindLabels()
                    self.detailTabs.setTabText(idx, player.localName)
                    player.manualRuleBoxes = [
                        RuleBox(x) for x in game.ruleset.allRules
                        if x.hasSelectable
                    ]
                    for ruleBox in player.manualRuleBoxes:
                        self.detailsLayout[idx].addWidget(ruleBox)
                        ruleBox.clicked.connect(self.slotInputChanged)
                player.refreshManualRules()

    def show(self):
        """only now compute content"""
        if self.game and not self.game.finished():
            self.slotInputChanged()
            QWidget.show(self)

    def penalty(self):
        """penalty button clicked"""
        dlg = PenaltyDialog(self.game)
        dlg.exec_()

    def slotLastTile(self):
        """called when the last tile changes"""
        newLastTile = self.computeLastTile()
        if not newLastTile:
            return
        if self.prevLastTile and self.prevLastTile.isExposed != newLastTile.isExposed:
            # state of last tile (concealed/exposed) changed:
            # for all checked boxes check if they still are applicable
            winner = self.game.winner
            if winner:
                for box in winner.manualRuleBoxes:
                    if box.isChecked():
                        box.setChecked(False)
                        if winner.hand.manualRuleMayApply(box.rule):
                            box.setChecked(True)
        self.prevLastTile = newLastTile
        self.fillLastMeldCombo()
        self.slotInputChanged()

    def computeLastTile(self):
        """returns the currently selected last tile"""
        idx = self.cbLastTile.currentIndex()
        if idx >= 0:
            return self.cbLastTile.itemData(idx)

    def clickedPlayerIdx(self, checkbox):
        """the player whose box has been clicked"""
        for idx in range(4):
            if checkbox == self.wonBoxes[idx]:
                return idx
        assert False

    def wonChanged(self):
        """if a new winner has been defined, uncheck any previous winner"""
        newWinner = None
        if self.sender() != self.draw:
            clicked = self.clickedPlayerIdx(self.sender())
            if self.wonBoxes[clicked].isChecked():
                newWinner = self.game.players[clicked]
            else:
                newWinner = None
        self.game.winner = newWinner
        for idx in range(4):
            if newWinner != self.game.players[idx]:
                self.wonBoxes[idx].setChecked(False)
        if newWinner:
            self.draw.setChecked(False)
        self.fillLastTileCombo()
        self.slotInputChanged()

    def updateManualRules(self):
        """enable/disable them"""
        # if an exclusive rule has been activated, deactivate it for
        # all other players
        ruleBox = self.sender()
        if isinstance(
                ruleBox,
                RuleBox) and ruleBox.isChecked() and ruleBox.rule.exclusive():
            for idx, player in enumerate(self.game.players):
                if ruleBox.parentWidget() != self.details[idx]:
                    for pBox in player.manualRuleBoxes:
                        if pBox.rule.name == ruleBox.rule.name:
                            pBox.setChecked(False)
        try:
            newState = bool(self.game.winner.handBoard.uiTiles)
        except AttributeError:
            newState = False
        self.lblLastTile.setEnabled(newState)
        self.cbLastTile.setEnabled(newState)
        self.lblLastMeld.setEnabled(newState)
        self.cbLastMeld.setEnabled(newState)
        if self.game:
            for player in self.game.players:
                player.refreshManualRules(self.sender())

    def clear(self):
        """prepare for next hand"""
        if self.game:
            for idx, player in enumerate(self.game.players):
                self.spValues[idx].clear()
                self.spValues[idx].setValue(0)
                self.wonBoxes[idx].setChecked(False)
                player.payment = 0
                player.invalidateHand()
        for box in self.wonBoxes:
            box.setVisible(False)
        self.draw.setChecked(False)
        self.updateManualRules()

        if self.game is None:
            self.hide()
        else:
            self.refreshWindLabels()
            self.computeScores()
            self.spValues[0].setFocus()
            self.spValues[0].selectAll()

    def refreshWindLabels(self):
        """update their wind and prevailing"""
        for idx, player in enumerate(self.game.players):
            self.windLabels[idx].wind = player.wind
            self.windLabels[idx].roundsFinished = self.game.roundsFinished

    def computeScores(self):
        """if tiles have been selected, compute their value"""
        # pylint: disable=too-many-branches
        # too many branches
        if not self.game:
            return
        if self.game.finished():
            self.hide()
            return
        for nameLabel, wonBox, spValue, player in zip(self.nameLabels,
                                                      self.wonBoxes,
                                                      self.spValues,
                                                      self.game.players):
            with BlockSignals([spValue, wonBox]):
                # we do not want that change to call computeScores again
                if player.handBoard and player.handBoard.uiTiles:
                    spValue.setEnabled(False)
                    nameLabel.setBuddy(wonBox)
                    for _ in range(10):
                        prevTotal = player.handTotal
                        player.invalidateHand()
                        wonBox.setVisible(player.hand.won)
                        if not wonBox.isVisibleTo(self) and wonBox.isChecked():
                            wonBox.setChecked(False)
                            self.game.winner = None
                        elif prevTotal == player.handTotal:
                            break
                        player.refreshManualRules()
                    spValue.setValue(player.handTotal)
                else:
                    if not spValue.isEnabled():
                        spValue.clear()
                        spValue.setValue(0)
                        spValue.setEnabled(True)
                        nameLabel.setBuddy(spValue)
                    wonBox.setVisible(
                        player.handTotal >= self.game.ruleset.minMJTotal())
                    if not wonBox.isVisibleTo(self) and wonBox.isChecked():
                        wonBox.setChecked(False)
                if not wonBox.isVisibleTo(self) and player is self.game.winner:
                    self.game.winner = None
        if Internal.scene.explainView:
            Internal.scene.explainView.refresh()

    def __lastMeldContent(self):
        """prepare content for lastmeld combo"""
        lastTiles = set()
        winnerTiles = []
        if self.game.winner and self.game.winner.handBoard:
            winnerTiles = self.game.winner.handBoard.uiTiles
            pairs = []
            for meld in self.game.winner.hand.melds:
                if len(meld) < 4:
                    pairs.extend(meld)
            for tile in winnerTiles:
                if tile.tile in pairs and not tile.isBonus:
                    lastTiles.add(tile.tile)
        return lastTiles, winnerTiles

    def __fillLastTileComboWith(self, lastTiles, winnerTiles):
        """fill last meld combo with prepared content"""
        self.comboTilePairs = lastTiles
        idx = self.cbLastTile.currentIndex()
        if idx < 0:
            idx = 0
        indexedTile = self.cbLastTile.itemData(idx)
        restoredIdx = None
        self.cbLastTile.clear()
        if not winnerTiles:
            return
        pmSize = winnerTiles[0].board.tileset.faceSize
        pmSize = QSize(pmSize.width() * 0.5, pmSize.height() * 0.5)
        self.cbLastTile.setIconSize(pmSize)
        QPixmapCache.clear()
        self.__tilePixMaps = []
        shownTiles = set()
        for tile in winnerTiles:
            if tile.tile in lastTiles and tile.tile not in shownTiles:
                shownTiles.add(tile.tile)
                self.cbLastTile.addItem(
                    QIcon(tile.pixmapFromSvg(pmSize, withBorders=False)), '',
                    tile.tile)
                if indexedTile is tile.tile:
                    restoredIdx = self.cbLastTile.count() - 1
        if not restoredIdx and indexedTile:
            # try again, maybe the tile changed between concealed and exposed
            indexedTile = indexedTile.exposed
            for idx in range(self.cbLastTile.count()):
                if indexedTile is self.cbLastTile.itemData(idx).exposed:
                    restoredIdx = idx
                    break
        if not restoredIdx:
            restoredIdx = 0
        self.cbLastTile.setCurrentIndex(restoredIdx)
        self.prevLastTile = self.computeLastTile()

    def clearLastTileCombo(self):
        """as the name says"""
        self.comboTilePairs = None
        self.cbLastTile.clear()

    def fillLastTileCombo(self):
        """fill the drop down list with all possible tiles.
        If the drop down had content before try to preserve the
        current index. Even if the tile changed state meanwhile."""
        if self.game is None:
            return
        lastTiles, winnerTiles = self.__lastMeldContent()
        if self.comboTilePairs == lastTiles:
            return
        with BlockSignals(self.cbLastTile):
            # we only want to emit the changed signal once
            self.__fillLastTileComboWith(lastTiles, winnerTiles)
        self.cbLastTile.currentIndexChanged.emit(0)

    def __fillLastMeldComboWith(self, winnerMelds, indexedMeld, lastTile):
        """fill last meld combo with prepared content"""
        winner = self.game.winner
        faceWidth = winner.handBoard.tileset.faceSize.width() * 0.5
        faceHeight = winner.handBoard.tileset.faceSize.height() * 0.5
        restoredIdx = None
        for meld in winnerMelds:
            pixMap = QPixmap(faceWidth * len(meld), faceHeight)
            pixMap.fill(Qt.transparent)
            self.__meldPixMaps.append(pixMap)
            painter = QPainter(pixMap)
            for element in meld:
                painter.drawPixmap(
                    0, 0,
                    winner.handBoard.tilesByElement(element)[0].pixmapFromSvg(
                        QSize(faceWidth, faceHeight), withBorders=False))
                painter.translate(QPointF(faceWidth, 0.0))
            self.cbLastMeld.addItem(QIcon(pixMap), '', str(meld))
            if indexedMeld == str(meld):
                restoredIdx = self.cbLastMeld.count() - 1
        if not restoredIdx and indexedMeld:
            # try again, maybe the meld changed between concealed and exposed
            indexedMeld = indexedMeld.lower()
            for idx in range(self.cbLastMeld.count()):
                meldContent = str(self.cbLastMeld.itemData(idx))
                if indexedMeld == meldContent.lower():
                    restoredIdx = idx
                    if lastTile not in meldContent:
                        lastTile = lastTile.swapped
                        assert lastTile in meldContent
                        with BlockSignals(self.cbLastTile
                                          ):  # we want to continue right here
                            idx = self.cbLastTile.findData(lastTile)
                            self.cbLastTile.setCurrentIndex(idx)
                    break
        if not restoredIdx:
            restoredIdx = 0
        self.cbLastMeld.setCurrentIndex(restoredIdx)
        self.cbLastMeld.setIconSize(QSize(faceWidth * 3, faceHeight))

    def fillLastMeldCombo(self):
        """fill the drop down list with all possible melds.
        If the drop down had content before try to preserve the
        current index. Even if the meld changed state meanwhile."""
        with BlockSignals(self.cbLastMeld
                          ):  # we only want to emit the changed signal once
            showCombo = False
            idx = self.cbLastMeld.currentIndex()
            if idx < 0:
                idx = 0
            indexedMeld = str(self.cbLastMeld.itemData(idx))
            self.cbLastMeld.clear()
            self.__meldPixMaps = []
            if not self.game.winner:
                return
            if self.cbLastTile.count() == 0:
                return
            lastTile = Internal.scene.computeLastTile()
            winnerMelds = [
                m for m in self.game.winner.hand.melds
                if len(m) < 4 and lastTile in m
            ]
            assert len(winnerMelds), 'lastTile %s missing in %s' % (
                lastTile, self.game.winner.hand.melds)
            if len(winnerMelds) == 1:
                self.cbLastMeld.addItem(QIcon(), '', str(winnerMelds[0]))
                self.cbLastMeld.setCurrentIndex(0)
                return
            showCombo = True
            self.__fillLastMeldComboWith(winnerMelds, indexedMeld, lastTile)
            self.lblLastMeld.setVisible(showCombo)
            self.cbLastMeld.setVisible(showCombo)
        self.cbLastMeld.currentIndexChanged.emit(0)

    def slotInputChanged(self):
        """some input fields changed: update"""
        for player in self.game.players:
            player.invalidateHand()
        self.updateManualRules()
        self.computeScores()
        self.validate()
        for player in self.game.players:
            player.showInfo()
        Internal.mainWindow.updateGUI()

    def validate(self):
        """update the status of the OK button"""
        game = self.game
        if game:
            valid = True
            if game.winner and game.winner.handTotal < game.ruleset.minMJTotal(
            ):
                valid = False
            elif not game.winner and not self.draw.isChecked():
                valid = False
            self.btnSave.setEnabled(valid)
class ProstateTRUSNavWidget(GuideletWidget):

  DEFAULT_PLUSSERVER_CHOOSER_TEXT = "Choose PlusServer.exe"
  DEFAULT_CONFIGURATION_CHOOSER_TEXT = "Select Configuration"

  def __init__(self, parent = None):
    GuideletWidget.__init__(self, parent)
    self.plusServerProcess = None
    self.configurationFile = self.getSetting('ConfigurationFile', self.DEFAULT_CONFIGURATION_CHOOSER_TEXT)
    self.serverExecutable = self.getSetting('PlusServer', self.DEFAULT_PLUSSERVER_CHOOSER_TEXT)

  def cleanup(self):
    GuideletWidget.cleanup(self)
    if self.plusServerProcess:
      self.plusServerProcess.terminate()

  def setup(self):
    showPlusServerWidget = True
    if _platform == "linux" or _platform == "linux2" or _platform == "darwin": #linux or linux or OS X
      message = "Attention: You are running Slicer on Linux or OS X. Do you have PlusServer installed on the current OS?"
      result = QMessageBox.question(slicer.util.mainWindow(), 'ProstateTRUSNav', message,
                                     QMessageBox.Yes | QMessageBox.No)
      showPlusServerWidget = result == QMessageBox.Yes

    if _platform == "win32" or showPlusServerWidget:
      # Windows...
      plusServerCollapsibleButton = ctkCollapsibleButton()
      plusServerCollapsibleButton.text = "PlusServer"
      self.layout.addWidget(plusServerCollapsibleButton)
      self.configurationFileChooserButton = QPushButton(self.configurationFile)
      self.configurationFileChooserButton.connect('clicked()', self.onConfigFileSelected)
      self.runPlusServerButton = QPushButton("Run PlusServer")
      self.runPlusServerButton.setCheckable(True)
      self.runPlusServerButton.connect('clicked()', self.onRunPlusServerButtonClicked)

      self.serverFormLayout = QFormLayout(plusServerCollapsibleButton)

      self.serverExecutableChooserButton = QPushButton(self.serverExecutable)
      self.serverExecutableChooserButton.connect('clicked()', self.onServerExecutableSelected)

      hbox = QHBoxLayout()
      hbox.addWidget(self.serverExecutableChooserButton)
      self.serverFormLayout.addRow(hbox)

      hbox = QHBoxLayout()
      hbox.addWidget(self.configurationFileChooserButton)
      hbox.addWidget(self.runPlusServerButton)
      self.serverFormLayout.addRow(hbox)

    GuideletWidget.setup(self)

    # do specific setup here
    if _platform == "win32" or showPlusServerWidget:
      self.launchGuideletButton.setEnabled(False)
      self.checkExecutableAndArgument()

  def checkExecutableAndArgument(self):
    if os.path.exists(self.serverExecutable) and os.path.exists(self.configurationFile):
      self.runPlusServerButton.setEnabled(True)
    else:
      self.runPlusServerButton.setEnabled(False)

  def getSetting(self, settingName, defaultValue=""):
    settings = QSettings()
    value = settings.value(self.moduleName + '/' + settingName, defaultValue)
    return value if value is not None and value != "" else defaultValue

  def setSetting(self, settingName, value):
    settings = QSettings()
    settings.setValue(self.moduleName + '/'+ settingName, value)

  def addLauncherWidgets(self):
    GuideletWidget.addLauncherWidgets(self)
    # add launcher widget here

  def onServerExecutableSelected(self):
    executable = QFileDialog.getOpenFileName(self.parent, "PlusServer Executable",
                                                self.serverExecutable, "*.exe")
    if executable != "" and executable.find("PlusServer.exe"):
      self.serverExecutable = executable
      self.serverExecutableChooserButton.setText(executable)
      self.setSetting("PlusServer", executable)
    self.checkExecutableAndArgument()

  def onConfigFileSelected(self):
    self.configurationFile = QFileDialog.getOpenFileName(self.parent, "Choose Configuration File",
                                                            self.configurationFile, "*.xml")
    if self.configurationFile != "":
      self.configurationFileChooserButton.setText(os.path.split(self.configurationFile)[1])
      self.setSetting("ConfigurationFile", self.configurationFile)
    self.checkExecutableAndArgument()

  def onRunPlusServerButtonClicked(self):
    if self.runPlusServerButton.isChecked():
      command = [self.serverExecutable, "--config-file="+self.configurationFile]
      logging.info("Executing %s %s" % tuple(command))
      self.plusServerProcess = Popen([self.serverExecutable, "--config-file="+self.configurationFile])
      if self.plusServerProcess:
        self.runPlusServerButton.setText("Quit Plus Server")
        self.launchGuideletButton.setEnabled(True)
    else:
      if self.plusServerProcess:
        self.plusServerProcess.terminate()
        self.runPlusServerButton.setText("Run PlusServer")
        self.launchGuideletButton.setEnabled(False)

  def collectParameterList(self):
    parameterList = GuideletWidget.collectParameterList(self)
    if not parameterList:
      parameterList = dict()
    parameterList['OfflineVolumeToReconstruct'] = 0,
    return parameterList

  def createGuideletInstance(self, parameterList = None):
    return ProstateTRUSNavGuidelet(None, self.guideletLogic,  parameterList)

  def createGuideletLogic(self):
    return ProstateTRUSNavLogic()
class ProstateTRUSNavUltrasound(UltraSound):

  OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha"
  SCOUT_VOLUME_FILENAME = "ScoutScan.mha"
  LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha"

  RECORDING_FILENAME = "Recording.mha"
  SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha"
  LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME

  SCOUT_VOLUME_NODE_NAME = "ScoutScan"
  OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference"
  LIVE_VOLUME_NODE_NAME = "liveReconstruction"

  APPLY_HOLE_FILLING_FOR_SNAPSHOT = False
  SNAPSHOT_INTERVAL = 3
  OUTPUT_VOLUME_SPACING = 3
  LIVE_OUTPUT_VOLUME_SPACING = 1

  @property
  def roiNode(self):
    return self._roiNode

  @roiNode.setter
  def roiNode(self, node):
    self._roiNode=node
    if node is not None:
      self.startStopLiveReconstructionButton.setEnabled(True)
    else:
      self.startStopLiveReconstructionButton.setEnabled(False)

  def __init__(self, guideletParent):
    UltraSound.__init__(self, guideletParent)

    self.parameterNode = guideletParent.parameterNode
    self.parameterNodeObserver = None
    self._roiNode = None
    self.liveOutputSpacingValue = [self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING,
                                   self.LIVE_OUTPUT_VOLUME_SPACING]
    self.outputSpacing = [self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING]
    self.roiOrigin = None
    self.roiExtent = None
    self.defaultParameterNode = None
    self.logic = ProstateTRUSNavUltrasoundLogic()

  def setupPanel(self, parentWidget):
    logging.debug('ProstateTRUSNavUltrasound.setupPanel')

    self.connectorNode = self.guideletParent.connectorNode
    self.connectorNodeConnected = False

    collapsibleButton = ctkCollapsibleButton()
    collapsibleButton.setProperty('collapsedHeight', 20)
    setButtonStyle(collapsibleButton, 2.0)
    collapsibleButton.text = "Ultrasound"
    parentWidget.addWidget(collapsibleButton)

    ultrasoundLayout = QFormLayout(collapsibleButton)
    ultrasoundLayout.setContentsMargins(12,4,4,4)
    ultrasoundLayout.setSpacing(4)

    self.connectDisconnectButton = QPushButton("Connect")
    self.connectDisconnectButton.setToolTip("If clicked, connection OpenIGTLink")

    hbox = QHBoxLayout()
    hbox.addWidget(self.connectDisconnectButton)
    ultrasoundLayout.addRow(hbox)

    self.setupIcons()

    self.captureIDSelector = QComboBox()
    self.captureIDSelector.setToolTip("Pick capture device ID")
    self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    self.volumeReconstructorIDSelector = QComboBox()
    self.volumeReconstructorIDSelector.setToolTip( "Pick volume reconstructor device ID" )
    self.volumeReconstructorIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    self.startStopRecordingButton = QPushButton("  Start Recording")
    self.startStopRecordingButton.setCheckable(True)
    self.startStopRecordingButton.setIcon(self.recordIcon)
    self.startStopRecordingButton.setEnabled(False)
    self.startStopRecordingButton.setToolTip("If clicked, start recording")
    self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    recordParametersControlsLayout = QGridLayout()

    self.filenameLabel = self.createLabel("Filename:", visible=False)
    recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0)

     # Offline Reconstruction
    self.offlineReconstructButton = QPushButton("  Offline Reconstruction")
    self.offlineReconstructButton.setCheckable(True)
    self.offlineReconstructButton.setIcon(self.recordIcon)
    self.offlineReconstructButton.setEnabled(False)
    self.offlineReconstructButton.setToolTip("If clicked, reconstruct recorded volume")
    self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    self.offlineVolumeToReconstructSelector = QComboBox()
    self.offlineVolumeToReconstructSelector.setEditable(True)
    self.offlineVolumeToReconstructSelector.setToolTip( "Pick/set volume to reconstruct" )
    self.offlineVolumeToReconstructSelector.visible = False

    hbox = QHBoxLayout()
    hbox.addWidget(self.startStopRecordingButton)
    hbox.addWidget(self.offlineReconstructButton)
    ultrasoundLayout.addRow(hbox)

    # Scout scan (record and low resolution reconstruction) and live reconstruction
    # Scout scan part

    self.startStopScoutScanButton = QPushButton("  Scout scan\n  Start recording")
    self.startStopScoutScanButton.setCheckable(True)
    self.startStopScoutScanButton.setIcon(self.recordIcon)
    self.startStopScoutScanButton.setToolTip("If clicked, start recording")
    self.startStopScoutScanButton.setEnabled(False)
    self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    self.startStopLiveReconstructionButton = QPushButton("  Start live reconstruction")
    self.startStopLiveReconstructionButton.setCheckable(True)
    self.startStopLiveReconstructionButton.setIcon(self.recordIcon)
    self.startStopLiveReconstructionButton.setToolTip("If clicked, start live reconstruction")
    self.startStopLiveReconstructionButton.setEnabled(False)
    self.startStopLiveReconstructionButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    self.displayRoiButton = QToolButton()
    self.displayRoiButton.setCheckable(True)
    self.displayRoiButton.setIcon(self.visibleOffIcon)
    self.displayRoiButton.setToolTip("If clicked, display ROI")

    hbox = QHBoxLayout()
    hbox.addWidget(self.startStopScoutScanButton)
    hbox.addWidget(self.startStopLiveReconstructionButton)
    # hbox.addWidget(self.displayRoiButton)
    ultrasoundLayout.addRow(hbox)

    self.snapshotTimer = QTimer()
    self.snapshotTimer.setSingleShot(True)

    self.onParameterSetSelected()

    return collapsibleButton

  def setupResliceDriver(self):
    layoutManager = slicer.app.layoutManager()
    # Show ultrasound in red view.
    redSlice = layoutManager.sliceWidget('Red')
    redSliceLogic = redSlice.sliceLogic()
    redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID(self.liveUltrasoundNode_Reference.GetID())

    resliceLogic = slicer.modules.volumereslicedriver.logic()
    if resliceLogic:
      redNode = slicer.util.getNode('vtkMRMLSliceNodeRed')
      redNode.SetSliceResolutionMode(slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes)
      resliceLogic.SetDriverForSlice(self.liveUltrasoundNode_Reference.GetID(), redNode)
      resliceLogic.SetModeForSlice(6, redNode) # Transverse mode, default for PLUS ultrasound.
    else:
      logging.warning('Logic not found for Volume Reslice Driver')

  def createCollapsibleButton(self, text, collapsed=False):
    collapsibleButton = ctkCollapsibleButton()
    collapsibleButton.text = text
    collapsibleButton.collapsed = collapsed
    return collapsibleButton

  def createLabel(self, text, visible=True):
    label = QLabel()
    label.setText(text)
    label.visible = visible
    return label

  def setupConnections(self):
    self.startStopRecordingButton.connect('clicked(bool)', self.onStartStopRecordingButtonClicked)
    self.offlineReconstructButton.connect('clicked(bool)', self.onReconstVolume)
    self.startStopScoutScanButton.connect('clicked(bool)', self.onStartStopScoutScanButtonClicked)
    self.startStopLiveReconstructionButton.connect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked)
    self.displayRoiButton.connect('clicked(bool)', self.onDisplayRoiButtonClicked)
    self.captureIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui)
    self.volumeReconstructorIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui)
    self.offlineVolumeToReconstructSelector.connect('currentIndexChanged(int)', self.updateParameterNodeFromGui)
    self.displayRoiButton.connect('clicked(bool)', self.updateParameterNodeFromGui)
    self.snapshotTimer.timeout.connect(self.onRequestVolumeReconstructionSnapshot)
    self.connectDisconnectButton.connect('clicked(bool)', self.onConnectDisconnectButtonClicked)

  def disconnect(self):
    self.startStopRecordingButton.disconnect('clicked(bool)', self.onStartStopRecordingButtonClicked)
    self.offlineReconstructButton.disconnect('clicked(bool)', self.onReconstVolume)
    self.startStopScoutScanButton.disconnect('clicked(bool)', self.onStartStopScoutScanButtonClicked)
    self.startStopLiveReconstructionButton.disconnect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked)
    self.displayRoiButton.disconnect('clicked(bool)', self.onDisplayRoiButtonClicked)
    self.captureIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui)
    self.volumeReconstructorIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui)
    self.offlineVolumeToReconstructSelector.disconnect('currentIndexChanged(int)', self.updateParameterNodeFromGui)
    self.displayRoiButton.disconnect('clicked(bool)', self.updateParameterNodeFromGui)
    self.snapshotTimer.timeout.disconnect(self.onRequestVolumeReconstructionSnapshot)
    self.connectDisconnectButton.disconnect('clicked(bool)', self.onConnectDisconnectButtonClicked)

  def setupIcons(self):
    self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace("PlusRemote.py", "")
    self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Record.png')
    self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Stop.png')
    self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Wait.png')
    self.visibleOffIcon = QIcon(":Icons\VisibleOff.png")
    self.visibleOnIcon = QIcon(":Icons\VisibleOn.png")

  def onParameterSetSelected(self):
    # Set up default values for new nodes
    if self.parameterNode:
      self.plusRemoteLogic.setDefaultParameters(self.parameterNode)
    self.updateGuiFromParameterNode()

  def updateGuiFromParameterNode(self):

    self.parameterVolumeList = {'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector}
    for parameter in self.parameterVolumeList:
      if self.parameterNode.GetParameter(parameter):
        self.parameterVolumeList[parameter].blockSignals(True)
      self.parameterVolumeList[parameter].blockSignals(False)

    if self.parameterNode.GetParameter('CaptureID'):
      self.captureIDSelector.blockSignals(True)
      for i in range(0, self.captureIDSelector.count):
        if self.parameterNode.GetParameter('CaptureID') == self.captureIDSelector.itemText(i):
          self.captureIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('CaptureIdIndex')))
      self.captureIDSelector.blockSignals(False)

    if self.parameterNode.GetParameter('VolumeReconstructor'):
      self.volumeReconstructorIDSelector.blockSignals(True)
      for i in range(0, self.volumeReconstructorIDSelector.count):
        if self.parameterNode.GetParameter('VolumeReconstructor') == self.volumeReconstructorIDSelector.itemText(i):
          self.volumeReconstructorIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('VolumeReconstructorIndex')))
      self.volumeReconstructorIDSelector.blockSignals(False)

      self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0)

  def updateParameterNodeFromGui(self):
    #Update parameter node value to save when user change value in the interface
    if not self.parameterNode:
      return
    self.parametersList = {'CaptureID': self.captureIDSelector.currentText,
                           'CaptureIdIndex': self.captureIDSelector.currentIndex,
                           'VolumeReconstructor': self.volumeReconstructorIDSelector.currentText,
                           'VolumeReconstructorIndex': self.volumeReconstructorIDSelector.currentIndex,
                           'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector.currentIndex}
    for parameter in self.parametersList:
      self.parameterNode.SetParameter(parameter, str(self.parametersList[parameter]))
    if self.roiNode:
      roiNodeID = self.roiNode.GetID()
      self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID)

#
# Connector observation and actions
#

  def onConnectorNodeConnected(self):
    logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected")
    self.connectorNodeConnected = True
    self.captureIDSelector.setDisabled(False)
    self.volumeReconstructorIDSelector.setDisabled(False)
    self.plusRemoteLogic.getCaptureDeviceIds(self.connectorNode.GetID(),
                                   self.onGetCaptureDeviceCommandResponseReceived)
    self.plusRemoteLogic.getVolumeReconstructorDeviceIds(self.connectorNode.GetID(),
                                               self.onGetVolumeReconstructorDeviceCommandResponseReceived)
    self.connectDisconnectButton.setText("Disconnect")

  def onConnectorNodeDisconnected(self):
    logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected")
    self.connectorNodeConnected = False
    self.startStopRecordingButton.setEnabled(False)
    self.startStopScoutScanButton.setEnabled(False)
    self.startStopLiveReconstructionButton.setEnabled(False)
    self.offlineReconstructButton.setEnabled(False)
    self.captureIDSelector.setDisabled(True)
    self.volumeReconstructorIDSelector.setDisabled(True)
    self.connectDisconnectButton.setText("Connect")

  def getLiveVolumeRecNode(self):
    liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME)
    return liveVolumeRecNode

  def getOfflineVolumeRecNode(self):
    offlineVolumeRecNode = slicer.util.getNode(self.OFFLINE_VOLUME_NODE_NAME)
    return offlineVolumeRecNode

  def getScoutVolumeNode(self):
    scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME)
    return scoutScanVolumeNode

  def onConnectDisconnectButtonClicked(self):
    if self.connectorNode.GetState() == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED:
      self.connectorNode.Stop()
    else:
      self.connectorNode.Start()

  def onStartStopRecordingButtonClicked(self):
    if self.startStopRecordingButton.isChecked():
      self.startStopRecordingButton.setText("  Stop Recording")
      self.startStopRecordingButton.setIcon(self.stopIcon)
      self.startStopRecordingButton.setToolTip( "If clicked, stop recording" )
      self.onStartRecording(self.generateRecordingOutputFilename())
    else:
      self.startStopRecordingButton.setText("  Start Recording")
      self.startStopRecordingButton.setIcon(self.recordIcon)
      self.startStopRecordingButton.setToolTip( "If clicked, start recording" )
      self.onStopRecording(self.onVolumeRecorded)

  def onStartStopScoutScanButtonClicked(self):
    if self.startStopScoutScanButton.isChecked():
      self.startStopScoutScanButton.setText("  Scout Scan\n  Stop Recording and Reconstruct Recorded Volume")
      self.startStopScoutScanButton.setIcon(self.stopIcon)
      self.startStopScoutScanButton.setToolTip( "If clicked, stop recording and reconstruct recorded volume" )
      self.onStartRecording(self.generateScoutRecordingOutputFilename())
    else:
      self.onStopRecording(self.onScoutVolumeRecorded)

  def onStartStopLiveReconstructionButtonClicked(self):
    if self.startStopLiveReconstructionButton.isChecked():
      if self.roiNode:
        self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING,
                                                                                       self.roiNode)
      self.startStopLiveReconstructionButton.setText("  Stop Live Reconstruction")
      self.startStopLiveReconstructionButton.setIcon(self.stopIcon)
      self.startStopLiveReconstructionButton.setToolTip( "If clicked, stop live reconstruction" )
      self.onStartRecording(self.getLiveRecordingOutputFilename())
      self.onStartReconstruction()
    else:
      self.startStopLiveReconstructionButton.setText("  Start Live Reconstruction")
      self.startStopLiveReconstructionButton.setIcon(self.recordIcon)
      self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction" )
      self.onStopRecording(self.printCommandResponse)
      self.onStopReconstruction()

  def onDisplayRoiButtonClicked(self):
    if self.displayRoiButton.isChecked():
      self.displayRoiButton.setIcon(self.visibleOnIcon)
      self.displayRoiButton.setToolTip("If clicked, hide ROI")
      if self.roiNode:
        self.roiNode.SetDisplayVisibility(1)
    else:
      self.displayRoiButton.setIcon(self.visibleOffIcon)
      self.displayRoiButton.setToolTip("If clicked, display ROI")
      if self.roiNode:
        self.roiNode.SetDisplayVisibility(0)

  def generateRecordingOutputFilename(self):
    return self.plusRemoteLogic.addTimestampToFilename(self.RECORDING_FILENAME)

  def generateScoutRecordingOutputFilename(self):
    return self.plusRemoteLogic.addTimestampToFilename(self.SCOUT_RECORDING_FILENAME)

  def getLiveRecordingOutputFilename(self):
    return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_RECORDING_FILENAME)

  def getLiveReconstructionOutputFilename(self):
    return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_VOLUME_FILENAME)

  def onStartRecording(self, filename):
    self.plusRemoteLogic.startRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText,
                                        filename, self.printCommandResponse)

  def onStopRecording(self, callback):
    self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, callback)

  def onStartReconstruction(self):
    if self.roiNode:
      self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING,
                                                                                     self.roiNode)
    self.plusRemoteLogic.startVolumeReconstuction(self.connectorNode.GetID(),
                                                   self.volumeReconstructorIDSelector.currentText,
                                                   self.liveOutputSpacingValue, self.roiOrigin,
                                                   self.roiExtent, self.printCommandResponse,
                                                   self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME)
    # Set up timer for requesting snapshot
    self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000)

  def onStopReconstruction(self):
    self.snapshotTimer.stop()
    self.plusRemoteLogic.stopVolumeReconstruction(self.connectorNode.GetID(),
                                                  self.volumeReconstructorIDSelector.currentText,
                                                  self.onVolumeLiveReconstructed,
                                                  self.getLiveReconstructionOutputFilename(),
                                                  self.LIVE_VOLUME_NODE_NAME)

  def onReconstVolume(self):
    self.offlineReconstructButton.setIcon(self.waitIcon)
    self.offlineReconstructButton.setText("  Offline Reconstruction in progress ...")
    self.offlineReconstructButton.setEnabled(False)
    self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText,
                                             self.offlineVolumeToReconstructSelector.currentText, self.outputSpacing,
                                             self.onVolumeReconstructed, self.OFFLINE_VOLUME_FILENAME,
                                             self.OFFLINE_VOLUME_NODE_NAME)

  def onScoutScanReconstVolume(self):
    self.startStopScoutScanButton.setIcon(self.waitIcon)
    self.startStopScoutScanButton.setText("  Scout Scan\n  Reconstruction in progress ...")
    self.startStopScoutScanButton.setEnabled(False)
    self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText,
                                             self.lastScoutRecordingOutputFilename, self.outputSpacing,
                                             self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME,
                                             self.SCOUT_VOLUME_NODE_NAME)

  def onRequestVolumeReconstructionSnapshot(self, stopFlag = ""):
    self.plusRemoteLogic.getVolumeReconstructionSnapshot(self.connectorNode.GetID(),
                                                         self.volumeReconstructorIDSelector.currentText,
                                                         self.LIVE_VOLUME_FILENAME,
                                                         self.LIVE_VOLUME_NODE_NAME,
                                                         self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired)

  def executeCommandDelayed(self, method, delay=100):
    # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same
    # therefore we wait a bit to make sure the image message is processed as well
    QTimer.singleShot(delay, method)

  def printCommandResponse(self, command, q):
    statusText = "Command {0} [{1}]: {2}\n".format(command.GetCommandName(), command.GetID(),
                                                   command.StatusToString(command.GetStatus()))
    if command.GetResponseMessage():
      statusText = statusText + command.GetResponseMessage()
    elif command.GetResponseText():
      statusText = statusText + command.GetResponseText()
    logging.debug(statusText)

  def onGetCaptureDeviceCommandResponseReceived(self, command, q):
    self.printCommandResponse(command, q)
    if command.GetStatus() != command.CommandSuccess:
      return

    captureDeviceIdsListString = command.GetResponseMessage()
    if captureDeviceIdsListString:
      captureDevicesIdsList = captureDeviceIdsListString.split(",")
    else:
      captureDevicesIdsList = []

    for i in range(0,len(captureDevicesIdsList)):
      if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1:
        self.captureIDSelector.addItem(captureDevicesIdsList[i])

  def onGetVolumeReconstructorDeviceCommandResponseReceived(self, command, q):
    self.printCommandResponse(command, q)
    if command.GetStatus() != command.CommandSuccess:
      return

    volumeReconstructorDeviceIdsListString = command.GetResponseMessage()
    if volumeReconstructorDeviceIdsListString:
      volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split(",")
    else:
      volumeReconstructorDeviceIdsList = []

    self.volumeReconstructorIDSelector.clear()
    self.volumeReconstructorIDSelector.addItems(volumeReconstructorDeviceIdsList)
    self.startStopRecordingButton.setEnabled(True)
    self.offlineReconstructButton.setEnabled(True)
    self.startStopScoutScanButton.setEnabled(True)
    if self.roiNode:
      self.startStopLiveReconstructionButton.setEnabled(True)

  def onVolumeRecorded(self, command, q):
    self.printCommandResponse(command, q)
    self.offlineReconstructButton.setEnabled(True)

    volumeToReconstructFileName = os.path.basename(command.GetResponseMessage())
    self.offlineVolumeToReconstructSelector.insertItem(0,volumeToReconstructFileName)
    self.offlineVolumeToReconstructSelector.setCurrentIndex(0)

  def onScoutVolumeRecorded(self, command, q):
    self.printCommandResponse(command,q)
    self.offlineReconstructButton.setEnabled(True)

    if command.GetStatus() == command.CommandExpired:
      logging.fatal("Scout Volume Recording: Timeout while waiting for volume reconstruction result")
      return

    if command.GetStatus() == command.CommandSuccess:
      self.lastScoutRecordingOutputFilename = os.path.basename(command.GetResponseMessage())
      self.onScoutScanReconstVolume()

  def onVolumeReconstructed(self, command, q):
    self.printCommandResponse(command,q)

    self.offlineReconstructButton.setIcon(self.recordIcon)
    self.offlineReconstructButton.setText("Offline Reconstruction")
    self.offlineReconstructButton.setEnabled(True)
    self.offlineReconstructButton.setChecked(False)

    if command.GetStatus() == command.CommandExpired:
      # volume reconstruction command timed out
      logging.fatal("Volume Reconstruction: Timeout while waiting for volume reconstruction result")
      return

    if command.GetStatus() != command.CommandSuccess:
      logging.debug("Volume Reconstruction: " + command.GetResponseMessage())
      return

    self.executeCommandDelayed(self.onVolumeReconstructedFinalize)

  def onVolumeReconstructedFinalize(self):
    applicationLogic = slicer.app.applicationLogic()
    applicationLogic.FitSliceToAll()
    self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode())

  def onScoutVolumeReconstructed(self, command, q):
    self.printCommandResponse(command,q)

    if command.GetStatus() == command.CommandExpired:
      logging.fatal("Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result")
      return

    self.startStopScoutScanButton.setIcon(self.recordIcon)
    self.startStopScoutScanButton.setText("  Scout Scan\n  Start Recording")
    self.startStopScoutScanButton.setEnabled(True)

    if command.GetStatus() != command.CommandSuccess:
      logging.debug("Scout Volume Reconstruction: " + command.GetResponseMessage())
      return

    self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize)

  def onScoutVolumeReconstructedFinalize(self):
    # Create and initialize ROI after scout scan because low resolution scout scan is used to set
    # a smaller ROI for the live high resolution reconstruction
    self.roiNode = self.logic.onRoiInitialization(self.SCOUT_VOLUME_NODE_NAME, self.roiNode)
    self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING,
                                                                                   self.roiNode)
    scoutScanVolumeNode = self.getScoutVolumeNode()

    applicationLogic = slicer.app.applicationLogic()
    applicationLogic.FitSliceToAll()

    self.guideletParent.showVolumeRendering(scoutScanVolumeNode)

  def onSnapshotAcquired(self, command, q):
    self.printCommandResponse(command,q)

    if not self.startStopLiveReconstructionButton.isChecked():
      # live volume reconstruction is not active
      return

    self.executeCommandDelayed(self.onSnapshotAcquiredFinalize)

  def onSnapshotAcquiredFinalize(self):
    self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())
    self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000)

  def onVolumeLiveReconstructed(self, command, q):
    self.printCommandResponse(command,q)

    if command.GetStatus() == command.CommandExpired:
      logging.fatal("LIVE Volume Reconstruction: Failed to stop volume reconstruction")
      return

    if command.GetStatus() != command.CommandSuccess:
      logging.debug("LIVE Volume Reconstruction " + command.GetResponseMessage())
      return

    self.executeCommandDelayed(self.getLiveVolumeRecNode)

  def onVolumeLiveReconstructedFinalize(self):
    self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())