def __init__(self): SelectRuleset.__init__(self) Players.load() decorateWindow(self, m18n('Select four players')) self.names = None self.nameWidgets = [] for idx, wind in enumerate(Wind.all4): cbName = QComboBox() cbName.manualSelect = False # increase width, we want to see the full window title cbName.setMinimumWidth(350) # is this good for all platforms? cbName.addItems(list(Players.humanNames.values())) self.grid.addWidget(cbName, idx + 1, 1) self.nameWidgets.append(cbName) self.grid.addWidget(WindLabel(wind), idx + 1, 0) cbName.currentIndexChanged.connect(self.slotValidate) query = Query( "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)") if len(query.records): with BlockSignals(self.nameWidgets): for cbName, playerId in zip(self.nameWidgets, query.records[0]): try: playerName = Players.humanNames[playerId] playerIdx = cbName.findText(playerName) if playerIdx >= 0: cbName.setCurrentIndex(playerIdx) except KeyError: logError(u'database is inconsistent: player with id %d is in game but not in player' % playerId) self.slotValidate()
def __init__(self): SelectRuleset.__init__(self) Players.load() decorateWindow(self, i18n('Select four players')) self.names = None self.nameWidgets = [] for idx, wind in enumerate(Wind.all4): cbName = QComboBox() cbName.manualSelect = False # increase width, we want to see the full window title cbName.setMinimumWidth(350) # is this good for all platforms? cbName.addItems(list(Players.humanNames.values())) self.grid.addWidget(cbName, idx + 1, 1) self.nameWidgets.append(cbName) self.grid.addWidget(WindLabel(wind), idx + 1, 0) cbName.currentIndexChanged.connect(self.slotValidate) query = Query( "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)" ) if len(query.records): with BlockSignals(self.nameWidgets): for cbName, playerId in zip(self.nameWidgets, query.records[0]): try: playerName = Players.humanNames[playerId] playerIdx = cbName.findText(playerName) if playerIdx >= 0: cbName.setCurrentIndex(playerIdx) except KeyError: logError( 'database is inconsistent: player with id %d is in game but not in player' % playerId) self.slotValidate()
class LoginDlg(QDialog): """login dialog for server""" def __init__(self): """self.servers is a list of tuples containing server and last playername""" QDialog.__init__(self, None) decorateWindow(self, i18nc('kajongg', 'Login')) self.setupUi() localName = i18nc('kajongg name for local game server', Query.localServerName) self.servers = Query( 'select url,lastname from server order by lasttime desc').records servers = list(x[0] for x in self.servers if x[0] != Query.localServerName) # the first server combobox item should be default: either the last used server # or localName for autoPlay if localName not in servers: servers.append(localName) if 'kajongg.org' not in servers: servers.append('kajongg.org') if Internal.autoPlay: demoHost = Options.host or localName if demoHost in servers: servers.remove( demoHost ) # we want a unique list, it will be re-used for all following games servers.insert(0, demoHost) # in this process but they will not be autoPlay self.cbServer.addItems(servers) self.passwords = Query( 'select url, p.name, passwords.password from passwords, player p ' 'where passwords.player=p.id').records Players.load() self.cbServer.editTextChanged.connect(self.serverChanged) self.cbUser.editTextChanged.connect(self.userChanged) self.serverChanged() StateSaver(self) def returns(self, dummyButton=None): """login data returned by this dialog""" return (Url(self.url), self.username, self.password, self.__defineRuleset()) def setupUi(self): """create all Ui elements but do not fill them""" self.buttonBox = KDialogButtonBox(self) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) # Ubuntu 11.10 unity is a bit strange - without this, it sets focus on # the cancel button (which it shows on the left). I found no obvious # way to use setDefault and setAutoDefault for fixing this. self.buttonBox.button(QDialogButtonBox.Ok).setFocus(True) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) vbox = QVBoxLayout(self) self.grid = QFormLayout() self.cbServer = QComboBox() self.cbServer.setEditable(True) self.grid.addRow(i18n('Game server:'), self.cbServer) self.cbUser = QComboBox() self.cbUser.setEditable(True) self.grid.addRow(i18n('Username:'******'Password:'******'kajongg', 'Ruleset:'), self.cbRuleset) vbox.addLayout(self.grid) vbox.addWidget(self.buttonBox) pol = QSizePolicy() pol.setHorizontalPolicy(QSizePolicy.Expanding) self.cbUser.setSizePolicy(pol) self.__port = None def serverChanged(self, dummyText=None): """the user selected a different server""" records = Query( 'select player.name from player, passwords ' 'where passwords.url=? and passwords.player = player.id', (self.url, )).records players = list(x[0] for x in records) preferPlayer = Options.player if preferPlayer: if preferPlayer in players: players.remove(preferPlayer) players.insert(0, preferPlayer) self.cbUser.clear() self.cbUser.addItems(players) if not self.cbUser.count(): user = KUser() if os.name == 'nt' else KUser(os.geteuid()) self.cbUser.addItem(user.fullName() or user.loginName()) if not preferPlayer: userNames = [x[1] for x in self.servers if x[0] == self.url] if userNames: userIdx = self.cbUser.findText(userNames[0]) if userIdx >= 0: self.cbUser.setCurrentIndex(userIdx) showPW = bool(self.url) and not Url(self.url).isLocalHost self.grid.labelForField(self.edPassword).setVisible(showPW) self.edPassword.setVisible(showPW) self.grid.labelForField(self.cbRuleset).setVisible( not showPW and not Options.ruleset) self.cbRuleset.setVisible(not showPW and not Options.ruleset) if not showPW: self.cbRuleset.clear() if Options.ruleset: self.cbRuleset.items = [Options.ruleset] else: self.cbRuleset.items = Ruleset.selectableRulesets(self.url) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(bool(self.url)) def __defineRuleset(self): """find out what ruleset to use""" if Options.ruleset: return Options.ruleset elif Internal.autoPlay or bool(Options.host): return Ruleset.selectableRulesets()[0] else: return self.cbRuleset.current def userChanged(self, text): """the username has been changed, lookup password""" if text == '': self.edPassword.clear() return passw = None for entry in self.passwords: if entry[0] == self.url and entry[1] == text: passw = entry[2] if passw: self.edPassword.setText(passw) else: self.edPassword.clear() @property def url(self): """abstracts the url of the dialog""" return english(self.cbServer.currentText()) @property def username(self): """abstracts the username of the dialog""" return self.cbUser.currentText() @property def password(self): """abstracts the password of the dialog""" return self.edPassword.text() @password.setter def password(self, password): """abstracts the password of the dialog""" self.edPassword.setText(password)
class ProstateTRUSNavUltrasound(UltraSound): OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha" SCOUT_VOLUME_FILENAME = "ScoutScan.mha" LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha" RECORDING_FILENAME = "Recording.mha" SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha" LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME SCOUT_VOLUME_NODE_NAME = "ScoutScan" OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference" LIVE_VOLUME_NODE_NAME = "liveReconstruction" APPLY_HOLE_FILLING_FOR_SNAPSHOT = False SNAPSHOT_INTERVAL = 3 OUTPUT_VOLUME_SPACING = 3 LIVE_OUTPUT_VOLUME_SPACING = 1 @property def roiNode(self): return self._roiNode @roiNode.setter def roiNode(self, node): self._roiNode = node if node is not None: self.startStopLiveReconstructionButton.setEnabled(True) else: self.startStopLiveReconstructionButton.setEnabled(False) def __init__(self, guideletParent): UltraSound.__init__(self, guideletParent) self.parameterNode = guideletParent.parameterNode self.parameterNodeObserver = None self._roiNode = None self.liveOutputSpacingValue = [ self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING ] self.outputSpacing = [ self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING ] self.roiOrigin = None self.roiExtent = None self.defaultParameterNode = None self.logic = ProstateTRUSNavUltrasoundLogic() def setupPanel(self, parentWidget): logging.debug('ProstateTRUSNavUltrasound.setupPanel') self.connectorNode = self.guideletParent.connectorNode self.connectorNodeConnected = False collapsibleButton = ctkCollapsibleButton() collapsibleButton.setProperty('collapsedHeight', 20) setButtonStyle(collapsibleButton, 2.0) collapsibleButton.text = "Ultrasound" parentWidget.addWidget(collapsibleButton) ultrasoundLayout = QFormLayout(collapsibleButton) ultrasoundLayout.setContentsMargins(12, 4, 4, 4) ultrasoundLayout.setSpacing(4) self.connectDisconnectButton = QPushButton("Connect") self.connectDisconnectButton.setToolTip( "If clicked, connection OpenIGTLink") hbox = QHBoxLayout() hbox.addWidget(self.connectDisconnectButton) ultrasoundLayout.addRow(hbox) self.setupIcons() self.captureIDSelector = QComboBox() self.captureIDSelector.setToolTip("Pick capture device ID") self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.volumeReconstructorIDSelector = QComboBox() self.volumeReconstructorIDSelector.setToolTip( "Pick volume reconstructor device ID") self.volumeReconstructorIDSelector.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopRecordingButton = QPushButton(" Start Recording") self.startStopRecordingButton.setCheckable(True) self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setEnabled(False) self.startStopRecordingButton.setToolTip("If clicked, start recording") self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) recordParametersControlsLayout = QGridLayout() self.filenameLabel = self.createLabel("Filename:", visible=False) recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0) # Offline Reconstruction self.offlineReconstructButton = QPushButton(" Offline Reconstruction") self.offlineReconstructButton.setCheckable(True) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setEnabled(False) self.offlineReconstructButton.setToolTip( "If clicked, reconstruct recorded volume") self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.offlineVolumeToReconstructSelector = QComboBox() self.offlineVolumeToReconstructSelector.setEditable(True) self.offlineVolumeToReconstructSelector.setToolTip( "Pick/set volume to reconstruct") self.offlineVolumeToReconstructSelector.visible = False hbox = QHBoxLayout() hbox.addWidget(self.startStopRecordingButton) hbox.addWidget(self.offlineReconstructButton) ultrasoundLayout.addRow(hbox) # Scout scan (record and low resolution reconstruction) and live reconstruction # Scout scan part self.startStopScoutScanButton = QPushButton( " Scout scan\n Start recording") self.startStopScoutScanButton.setCheckable(True) self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setToolTip("If clicked, start recording") self.startStopScoutScanButton.setEnabled(False) self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopLiveReconstructionButton = QPushButton( " Start live reconstruction") self.startStopLiveReconstructionButton.setCheckable(True) self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction") self.startStopLiveReconstructionButton.setEnabled(False) self.startStopLiveReconstructionButton.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding) self.displayRoiButton = QToolButton() self.displayRoiButton.setCheckable(True) self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") hbox = QHBoxLayout() hbox.addWidget(self.startStopScoutScanButton) hbox.addWidget(self.startStopLiveReconstructionButton) # hbox.addWidget(self.displayRoiButton) ultrasoundLayout.addRow(hbox) self.snapshotTimer = QTimer() self.snapshotTimer.setSingleShot(True) self.onParameterSetSelected() return collapsibleButton def setupResliceDriver(self): layoutManager = slicer.app.layoutManager() # Show ultrasound in red view. redSlice = layoutManager.sliceWidget('Red') redSliceLogic = redSlice.sliceLogic() redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID( self.liveUltrasoundNode_Reference.GetID()) resliceLogic = slicer.modules.volumereslicedriver.logic() if resliceLogic: redNode = slicer.util.getNode('vtkMRMLSliceNodeRed') redNode.SetSliceResolutionMode( slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes) resliceLogic.SetDriverForSlice( self.liveUltrasoundNode_Reference.GetID(), redNode) resliceLogic.SetModeForSlice( 6, redNode) # Transverse mode, default for PLUS ultrasound. else: logging.warning('Logic not found for Volume Reslice Driver') def createCollapsibleButton(self, text, collapsed=False): collapsibleButton = ctkCollapsibleButton() collapsibleButton.text = text collapsibleButton.collapsed = collapsed return collapsibleButton def createLabel(self, text, visible=True): label = QLabel() label.setText(text) label.visible = visible return label def setupConnections(self): self.startStopRecordingButton.connect( 'clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.connect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.connect( 'clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.connect( 'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.connect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.connect( 'currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.connect( 'currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.connect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.connect( self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.connect( 'clicked(bool)', self.onConnectDisconnectButtonClicked) def disconnect(self): self.startStopRecordingButton.disconnect( 'clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.disconnect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.disconnect( 'clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.disconnect( 'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.disconnect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.disconnect( 'currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.disconnect( 'currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.disconnect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.disconnect( self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.disconnect( 'clicked(bool)', self.onConnectDisconnectButtonClicked) def setupIcons(self): self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace( "PlusRemote.py", "") self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Record.png') self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Stop.png') self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Wait.png') self.visibleOffIcon = QIcon(":Icons\VisibleOff.png") self.visibleOnIcon = QIcon(":Icons\VisibleOn.png") def onParameterSetSelected(self): # Set up default values for new nodes if self.parameterNode: self.plusRemoteLogic.setDefaultParameters(self.parameterNode) self.updateGuiFromParameterNode() def updateGuiFromParameterNode(self): self.parameterVolumeList = { 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector } for parameter in self.parameterVolumeList: if self.parameterNode.GetParameter(parameter): self.parameterVolumeList[parameter].blockSignals(True) self.parameterVolumeList[parameter].blockSignals(False) if self.parameterNode.GetParameter('CaptureID'): self.captureIDSelector.blockSignals(True) for i in range(0, self.captureIDSelector.count): if self.parameterNode.GetParameter( 'CaptureID') == self.captureIDSelector.itemText(i): self.captureIDSelector.setCurrentIndex( int(self.parameterNode.GetParameter('CaptureIdIndex'))) self.captureIDSelector.blockSignals(False) if self.parameterNode.GetParameter('VolumeReconstructor'): self.volumeReconstructorIDSelector.blockSignals(True) for i in range(0, self.volumeReconstructorIDSelector.count): if self.parameterNode.GetParameter( 'VolumeReconstructor' ) == self.volumeReconstructorIDSelector.itemText(i): self.volumeReconstructorIDSelector.setCurrentIndex( int( self.parameterNode.GetParameter( 'VolumeReconstructorIndex'))) self.volumeReconstructorIDSelector.blockSignals(False) self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0) def updateParameterNodeFromGui(self): #Update parameter node value to save when user change value in the interface if not self.parameterNode: return self.parametersList = { 'CaptureID': self.captureIDSelector.currentText, 'CaptureIdIndex': self.captureIDSelector.currentIndex, 'VolumeReconstructor': self.volumeReconstructorIDSelector.currentText, 'VolumeReconstructorIndex': self.volumeReconstructorIDSelector.currentIndex, 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector.currentIndex } for parameter in self.parametersList: self.parameterNode.SetParameter( parameter, str(self.parametersList[parameter])) if self.roiNode: roiNodeID = self.roiNode.GetID() self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID) # # Connector observation and actions # def onConnectorNodeConnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected") self.connectorNodeConnected = True self.captureIDSelector.setDisabled(False) self.volumeReconstructorIDSelector.setDisabled(False) self.plusRemoteLogic.getCaptureDeviceIds( self.connectorNode.GetID(), self.onGetCaptureDeviceCommandResponseReceived) self.plusRemoteLogic.getVolumeReconstructorDeviceIds( self.connectorNode.GetID(), self.onGetVolumeReconstructorDeviceCommandResponseReceived) self.connectDisconnectButton.setText("Disconnect") def onConnectorNodeDisconnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected") self.connectorNodeConnected = False self.startStopRecordingButton.setEnabled(False) self.startStopScoutScanButton.setEnabled(False) self.startStopLiveReconstructionButton.setEnabled(False) self.offlineReconstructButton.setEnabled(False) self.captureIDSelector.setDisabled(True) self.volumeReconstructorIDSelector.setDisabled(True) self.connectDisconnectButton.setText("Connect") def getLiveVolumeRecNode(self): liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME) return liveVolumeRecNode def getOfflineVolumeRecNode(self): offlineVolumeRecNode = slicer.util.getNode( self.OFFLINE_VOLUME_NODE_NAME) return offlineVolumeRecNode def getScoutVolumeNode(self): scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME) return scoutScanVolumeNode def onConnectDisconnectButtonClicked(self): if self.connectorNode.GetState( ) == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED: self.connectorNode.Stop() else: self.connectorNode.Start() def onStartStopRecordingButtonClicked(self): if self.startStopRecordingButton.isChecked(): self.startStopRecordingButton.setText(" Stop Recording") self.startStopRecordingButton.setIcon(self.stopIcon) self.startStopRecordingButton.setToolTip( "If clicked, stop recording") self.onStartRecording(self.generateRecordingOutputFilename()) else: self.startStopRecordingButton.setText(" Start Recording") self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setToolTip( "If clicked, start recording") self.onStopRecording(self.onVolumeRecorded) def onStartStopScoutScanButtonClicked(self): if self.startStopScoutScanButton.isChecked(): self.startStopScoutScanButton.setText( " Scout Scan\n Stop Recording and Reconstruct Recorded Volume" ) self.startStopScoutScanButton.setIcon(self.stopIcon) self.startStopScoutScanButton.setToolTip( "If clicked, stop recording and reconstruct recorded volume") self.onStartRecording(self.generateScoutRecordingOutputFilename()) else: self.onStopRecording(self.onScoutVolumeRecorded) def onStartStopLiveReconstructionButtonClicked(self): if self.startStopLiveReconstructionButton.isChecked(): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.startStopLiveReconstructionButton.setText( " Stop Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.stopIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, stop live reconstruction") self.onStartRecording(self.getLiveRecordingOutputFilename()) self.onStartReconstruction() else: self.startStopLiveReconstructionButton.setText( " Start Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction") self.onStopRecording(self.printCommandResponse) self.onStopReconstruction() def onDisplayRoiButtonClicked(self): if self.displayRoiButton.isChecked(): self.displayRoiButton.setIcon(self.visibleOnIcon) self.displayRoiButton.setToolTip("If clicked, hide ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(1) else: self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(0) def generateRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.RECORDING_FILENAME) def generateScoutRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.SCOUT_RECORDING_FILENAME) def getLiveRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.LIVE_RECORDING_FILENAME) def getLiveReconstructionOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.LIVE_VOLUME_FILENAME) def onStartRecording(self, filename): self.plusRemoteLogic.startRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, filename, self.printCommandResponse) def onStopRecording(self, callback): self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, callback) def onStartReconstruction(self): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.plusRemoteLogic.startVolumeReconstuction( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.liveOutputSpacingValue, self.roiOrigin, self.roiExtent, self.printCommandResponse, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) # Set up timer for requesting snapshot self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000) def onStopReconstruction(self): self.snapshotTimer.stop() self.plusRemoteLogic.stopVolumeReconstruction( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.onVolumeLiveReconstructed, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) def onReconstVolume(self): self.offlineReconstructButton.setIcon(self.waitIcon) self.offlineReconstructButton.setText( " Offline Reconstruction in progress ...") self.offlineReconstructButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.offlineVolumeToReconstructSelector.currentText, self.outputSpacing, self.onVolumeReconstructed, self.OFFLINE_VOLUME_FILENAME, self.OFFLINE_VOLUME_NODE_NAME) def onScoutScanReconstVolume(self): self.startStopScoutScanButton.setIcon(self.waitIcon) self.startStopScoutScanButton.setText( " Scout Scan\n Reconstruction in progress ...") self.startStopScoutScanButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.lastScoutRecordingOutputFilename, self.outputSpacing, self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME, self.SCOUT_VOLUME_NODE_NAME) def onRequestVolumeReconstructionSnapshot(self, stopFlag=""): self.plusRemoteLogic.getVolumeReconstructionSnapshot( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.LIVE_VOLUME_FILENAME, self.LIVE_VOLUME_NODE_NAME, self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired) def executeCommandDelayed(self, method, delay=100): # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same # therefore we wait a bit to make sure the image message is processed as well QTimer.singleShot(delay, method) def printCommandResponse(self, command, q): statusText = "Command {0} [{1}]: {2}\n".format( command.GetCommandName(), command.GetID(), command.StatusToString(command.GetStatus())) if command.GetResponseMessage(): statusText = statusText + command.GetResponseMessage() elif command.GetResponseText(): statusText = statusText + command.GetResponseText() logging.debug(statusText) def onGetCaptureDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return captureDeviceIdsListString = command.GetResponseMessage() if captureDeviceIdsListString: captureDevicesIdsList = captureDeviceIdsListString.split(",") else: captureDevicesIdsList = [] for i in range(0, len(captureDevicesIdsList)): if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1: self.captureIDSelector.addItem(captureDevicesIdsList[i]) def onGetVolumeReconstructorDeviceCommandResponseReceived( self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return volumeReconstructorDeviceIdsListString = command.GetResponseMessage() if volumeReconstructorDeviceIdsListString: volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split( ",") else: volumeReconstructorDeviceIdsList = [] self.volumeReconstructorIDSelector.clear() self.volumeReconstructorIDSelector.addItems( volumeReconstructorDeviceIdsList) self.startStopRecordingButton.setEnabled(True) self.offlineReconstructButton.setEnabled(True) self.startStopScoutScanButton.setEnabled(True) if self.roiNode: self.startStopLiveReconstructionButton.setEnabled(True) def onVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) volumeToReconstructFileName = os.path.basename( command.GetResponseMessage()) self.offlineVolumeToReconstructSelector.insertItem( 0, volumeToReconstructFileName) self.offlineVolumeToReconstructSelector.setCurrentIndex(0) def onScoutVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) if command.GetStatus() == command.CommandExpired: logging.fatal( "Scout Volume Recording: Timeout while waiting for volume reconstruction result" ) return if command.GetStatus() == command.CommandSuccess: self.lastScoutRecordingOutputFilename = os.path.basename( command.GetResponseMessage()) self.onScoutScanReconstVolume() def onVolumeReconstructed(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setText("Offline Reconstruction") self.offlineReconstructButton.setEnabled(True) self.offlineReconstructButton.setChecked(False) if command.GetStatus() == command.CommandExpired: # volume reconstruction command timed out logging.fatal( "Volume Reconstruction: Timeout while waiting for volume reconstruction result" ) return if command.GetStatus() != command.CommandSuccess: logging.debug("Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onVolumeReconstructedFinalize) def onVolumeReconstructedFinalize(self): applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode()) def onScoutVolumeReconstructed(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() == command.CommandExpired: logging.fatal( "Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result" ) return self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setText( " Scout Scan\n Start Recording") self.startStopScoutScanButton.setEnabled(True) if command.GetStatus() != command.CommandSuccess: logging.debug("Scout Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize) def onScoutVolumeReconstructedFinalize(self): # Create and initialize ROI after scout scan because low resolution scout scan is used to set # a smaller ROI for the live high resolution reconstruction self.roiNode = self.logic.onRoiInitialization( self.SCOUT_VOLUME_NODE_NAME, self.roiNode) self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) scoutScanVolumeNode = self.getScoutVolumeNode() applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(scoutScanVolumeNode) def onSnapshotAcquired(self, command, q): self.printCommandResponse(command, q) if not self.startStopLiveReconstructionButton.isChecked(): # live volume reconstruction is not active return self.executeCommandDelayed(self.onSnapshotAcquiredFinalize) def onSnapshotAcquiredFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode()) self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000) def onVolumeLiveReconstructed(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() == command.CommandExpired: logging.fatal( "LIVE Volume Reconstruction: Failed to stop volume reconstruction" ) return if command.GetStatus() != command.CommandSuccess: logging.debug("LIVE Volume Reconstruction " + command.GetResponseMessage()) return self.executeCommandDelayed(self.getLiveVolumeRecNode) def onVolumeLiveReconstructedFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())
class ProstateTRUSNavUltrasound(UltraSound): OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha" SCOUT_VOLUME_FILENAME = "ScoutScan.mha" LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha" RECORDING_FILENAME = "Recording.mha" SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha" LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME SCOUT_VOLUME_NODE_NAME = "ScoutScan" OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference" LIVE_VOLUME_NODE_NAME = "liveReconstruction" APPLY_HOLE_FILLING_FOR_SNAPSHOT = False SNAPSHOT_INTERVAL = 3 OUTPUT_VOLUME_SPACING = 3 LIVE_OUTPUT_VOLUME_SPACING = 1 @property def roiNode(self): return self._roiNode @roiNode.setter def roiNode(self, node): self._roiNode=node if node is not None: self.startStopLiveReconstructionButton.setEnabled(True) else: self.startStopLiveReconstructionButton.setEnabled(False) def __init__(self, guideletParent): UltraSound.__init__(self, guideletParent) self.parameterNode = guideletParent.parameterNode self.parameterNodeObserver = None self._roiNode = None self.liveOutputSpacingValue = [self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING] self.outputSpacing = [self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING] self.roiOrigin = None self.roiExtent = None self.defaultParameterNode = None self.logic = ProstateTRUSNavUltrasoundLogic() def setupPanel(self, parentWidget): logging.debug('ProstateTRUSNavUltrasound.setupPanel') self.connectorNode = self.guideletParent.connectorNode self.connectorNodeConnected = False collapsibleButton = ctkCollapsibleButton() collapsibleButton.setProperty('collapsedHeight', 20) setButtonStyle(collapsibleButton, 2.0) collapsibleButton.text = "Ultrasound" parentWidget.addWidget(collapsibleButton) ultrasoundLayout = QFormLayout(collapsibleButton) ultrasoundLayout.setContentsMargins(12,4,4,4) ultrasoundLayout.setSpacing(4) self.connectDisconnectButton = QPushButton("Connect") self.connectDisconnectButton.setToolTip("If clicked, connection OpenIGTLink") hbox = QHBoxLayout() hbox.addWidget(self.connectDisconnectButton) ultrasoundLayout.addRow(hbox) self.setupIcons() self.captureIDSelector = QComboBox() self.captureIDSelector.setToolTip("Pick capture device ID") self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.volumeReconstructorIDSelector = QComboBox() self.volumeReconstructorIDSelector.setToolTip( "Pick volume reconstructor device ID" ) self.volumeReconstructorIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopRecordingButton = QPushButton(" Start Recording") self.startStopRecordingButton.setCheckable(True) self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setEnabled(False) self.startStopRecordingButton.setToolTip("If clicked, start recording") self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) recordParametersControlsLayout = QGridLayout() self.filenameLabel = self.createLabel("Filename:", visible=False) recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0) # Offline Reconstruction self.offlineReconstructButton = QPushButton(" Offline Reconstruction") self.offlineReconstructButton.setCheckable(True) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setEnabled(False) self.offlineReconstructButton.setToolTip("If clicked, reconstruct recorded volume") self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.offlineVolumeToReconstructSelector = QComboBox() self.offlineVolumeToReconstructSelector.setEditable(True) self.offlineVolumeToReconstructSelector.setToolTip( "Pick/set volume to reconstruct" ) self.offlineVolumeToReconstructSelector.visible = False hbox = QHBoxLayout() hbox.addWidget(self.startStopRecordingButton) hbox.addWidget(self.offlineReconstructButton) ultrasoundLayout.addRow(hbox) # Scout scan (record and low resolution reconstruction) and live reconstruction # Scout scan part self.startStopScoutScanButton = QPushButton(" Scout scan\n Start recording") self.startStopScoutScanButton.setCheckable(True) self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setToolTip("If clicked, start recording") self.startStopScoutScanButton.setEnabled(False) self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopLiveReconstructionButton = QPushButton(" Start live reconstruction") self.startStopLiveReconstructionButton.setCheckable(True) self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip("If clicked, start live reconstruction") self.startStopLiveReconstructionButton.setEnabled(False) self.startStopLiveReconstructionButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.displayRoiButton = QToolButton() self.displayRoiButton.setCheckable(True) self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") hbox = QHBoxLayout() hbox.addWidget(self.startStopScoutScanButton) hbox.addWidget(self.startStopLiveReconstructionButton) # hbox.addWidget(self.displayRoiButton) ultrasoundLayout.addRow(hbox) self.snapshotTimer = QTimer() self.snapshotTimer.setSingleShot(True) self.onParameterSetSelected() return collapsibleButton def setupResliceDriver(self): layoutManager = slicer.app.layoutManager() # Show ultrasound in red view. redSlice = layoutManager.sliceWidget('Red') redSliceLogic = redSlice.sliceLogic() redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID(self.liveUltrasoundNode_Reference.GetID()) resliceLogic = slicer.modules.volumereslicedriver.logic() if resliceLogic: redNode = slicer.util.getNode('vtkMRMLSliceNodeRed') redNode.SetSliceResolutionMode(slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes) resliceLogic.SetDriverForSlice(self.liveUltrasoundNode_Reference.GetID(), redNode) resliceLogic.SetModeForSlice(6, redNode) # Transverse mode, default for PLUS ultrasound. else: logging.warning('Logic not found for Volume Reslice Driver') def createCollapsibleButton(self, text, collapsed=False): collapsibleButton = ctkCollapsibleButton() collapsibleButton.text = text collapsibleButton.collapsed = collapsed return collapsibleButton def createLabel(self, text, visible=True): label = QLabel() label.setText(text) label.visible = visible return label def setupConnections(self): self.startStopRecordingButton.connect('clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.connect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.connect('clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.connect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.connect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.connect('currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.connect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.connect(self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.connect('clicked(bool)', self.onConnectDisconnectButtonClicked) def disconnect(self): self.startStopRecordingButton.disconnect('clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.disconnect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.disconnect('clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.disconnect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.disconnect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.disconnect('currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.disconnect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.disconnect(self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.disconnect('clicked(bool)', self.onConnectDisconnectButtonClicked) def setupIcons(self): self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace("PlusRemote.py", "") self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Record.png') self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Stop.png') self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Wait.png') self.visibleOffIcon = QIcon(":Icons\VisibleOff.png") self.visibleOnIcon = QIcon(":Icons\VisibleOn.png") def onParameterSetSelected(self): # Set up default values for new nodes if self.parameterNode: self.plusRemoteLogic.setDefaultParameters(self.parameterNode) self.updateGuiFromParameterNode() def updateGuiFromParameterNode(self): self.parameterVolumeList = {'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector} for parameter in self.parameterVolumeList: if self.parameterNode.GetParameter(parameter): self.parameterVolumeList[parameter].blockSignals(True) self.parameterVolumeList[parameter].blockSignals(False) if self.parameterNode.GetParameter('CaptureID'): self.captureIDSelector.blockSignals(True) for i in range(0, self.captureIDSelector.count): if self.parameterNode.GetParameter('CaptureID') == self.captureIDSelector.itemText(i): self.captureIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('CaptureIdIndex'))) self.captureIDSelector.blockSignals(False) if self.parameterNode.GetParameter('VolumeReconstructor'): self.volumeReconstructorIDSelector.blockSignals(True) for i in range(0, self.volumeReconstructorIDSelector.count): if self.parameterNode.GetParameter('VolumeReconstructor') == self.volumeReconstructorIDSelector.itemText(i): self.volumeReconstructorIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('VolumeReconstructorIndex'))) self.volumeReconstructorIDSelector.blockSignals(False) self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0) def updateParameterNodeFromGui(self): #Update parameter node value to save when user change value in the interface if not self.parameterNode: return self.parametersList = {'CaptureID': self.captureIDSelector.currentText, 'CaptureIdIndex': self.captureIDSelector.currentIndex, 'VolumeReconstructor': self.volumeReconstructorIDSelector.currentText, 'VolumeReconstructorIndex': self.volumeReconstructorIDSelector.currentIndex, 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector.currentIndex} for parameter in self.parametersList: self.parameterNode.SetParameter(parameter, str(self.parametersList[parameter])) if self.roiNode: roiNodeID = self.roiNode.GetID() self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID) # # Connector observation and actions # def onConnectorNodeConnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected") self.connectorNodeConnected = True self.captureIDSelector.setDisabled(False) self.volumeReconstructorIDSelector.setDisabled(False) self.plusRemoteLogic.getCaptureDeviceIds(self.connectorNode.GetID(), self.onGetCaptureDeviceCommandResponseReceived) self.plusRemoteLogic.getVolumeReconstructorDeviceIds(self.connectorNode.GetID(), self.onGetVolumeReconstructorDeviceCommandResponseReceived) self.connectDisconnectButton.setText("Disconnect") def onConnectorNodeDisconnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected") self.connectorNodeConnected = False self.startStopRecordingButton.setEnabled(False) self.startStopScoutScanButton.setEnabled(False) self.startStopLiveReconstructionButton.setEnabled(False) self.offlineReconstructButton.setEnabled(False) self.captureIDSelector.setDisabled(True) self.volumeReconstructorIDSelector.setDisabled(True) self.connectDisconnectButton.setText("Connect") def getLiveVolumeRecNode(self): liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME) return liveVolumeRecNode def getOfflineVolumeRecNode(self): offlineVolumeRecNode = slicer.util.getNode(self.OFFLINE_VOLUME_NODE_NAME) return offlineVolumeRecNode def getScoutVolumeNode(self): scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME) return scoutScanVolumeNode def onConnectDisconnectButtonClicked(self): if self.connectorNode.GetState() == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED: self.connectorNode.Stop() else: self.connectorNode.Start() def onStartStopRecordingButtonClicked(self): if self.startStopRecordingButton.isChecked(): self.startStopRecordingButton.setText(" Stop Recording") self.startStopRecordingButton.setIcon(self.stopIcon) self.startStopRecordingButton.setToolTip( "If clicked, stop recording" ) self.onStartRecording(self.generateRecordingOutputFilename()) else: self.startStopRecordingButton.setText(" Start Recording") self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setToolTip( "If clicked, start recording" ) self.onStopRecording(self.onVolumeRecorded) def onStartStopScoutScanButtonClicked(self): if self.startStopScoutScanButton.isChecked(): self.startStopScoutScanButton.setText(" Scout Scan\n Stop Recording and Reconstruct Recorded Volume") self.startStopScoutScanButton.setIcon(self.stopIcon) self.startStopScoutScanButton.setToolTip( "If clicked, stop recording and reconstruct recorded volume" ) self.onStartRecording(self.generateScoutRecordingOutputFilename()) else: self.onStopRecording(self.onScoutVolumeRecorded) def onStartStopLiveReconstructionButtonClicked(self): if self.startStopLiveReconstructionButton.isChecked(): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.startStopLiveReconstructionButton.setText(" Stop Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.stopIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, stop live reconstruction" ) self.onStartRecording(self.getLiveRecordingOutputFilename()) self.onStartReconstruction() else: self.startStopLiveReconstructionButton.setText(" Start Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction" ) self.onStopRecording(self.printCommandResponse) self.onStopReconstruction() def onDisplayRoiButtonClicked(self): if self.displayRoiButton.isChecked(): self.displayRoiButton.setIcon(self.visibleOnIcon) self.displayRoiButton.setToolTip("If clicked, hide ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(1) else: self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(0) def generateRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.RECORDING_FILENAME) def generateScoutRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.SCOUT_RECORDING_FILENAME) def getLiveRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_RECORDING_FILENAME) def getLiveReconstructionOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_VOLUME_FILENAME) def onStartRecording(self, filename): self.plusRemoteLogic.startRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, filename, self.printCommandResponse) def onStopRecording(self, callback): self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, callback) def onStartReconstruction(self): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.plusRemoteLogic.startVolumeReconstuction(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.liveOutputSpacingValue, self.roiOrigin, self.roiExtent, self.printCommandResponse, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) # Set up timer for requesting snapshot self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000) def onStopReconstruction(self): self.snapshotTimer.stop() self.plusRemoteLogic.stopVolumeReconstruction(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.onVolumeLiveReconstructed, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) def onReconstVolume(self): self.offlineReconstructButton.setIcon(self.waitIcon) self.offlineReconstructButton.setText(" Offline Reconstruction in progress ...") self.offlineReconstructButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.offlineVolumeToReconstructSelector.currentText, self.outputSpacing, self.onVolumeReconstructed, self.OFFLINE_VOLUME_FILENAME, self.OFFLINE_VOLUME_NODE_NAME) def onScoutScanReconstVolume(self): self.startStopScoutScanButton.setIcon(self.waitIcon) self.startStopScoutScanButton.setText(" Scout Scan\n Reconstruction in progress ...") self.startStopScoutScanButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.lastScoutRecordingOutputFilename, self.outputSpacing, self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME, self.SCOUT_VOLUME_NODE_NAME) def onRequestVolumeReconstructionSnapshot(self, stopFlag = ""): self.plusRemoteLogic.getVolumeReconstructionSnapshot(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.LIVE_VOLUME_FILENAME, self.LIVE_VOLUME_NODE_NAME, self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired) def executeCommandDelayed(self, method, delay=100): # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same # therefore we wait a bit to make sure the image message is processed as well QTimer.singleShot(delay, method) def printCommandResponse(self, command, q): statusText = "Command {0} [{1}]: {2}\n".format(command.GetCommandName(), command.GetID(), command.StatusToString(command.GetStatus())) if command.GetResponseMessage(): statusText = statusText + command.GetResponseMessage() elif command.GetResponseText(): statusText = statusText + command.GetResponseText() logging.debug(statusText) def onGetCaptureDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return captureDeviceIdsListString = command.GetResponseMessage() if captureDeviceIdsListString: captureDevicesIdsList = captureDeviceIdsListString.split(",") else: captureDevicesIdsList = [] for i in range(0,len(captureDevicesIdsList)): if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1: self.captureIDSelector.addItem(captureDevicesIdsList[i]) def onGetVolumeReconstructorDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return volumeReconstructorDeviceIdsListString = command.GetResponseMessage() if volumeReconstructorDeviceIdsListString: volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split(",") else: volumeReconstructorDeviceIdsList = [] self.volumeReconstructorIDSelector.clear() self.volumeReconstructorIDSelector.addItems(volumeReconstructorDeviceIdsList) self.startStopRecordingButton.setEnabled(True) self.offlineReconstructButton.setEnabled(True) self.startStopScoutScanButton.setEnabled(True) if self.roiNode: self.startStopLiveReconstructionButton.setEnabled(True) def onVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) volumeToReconstructFileName = os.path.basename(command.GetResponseMessage()) self.offlineVolumeToReconstructSelector.insertItem(0,volumeToReconstructFileName) self.offlineVolumeToReconstructSelector.setCurrentIndex(0) def onScoutVolumeRecorded(self, command, q): self.printCommandResponse(command,q) self.offlineReconstructButton.setEnabled(True) if command.GetStatus() == command.CommandExpired: logging.fatal("Scout Volume Recording: Timeout while waiting for volume reconstruction result") return if command.GetStatus() == command.CommandSuccess: self.lastScoutRecordingOutputFilename = os.path.basename(command.GetResponseMessage()) self.onScoutScanReconstVolume() def onVolumeReconstructed(self, command, q): self.printCommandResponse(command,q) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setText("Offline Reconstruction") self.offlineReconstructButton.setEnabled(True) self.offlineReconstructButton.setChecked(False) if command.GetStatus() == command.CommandExpired: # volume reconstruction command timed out logging.fatal("Volume Reconstruction: Timeout while waiting for volume reconstruction result") return if command.GetStatus() != command.CommandSuccess: logging.debug("Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onVolumeReconstructedFinalize) def onVolumeReconstructedFinalize(self): applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode()) def onScoutVolumeReconstructed(self, command, q): self.printCommandResponse(command,q) if command.GetStatus() == command.CommandExpired: logging.fatal("Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result") return self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setText(" Scout Scan\n Start Recording") self.startStopScoutScanButton.setEnabled(True) if command.GetStatus() != command.CommandSuccess: logging.debug("Scout Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize) def onScoutVolumeReconstructedFinalize(self): # Create and initialize ROI after scout scan because low resolution scout scan is used to set # a smaller ROI for the live high resolution reconstruction self.roiNode = self.logic.onRoiInitialization(self.SCOUT_VOLUME_NODE_NAME, self.roiNode) self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) scoutScanVolumeNode = self.getScoutVolumeNode() applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(scoutScanVolumeNode) def onSnapshotAcquired(self, command, q): self.printCommandResponse(command,q) if not self.startStopLiveReconstructionButton.isChecked(): # live volume reconstruction is not active return self.executeCommandDelayed(self.onSnapshotAcquiredFinalize) def onSnapshotAcquiredFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode()) self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000) def onVolumeLiveReconstructed(self, command, q): self.printCommandResponse(command,q) if command.GetStatus() == command.CommandExpired: logging.fatal("LIVE Volume Reconstruction: Failed to stop volume reconstruction") return if command.GetStatus() != command.CommandSuccess: logging.debug("LIVE Volume Reconstruction " + command.GetResponseMessage()) return self.executeCommandDelayed(self.getLiveVolumeRecNode) def onVolumeLiveReconstructedFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())