Example #1
0
class BaseLogBrowser(KTextBrowser):
    def __init__(self, parent, name='BaseLogBrowser'):
        KTextBrowser.__init__(self, parent, name)
        self.setTextFormat(self.LogText)
        self.timer = QTimer(self)
        self.connect(self.timer, SIGNAL('timeout()'), self.update_logtext)
        self.resume_logging()
        
    def pause_logging(self):
        self.timer.stop()

    def resume_logging(self):
        self.timer.start(500)
        
    def update_logtext(self):
        raise NotImplementedError
Example #2
0
class BaseLogBrowser(KTextBrowser):
    def __init__(self, parent, name='BaseLogBrowser'):
        KTextBrowser.__init__(self, parent, name)
        self.setTextFormat(self.LogText)
        self.timer = QTimer(self)
        self.connect(self.timer, SIGNAL('timeout()'), self.update_logtext)
        self.resume_logging()

    def pause_logging(self):
        self.timer.stop()

    def resume_logging(self):
        self.timer.start(500)

    def update_logtext(self):
        raise NotImplementedError
Example #3
0
class ClientDialog(QDialog):

    """a simple popup dialog for asking the player what he wants to do"""

    def __init__(self, client, parent=None):
        QDialog.__init__(self, parent)
        decorateWindow(self, m18n('Choose'))
        self.setObjectName('ClientDialog')
        self.client = client
        self.layout = QGridLayout(self)
        self.progressBar = QProgressBar()
        self.timer = QTimer()
        if not client.game.autoPlay:
            self.timer.timeout.connect(self.timeout)
        self.deferred = None
        self.buttons = []
        self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint)
        self.setModal(False)
        self.btnHeight = 0
        self.answered = False
        self.move = None
        self.sorry = None

    def keyPressEvent(self, event):
        """ESC selects default answer"""
        if not self.client.game or self.client.game.autoPlay:
            return
        if event.key() in [Qt.Key_Escape, Qt.Key_Space]:
            self.selectButton()
            event.accept()
        else:
            for btn in self.buttons:
                if str(event.text()).upper() == btn.message.shortcut:
                    self.selectButton(btn)
                    event.accept()
                    return
            QDialog.keyPressEvent(self, event)

    def __declareButton(self, message):
        """define a button"""
        maySay = self.client.game.myself.sayable[message]
        if Internal.Preferences.showOnlyPossibleActions and not maySay:
            return
        btn = DlgButton(message, self)
        btn.setAutoDefault(True)
        btn.clicked.connect(self.selectedAnswer)
        self.buttons.append(btn)

    def focusTileChanged(self):
        """update icon and tooltip for the discard button"""
        if not self.client.game:
            return
        for button in self.buttons:
            button.decorate(self.client.game.myself.handBoard.focusTile)
        for uiTile in self.client.game.myself.handBoard.lowerHalfTiles():
            txt = []
            for button in self.buttons:
                _, _, tileTxt = button.message.toolTip(button, uiTile.tile)
                if tileTxt:
                    txt.append(tileTxt)
            uiTile.setToolTip('<br><br>'.join(txt))
        if self.client.game.activePlayer == self.client.game.myself:
            Internal.scene.handSelectorChanged(
                self.client.game.myself.handBoard)

    def checkTiles(self):
        """does the logical state match the displayed tiles?"""
        for player in self.client.game.players:
            player.handBoard.checkTiles()

    def messages(self):
        """a list of all messages returned by the declared buttons"""
        return list(x.message for x in self.buttons)

    def proposeAction(self):
        """either intelligently or first button by default. May also
        focus a proposed tile depending on the action."""
        result = self.buttons[0]
        game = self.client.game
        if game.autoPlay or Internal.Preferences.propose:
            answer, parameter = game.myself.intelligence.selectAnswer(
                self.messages())
            result = [x for x in self.buttons if x.message == answer][0]
            result.setFocus()
            if answer in [Message.Discard, Message.OriginalCall]:
                for uiTile in game.myself.handBoard.uiTiles:
                    if uiTile.tile is parameter:
                        game.myself.handBoard.focusTile = uiTile
        return result

    def askHuman(self, move, answers, deferred):
        """make buttons specified by answers visible. The first answer is default.
        The default button only appears with blue border when this dialog has
        focus but we always want it to be recognizable. Hence setBackgroundRole."""
        self.move = move
        self.deferred = deferred
        for answer in answers:
            self.__declareButton(answer)
        self.focusTileChanged()
        self.show()
        self.checkTiles()
        game = self.client.game
        myTurn = game.activePlayer == game.myself
        prefButton = self.proposeAction()
        if game.autoPlay:
            self.selectButton(prefButton)
            return
        prefButton.setFocus()

        self.progressBar.setVisible(not myTurn)
        if not myTurn:
            msecs = 50
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(
                game.ruleset.claimTimeout * 1000 // msecs)
            self.progressBar.reset()
            self.timer.start(msecs)

    def placeInField(self):
        """place the dialog at bottom or to the right depending on space."""
        mainWindow = Internal.scene.mainWindow
        cwi = mainWindow.centralWidget()
        view = mainWindow.centralView
        geometry = self.geometry()
        if not self.btnHeight:
            self.btnHeight = self.buttons[0].height()
        vertical = view.width() > view.height() * 1.2
        if vertical:
            height = (len(self.buttons) + 1) * self.btnHeight * 1.2
            width = (cwi.width() - cwi.height()) // 2
            geometry.setX(cwi.width() - width)
            geometry.setY(min(cwi.height() // 3, cwi.height() - height))
        else:
            handBoard = self.client.game.myself.handBoard
            if not handBoard:
                # we are in the progress of logging out
                return
            hbLeftTop = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().topLeft()))
            hbRightBottom = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().bottomRight()))
            width = hbRightBottom.x() - hbLeftTop.x()
            height = self.btnHeight
            geometry.setY(cwi.height() - height)
            geometry.setX(hbLeftTop.x())
        for idx, btn in enumerate(self.buttons + [self.progressBar]):
            self.layout.addWidget(
                btn,
                idx +
                1 if vertical else 0,
                idx +
                1 if not vertical else 0)
        idx = len(self.buttons) + 2
        spacer = QSpacerItem(
            20,
            20,
            QSizePolicy.Expanding,
            QSizePolicy.Expanding)
        self.layout.addItem(
            spacer,
            idx if vertical else 0,
            idx if not vertical else 0)

        geometry.setWidth(width)
        geometry.setHeight(height)
        self.setGeometry(geometry)

    def showEvent(self, dummyEvent):
        """try to place the dialog such that it does not cover interesting information"""
        self.placeInField()

    def timeout(self):
        """the progressboard wants an update"""
        pBar = self.progressBar
        if isAlive(pBar):
            pBar.setValue(pBar.value() + 1)
            pBar.setVisible(True)
            if pBar.value() == pBar.maximum():
                # timeout: we always return the original default answer, not
                # the one with focus
                self.selectButton()
                pBar.setVisible(False)

    def selectButton(self, button=None):
        """select default answer. button may also be of type Message."""
        if self.answered:
            # sometimes we get this event twice
            return
        if button is None:
            button = self.focusWidget()
        if isinstance(button, Message):
            assert any(x.message == button for x in self.buttons)
            answer = button
        else:
            answer = button.message
        if not self.client.game.myself.sayable[answer]:
            self.proposeAction().setFocus() # go back to default action
            self.sorry = Sorry(m18n('You cannot say %1', answer.i18nName))
            return
        self.timer.stop()
        self.answered = True
        if self.sorry:
            self.sorry.cancel()
        self.sorry = None
        Internal.scene.clientDialog = None
        self.deferred.callback(answer)

    def selectedAnswer(self, dummyChecked):
        """the user clicked one of the buttons"""
        game = self.client.game
        if game and not game.autoPlay:
            self.selectButton(self.sender())
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 SampleControl(QObject):
    """ Manages a sample's port connections and emits signals when it has changed.
        SIGNALS:
            PYSIGNAL('loaded'), ()
            PYSIGNAL('unloaded'), ()
            PYSIGNAL('volumeChanged'), (vol,)
            PYSIGNAL('start'), ()
            PYSIGNAL('pause'), (pos,)
            PYSIGNAL('unpause'), (pos,)
            PYSIGNAL('cue'), ()
            PYSIGNAL('cueChanged'), (cue,)
            PYSIGNAL('positionChanged'), (pos,)
            PYSIGNAL('reverbChanged'), (rev,)
            PYSIGNAL('delayChanged'), (del,)
            PYSIGNAL('looping'), (bool,)
            PYSIGNAL('pitchChanged'), (pitch,)
            PYSIGNAL('midiPitchChanged'), (pitch, )
            PYSIGNAL('grouped'), (grouped,)
            PYSIGNAL('zoneChanged'), (zone, a0)
            # slotStart called, awaiting forceSlotStart()
            PYSIGNAL('waitingForStart'), ())
    """
    
    def __init__(self):
        """ sample -> splitter |-> volume -> pan -> muter -> mixers[0]
                               |-> volume -> pan -> muter -> mixers[1]
                               |                   ...
                               |-> volume -> pan -> mute from globals import *
        """
        QObject.__init__(self)
        
        self.sample = None
        self.currentCue = 0
        self.cues = []
        self.groupingEnabled = 0
        self.beatSynced = 0
        self.pitchRange = PITCH_RANGE
        self.savedPitchRange = 0
        
        self.driver = pkaudio.Driver()
        # Pitch : when real/temp pitch match, pitch is not bent
        self.pitchTimer = QTimer(self)
        QObject.connect(self.pitchTimer, SIGNAL("timeout()"),
                        self.slotDoPitchBend)
        self.pitchTemp = 0.0 # the bent pitch
        self.realPitch = 0.0 # the original pitch
        self.pitchDirection = "back"
        self.path = None
        
        self.splitter = pkaudio.Splitter()
        
        # Effect chains
        effectChain0 = {}
        effectChain1 = {}
        self.effectChains = [effectChain0, effectChain1]

        # connect eveything up
        for i, chain in enumerate(self.effectChains):

            chain['volume'] = pkaudio.Volume()
            chain['pan'] = pkaudio.Pan()
            chain['peak'] = pkaudio.PeakModule()
            chain['muter'] = pkaudio.Muter()
            chain['muter'].setOn(0)
            
            chain['volume'].outputPort().connect(chain['pan'].inputPort())
            chain['pan'].outputPort().connect(chain['peak'].inputPort())
            chain['peak'].outputPort().connect(chain['muter'].inputPort())
            if i != 1:
                self.splitter.outputPort(i).connect(chain['volume'].inputPort())
            else:
                self.splitter.outputPort(i).connect(chain['pan'].inputPort())
        
        self.effectChains[0]['volume'].setProperty('volume', 75)
        self.slotConnectMixers()
        
    def __del__(self):
        for chain in self.effectChains:
            if chain['muter'].outputPort() != None:
                chain['muter'].outputPort().disconnect()
            self.unload()

        
    def load(self, path):
        """ Load the file and persistent data at 'path'. """
        try:
            self.sample = pkaudio.Sample(path)
        except pkaudio.FileError:
            Globals.Print('Could not open '+path)
            self.sample = None
            return
            
        self.path = path
        conf = Globals.ConfFile()
        conf.readSampleData(self)
        
        # set cue points
        if len(self.cues):
            self.sample.setProperty('start', self.cues[self.currentCue][0])
            self.sample.setProperty('end', self.cues[self.currentCue][1])
        else:
            self.sample.setProperty('start', 0)
            self.sample.setProperty('end', self.sample.length())
            
        # Set looping on samples < 300 secs (5 min)
        if self.sample.length() < 13230000:
            self.slotLooping(1)
        self.sample.outputPort().connect(self.splitter.inputPort())
        self.emit(PYSIGNAL('loaded'), ())
    
    def isLoaded(self):
        """ Returns true if the sample is loaded and can be played. """
        return self.sample and True or False
        
    def unload(self):
        """ Delete the sample, saves persistent data. """
        if self.isLoaded():
            conf = Globals.ConfFile()
            conf.writeSampleData(self)
            conf.save()
            conf = None
            self.sample = None
            self.emit(PYSIGNAL('unloaded'), ())
            self.path = None

    def getShortName(self):
        bn = os.path.basename(self.path)
        return bn[:bn.rfind('.')]
            
    def getPeak(self):
        chain = self.effectChains[0]
        return (chain['peak'].right() + chain['peak'].left()) / 1.5
            
    def sampleId(self):
        """ Return he id of the sample, or -1 if no sampfrom Globals import * """
        if self.sample:
            return self.sample.id()
        else:
            return -1
            
    def isPlaying(self):
        if self.sample:
            return self.sample.isPlaying()
        else:
            return False
        
    def pos(self):
        if self.sample:
            return self.sample.pos()
        else:
            return 0
            
    def getLength(self):
        if self.sample:
            return self.sample.length()
        else:
            return 0

    def getVolume(self, zone=0):
        """ Return the volume for the passed zone. """
        if not zone in self.effectChains:
            zone = 0
        return self.effectChains[zone]['volume'].getProperty('volume')
        
    def setVolume(self, vol, zone=0):
        """ Just set the volume, don't emit the signal. """
        if not zone in self.effectChains:
            zone = 0
        self.effectChains[0]['volume'].setProperty('volume', vol)
        
    def slotVolume(self, a0):
        """ Called by the slider. 0 <= a0 <= 127. """
        a0 = 127 - a0
        volume = int((a0 * 100) / 127)
        self.setVolume(volume)
        self.emit(PYSIGNAL('volumeChanged'), (volume,))

    def setGrouped(self, a0):
        """ Set whether the sample should be consdered part of a group.
            This value can be used for anything.
        """
        self.emit(PYSIGNAL('grouped'), (a0,))
        self.groupingEnabled = a0
        return self.groupingEnabled
        
    def grouped(self):
        return self.groupingEnabled

    def setDeepGroup(self, id):
        """ Tell the C++ code that it manages this sample's grouping. """
        if self.sample:
            self.sample.setGroup(int(id))
        
    def slotStart(self):
        """ Starts playing the sample from the cue point. """
        if self.sample:
            # the delay
            if self.sample.getGroup() > 0:
                self.startDeepSync()
            else:
                self.forceStart()
                                
    def startDeepSync(self):
        """ Start using the more accurate C++ group.   """
        if self.sample and self.sample.getGroup() > 0:
            self.sample.startWithGroup()

    def notifyYouStarted(self):
        """ Get all the widgets rolling a/i the sample started.
            This will usually get called as a callback from startDeepSync().
            """
        self.emitStarted()
    
    def forceStart(self):
        """ Just start now. """
        if self.sample:
            self.slotCue()
            self.sample.play()
            self.emitStarted()
            
    def emitStarted(self):
        """ Just emit the signal w/o starting the stream. """
        if self.sample:
            self.emit(PYSIGNAL('start'), ())
                
    def slotPause(self):
        """ Pauses/UnPauses the sample. """
        if self.sample:
            if self.sample.isPlaying():
                self.sample.stop()
                self.emit(PYSIGNAL('pause'), (self.sample.pos(), ))
            else:
                self.sample.play()
                self.emit(PYSIGNAL('unpause'), (self.sample.pos(), ))
                
    def slotUnpause(self):
        """ Pauses/UnPauses the sample. """
        if self.sample:
            if not self.sample.isPlaying():
                self.sample.play()
                self.emit(PYSIGNAL('unpause'), (self.sample.pos(), ))
        
    def slotCue(self):
        """ Plays or stops the play object """
        if self.sample:
            self.sample.stop()
            self.sample.reset()
            self.emit(PYSIGNAL('cue'), ())
            
    def slotSearch(self, pos):
        """ Set the location of the sample. """
        if self.sample:
            self.sample.pos(pos)
            self.emit(PYSIGNAL('positionChanged'), (pos,))
            
    def slotCuePoint(self, index):
        """ Sets the current cue point. """
        if index < 0:
            index = 0
        elif index >= Globals.n_cues:
            index = Globals.n_cues-1
        if index != self.currentCue and self.sample:
            self.currentCue = index
            self.sample.setProperty('start', self.cues[self.currentCue][0])
            self.sample.setProperty('end', self.cues[self.currentCue][1])
            self.emit(PYSIGNAL('cueChanged'), (self.currentCue,))
            
    def slotReverb(self, v):
        #if self.reverb.numControls():
        #    self.reverb.control(0).tick(v)
        self.emit(PYSIGNAL('reverbChanged'), (v,))
        
    def slotDelay(self, v):
        #if self.delay.numControls():
        #    self.delay.control(1).tick((v/127.0) * 5)
        self.emit(PYSIGNAL('delayChanged'), (v,))
        
    def slotLooping(self, a0):
        """ Set looping to a0. """
        if self.sample:
            self.sample.setLooping(a0)
            self.emit(PYSIGNAL('looping'), (a0,))
        
    def slotPitchUp(self):
        """ Starts bending the pitch up. """
        if self.sample:
            self.pitchDirection = "up"
            self.pitchTemp = self.sample.getPitch()
            self.pitchTimer.stop()
            self.pitchTimer.start(PITCH_RES)

    def slotPitchDown(self):
        """ Starts bending the pitch down. """
        if self.sample:
            self.pitchDirection = "down"
            self.pitchTemp = self.sample.getPitch()
            self.pitchTimer.stop()
            self.pitchTimer.start(PITCH_RES)
            
    def slotPitchStop(self):
        """ Starts bending the pitch back to normal. """
        self.pitchDirection = "back"
            
    def slotDoPitchBend(self):
        """ Does the pitch bend, called by the pitch timer. """
        if self.sample:
            if self.pitchDirection == "up":
                if self.pitchTemp < self.realPitch + PITCH_MAX:
                    self.pitchTemp += PITCH_INC
            
            elif self.pitchDirection == "down":
                if self.pitchTemp > self.realPitch - PITCH_MAX:
                    self.pitchTemp -= PITCH_INC
                
            elif self.pitchDirection == "back":
                if self.pitchTemp == self.realPitch:
                    self.pitchTimer.stop()
                # adjust it
                else:
                    # done
                    if abs(self.pitchTemp - self.realPitch) < PITCH_INC:
                        self.pitchTemp = self.realPitch
                        self.pitchTimer.stop()
                    # rolling up
                    elif self.pitchTemp < self.realPitch:
                        self.pitchTemp += PITCH_INC
                    #rolling down
                    elif self.pitchTemp > self.realPitch:
                        self.pitchTemp -= PITCH_INC
            
            self.sample.setPitch(self.pitchTemp)
            self.emit(PYSIGNAL('pitch'), ())        

    def slotNudgeUp(self):
        """ Nudges the pitch setting up. """
        if self.sample:
            if self.pitchTemp != self.realPitch:
                # then the pitch is being bent,
                # just set the real pitch
                self.realPitch += .02
                self.emit(PYSIGNAL('pitchChanged'), (self.realPitch,))
            else:
                self.slotPitch(self.sample.getPitch() + .02)

    def slotNudgeDown(self):
        """ Nudges the pitch setting down. """
        if self.sample:
            if self.pitchTemp != self.realPitch:
                # then the pitch is being bent,
                # just set the real pitch
                self.realPitch -= .02
                self.emit(PYSIGNAL('pitchChanged'), (self.realPitch,))
            else:
                self.slotPitch(self.sample.getPitch() - .02)
        
    def slotPitch(self, pitch):
        """ Pass a floating point value.
            pitch should be in PK::DJEngine range.
        """
        # self.pitchLabel.setText(str(fpformat.fix(pitch, 2)) + "%")
        if self.sample:
            self.sample.setPitch(pitch)
            self.realPitch = self.sample.getPitch()
            self.emit(PYSIGNAL('pitchChanged'), (self.sample.getPitch(),))
        
    def slotMidiPitch(self, a0):
        """ Pass a midi value. """
        self.emit(PYSIGNAL('midiPitchChanged'), (a0,))
        a0 = 127 - a0
        if a0 >= 64:
            a0 -= 1
            pitch = self.pitchRange - (a0 / 63.0) * self.pitchRange
        elif a0 < 63:
            pitch = (self.pitchRange - (1 - ((a0 - 63) / 63.0)) * self.pitchRange) * -1
        else:
            pitch = 0.0
            
        self.slotPitch(pitch)
        
    def slotSetZone(self, zone, a0):
        """ Toggles the mute on a mixer. """
        if zone < len(self.effectChains):
            self.effectChains[zone]['muter'].setOn(a0)
            self.emit(PYSIGNAL('zoneChanged'), (zone, a0,))
        else:
            Globals.PrintErr('SampleControl.setZone: no such zone: '+str(zone))

    def getZone(self, zone):
        if zone < len(self.effectChains):
            return self.effectChains[zone]['muter'].isOn()
        else:
            Globals.PrintErr('SampleControl.setZone: no such zone: '+str(zone))
        
    def getPitch(self):
        """ Return a floating point value. """
        if self.sample:
            return self.sample.getPitch()
        else:
            return 0.0

    def looping(self):
        if self.sample and self.sample.getLooping():
            return True
        else:
            return False
            
    def atEnd(self):
        if self.sample:
            return self.sample.atEnd()
        else:
            return True
            
    def setPitchRange(self, r):
        """ set the pitch range of the sample. """
        self.savedPitchRange = 1
        self.pitchRange = r
        
    def slotConnectMixers(self):
        """ Connect the effect chains to the mixers. 
            Initialize mainMixer, cueMixer if necessary.
        """
        global mainMixer, cueMixer
        for i, chain in enumerate(self.effectChains):
            if self.driver.numMixers() > i:
                if i == 0:
                    if mainMixer == None:
                        mainMixer = self.driver.getMixer(0)
                    mainMixer.connect(self.effectChains[0]['muter'].outputPort())
                elif i == 1:
                    if cueMixer == None:
                        cueMixer = self.driver.getMixer(1)
                    cueMixer.connect(self.effectChains[1]['muter'].outputPort())
                else:
                    self.driver.getMixer(i).connect(self.effectChains[i]['muter'].outputPort())
Example #6
0
class ClientDialog(QDialog):

    """a simple popup dialog for asking the player what he wants to do"""

    def __init__(self, client, parent=None):
        QDialog.__init__(self, parent)
        decorateWindow(self, i18n('Choose'))
        self.setObjectName('ClientDialog')
        self.client = client
        self.layout = QGridLayout(self)
        self.progressBar = QProgressBar()
        self.progressBar.setMinimumHeight(25)
        self.timer = QTimer()
        if not client.game.autoPlay:
            self.timer.timeout.connect(self.timeout)
        self.deferred = None
        self.buttons = []
        self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint)
        self.setModal(False)
        self.btnHeight = 0
        self.answered = False
        self.move = None
        self.sorry = None

    def keyPressEvent(self, event):
        """ESC selects default answer"""
        if not self.client.game or self.client.game.autoPlay:
            return
        if event.key() in [Qt.Key_Escape, Qt.Key_Space]:
            self.selectButton()
            event.accept()
        else:
            for btn in self.buttons:
                if str(event.text()).upper() == btn.message.shortcut:
                    self.selectButton(btn)
                    event.accept()
                    return
            QDialog.keyPressEvent(self, event)

    def __declareButton(self, message):
        """define a button"""
        maySay = self.client.game.myself.sayable[message]
        if Internal.Preferences.showOnlyPossibleActions and not maySay:
            return
        btn = DlgButton(message, self)
        btn.setAutoDefault(True)
        btn.clicked.connect(self.selectedAnswer)
        self.buttons.append(btn)

    def focusTileChanged(self):
        """update icon and tooltip for the discard button"""
        if not self.client.game:
            return
        for button in self.buttons:
            button.setMeaning(self.client.game.myself.handBoard.focusTile)
        for uiTile in self.client.game.myself.handBoard.lowerHalfTiles():
            txt = []
            for button in self.buttons:
                _, _, tileTxt = button.message.toolTip(button, uiTile.tile)
                if tileTxt:
                    txt.append(tileTxt)
            uiTile.setToolTip('<br><br>'.join(txt))
        if self.client.game.activePlayer == self.client.game.myself:
            Internal.scene.handSelectorChanged(
                self.client.game.myself.handBoard)

    def checkTiles(self):
        """does the logical state match the displayed tiles?"""
        for player in self.client.game.players:
            player.handBoard.checkTiles()

    def messages(self):
        """a list of all messages returned by the declared buttons"""
        return list(x.message for x in self.buttons)

    def proposeAction(self):
        """either intelligently or first button by default. May also
        focus a proposed tile depending on the action."""
        result = self.buttons[0]
        game = self.client.game
        if game.autoPlay or Internal.Preferences.propose:
            answer, parameter = game.myself.intelligence.selectAnswer(
                self.messages())
            result = [x for x in self.buttons if x.message == answer][0]
            result.setFocus()
            if answer in [Message.Discard, Message.OriginalCall]:
                for uiTile in game.myself.handBoard.uiTiles:
                    if uiTile.tile is parameter:
                        game.myself.handBoard.focusTile = uiTile
        return result

    def askHuman(self, move, answers, deferred):
        """make buttons specified by answers visible. The first answer is default.
        The default button only appears with blue border when this dialog has
        focus but we always want it to be recognizable. Hence setBackgroundRole."""
        self.move = move
        self.deferred = deferred
        for answer in answers:
            self.__declareButton(answer)
        self.focusTileChanged()
        self.show()
        self.checkTiles()
        game = self.client.game
        myTurn = game.activePlayer == game.myself
        prefButton = self.proposeAction()
        if game.autoPlay:
            self.selectButton(prefButton)
            return
        prefButton.setFocus()

        self.progressBar.setVisible(not myTurn)
        if not myTurn:
            msecs = 50
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(
                game.ruleset.claimTimeout * 1000 // msecs)
            self.progressBar.reset()
            self.timer.start(msecs)

    def placeInField(self):
        """place the dialog at bottom or to the right depending on space."""
        mainWindow = Internal.scene.mainWindow
        cwi = mainWindow.centralWidget()
        view = mainWindow.centralView
        geometry = self.geometry()
        if not self.btnHeight:
            self.btnHeight = self.buttons[0].height()
        vertical = view.width() > view.height() * 1.2
        if vertical:
            height = (len(self.buttons) + 1) * self.btnHeight * 1.2
            width = (cwi.width() - cwi.height()) // 2
            geometry.setX(cwi.width() - width)
            geometry.setY(min(cwi.height() // 3, cwi.height() - height))
        else:
            handBoard = self.client.game.myself.handBoard
            if not handBoard:
                # we are in the progress of logging out
                return
            hbLeftTop = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().topLeft()))
            hbRightBottom = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().bottomRight()))
            width = hbRightBottom.x() - hbLeftTop.x()
            height = self.btnHeight
            geometry.setY(cwi.height() - height)
            geometry.setX(hbLeftTop.x())
        for idx, btn in enumerate(self.buttons + [self.progressBar]):
            self.layout.addWidget(
                btn,
                idx +
                1 if vertical else 0,
                idx +
                1 if not vertical else 0)
        idx = len(self.buttons) + 2
        spacer = QSpacerItem(
            20,
            20,
            QSizePolicy.Expanding,
            QSizePolicy.Expanding)
        self.layout.addItem(
            spacer,
            idx if vertical else 0,
            idx if not vertical else 0)

        geometry.setWidth(width)
        geometry.setHeight(height)
        self.setGeometry(geometry)

    def showEvent(self, dummyEvent):
        """try to place the dialog such that it does not cover interesting information"""
        self.placeInField()

    def timeout(self):
        """the progressboard wants an update"""
        pBar = self.progressBar
        if isAlive(pBar):
            pBar.setValue(pBar.value() + 1)
            pBar.setVisible(True)
            if pBar.value() == pBar.maximum():
                # timeout: we always return the original default answer, not
                # the one with focus
                self.selectButton()
                pBar.setVisible(False)

    def selectButton(self, button=None):
        """select default answer. button may also be of type Message."""
        if self.answered:
            # sometimes we get this event twice
            return
        if button is None:
            button = self.focusWidget()
        if isinstance(button, Message):
            assert any(x.message == button for x in self.buttons)
            answer = button
        else:
            answer = button.message
        if not self.client.game.myself.sayable[answer]:
            self.proposeAction().setFocus() # go back to default action
            self.sorry = Sorry(i18n('You cannot say %1', answer.i18nName))
            return
        self.timer.stop()
        self.answered = True
        if self.sorry:
            self.sorry.cancel()
        self.sorry = None
        Internal.scene.clientDialog = None
        self.deferred.callback(answer)

    def selectedAnswer(self, dummyChecked):
        """the user clicked one of the buttons"""
        game = self.client.game
        if game and not game.autoPlay:
            self.selectButton(self.sender())
Example #7
0
class QTReactor(posixbase.PosixReactorBase):
    """Qt based reactor."""

    # Reference to a DelayedCall for self.crash() when the reactor is
    # entered through .iterate()
    _crashCall = None

    _timer = None

    def __init__(self, app=None):
        self.running = 0
        posixbase.PosixReactorBase.__init__(self)
        if app is None:
            app = QApplication([])
        self.qApp = app
        self.addSystemEventTrigger('after', 'shutdown', self.cleanup)

    def addReader(self, reader):
        if not hasReader(reader):
            reads[reader] = TwistedSocketNotifier(self, reader,
                                                  QSocketNotifier.Read)

    def addWriter(self, writer):
        if not hasWriter(writer):
            writes[writer] = TwistedSocketNotifier(self, writer,
                                                   QSocketNotifier.Write)

    def removeReader(self, reader):
        if hasReader(reader):
            reads[reader].shutdown()
            del reads[reader]

    def removeWriter(self, writer):
        if hasWriter(writer):
            writes[writer].shutdown()
            del writes[writer]

    def removeAll(self):
        return self._removeAll(reads, writes)

    def simulate(self):
        if self._timer is not None:
            self._timer.stop()
            self._timer = None

        if not self.running:
            self.running = 1
            self.qApp.exit_loop()
            return
        self.runUntilCurrent()

        if self._crashCall is not None:
            self._crashCall.reset(0)

        # gah
        timeout = self.timeout()
        if timeout is None:
            timeout = 1.0
        timeout = min(timeout, 0.1) * 1010

        if self._timer is None:
            self._timer = QTimer()
            QObject.connect(self._timer, SIGNAL("timeout()"), self.simulate)
        self._timer.start(timeout, 1)

    def cleanup(self):
        if self._timer is not None:
            self._timer.stop()
            self._timer = None

    def iterate(self, delay=0.0):
        log.msg(channel='system', event='iteration', reactor=self)
        self._crashCall = self.callLater(delay, self.crash)
        self.run()

    def run(self, installSignalHandlers=1):
        self.running = 1
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.simulate()
        self.qApp.enter_loop()

    def crash(self):
        if self._crashCall is not None:
            if self._crashCall.active():
                self._crashCall.cancel()
            self._crashCall = None
        self.running = 0
Example #8
0
class QtReactor(posixbase.PosixReactorBase):
    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
        Private method for adding a descriptor from the event loop.

        It takes care of adding it if  new or modifying it if already added
        for another state (read -> read/write for example).
        """
        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """
        Add a FileDescriptor for notification of data available to read.
        """
        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """
        Add a FileDescriptor for notification of data available to write.
        """
        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
        Private method for removing a descriptor from the event loop.

        It does the inverse job of _add, and also add a check in case of the fd
        has gone away.
        """
        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """
        Remove a Selectable for notification of data available to read.
        """
        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """
        Remove a Selectable for notification of data available to write.
        """
        self._remove(writer, self._writes)

    def removeAll(self):
        """
        Remove all selectables, and return a list of them.
        """
        rv = self._removeAll(self._reads, self._writes)
        return rv

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """See twisted.internet.interfaces.IReactorCore.iterate.
        """
        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        'This method is called by a Qt timer or by network activity on a file descriptor'

        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        delay = max(delay or 0, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
class KSmartTray(QObject):

    class State:
         Waiting = 'StateWaiting'
         Updating = 'StateUpdating'
         Checking = 'StateChecking'
         Upgrading = 'StateUpgrading'
         RunningSmart = 'StateRunningSmart'

    def __init__(self):
        QObject.__init__(self)
        self.sysTray = KMySystemTray()
        self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))
        self.sysTray.show()
    
        self.process = KProcIO()
    
        self.state = KSmartTray.State.Waiting
        self.lastKnownStatus = ""
    
        self.blinkFlag = False
        self.updateFailed = False
    
        self.checkTimer = QTimer()
        self.blinkTimer = QTimer()

        QObject.connect(self.checkTimer, SIGNAL("timeout()"), self.checkUpgrades)
        QObject.connect(self.process, SIGNAL("processExited(KProcess *)"),
                self.processDone)
    
        QObject.connect(self, PYSIGNAL("foundNewUpgrades()"), self.startBlinking)
        QObject.connect(self, PYSIGNAL("foundNoUpgrades()"), self.stopBlinking)
        QObject.connect(self.sysTray, PYSIGNAL("mouseEntered()"), self.stopBlinking)
        QObject.connect(self.blinkTimer, SIGNAL("timeout()"), self.toggleBlink)
    
        QObject.connect(self.sysTray.checkAction, SIGNAL("activated()"),
                self.manualCheckUpgrades)
        QObject.connect(self.sysTray.startSmartAction, SIGNAL("activated()"),
                self.startSmart)
        QObject.connect(self.sysTray.stopAction, SIGNAL("activated()"),
                self.stopChecking)
        QObject.connect(self.sysTray, SIGNAL("quitSelected()"),
                KApplication.kApplication(), SLOT("quit()"))
    
        QObject.connect(self.sysTray, PYSIGNAL("activated()"), self.runUpgrades)
    
        self.checkTimer.start(5*60*1000)
    
        self.checkUpgrades()
    
    def internalCheckUpgrades(self, manual):
        if not manual and self.blinkTimer.isActive():
            return
        if self.state == KSmartTray.State.Waiting:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(True)
            self.process.resetAll()
            if manual:
                self.process.setArguments(["smart-update"])
            else:
                self.process.setArguments(["smart-update", "--after", "60"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                     "Couldn't run 'smart-update'.")
            else:
                QToolTip.add(self.sysTray, "Updating channels...")
                self.state = KSmartTray.State.Updating
    
    def checkUpgrades(self):
        self.internalCheckUpgrades(False)
    
    def manualCheckUpgrades(self):
        self.internalCheckUpgrades(True)
    
    def runUpgrades(self):
        if self.state != KSmartTray.State.Waiting:
            KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                 "There is a running process.")
        else:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(False)
            self.process.resetAll()
            self.process.setArguments(["kdesu", "-d", "-c", "smart --gui upgrade"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                     "Couldn't run 'smart upgrade'.")
            else:
                self.state = KSmartTray.State.Upgrading
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, "Running Smart Package Manager...")
    
    def startSmart(self):
        if self.state != KSmartTray.State.Waiting:
            KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                 "There is a running process.")
        else:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(False)
            self.process.resetAll()
            self.process.setArguments(["kdesu", "-d", "-c", "smart --gui"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                     "Couldn't run 'smart'.")
            else:
                self.state = KSmartTray.State.RunningSmart
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, "Running Smart Package Manager...")
    
    def stopChecking(self):
        self.process.kill()
    
    def processDone(self, process):
        if self.state == KSmartTray.State.Updating:
            if not process.normalExit() or process.exitStatus() != 0:
                self.updateFailed = True
            if self.updateFailed and not self.lastKnownStatus == "":
                self.state = KSmartTray.State.Waiting
            else:
                process.resetAll()
                process.setArguments(["smart", "upgrade", "--check-update"])
                if not process.start():
                    KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                         "Couldn't run 'smart upgrade'.")
                    self.state = KSmartTray.State.Waiting
                    self.lastKnownStatus = ""
                else:
                    QToolTip.remove(self.sysTray)
                    QToolTip.add(self.sysTray,
                                  "Verifying upgradable packages...")
                    self.state = KSmartTray.State.Checking
        elif self.state == KSmartTray.State.Checking:
            self.state = KSmartTray.State.Waiting
            if process.normalExit():
                if process.exitStatus() == 0:
                    self.lastKnownStatus = "There are new upgrades available!"
                    KNotifyClient.event(self.sysTray.winId(), "found-new-upgrades",
                                         self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundNewUpgrades()"), ())
                elif process.exitStatus() == 1:
                    self.lastKnownStatus = "There are pending upgrades!"
                    if not self.updateFailed:
                        KNotifyClient.event(self.sysTray.winId(),
                                             "found-old-upgrades",
                                             self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundOldUpgrades()"), ())
                elif process.exitStatus() == 2:
                    self.lastKnownStatus = "No interesting upgrades available."
                    if not self.updateFailed:
                        KNotifyClient.event(self.sysTray.winId(),
                                             "found-no-upgrades",
                                             self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundNoUpgrades()"), ())
                else:
                    self.lastKnownStatus = ""
        elif self.state == KSmartTray.State.Upgrading:
            self.state = KSmartTray.State.Waiting
            self.lastKnownStatus = ""
        elif self.state ==  KSmartTray.State.RunningSmart:
            self.state = KSmartTray.State.Waiting
            self.lastKnownStatus = ""
        else:
            # Error!
            pass

        if self.state == KSmartTray.State.Waiting:
            self.updateFailed = False
            self.sysTray.checkAction.setEnabled(True)
            self.sysTray.startSmartAction.setEnabled(True)
            self.sysTray.stopAction.setEnabled(False)
            if not self.lastKnownStatus == "":
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, self.lastKnownStatus)
            else:
                QToolTip.remove(self.sysTray)
    
    def startBlinking(self):
        if not self.blinkTimer.isActive():
            self.blinkTimer.start(500)
    
    def stopBlinking(self):
        if self.blinkTimer.isActive():
            self.blinkTimer.stop()
        self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))
    
    def toggleBlink(self):
        if self.blinkFlag:
            self.sysTray.setPixmap(QPixmap())
        else:
            self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))
        self.blinkFlag = not self.blinkFlag
Example #10
0
class QtReactor(posixbase.PosixReactorBase):

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
        Private method for adding a descriptor from the event loop.

        It takes care of adding it if  new or modifying it if already added
        for another state (read -> read/write for example).
        """
        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """
        Add a FileDescriptor for notification of data available to read.
        """
        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """
        Add a FileDescriptor for notification of data available to write.
        """
        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
        Private method for removing a descriptor from the event loop.

        It does the inverse job of _add, and also add a check in case of the fd
        has gone away.
        """
        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """
        Remove a Selectable for notification of data available to read.
        """
        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """
        Remove a Selectable for notification of data available to write.
        """
        self._remove(writer, self._writes)

    def removeAll(self):
        """
        Remove all selectables, and return a list of them.
        """
        rv = self._removeAll(self._reads, self._writes)
        return rv

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """See twisted.internet.interfaces.IReactorCore.iterate.
        """
        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        'This method is called by a Qt timer or by network activity on a file descriptor'

        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        delay = max(delay or 0, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
Example #11
0
class MainWin(KMainWindow):

    """Main window"""

    SB_TEXT = 1
    SB_TIMEOUT = 10000
    MON_TIMEOUT = 1000
    CHANGE_TIMEOUT = 3001

    def __init__(self, *args):
        apply(KMainWindow.__init__, (self,) + args)
        
        self.lastDir = "/var/log"
        self.monitors = []
        self.currentPage = None
        self.tab = QTabWidget(self)
        self.settingsDlg = SettingsDlg(self)
        self.cfg = LoviConfig().getInstance()
        self.bellIcon = \
            QIconSet(KIconLoader().loadIcon("idea", KIcon.Small, 11))
        self.noIcon = QIconSet()
        self.findDlg = KEdFind(self, "find", False)
        self.connect(self.findDlg, SIGNAL("search()"), self.doFind)
        
        self.setCentralWidget(self.tab)
        self.connect(self.tab, SIGNAL("currentChanged(QWidget *)"), 
            self.onPageChange)
        self.setGeometry(0, 0, 600, 400)
        self.setCaption(makeCaption("(none)"))

        # Timers
        self.timer = QTimer(self)
        self.timer.start(MainWin.MON_TIMEOUT)
        self.statusTimer = QTimer(self)
        self.connect(self.statusTimer, SIGNAL("timeout()"), 
            self.onStatusTimeout)
        self.changeTimer = QTimer(self)
        self.changeTimer.start(MainWin.CHANGE_TIMEOUT)
        self.connect(self.changeTimer, SIGNAL("timeout()"),
            self.onChangeTimeout)

        # Initialize actions
        actions = self.actionCollection()
        self.openAction = KStdAction.open(self.onOpen, actions)
        self.closeAction = KStdAction.close(self.onClose, actions)
        self.closeAction.setEnabled(False)
        self.quitAction = KStdAction.quit(self.onQuit, actions)
        self.copyAction = KStdAction.copy(self.onCopy, actions)
        self.copyAction.setEnabled(False)
        self.clearAction = KStdAction.clear(self.onClear, actions)
        self.clearAction.setEnabled(False)
        self.selectAllAction = KStdAction.selectAll(self.onSelectAll, actions)
        self.selectAllAction.setEnabled(False)
        self.addBookmarkAction = \
            KStdAction.addBookmark(self.onAddBookmark, actions)
        self.addBookmarkAction.setEnabled(False)
        self.settingsAction = KStdAction.preferences(self.onSettings, actions)
        self.findAction = KStdAction.find(self.onFind, actions)
        self.findAction.setEnabled(False)
        self.findNextAction = KStdAction.findNext(self.onFindNext, actions)
        self.findNextAction.setEnabled(False)
        self.findPrevAction = KStdAction.findPrev(self.onFindPrev, actions)
        self.findPrevAction.setEnabled(False)
        
        # Initialize menus
        
        fileMenu = QPopupMenu(self)
        self.openAction.plug(fileMenu)
        self.closeAction.plug(fileMenu)
        fileMenu.insertSeparator()
        self.quitAction.plug(fileMenu)
        self.menuBar().insertItem(i18n("&File"), fileMenu)
        
        editMenu = QPopupMenu(self)
        self.copyAction.plug(editMenu)
        self.clearAction.plug(editMenu)
        editMenu.insertSeparator()
        self.selectAllAction.plug(editMenu)
        self.addBookmarkAction.plug(editMenu)
        editMenu.insertSeparator()
        self.findAction.plug(editMenu)
        self.findNextAction.plug(editMenu)
        self.findPrevAction.plug(editMenu)
        self.menuBar().insertItem(i18n("&Edit"), editMenu)
        
        settingsMenu = QPopupMenu(self)
        self.settingsAction.plug(settingsMenu)
        self.menuBar().insertItem(i18n("&Settings"), settingsMenu)
        
        helpMenu = self.helpMenu("")
        self.menuBar().insertItem(i18n("&Help"), helpMenu)
        
        # Initialize status bar
        self.sb = self.statusBar()
        self.bell = BellButton(None)
        self.displayStatus(False, "")
        
    def displayStatus(self, changed, msg):
        """Display a message in the status bar."""
        self.statusTimer.stop()
        self.sb.removeWidget(self.bell)
        self.sb.removeItem(MainWin.SB_TEXT)
        if changed:
            self.sb.addWidget(self.bell, 1, False)
        self.sb.insertItem(msg, MainWin.SB_TEXT, 1000, True)
        self.sb.setItemAlignment(MainWin.SB_TEXT, 
                                 Qt.AlignLeft|Qt.AlignVCenter)
        self.statusTimer.start(MainWin.SB_TIMEOUT, True)
       
    def onOpen(self, id = -1):
        """Open file for monitoring."""
        fileName = KFileDialog.getOpenFileName(self.lastDir, "*", self, 
            str(i18n("Open Log File")))
        if not fileName.isEmpty():
            fileName = str(fileName)
            self.lastDir = os.path.dirname(fileName)
            self.monitor(fileName)
    
    def onClose(self, id = -1):
        """Close a monitored file."""
        self.monitors.remove(self.currentPage)
        self.currentPage.close()
        self.tab.removePage(self.currentPage)
        self.displayStatus(False, "")
        self.saveFileList()
        if len(self.monitors) == 0:
            # Update interface when the last page is deleted
            self.setCaption(makeCaption("(none)"))
            self.closeAction.setEnabled(False)
            self.copyAction.setEnabled(False)
            self.selectAllAction.setEnabled(False)
            self.clearAction.setEnabled(False)
            self.addBookmarkAction.setEnabled(False)
            self.findAction.setEnabled(False)
            self.findNextAction.setEnabled(False)
            self.findPrevAction.setEnabled(False)

    def onQuit(self, id = -1):
        """Quit application."""
        self.close()
        
    def onCopy(self, id = -1):
        """Copy text to clipboard."""
        self.currentPage.copy()
        
    def onClear(self, id = -1):
        """Clear text window."""
        self.currentPage.setText("")
        
    def onSelectAll(self, id = -1):
        """Select all text."""
        self.currentPage.selectAll(True)
        
    def onAddBookmark(self, id = -1):
        """Add a bookmark to the log."""
        bookmark = "<font color=\"blue\">"
        bookmark += datetime.datetime.now().strftime("%b %d %H:%M:%S ")
        bookmark += "--------------------------------------------------------"
        bookmark += "</font>"
        self.currentPage.append(bookmark)
    
    def onSettings(self, id = -1):
        """Display settings dialog"""
        if self.settingsDlg.exec_loop():
            self.cfg.writeConfig()
            self.cfg.processConfig()
            self.reconfigure()
        
    def onPageChange(self, page):
        """Update widget when the top level tab changes."""
        self.currentPage = page
        self.setCaption(makeCaption(os.path.basename(page.getFileName())))
        self.copyAction.setEnabled(page.hasSelectedText())
        # self.tab.setTabIconSet(page, self.noIcon)
                        
    def onStatusTimeout(self):
        """Clear status bar on timeout."""
        self.displayStatus(False, "")
        for m in self.monitors:
            self.tab.setTabIconSet(m, self.noIcon)
        
    def onChangeTimeout(self):
        """Look for changes in monitored files. """
        changeList = []
        for m in self.monitors:
            if m.isChanged():
                changeList.append(os.path.basename(m.getFileName()))
                self.tab.setTabIconSet(m, self.bellIcon)
        if len(changeList):
            msg = changeList[0]
            for f in changeList[1:]:
                msg += ", %s" % f
            msg = str(i18n("Change to %s")) % msg
            self.displayStatus(True, msg)
            
    def onCopyAvailable(self, available):
        """Update Copy menu item when there is a selection available."""
        self.copyAction.setEnabled(available)
        
    def onFind(self):
        self.findDlg.show()
    
    def onFindPrev(self):
        if self.findDlg.getText() == "":
            self.onFind()
        else:
            self.currentPage.find(self.findDlg.getText(), 
                self.findDlg.case_sensitive(), True)
    
    def onFindNext(self):
        if self.findDlg.getText() == "":
            self.onFind()
        else:
            self.currentPage.find(self.findDlg.getText(), 
                self.findDlg.case_sensitive(), False)

    def monitor(self, fileName):
        """Start monitoring a file."""
        try:
            tailer = Tail(fileName)
        except:
            KMessageBox.error(self, 
                str(i18n("Cannot open file for monitoring:\n%s")) % 
                    fileName, makeCaption("Error"))
            return
        mon = Monitor(self.tab, tailer)
        base = os.path.basename(fileName)
        self.monitors.append(mon)
        self.tab.addTab(mon, base)
        self.tab.showPage(mon)
        self.tab.setTabToolTip(mon, fileName)
        self.currentPage = mon
        self.setCaption(makeCaption(base))
        self.displayStatus(False, str(i18n("Monitoring %s")) % fileName)
        self.connect(self.timer, SIGNAL("timeout()"), mon.follow)
        self.saveFileList()
        self.connect(mon, SIGNAL("copyAvailable(bool)"), self.onCopyAvailable)
        self.closeAction.setEnabled(True)
        self.copyAction.setEnabled(False)
        self.clearAction.setEnabled(True)
        self.selectAllAction.setEnabled(True)
        self.addBookmarkAction.setEnabled(True)
        self.findAction.setEnabled(True)
        self.findNextAction.setEnabled(True)
        self.findPrevAction.setEnabled(True)
        
    def saveFileList(self):
        """Update the list of monitored files in the configuration file."""
        files = []
        for mon in self.monitors:
            files.append(mon.getFileName())
        cfg = KApplication.kApplication().config()
        cfg.setGroup("Monitor")
        cfg.writeEntry("files", files)
        
    def reconfigure(self):
        """Update self with configuration changes."""
        for mon in self.monitors:
            mon.reconfigure()
            
    def doFind(self):
        self.currentPage.find(self.findDlg.getText(), 
            self.findDlg.case_sensitive(), self.findDlg.get_direction())
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 qSlicerMultiVolumeExplorerSimplifiedModuleWidget:
    def __init__(self, parent=None):
        logging.debug(
            "qSlicerMultiVolumeExplorerSimplifiedModuleWidget:init() called")
        if not parent or not hasattr(parent, "layout"):
            self.parent = slicer.qMRMLWidget()
            self.parent.setLayout(QVBoxLayout())
        else:
            self.parent = parent

        self.layout = self.parent.layout()

        self._bgMultiVolumeNode = None
        self._fgMultiVolumeNode = None

        self.styleObserverTags = []
        self.sliceWidgetsPerStyle = {}

        self.chartPopupWindow = None
        self.chartPopupSize = QSize(600, 300)
        self.chartPopupPosition = QPoint(0, 0)

    def hide(self):
        self.widget.hide()

    def show(self):
        self.widget.show()

    def setup(self):
        self.widget = QWidget()
        layout = QGridLayout()
        self.widget.setLayout(layout)
        self.layout.addWidget(self.widget)
        self.widget.show()
        self.layout = layout

        self.setupInputFrame()
        self.setupFrameControlFrame()
        self.setupAdditionalFrames()
        self.setupPlottingFrame()

        self.setFramesEnabled(False)

        self.timer = QTimer()
        self.timer.setInterval(50)

        self.setupConnections()

        # initialize slice observers (from DataProbe.py)
        # keep list of pairs: [observee,tag] so they can be removed easily
        self.styleObserverTags = []
        # keep a map of interactor styles to sliceWidgets so we can easily get sliceLogic
        self.sliceWidgetsPerStyle = {}
        self.refreshObservers()

    def setupInputFrame(self, parent=None):
        if not parent:
            parent = self.layout
        self.bgMultiVolumeSelector = slicer.qMRMLNodeComboBox()
        self.bgMultiVolumeSelector.nodeTypes = ['vtkMRMLMultiVolumeNode']
        self.bgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
        self.bgMultiVolumeSelector.addEnabled = 0
        self._bgMultiVolumeSelectorLabel = QLabel('Input multivolume')
        inputFrameWidget = QWidget()
        self.inputFrameLayout = QFormLayout()
        inputFrameWidget.setLayout(self.inputFrameLayout)
        self.inputFrameLayout.addRow(self._bgMultiVolumeSelectorLabel,
                                     self.bgMultiVolumeSelector)
        parent.addWidget(inputFrameWidget)

    def setupFrameControlFrame(self):
        # TODO: initialize the slider based on the contents of the labels array
        self.frameSlider = ctk.ctkSliderWidget()
        self.frameSlider.setSizePolicy(QSizePolicy.Ignored,
                                       QSizePolicy.Preferred)
        self.frameLabel = QLabel('Current frame number')
        self.playButton = QPushButton('Play')
        self.playButton.toolTip = 'Iterate over multivolume frames'
        self.playButton.checkable = True
        frameControlHBox = QHBoxLayout()
        frameControlHBox.addWidget(self.frameLabel)
        frameControlHBox.addWidget(self.frameSlider)
        frameControlHBox.addWidget(self.playButton)
        self.inputFrameLayout.addRow(frameControlHBox)

    def setupAdditionalFrames(self):
        pass

    def setupPlottingFrame(self, parent=None):
        if not parent:
            parent = self.layout
        self.plottingFrameWidget = QWidget()
        self.plottingFrameLayout = QGridLayout()
        self.plottingFrameWidget.setLayout(self.plottingFrameLayout)
        self._multiVolumeIntensityChart = MultiVolumeIntensityChartView()
        self.popupChartButton = QPushButton("Undock chart")
        self.popupChartButton.setCheckable(True)
        self.plottingFrameLayout.addWidget(
            self._multiVolumeIntensityChart.chartView)
        self.plottingFrameLayout.addWidget(self.popupChartButton)
        parent.addWidget(self.plottingFrameWidget)

    def setupConnections(self):
        self.parent.connect('mrmlSceneChanged(vtkMRMLScene*)',
                            self.onVCMRMLSceneChanged)
        self.bgMultiVolumeSelector.connect('currentNodeChanged(vtkMRMLNode*)',
                                           self.onBackgroundInputChanged)
        self.playButton.connect('toggled(bool)', self.onPlayButtonToggled)
        self.frameSlider.connect('valueChanged(double)', self.onSliderChanged)
        self.timer.connect('timeout()', self.goToNext)
        self.popupChartButton.connect('toggled(bool)',
                                      self.onDockChartViewToggled)

    def onDockChartViewToggled(self, checked):
        if checked:
            self.chartPopupWindow = QDialog()
            self.chartPopupWindow.setWindowFlags(
                PythonQt.QtCore.Qt.WindowStaysOnTopHint)
            layout = QGridLayout()
            self.chartPopupWindow.setLayout(layout)
            layout.addWidget(self._multiVolumeIntensityChart.chartView)
            layout.addWidget(self.popupChartButton)
            self.chartPopupWindow.finished.connect(self.dockChartView)
            self.chartPopupWindow.resize(self.chartPopupSize)
            self.chartPopupWindow.move(self.chartPopupPosition)
            self.chartPopupWindow.show()
            self.popupChartButton.setText("Dock chart")
            self._multiVolumeIntensityChart.chartView.show()
        else:
            self.chartPopupWindow.close()

    def dockChartView(self):
        self.chartPopupSize = self.chartPopupWindow.size
        self.chartPopupPosition = self.chartPopupWindow.pos
        self.plottingFrameLayout.addWidget(
            self._multiVolumeIntensityChart.chartView)
        self.plottingFrameLayout.addWidget(self.popupChartButton)
        self.popupChartButton.setText("Undock chart")
        self.popupChartButton.disconnect('toggled(bool)',
                                         self.onDockChartViewToggled)
        self.popupChartButton.checked = False
        self.popupChartButton.connect('toggled(bool)',
                                      self.onDockChartViewToggled)

    def onSliderChanged(self, frameId):
        if self._bgMultiVolumeNode is None:
            return
        newValue = int(frameId)
        self.setCurrentFrameNumber(newValue)

    def onVCMRMLSceneChanged(self, mrmlScene):
        logging.debug(
            "qSlicerMultiVolumeExplorerSimplifiedModuleWidget:onVCMRMLSceneChanged"
        )
        self.bgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
        self.onBackgroundInputChanged()

    def refreshGUIForNewBackgroundImage(self):
        self._multiVolumeIntensityChart.reset()
        self.setFramesEnabled(True)
        if self._fgMultiVolumeNode and self._bgMultiVolumeNode:
            Helper.SetBgFgVolumes(self._bgMultiVolumeNode.GetID(),
                                  self._fgMultiVolumeNode.GetID())
        else:
            Helper.SetBgVolume(self._bgMultiVolumeNode.GetID())
        self.refreshFrameSlider()
        self._multiVolumeIntensityChart.bgMultiVolumeNode = self._bgMultiVolumeNode
        self.refreshObservers()

    def getBackgroundMultiVolumeNode(self):
        return self.bgMultiVolumeSelector.currentNode()

    def onBackgroundInputChanged(self):
        self._bgMultiVolumeNode = self.getBackgroundMultiVolumeNode()

        if self._bgMultiVolumeNode is not None:
            self.refreshGUIForNewBackgroundImage()
        else:
            self.setFramesEnabled(False)

    def onPlayButtonToggled(self, checked):
        if self._bgMultiVolumeNode is None:
            return
        if checked:
            self.timer.start()
            self.playButton.text = 'Stop'
        else:
            self.timer.stop()
            self.playButton.text = 'Play'

    def processEvent(self, observee, event):
        # logging.debug("processing event %s" % event)
        if self._bgMultiVolumeNode is None:
            return

        # TODO: use a timer to delay calculation and compress events
        if event == 'LeaveEvent':
            # reset all the readouts
            # TODO: reset the label text
            return

        if not self.sliceWidgetsPerStyle.has_key(observee):
            return

        interactor = observee.GetInteractor()
        self.createChart(self.sliceWidgetsPerStyle[observee],
                         interactor.GetEventPosition())

    def createChart(self, sliceWidget, position):
        self._multiVolumeIntensityChart.createChart(sliceWidget, position)

    def setCurrentFrameNumber(self, frameNumber):
        mvDisplayNode = self._bgMultiVolumeNode.GetDisplayNode()
        mvDisplayNode.SetFrameComponent(frameNumber)

    def setFramesEnabled(self, enabled):
        pass

    def refreshObservers(self):
        """ When the layout changes, drop the observers from
    all the old widgets and create new observers for the
    newly created widgets"""
        self.removeObservers()
        # get new slice nodes
        layoutManager = slicer.app.layoutManager()
        sliceNodeCount = slicer.mrmlScene.GetNumberOfNodesByClass(
            'vtkMRMLSliceNode')
        for nodeIndex in xrange(sliceNodeCount):
            # find the widget for each node in scene
            sliceNode = slicer.mrmlScene.GetNthNodeByClass(
                nodeIndex, 'vtkMRMLSliceNode')
            sliceWidget = layoutManager.sliceWidget(sliceNode.GetLayoutName())
            if sliceWidget:
                # add observers and keep track of tags
                style = sliceWidget.sliceView().interactorStyle()
                self.sliceWidgetsPerStyle[style] = sliceWidget
                events = ("MouseMoveEvent", "EnterEvent", "LeaveEvent")
                for event in events:
                    tag = style.AddObserver(event, self.processEvent)
                    self.styleObserverTags.append([style, tag])

    def removeObservers(self):
        for observee, tag in self.styleObserverTags:
            observee.RemoveObserver(tag)
        self.styleObserverTags = []
        self.sliceWidgetsPerStyle = {}

    def refreshFrameSlider(self):
        self.frameSlider.minimum = 0
        if not self._bgMultiVolumeNode:
            self.frameSlider.maximum = 0
            return
        nFrames = self._bgMultiVolumeNode.GetNumberOfFrames()
        self.frameSlider.maximum = nFrames - 1

    def goToNext(self):
        currentElement = self.frameSlider.value
        currentElement += 1
        if currentElement > self.frameSlider.maximum:
            currentElement = 0
        self.frameSlider.value = currentElement
Example #14
0
class QTReactor(posixbase.PosixReactorBase):
    """Qt based reactor."""
    _crashCall = None
    _timer = None
    def __init__(self, app=None):
        self.running = 0
        posixbase.PosixReactorBase.__init__(self)
        if app is None:
            app = QApplication([])
        self.qApp = app
        self.addSystemEventTrigger('after', 'shutdown', self.cleanup)
    def addReader(self, reader):
        if not hasReader(reader):
            reads[reader] = TwistedSocketNotifier(self, reader, QSocketNotifier.Read)
    def addWriter(self, writer):
        if not hasWriter(writer):
            writes[writer] = TwistedSocketNotifier(self, writer, QSocketNotifier.Write)
    def removeReader(self, reader):
        if hasReader(reader):
            reads[reader].shutdown()
            del reads[reader]
    def removeWriter(self, writer):
        if hasWriter(writer):
            writes[writer].shutdown()
            del writes[writer]
    def removeAll(self):
        return self._removeAll(reads, writes)
    def simulate(self):
        if self._timer is not None:
            self._timer.stop()
            self._timer = None
        if not self.running:
            self.running = 1
            self.qApp.exit_loop()
            return
        self.runUntilCurrent()
        if self._crashCall is not None:
            self._crashCall.reset(0)
        timeout = self.timeout()
        if timeout is None:
            timeout = 1.0
        timeout = min(timeout, 0.1) * 1010
        if self._timer is None:
            self._timer = QTimer()
            QObject.connect(self._timer, SIGNAL("timeout()"), self.simulate)
        self._timer.start(timeout, 1)
    def cleanup(self):
        if self._timer is not None:
            self._timer.stop()
            self._timer = None
    def iterate(self, delay=0.0):
        log.msg(channel='system', event='iteration', reactor=self)
        self._crashCall = self.callLater(delay, self.crash)
        self.run()
    def run(self, installSignalHandlers=1):
        self.running = 1
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.simulate()
        self.qApp.enter_loop()
    def crash(self):
        if self._crashCall is not None:
            if self._crashCall.active():
                self._crashCall.cancel()
            self._crashCall = None
        self.running = 0
Example #15
0
class KSmartTray(QObject):
    class State:
        Waiting = 'StateWaiting'
        Updating = 'StateUpdating'
        Checking = 'StateChecking'
        Upgrading = 'StateUpgrading'
        RunningSmart = 'StateRunningSmart'

    def __init__(self):
        QObject.__init__(self)
        self.sysTray = KMySystemTray()
        self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))
        self.sysTray.show()

        self.process = KProcIO()

        self.state = KSmartTray.State.Waiting
        self.lastKnownStatus = ""

        self.blinkFlag = False
        self.updateFailed = False

        self.checkTimer = QTimer()
        self.blinkTimer = QTimer()

        QObject.connect(self.checkTimer, SIGNAL("timeout()"),
                        self.checkUpgrades)
        QObject.connect(self.process, SIGNAL("processExited(KProcess *)"),
                        self.processDone)

        QObject.connect(self, PYSIGNAL("foundNewUpgrades()"),
                        self.startBlinking)
        QObject.connect(self, PYSIGNAL("foundNoUpgrades()"), self.stopBlinking)
        QObject.connect(self.sysTray, PYSIGNAL("mouseEntered()"),
                        self.stopBlinking)
        QObject.connect(self.blinkTimer, SIGNAL("timeout()"), self.toggleBlink)

        QObject.connect(self.sysTray.checkAction, SIGNAL("activated()"),
                        self.manualCheckUpgrades)
        QObject.connect(self.sysTray.startSmartAction, SIGNAL("activated()"),
                        self.startSmart)
        QObject.connect(self.sysTray.stopAction, SIGNAL("activated()"),
                        self.stopChecking)
        QObject.connect(self.sysTray, SIGNAL("quitSelected()"),
                        KApplication.kApplication(), SLOT("quit()"))

        QObject.connect(self.sysTray, PYSIGNAL("activated()"),
                        self.runUpgrades)

        self.checkTimer.start(5 * 60 * 1000)

        self.checkUpgrades()

    def internalCheckUpgrades(self, manual):
        if not manual and self.blinkTimer.isActive():
            return
        if self.state == KSmartTray.State.Waiting:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(True)
            self.process.resetAll()
            if manual:
                self.process.setArguments(["smart-update"])
            else:
                self.process.setArguments(["smart-update", "--after", "60"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                    "Couldn't run 'smart-update'.")
            else:
                QToolTip.add(self.sysTray, "Updating channels...")
                self.state = KSmartTray.State.Updating

    def checkUpgrades(self):
        self.internalCheckUpgrades(False)

    def manualCheckUpgrades(self):
        self.internalCheckUpgrades(True)

    def runUpgrades(self):
        if self.state != KSmartTray.State.Waiting:
            KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                "There is a running process.")
        else:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(False)
            self.process.resetAll()
            self.process.setArguments(
                ["kdesu", "-d", "-c", "smart --gui upgrade"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                    "Couldn't run 'smart upgrade'.")
            else:
                self.state = KSmartTray.State.Upgrading
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, "Running Smart Package Manager...")

    def startSmart(self):
        if self.state != KSmartTray.State.Waiting:
            KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                "There is a running process.")
        else:
            self.sysTray.checkAction.setEnabled(False)
            self.sysTray.startSmartAction.setEnabled(False)
            self.sysTray.stopAction.setEnabled(False)
            self.process.resetAll()
            self.process.setArguments(["kdesu", "-d", "-c", "smart --gui"])
            if not self.process.start():
                KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                    "Couldn't run 'smart'.")
            else:
                self.state = KSmartTray.State.RunningSmart
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, "Running Smart Package Manager...")

    def stopChecking(self):
        self.process.kill()

    def processDone(self, process):
        if self.state == KSmartTray.State.Updating:
            if not process.normalExit() or process.exitStatus() != 0:
                self.updateFailed = True
            if self.updateFailed and not self.lastKnownStatus == "":
                self.state = KSmartTray.State.Waiting
            else:
                process.resetAll()
                process.setArguments(["smart", "upgrade", "--check-update"])
                if not process.start():
                    KNotifyClient.event(self.sysTray.winId(), "fatalerror",
                                        "Couldn't run 'smart upgrade'.")
                    self.state = KSmartTray.State.Waiting
                    self.lastKnownStatus = ""
                else:
                    QToolTip.remove(self.sysTray)
                    QToolTip.add(self.sysTray,
                                 "Verifying upgradable packages...")
                    self.state = KSmartTray.State.Checking
        elif self.state == KSmartTray.State.Checking:
            self.state = KSmartTray.State.Waiting
            if process.normalExit():
                if process.exitStatus() == 0:
                    self.lastKnownStatus = "There are new upgrades available!"
                    KNotifyClient.event(self.sysTray.winId(),
                                        "found-new-upgrades",
                                        self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundNewUpgrades()"), ())
                elif process.exitStatus() == 1:
                    self.lastKnownStatus = "There are pending upgrades!"
                    if not self.updateFailed:
                        KNotifyClient.event(self.sysTray.winId(),
                                            "found-old-upgrades",
                                            self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundOldUpgrades()"), ())
                elif process.exitStatus() == 2:
                    self.lastKnownStatus = "No interesting upgrades available."
                    if not self.updateFailed:
                        KNotifyClient.event(self.sysTray.winId(),
                                            "found-no-upgrades",
                                            self.lastKnownStatus)
                    self.emit(PYSIGNAL("foundNoUpgrades()"), ())
                else:
                    self.lastKnownStatus = ""
        elif self.state == KSmartTray.State.Upgrading:
            self.state = KSmartTray.State.Waiting
            self.lastKnownStatus = ""
        elif self.state == KSmartTray.State.RunningSmart:
            self.state = KSmartTray.State.Waiting
            self.lastKnownStatus = ""
        else:
            # Error!
            pass

        if self.state == KSmartTray.State.Waiting:
            self.updateFailed = False
            self.sysTray.checkAction.setEnabled(True)
            self.sysTray.startSmartAction.setEnabled(True)
            self.sysTray.stopAction.setEnabled(False)
            if not self.lastKnownStatus == "":
                QToolTip.remove(self.sysTray)
                QToolTip.add(self.sysTray, self.lastKnownStatus)
            else:
                QToolTip.remove(self.sysTray)

    def startBlinking(self):
        if not self.blinkTimer.isActive():
            self.blinkTimer.start(500)

    def stopBlinking(self):
        if self.blinkTimer.isActive():
            self.blinkTimer.stop()
        self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))

    def toggleBlink(self):
        if self.blinkFlag:
            self.sysTray.setPixmap(QPixmap())
        else:
            self.sysTray.setPixmap(self.sysTray.loadIcon("ksmarttray"))
        self.blinkFlag = not self.blinkFlag
class qSlicerMultiVolumeExplorerSimplifiedModuleWidget:

  def __init__(self, parent=None):
    logging.debug("qSlicerMultiVolumeExplorerSimplifiedModuleWidget:init() called")
    if not parent or not hasattr(parent, "layout"):
      self.parent = slicer.qMRMLWidget()
      self.parent.setLayout(QVBoxLayout())
    else:
      self.parent = parent

    self.layout = self.parent.layout()

    self._bgMultiVolumeNode = None
    self._fgMultiVolumeNode = None

    self.styleObserverTags = []
    self.sliceWidgetsPerStyle = {}

    self.chartPopupWindow = None
    self.chartPopupSize = QSize(600, 300)
    self.chartPopupPosition = QPoint(0,0)

  def hide(self):
    self.widget.hide()

  def show(self):
    self.widget.show()

  def setup(self):
    self.widget = QWidget()
    layout = QGridLayout()
    self.widget.setLayout(layout)
    self.layout.addWidget(self.widget)
    self.widget.show()
    self.layout = layout

    self.setupInputFrame()
    self.setupFrameControlFrame()
    self.setupAdditionalFrames()
    self.setupPlottingFrame()

    self.setFramesEnabled(False)

    self.timer = QTimer()
    self.timer.setInterval(50)

    self.setupConnections()

    # initialize slice observers (from DataProbe.py)
    # keep list of pairs: [observee,tag] so they can be removed easily
    self.styleObserverTags = []
    # keep a map of interactor styles to sliceWidgets so we can easily get sliceLogic
    self.sliceWidgetsPerStyle = {}
    self.refreshObservers()

  def setupInputFrame(self, parent=None):
    if not parent:
      parent = self.layout
    self.bgMultiVolumeSelector = slicer.qMRMLNodeComboBox()
    self.bgMultiVolumeSelector.nodeTypes = ['vtkMRMLMultiVolumeNode']
    self.bgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
    self.bgMultiVolumeSelector.addEnabled = 0
    self._bgMultiVolumeSelectorLabel = QLabel('Input multivolume')
    inputFrameWidget = QWidget()
    self.inputFrameLayout = QFormLayout()
    inputFrameWidget.setLayout(self.inputFrameLayout)
    self.inputFrameLayout.addRow(self._bgMultiVolumeSelectorLabel, self.bgMultiVolumeSelector)
    parent.addWidget(inputFrameWidget)

  def setupFrameControlFrame(self):
    # TODO: initialize the slider based on the contents of the labels array
    self.frameSlider = ctk.ctkSliderWidget()
    self.frameLabel = QLabel('Current frame number')
    self.playButton = QPushButton('Play')
    self.playButton.toolTip = 'Iterate over multivolume frames'
    self.playButton.checkable = True
    frameControlHBox = QHBoxLayout()
    frameControlHBox.addWidget(self.frameLabel)
    frameControlHBox.addWidget(self.frameSlider)
    frameControlHBox.addWidget(self.playButton)
    self.inputFrameLayout.addRow(frameControlHBox)

  def setupAdditionalFrames(self):
    pass

  def setupPlottingFrame(self, parent=None):
    if not parent:
      parent = self.layout
    self.plottingFrameWidget = QWidget()
    self.plottingFrameLayout = QGridLayout()
    self.plottingFrameWidget.setLayout(self.plottingFrameLayout)
    self._multiVolumeIntensityChart = MultiVolumeIntensityChartView()
    self.popupChartButton = QPushButton("Undock chart")
    self.popupChartButton.setCheckable(True)
    self.plottingFrameLayout.addWidget(self._multiVolumeIntensityChart.chartView)
    self.plottingFrameLayout.addWidget(self.popupChartButton)
    parent.addWidget(self.plottingFrameWidget)

  def setupConnections(self):
    self.parent.connect('mrmlSceneChanged(vtkMRMLScene*)', self.onVCMRMLSceneChanged)
    self.bgMultiVolumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onBackgroundInputChanged)
    self.playButton.connect('toggled(bool)', self.onPlayButtonToggled)
    self.frameSlider.connect('valueChanged(double)', self.onSliderChanged)
    self.timer.connect('timeout()', self.goToNext)
    self.popupChartButton.connect('toggled(bool)', self.onDockChartViewToggled)

  def onDockChartViewToggled(self, checked):
    if checked:
      self.chartPopupWindow = QDialog()
      self.chartPopupWindow.setWindowFlags(PythonQt.QtCore.Qt.WindowStaysOnTopHint)
      layout = QGridLayout()
      self.chartPopupWindow.setLayout(layout)
      layout.addWidget(self._multiVolumeIntensityChart.chartView)
      layout.addWidget(self.popupChartButton)
      self.chartPopupWindow.finished.connect(self.dockChartView)
      self.chartPopupWindow.resize(self.chartPopupSize)
      self.chartPopupWindow.move(self.chartPopupPosition)
      self.chartPopupWindow.show()
      self.popupChartButton.setText("Dock chart")
      self._multiVolumeIntensityChart.chartView.show()
    else:
      self.chartPopupWindow.close()

  def dockChartView(self):
    self.chartPopupSize = self.chartPopupWindow.size
    self.chartPopupPosition = self.chartPopupWindow.pos
    self.plottingFrameLayout.addWidget(self._multiVolumeIntensityChart.chartView)
    self.plottingFrameLayout.addWidget(self.popupChartButton)
    self.popupChartButton.setText("Undock chart")
    self.popupChartButton.disconnect('toggled(bool)', self.onDockChartViewToggled)
    self.popupChartButton.checked = False
    self.popupChartButton.connect('toggled(bool)', self.onDockChartViewToggled)

  def onSliderChanged(self, frameId):
    if self._bgMultiVolumeNode is None:
      return
    newValue = int(frameId)
    self.setCurrentFrameNumber(newValue)

  def onVCMRMLSceneChanged(self, mrmlScene):
    logging.debug("qSlicerMultiVolumeExplorerSimplifiedModuleWidget:onVCMRMLSceneChanged")
    self.bgMultiVolumeSelector.setMRMLScene(slicer.mrmlScene)
    self.onBackgroundInputChanged()

  def refreshGUIForNewBackgroundImage(self):
    self._multiVolumeIntensityChart.reset()
    self.setFramesEnabled(True)
    if self._fgMultiVolumeNode and self._bgMultiVolumeNode:
      Helper.SetBgFgVolumes(self._bgMultiVolumeNode.GetID(), self._fgMultiVolumeNode.GetID())
    else:
      Helper.SetBgVolume(self._bgMultiVolumeNode.GetID())
    self.refreshFrameSlider()
    self._multiVolumeIntensityChart.bgMultiVolumeNode = self._bgMultiVolumeNode
    self.refreshObservers()

  def getBackgroundMultiVolumeNode(self):
    return self.bgMultiVolumeSelector.currentNode()

  def onBackgroundInputChanged(self):
    self._bgMultiVolumeNode = self.getBackgroundMultiVolumeNode()

    if self._bgMultiVolumeNode is not None:
      self.refreshGUIForNewBackgroundImage()
    else:
      self.setFramesEnabled(False)

  def onPlayButtonToggled(self, checked):
    if self._bgMultiVolumeNode is None:
      return
    if checked:
      self.timer.start()
      self.playButton.text = 'Stop'
    else:
      self.timer.stop()
      self.playButton.text = 'Play'

  def processEvent(self, observee, event):
    # logging.debug("processing event %s" % event)
    if self._bgMultiVolumeNode is None:
      return

    # TODO: use a timer to delay calculation and compress events
    if event == 'LeaveEvent':
      # reset all the readouts
      # TODO: reset the label text
      return

    if not self.sliceWidgetsPerStyle.has_key(observee):
      return

    interactor = observee.GetInteractor()
    self.createChart(self.sliceWidgetsPerStyle[observee], interactor.GetEventPosition())

  def createChart(self, sliceWidget, position):
    self._multiVolumeIntensityChart.createChart(sliceWidget, position)

  def setCurrentFrameNumber(self, frameNumber):
    mvDisplayNode = self._bgMultiVolumeNode.GetDisplayNode()
    mvDisplayNode.SetFrameComponent(frameNumber)

  def setFramesEnabled(self, enabled):
    pass

  def refreshObservers(self):
    """ When the layout changes, drop the observers from
    all the old widgets and create new observers for the
    newly created widgets"""
    self.removeObservers()
    # get new slice nodes
    layoutManager = slicer.app.layoutManager()
    sliceNodeCount = slicer.mrmlScene.GetNumberOfNodesByClass('vtkMRMLSliceNode')
    for nodeIndex in xrange(sliceNodeCount):
      # find the widget for each node in scene
      sliceNode = slicer.mrmlScene.GetNthNodeByClass(nodeIndex, 'vtkMRMLSliceNode')
      sliceWidget = layoutManager.sliceWidget(sliceNode.GetLayoutName())
      if sliceWidget:
        # add observers and keep track of tags
        style = sliceWidget.sliceView().interactorStyle()
        self.sliceWidgetsPerStyle[style] = sliceWidget
        events = ("MouseMoveEvent", "EnterEvent", "LeaveEvent")
        for event in events:
          tag = style.AddObserver(event, self.processEvent)
          self.styleObserverTags.append([style,tag])

  def removeObservers(self):
    for observee,tag in self.styleObserverTags:
      observee.RemoveObserver(tag)
    self.styleObserverTags = []
    self.sliceWidgetsPerStyle = {}

  def refreshFrameSlider(self):
    self.frameSlider.minimum = 0
    if not self._bgMultiVolumeNode:
      self.frameSlider.maximum = 0
      return
    nFrames = self._bgMultiVolumeNode.GetNumberOfFrames()
    self.frameSlider.maximum = nFrames - 1

  def goToNext(self):
    currentElement = self.frameSlider.value
    currentElement += 1
    if currentElement > self.frameSlider.maximum:
      currentElement = 0
    self.frameSlider.value = currentElement
Example #17
-1
class KTVMainWindow(KParts.MainWindow):
	"""Implements the main KatchTV application window."""

	def __init__(self, title):
		"""Initialises a new window object."""
		self._stopped = True
		
		KParts.MainWindow.__init__(self, None, str(title))
		self.setCaption(config.appFullVersion)
		
		self._initWidgets()
		
		self.__mediaManager = Media.Manager()
		
		self.__downloadTimer = QTimer()
		self.__downloadTimer.connect(self.__downloadTimer,SIGNAL(str(u"timeout()")), self.__updateDownloads)
		
		self.browser.goToURL(u'katchtv:welcome:')

	def saveAll(self):
		self.__mediaManager._saveItems()
	
	def __updateDownloads(self):
		self.__mediaManager.beginNewDownloads()
	
	def enableThreads(self):
		# start everything, and mark as running
		self.__downloadTimer.start(config.updateDownloadsTimerMillisecs)
		self.bmarksList.enableThreads()
		self._stopped = False
	
	def disableThreads(self):
		# stop in the order of slowest to fastest, hoping it'll
		# all finish around the same time
		self.bmarksList.disableThreads()
		self.__mediaManager.stop()
		self.__downloadTimer.stop()
		self._stopped = True
	
	def getMediaManager(self):
		return self.__mediaManager
	
	def isStopped(self):
		"""Returns True if the application should not run yet (ie, if
		it has not been fully initialised)."""
		return self._stopped

	def _initWidgets(self):
		"""Initialises all of the main widgets in the window."""
		global appRoot
		
		appIconPath = os.path.join(appRoot, "images/miniicon.png")
		self.setIcon(QPixmap(appIconPath))
		
		self.mainBox = QHBox(self)
		self.mainSplitter = QSplitter(self.mainBox)
		
		self.bmarksListBox = QVBox(self.mainSplitter)
		self.bmarksListBox.setMaximumWidth(250)
		
		self.bmarksList = KTVBookmarksListView.KTVBookmarksListView(self.bmarksListBox, self)
		QObject.connect(self.bmarksList, SIGNAL(str(u'doubleClicked(QListViewItem *)')), self._bookmarkChosen)
		
		self.browserBox = QVBox(self.mainSplitter)
		self.browser = KTVHTMLPart.KTVHTMLPart(self.browserBox, self)
		
		self.setCentralWidget(self.mainBox)
		
		self._buttonBox = QHBox(self.bmarksListBox)
		self._addButton = QPushButton(u"&Add", self._buttonBox, str(u""))
		QObject.connect(self._addButton, SIGNAL(str(u'clicked()')), self._addItem)
		
		self._deleteButton = QPushButton(u"&Delete", self._buttonBox, str(u""))
		QObject.connect(self._deleteButton, SIGNAL(str(u'clicked()')), self._deleteItem)
		
		self._backButton = QPushButton(u"&Back", self.bmarksListBox, str(u""))
		QObject.connect(self._backButton, SIGNAL(str(u'clicked()')), self._back)
		
		self._helpButton = QPushButton(u"&Help", self.bmarksListBox, str(u""))
		QObject.connect(self._helpButton, SIGNAL(str(u'clicked()')), self._help)
		
		self.statusBar().message(u"KatchTV is now ready for use.")

	def _help(self):
		"""A hook (a Qt signal slot, actually) which is called when the user
		clicks the "Help" button.  Loads the manual into the browser."""
		self.browser.goToURL(u'katchtv:help:')
	
	def _back(self):
		"""A hook (a Qt signal slot) which is called when the user clicks the
		"Back" button.  Navigates one step back through the browser history."""
		self.browser.loadPreviousPage()
	
	def _addItem(self):
		"""A hook (Qt signal slot) which is called when the user clicks
		the "Add" button.  Presents a dialog, asking the user for the
		feed URI to add."""
		validURI = False
		firstTry = True
		while not validURI:
			if firstTry:
				uri, confirmed = KInputDialog.text(u"Add Feed Bookmark", QString(u"Please enter the channel's feed URI (or URL) here."))
				uri = unicode(uri)
			else:
				uri, confirmed = KInputDialog.text(u"Add Feed Bookmark", u"The URI you entered was invalid.  Please try again, or cancel.", QString(uri))
				uri = unicode(uri)
			firstTry = False
			if confirmed:
				validURI = utils.isAFeed(uri)
			else:
				# user cancelled the input
				return
		feed = Feed.FeedCache.getFeedFromURINoLock(uri)
		self.bmarksList.addChannel(feed)
	
	def _deleteItem(self):
		"""A hook (Qt signal slot) which is called when the user
		clicks the Delete button.  Deletes the selected item,
		if that item is a subscribed channel.  Otherwise, informs
		the user that they cannot delete the item."""
		self.bmarksList.delCurrentItem()

	def _bookmarkChosen(self, item):
		"""A hook (Qt signal slot) which is called when the user double-clicks
		on an item in the listview.  Checks if the item has a URI column entry,
		and if so, navigates to it."""
		title = item.text(KTVBookmarksListView.COL_NAME)
		uri = item.text(KTVBookmarksListView.COL_URI)
		if title != u'' and uri != u'':
			self.browser.goToURL(uri)

	def getBookmarksList(self):
		return self.bmarksList