Exemple #1
0
    def __init__(self):
        SelectRuleset.__init__(self)
        Players.load()
        decorateWindow(self, m18n('Select four players'))
        self.names = None
        self.nameWidgets = []
        for idx, wind in enumerate(Wind.all4):
            cbName = QComboBox()
            cbName.manualSelect = False
            # increase width, we want to see the full window title
            cbName.setMinimumWidth(350)  # is this good for all platforms?
            cbName.addItems(list(Players.humanNames.values()))
            self.grid.addWidget(cbName, idx + 1, 1)
            self.nameWidgets.append(cbName)
            self.grid.addWidget(WindLabel(wind), idx + 1, 0)
            cbName.currentIndexChanged.connect(self.slotValidate)

        query = Query(
            "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)")
        if len(query.records):
            with BlockSignals(self.nameWidgets):
                for cbName, playerId in zip(self.nameWidgets, query.records[0]):
                    try:
                        playerName = Players.humanNames[playerId]
                        playerIdx = cbName.findText(playerName)
                        if playerIdx >= 0:
                            cbName.setCurrentIndex(playerIdx)
                    except KeyError:
                        logError(u'database is inconsistent: player with id %d is in game but not in player'
                                 % playerId)
        self.slotValidate()
Exemple #2
0
    def __init__(self):
        SelectRuleset.__init__(self)
        Players.load()
        decorateWindow(self, i18n('Select four players'))
        self.names = None
        self.nameWidgets = []
        for idx, wind in enumerate(Wind.all4):
            cbName = QComboBox()
            cbName.manualSelect = False
            # increase width, we want to see the full window title
            cbName.setMinimumWidth(350)  # is this good for all platforms?
            cbName.addItems(list(Players.humanNames.values()))
            self.grid.addWidget(cbName, idx + 1, 1)
            self.nameWidgets.append(cbName)
            self.grid.addWidget(WindLabel(wind), idx + 1, 0)
            cbName.currentIndexChanged.connect(self.slotValidate)

        query = Query(
            "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)"
        )
        if len(query.records):
            with BlockSignals(self.nameWidgets):
                for cbName, playerId in zip(self.nameWidgets,
                                            query.records[0]):
                    try:
                        playerName = Players.humanNames[playerId]
                        playerIdx = cbName.findText(playerName)
                        if playerIdx >= 0:
                            cbName.setCurrentIndex(playerIdx)
                    except KeyError:
                        logError(
                            'database is inconsistent: player with id %d is in game but not in player'
                            % playerId)
        self.slotValidate()
Exemple #3
0
class LoginDlg(QDialog):
    """login dialog for server"""
    def __init__(self):
        """self.servers is a list of tuples containing server and last playername"""
        QDialog.__init__(self, None)
        decorateWindow(self, i18nc('kajongg', 'Login'))
        self.setupUi()

        localName = i18nc('kajongg name for local game server',
                          Query.localServerName)
        self.servers = Query(
            'select url,lastname from server order by lasttime desc').records
        servers = list(x[0] for x in self.servers
                       if x[0] != Query.localServerName)
        # the first server combobox item should be default: either the last used server
        # or localName for autoPlay
        if localName not in servers:
            servers.append(localName)
        if 'kajongg.org' not in servers:
            servers.append('kajongg.org')
        if Internal.autoPlay:
            demoHost = Options.host or localName
            if demoHost in servers:
                servers.remove(
                    demoHost
                )  # we want a unique list, it will be re-used for all following games
            servers.insert(0, demoHost)
            # in this process but they will not be autoPlay
        self.cbServer.addItems(servers)
        self.passwords = Query(
            'select url, p.name, passwords.password from passwords, player p '
            'where passwords.player=p.id').records
        Players.load()
        self.cbServer.editTextChanged.connect(self.serverChanged)
        self.cbUser.editTextChanged.connect(self.userChanged)
        self.serverChanged()
        StateSaver(self)

    def returns(self, dummyButton=None):
        """login data returned by this dialog"""
        return (Url(self.url), self.username, self.password,
                self.__defineRuleset())

    def setupUi(self):
        """create all Ui elements but do not fill them"""
        self.buttonBox = KDialogButtonBox(self)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)
        # Ubuntu 11.10 unity is a bit strange - without this, it sets focus on
        # the cancel button (which it shows on the left). I found no obvious
        # way to use setDefault and setAutoDefault for fixing this.
        self.buttonBox.button(QDialogButtonBox.Ok).setFocus(True)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        vbox = QVBoxLayout(self)
        self.grid = QFormLayout()
        self.cbServer = QComboBox()
        self.cbServer.setEditable(True)
        self.grid.addRow(i18n('Game server:'), self.cbServer)
        self.cbUser = QComboBox()
        self.cbUser.setEditable(True)
        self.grid.addRow(i18n('Username:'******'Password:'******'kajongg', 'Ruleset:'), self.cbRuleset)
        vbox.addLayout(self.grid)
        vbox.addWidget(self.buttonBox)
        pol = QSizePolicy()
        pol.setHorizontalPolicy(QSizePolicy.Expanding)
        self.cbUser.setSizePolicy(pol)
        self.__port = None

    def serverChanged(self, dummyText=None):
        """the user selected a different server"""
        records = Query(
            'select player.name from player, passwords '
            'where passwords.url=? and passwords.player = player.id',
            (self.url, )).records
        players = list(x[0] for x in records)
        preferPlayer = Options.player
        if preferPlayer:
            if preferPlayer in players:
                players.remove(preferPlayer)
            players.insert(0, preferPlayer)
        self.cbUser.clear()
        self.cbUser.addItems(players)
        if not self.cbUser.count():
            user = KUser() if os.name == 'nt' else KUser(os.geteuid())
            self.cbUser.addItem(user.fullName() or user.loginName())
        if not preferPlayer:
            userNames = [x[1] for x in self.servers if x[0] == self.url]
            if userNames:
                userIdx = self.cbUser.findText(userNames[0])
                if userIdx >= 0:
                    self.cbUser.setCurrentIndex(userIdx)
        showPW = bool(self.url) and not Url(self.url).isLocalHost
        self.grid.labelForField(self.edPassword).setVisible(showPW)
        self.edPassword.setVisible(showPW)
        self.grid.labelForField(self.cbRuleset).setVisible(
            not showPW and not Options.ruleset)
        self.cbRuleset.setVisible(not showPW and not Options.ruleset)
        if not showPW:
            self.cbRuleset.clear()
            if Options.ruleset:
                self.cbRuleset.items = [Options.ruleset]
            else:
                self.cbRuleset.items = Ruleset.selectableRulesets(self.url)
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(bool(self.url))

    def __defineRuleset(self):
        """find out what ruleset to use"""
        if Options.ruleset:
            return Options.ruleset
        elif Internal.autoPlay or bool(Options.host):
            return Ruleset.selectableRulesets()[0]
        else:
            return self.cbRuleset.current

    def userChanged(self, text):
        """the username has been changed, lookup password"""
        if text == '':
            self.edPassword.clear()
            return
        passw = None
        for entry in self.passwords:
            if entry[0] == self.url and entry[1] == text:
                passw = entry[2]
        if passw:
            self.edPassword.setText(passw)
        else:
            self.edPassword.clear()

    @property
    def url(self):
        """abstracts the url of the dialog"""
        return english(self.cbServer.currentText())

    @property
    def username(self):
        """abstracts the username of the dialog"""
        return self.cbUser.currentText()

    @property
    def password(self):
        """abstracts the password of the dialog"""
        return self.edPassword.text()

    @password.setter
    def password(self, password):
        """abstracts the password of the dialog"""
        self.edPassword.setText(password)
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 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())