def SimpleButton(parent, title, handler, page=None, image=None): tabpage = parent.getPage(page) button = QPushButton(tabpage) if image : bi = QPixmap() bi.loadFromData(image, "PNG") button.setIconSet(QIconSet(bi)) w = bi.width()+3 h = bi.height()+3 if w < 32: w = 32 if h < 32: h = 32 button.setMaximumSize(QSize(w, h)) else: button.setText(title) QToolTip.add(button, title) parent.connect(button, SIGNAL("clicked()"), handler) button.show() p = ToolbarPlugin(parent) p.getWidget = lambda: button
def SimpleButton(parent, title, handler, page=None, image=None): tabpage = parent.getPage(page) button = QPushButton(tabpage) if image: bi = QPixmap() bi.loadFromData(image, "PNG") button.setIconSet(QIconSet(bi)) w = bi.width() + 3 h = bi.height() + 3 if w < 32: w = 32 if h < 32: h = 32 button.setMaximumSize(QSize(w, h)) else: button.setText(title) QToolTip.add(button, title) parent.connect(button, SIGNAL("clicked()"), handler) button.show() p = ToolbarPlugin(parent) p.getWidget = lambda: button
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
class RulesetSelector(QWidget): """presents all available rulesets with previews""" def __init__(self, parent=None): super(RulesetSelector, self).__init__(parent) self.setContentsMargins(0, 0, 0, 0) self.setupUi() def setupUi(self): """layout the window""" decorateWindow(self, i18n('Customize rulesets')) self.setObjectName('Rulesets') hlayout = QHBoxLayout(self) v1layout = QVBoxLayout() self.v1widget = QWidget() v1layout = QVBoxLayout(self.v1widget) v2layout = QVBoxLayout() hlayout.addWidget(self.v1widget) hlayout.addLayout(v2layout) for widget in [self.v1widget, hlayout, v1layout, v2layout]: widget.setContentsMargins(0, 0, 0, 0) hlayout.setStretchFactor(self.v1widget, 10) self.btnCopy = QPushButton() self.btnRemove = QPushButton() self.btnCompare = QPushButton() self.btnClose = QPushButton() self.rulesetView = RuleTreeView( i18ncE('kajongg', 'Rule'), self.btnCopy, self.btnRemove, self.btnCompare) v1layout.addWidget(self.rulesetView) self.rulesetView.setWordWrap(True) self.rulesetView.setMouseTracking(True) spacerItem = QSpacerItem( 20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) v2layout.addWidget(self.btnCopy) v2layout.addWidget(self.btnRemove) v2layout.addWidget(self.btnCompare) self.btnCopy.clicked.connect(self.rulesetView.copyRow) self.btnRemove.clicked.connect(self.rulesetView.removeRow) self.btnCompare.clicked.connect(self.rulesetView.compareRow) self.btnClose.clicked.connect(self.hide) v2layout.addItem(spacerItem) v2layout.addWidget(self.btnClose) self.retranslateUi() StateSaver(self) self.show() def sizeHint(self): """we never want a horizontal scrollbar for player names, we always want to see them in full""" result = QWidget.sizeHint(self) available = Internal.app.desktop().availableGeometry() height = max(result.height(), available.height() * 2 // 3) width = max(result.width(), available.width() // 2) return QSize(width, height) def minimumSizeHint(self): """we never want a horizontal scrollbar for player names, we always want to see them in full""" return self.sizeHint() def showEvent(self, dummyEvent): """reload the rulesets""" self.refresh() def refresh(self): """retranslate and reload rulesets""" self.retranslateUi() self.rulesetView.rulesets = Ruleset.availableRulesets() def hideEvent(self, event): """close all differ dialogs""" for differ in self.rulesetView.differs: differ.hide() del differ QWidget.hideEvent(self, event) def retranslateUi(self): """translate to current language""" self.btnCopy.setText(i18n("C&opy")) self.btnCompare.setText(i18nc('Kajongg ruleset comparer', 'Co&mpare')) self.btnRemove.setText(i18n('&Remove')) self.btnClose.setText(i18n('&Close'))
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
import sys from qt import QApplication, QPushButton app=QApplication(sys.argv) button=QPushButton(None) button.setText("Hello World") app.setMainWidget(button) button.show() app.exec_loop()
class RulesetSelector(QWidget): """presents all available rulesets with previews""" def __init__(self, parent=None): super(RulesetSelector, self).__init__(parent) self.setContentsMargins(0, 0, 0, 0) self.setupUi() def setupUi(self): """layout the window""" decorateWindow(self, m18n('Customize rulesets')) self.setObjectName('Rulesets') hlayout = QHBoxLayout(self) v1layout = QVBoxLayout() self.v1widget = QWidget() v1layout = QVBoxLayout(self.v1widget) v2layout = QVBoxLayout() hlayout.addWidget(self.v1widget) hlayout.addLayout(v2layout) for widget in [self.v1widget, hlayout, v1layout, v2layout]: widget.setContentsMargins(0, 0, 0, 0) hlayout.setStretchFactor(self.v1widget, 10) self.btnCopy = QPushButton() self.btnRemove = QPushButton() self.btnCompare = QPushButton() self.btnClose = QPushButton() self.rulesetView = RuleTreeView( m18nc('kajongg', 'Rule'), self.btnCopy, self.btnRemove, self.btnCompare) v1layout.addWidget(self.rulesetView) self.rulesetView.setWordWrap(True) self.rulesetView.setMouseTracking(True) spacerItem = QSpacerItem( 20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) v2layout.addWidget(self.btnCopy) v2layout.addWidget(self.btnRemove) v2layout.addWidget(self.btnCompare) self.btnCopy.clicked.connect(self.rulesetView.copyRow) self.btnRemove.clicked.connect(self.rulesetView.removeRow) self.btnCompare.clicked.connect(self.rulesetView.compareRow) self.btnClose.clicked.connect(self.hide) v2layout.addItem(spacerItem) v2layout.addWidget(self.btnClose) self.retranslateUi() StateSaver(self) self.show() def sizeHint(self): """we never want a horizontal scrollbar for player names, we always want to see them in full""" result = QWidget.sizeHint(self) available = KApplication.kApplication().desktop().availableGeometry() height = max(result.height(), available.height() * 2 // 3) width = max(result.width(), available.width() // 2) return QSize(width, height) def minimumSizeHint(self): """we never want a horizontal scrollbar for player names, we always want to see them in full""" return self.sizeHint() def showEvent(self, dummyEvent): """reload the rulesets""" self.refresh() def refresh(self): """retranslate and reload rulesets""" self.retranslateUi() self.rulesetView.rulesets = Ruleset.availableRulesets() def hideEvent(self, event): """close all differ dialogs""" for differ in self.rulesetView.differs: differ.hide() del differ QWidget.hideEvent(self, event) def retranslateUi(self): """translate to current language""" self.btnCopy.setText(m18n("Copy")) self.btnCompare.setText(m18nc('Kajongg ruleset comparer', 'Compare')) self.btnRemove.setText(m18n("Remove")) self.btnClose.setText(m18n('Close'))
class ProstateTRUSNavUltrasound(UltraSound): OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha" SCOUT_VOLUME_FILENAME = "ScoutScan.mha" LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha" RECORDING_FILENAME = "Recording.mha" SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha" LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME SCOUT_VOLUME_NODE_NAME = "ScoutScan" OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference" LIVE_VOLUME_NODE_NAME = "liveReconstruction" APPLY_HOLE_FILLING_FOR_SNAPSHOT = False SNAPSHOT_INTERVAL = 3 OUTPUT_VOLUME_SPACING = 3 LIVE_OUTPUT_VOLUME_SPACING = 1 @property def roiNode(self): return self._roiNode @roiNode.setter def roiNode(self, node): self._roiNode = node if node is not None: self.startStopLiveReconstructionButton.setEnabled(True) else: self.startStopLiveReconstructionButton.setEnabled(False) def __init__(self, guideletParent): UltraSound.__init__(self, guideletParent) self.parameterNode = guideletParent.parameterNode self.parameterNodeObserver = None self._roiNode = None self.liveOutputSpacingValue = [ self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING ] self.outputSpacing = [ self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING ] self.roiOrigin = None self.roiExtent = None self.defaultParameterNode = None self.logic = ProstateTRUSNavUltrasoundLogic() def setupPanel(self, parentWidget): logging.debug('ProstateTRUSNavUltrasound.setupPanel') self.connectorNode = self.guideletParent.connectorNode self.connectorNodeConnected = False collapsibleButton = ctkCollapsibleButton() collapsibleButton.setProperty('collapsedHeight', 20) setButtonStyle(collapsibleButton, 2.0) collapsibleButton.text = "Ultrasound" parentWidget.addWidget(collapsibleButton) ultrasoundLayout = QFormLayout(collapsibleButton) ultrasoundLayout.setContentsMargins(12, 4, 4, 4) ultrasoundLayout.setSpacing(4) self.connectDisconnectButton = QPushButton("Connect") self.connectDisconnectButton.setToolTip( "If clicked, connection OpenIGTLink") hbox = QHBoxLayout() hbox.addWidget(self.connectDisconnectButton) ultrasoundLayout.addRow(hbox) self.setupIcons() self.captureIDSelector = QComboBox() self.captureIDSelector.setToolTip("Pick capture device ID") self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.volumeReconstructorIDSelector = QComboBox() self.volumeReconstructorIDSelector.setToolTip( "Pick volume reconstructor device ID") self.volumeReconstructorIDSelector.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopRecordingButton = QPushButton(" Start Recording") self.startStopRecordingButton.setCheckable(True) self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setEnabled(False) self.startStopRecordingButton.setToolTip("If clicked, start recording") self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) recordParametersControlsLayout = QGridLayout() self.filenameLabel = self.createLabel("Filename:", visible=False) recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0) # Offline Reconstruction self.offlineReconstructButton = QPushButton(" Offline Reconstruction") self.offlineReconstructButton.setCheckable(True) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setEnabled(False) self.offlineReconstructButton.setToolTip( "If clicked, reconstruct recorded volume") self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.offlineVolumeToReconstructSelector = QComboBox() self.offlineVolumeToReconstructSelector.setEditable(True) self.offlineVolumeToReconstructSelector.setToolTip( "Pick/set volume to reconstruct") self.offlineVolumeToReconstructSelector.visible = False hbox = QHBoxLayout() hbox.addWidget(self.startStopRecordingButton) hbox.addWidget(self.offlineReconstructButton) ultrasoundLayout.addRow(hbox) # Scout scan (record and low resolution reconstruction) and live reconstruction # Scout scan part self.startStopScoutScanButton = QPushButton( " Scout scan\n Start recording") self.startStopScoutScanButton.setCheckable(True) self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setToolTip("If clicked, start recording") self.startStopScoutScanButton.setEnabled(False) self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopLiveReconstructionButton = QPushButton( " Start live reconstruction") self.startStopLiveReconstructionButton.setCheckable(True) self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction") self.startStopLiveReconstructionButton.setEnabled(False) self.startStopLiveReconstructionButton.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding) self.displayRoiButton = QToolButton() self.displayRoiButton.setCheckable(True) self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") hbox = QHBoxLayout() hbox.addWidget(self.startStopScoutScanButton) hbox.addWidget(self.startStopLiveReconstructionButton) # hbox.addWidget(self.displayRoiButton) ultrasoundLayout.addRow(hbox) self.snapshotTimer = QTimer() self.snapshotTimer.setSingleShot(True) self.onParameterSetSelected() return collapsibleButton def setupResliceDriver(self): layoutManager = slicer.app.layoutManager() # Show ultrasound in red view. redSlice = layoutManager.sliceWidget('Red') redSliceLogic = redSlice.sliceLogic() redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID( self.liveUltrasoundNode_Reference.GetID()) resliceLogic = slicer.modules.volumereslicedriver.logic() if resliceLogic: redNode = slicer.util.getNode('vtkMRMLSliceNodeRed') redNode.SetSliceResolutionMode( slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes) resliceLogic.SetDriverForSlice( self.liveUltrasoundNode_Reference.GetID(), redNode) resliceLogic.SetModeForSlice( 6, redNode) # Transverse mode, default for PLUS ultrasound. else: logging.warning('Logic not found for Volume Reslice Driver') def createCollapsibleButton(self, text, collapsed=False): collapsibleButton = ctkCollapsibleButton() collapsibleButton.text = text collapsibleButton.collapsed = collapsed return collapsibleButton def createLabel(self, text, visible=True): label = QLabel() label.setText(text) label.visible = visible return label def setupConnections(self): self.startStopRecordingButton.connect( 'clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.connect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.connect( 'clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.connect( 'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.connect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.connect( 'currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.connect( 'currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.connect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.connect( self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.connect( 'clicked(bool)', self.onConnectDisconnectButtonClicked) def disconnect(self): self.startStopRecordingButton.disconnect( 'clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.disconnect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.disconnect( 'clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.disconnect( 'clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.disconnect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.disconnect( 'currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.disconnect( 'currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.disconnect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.disconnect( self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.disconnect( 'clicked(bool)', self.onConnectDisconnectButtonClicked) def setupIcons(self): self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace( "PlusRemote.py", "") self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Record.png') self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Stop.png') self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Wait.png') self.visibleOffIcon = QIcon(":Icons\VisibleOff.png") self.visibleOnIcon = QIcon(":Icons\VisibleOn.png") def onParameterSetSelected(self): # Set up default values for new nodes if self.parameterNode: self.plusRemoteLogic.setDefaultParameters(self.parameterNode) self.updateGuiFromParameterNode() def updateGuiFromParameterNode(self): self.parameterVolumeList = { 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector } for parameter in self.parameterVolumeList: if self.parameterNode.GetParameter(parameter): self.parameterVolumeList[parameter].blockSignals(True) self.parameterVolumeList[parameter].blockSignals(False) if self.parameterNode.GetParameter('CaptureID'): self.captureIDSelector.blockSignals(True) for i in range(0, self.captureIDSelector.count): if self.parameterNode.GetParameter( 'CaptureID') == self.captureIDSelector.itemText(i): self.captureIDSelector.setCurrentIndex( int(self.parameterNode.GetParameter('CaptureIdIndex'))) self.captureIDSelector.blockSignals(False) if self.parameterNode.GetParameter('VolumeReconstructor'): self.volumeReconstructorIDSelector.blockSignals(True) for i in range(0, self.volumeReconstructorIDSelector.count): if self.parameterNode.GetParameter( 'VolumeReconstructor' ) == self.volumeReconstructorIDSelector.itemText(i): self.volumeReconstructorIDSelector.setCurrentIndex( int( self.parameterNode.GetParameter( 'VolumeReconstructorIndex'))) self.volumeReconstructorIDSelector.blockSignals(False) self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0) def updateParameterNodeFromGui(self): #Update parameter node value to save when user change value in the interface if not self.parameterNode: return self.parametersList = { 'CaptureID': self.captureIDSelector.currentText, 'CaptureIdIndex': self.captureIDSelector.currentIndex, 'VolumeReconstructor': self.volumeReconstructorIDSelector.currentText, 'VolumeReconstructorIndex': self.volumeReconstructorIDSelector.currentIndex, 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector.currentIndex } for parameter in self.parametersList: self.parameterNode.SetParameter( parameter, str(self.parametersList[parameter])) if self.roiNode: roiNodeID = self.roiNode.GetID() self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID) # # Connector observation and actions # def onConnectorNodeConnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected") self.connectorNodeConnected = True self.captureIDSelector.setDisabled(False) self.volumeReconstructorIDSelector.setDisabled(False) self.plusRemoteLogic.getCaptureDeviceIds( self.connectorNode.GetID(), self.onGetCaptureDeviceCommandResponseReceived) self.plusRemoteLogic.getVolumeReconstructorDeviceIds( self.connectorNode.GetID(), self.onGetVolumeReconstructorDeviceCommandResponseReceived) self.connectDisconnectButton.setText("Disconnect") def onConnectorNodeDisconnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected") self.connectorNodeConnected = False self.startStopRecordingButton.setEnabled(False) self.startStopScoutScanButton.setEnabled(False) self.startStopLiveReconstructionButton.setEnabled(False) self.offlineReconstructButton.setEnabled(False) self.captureIDSelector.setDisabled(True) self.volumeReconstructorIDSelector.setDisabled(True) self.connectDisconnectButton.setText("Connect") def getLiveVolumeRecNode(self): liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME) return liveVolumeRecNode def getOfflineVolumeRecNode(self): offlineVolumeRecNode = slicer.util.getNode( self.OFFLINE_VOLUME_NODE_NAME) return offlineVolumeRecNode def getScoutVolumeNode(self): scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME) return scoutScanVolumeNode def onConnectDisconnectButtonClicked(self): if self.connectorNode.GetState( ) == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED: self.connectorNode.Stop() else: self.connectorNode.Start() def onStartStopRecordingButtonClicked(self): if self.startStopRecordingButton.isChecked(): self.startStopRecordingButton.setText(" Stop Recording") self.startStopRecordingButton.setIcon(self.stopIcon) self.startStopRecordingButton.setToolTip( "If clicked, stop recording") self.onStartRecording(self.generateRecordingOutputFilename()) else: self.startStopRecordingButton.setText(" Start Recording") self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setToolTip( "If clicked, start recording") self.onStopRecording(self.onVolumeRecorded) def onStartStopScoutScanButtonClicked(self): if self.startStopScoutScanButton.isChecked(): self.startStopScoutScanButton.setText( " Scout Scan\n Stop Recording and Reconstruct Recorded Volume" ) self.startStopScoutScanButton.setIcon(self.stopIcon) self.startStopScoutScanButton.setToolTip( "If clicked, stop recording and reconstruct recorded volume") self.onStartRecording(self.generateScoutRecordingOutputFilename()) else: self.onStopRecording(self.onScoutVolumeRecorded) def onStartStopLiveReconstructionButtonClicked(self): if self.startStopLiveReconstructionButton.isChecked(): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.startStopLiveReconstructionButton.setText( " Stop Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.stopIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, stop live reconstruction") self.onStartRecording(self.getLiveRecordingOutputFilename()) self.onStartReconstruction() else: self.startStopLiveReconstructionButton.setText( " Start Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction") self.onStopRecording(self.printCommandResponse) self.onStopReconstruction() def onDisplayRoiButtonClicked(self): if self.displayRoiButton.isChecked(): self.displayRoiButton.setIcon(self.visibleOnIcon) self.displayRoiButton.setToolTip("If clicked, hide ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(1) else: self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(0) def generateRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.RECORDING_FILENAME) def generateScoutRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.SCOUT_RECORDING_FILENAME) def getLiveRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.LIVE_RECORDING_FILENAME) def getLiveReconstructionOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename( self.LIVE_VOLUME_FILENAME) def onStartRecording(self, filename): self.plusRemoteLogic.startRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, filename, self.printCommandResponse) def onStopRecording(self, callback): self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, callback) def onStartReconstruction(self): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.plusRemoteLogic.startVolumeReconstuction( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.liveOutputSpacingValue, self.roiOrigin, self.roiExtent, self.printCommandResponse, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) # Set up timer for requesting snapshot self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000) def onStopReconstruction(self): self.snapshotTimer.stop() self.plusRemoteLogic.stopVolumeReconstruction( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.onVolumeLiveReconstructed, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) def onReconstVolume(self): self.offlineReconstructButton.setIcon(self.waitIcon) self.offlineReconstructButton.setText( " Offline Reconstruction in progress ...") self.offlineReconstructButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.offlineVolumeToReconstructSelector.currentText, self.outputSpacing, self.onVolumeReconstructed, self.OFFLINE_VOLUME_FILENAME, self.OFFLINE_VOLUME_NODE_NAME) def onScoutScanReconstVolume(self): self.startStopScoutScanButton.setIcon(self.waitIcon) self.startStopScoutScanButton.setText( " Scout Scan\n Reconstruction in progress ...") self.startStopScoutScanButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.lastScoutRecordingOutputFilename, self.outputSpacing, self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME, self.SCOUT_VOLUME_NODE_NAME) def onRequestVolumeReconstructionSnapshot(self, stopFlag=""): self.plusRemoteLogic.getVolumeReconstructionSnapshot( self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.LIVE_VOLUME_FILENAME, self.LIVE_VOLUME_NODE_NAME, self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired) def executeCommandDelayed(self, method, delay=100): # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same # therefore we wait a bit to make sure the image message is processed as well QTimer.singleShot(delay, method) def printCommandResponse(self, command, q): statusText = "Command {0} [{1}]: {2}\n".format( command.GetCommandName(), command.GetID(), command.StatusToString(command.GetStatus())) if command.GetResponseMessage(): statusText = statusText + command.GetResponseMessage() elif command.GetResponseText(): statusText = statusText + command.GetResponseText() logging.debug(statusText) def onGetCaptureDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return captureDeviceIdsListString = command.GetResponseMessage() if captureDeviceIdsListString: captureDevicesIdsList = captureDeviceIdsListString.split(",") else: captureDevicesIdsList = [] for i in range(0, len(captureDevicesIdsList)): if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1: self.captureIDSelector.addItem(captureDevicesIdsList[i]) def onGetVolumeReconstructorDeviceCommandResponseReceived( self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return volumeReconstructorDeviceIdsListString = command.GetResponseMessage() if volumeReconstructorDeviceIdsListString: volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split( ",") else: volumeReconstructorDeviceIdsList = [] self.volumeReconstructorIDSelector.clear() self.volumeReconstructorIDSelector.addItems( volumeReconstructorDeviceIdsList) self.startStopRecordingButton.setEnabled(True) self.offlineReconstructButton.setEnabled(True) self.startStopScoutScanButton.setEnabled(True) if self.roiNode: self.startStopLiveReconstructionButton.setEnabled(True) def onVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) volumeToReconstructFileName = os.path.basename( command.GetResponseMessage()) self.offlineVolumeToReconstructSelector.insertItem( 0, volumeToReconstructFileName) self.offlineVolumeToReconstructSelector.setCurrentIndex(0) def onScoutVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) if command.GetStatus() == command.CommandExpired: logging.fatal( "Scout Volume Recording: Timeout while waiting for volume reconstruction result" ) return if command.GetStatus() == command.CommandSuccess: self.lastScoutRecordingOutputFilename = os.path.basename( command.GetResponseMessage()) self.onScoutScanReconstVolume() def onVolumeReconstructed(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setText("Offline Reconstruction") self.offlineReconstructButton.setEnabled(True) self.offlineReconstructButton.setChecked(False) if command.GetStatus() == command.CommandExpired: # volume reconstruction command timed out logging.fatal( "Volume Reconstruction: Timeout while waiting for volume reconstruction result" ) return if command.GetStatus() != command.CommandSuccess: logging.debug("Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onVolumeReconstructedFinalize) def onVolumeReconstructedFinalize(self): applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode()) def onScoutVolumeReconstructed(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() == command.CommandExpired: logging.fatal( "Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result" ) return self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setText( " Scout Scan\n Start Recording") self.startStopScoutScanButton.setEnabled(True) if command.GetStatus() != command.CommandSuccess: logging.debug("Scout Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize) def onScoutVolumeReconstructedFinalize(self): # Create and initialize ROI after scout scan because low resolution scout scan is used to set # a smaller ROI for the live high resolution reconstruction self.roiNode = self.logic.onRoiInitialization( self.SCOUT_VOLUME_NODE_NAME, self.roiNode) self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI( self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) scoutScanVolumeNode = self.getScoutVolumeNode() applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(scoutScanVolumeNode) def onSnapshotAcquired(self, command, q): self.printCommandResponse(command, q) if not self.startStopLiveReconstructionButton.isChecked(): # live volume reconstruction is not active return self.executeCommandDelayed(self.onSnapshotAcquiredFinalize) def onSnapshotAcquiredFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode()) self.snapshotTimer.start(self.SNAPSHOT_INTERVAL * 1000) def onVolumeLiveReconstructed(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() == command.CommandExpired: logging.fatal( "LIVE Volume Reconstruction: Failed to stop volume reconstruction" ) return if command.GetStatus() != command.CommandSuccess: logging.debug("LIVE Volume Reconstruction " + command.GetResponseMessage()) return self.executeCommandDelayed(self.getLiveVolumeRecNode) def onVolumeLiveReconstructedFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())
class ProstateTRUSNavWidget(GuideletWidget): DEFAULT_PLUSSERVER_CHOOSER_TEXT = "Choose PlusServer.exe" DEFAULT_CONFIGURATION_CHOOSER_TEXT = "Select Configuration" def __init__(self, parent=None): GuideletWidget.__init__(self, parent) self.plusServerProcess = None self.configurationFile = self.getSetting( 'ConfigurationFile', self.DEFAULT_CONFIGURATION_CHOOSER_TEXT) self.serverExecutable = self.getSetting( 'PlusServer', self.DEFAULT_PLUSSERVER_CHOOSER_TEXT) def cleanup(self): GuideletWidget.cleanup(self) if self.plusServerProcess: self.plusServerProcess.terminate() def setup(self): showPlusServerWidget = True if _platform == "linux" or _platform == "linux2" or _platform == "darwin": #linux or linux or OS X message = "Attention: You are running Slicer on Linux or OS X. Do you have PlusServer installed on the current OS?" result = QMessageBox.question(slicer.util.mainWindow(), 'ProstateTRUSNav', message, QMessageBox.Yes | QMessageBox.No) showPlusServerWidget = result == QMessageBox.Yes if _platform == "win32" or showPlusServerWidget: # Windows... plusServerCollapsibleButton = ctkCollapsibleButton() plusServerCollapsibleButton.text = "PlusServer" self.layout.addWidget(plusServerCollapsibleButton) self.configurationFileChooserButton = QPushButton( self.configurationFile) self.configurationFileChooserButton.connect( 'clicked()', self.onConfigFileSelected) self.runPlusServerButton = QPushButton("Run PlusServer") self.runPlusServerButton.setCheckable(True) self.runPlusServerButton.connect('clicked()', self.onRunPlusServerButtonClicked) self.serverFormLayout = QFormLayout(plusServerCollapsibleButton) self.serverExecutableChooserButton = QPushButton( self.serverExecutable) self.serverExecutableChooserButton.connect( 'clicked()', self.onServerExecutableSelected) hbox = QHBoxLayout() hbox.addWidget(self.serverExecutableChooserButton) self.serverFormLayout.addRow(hbox) hbox = QHBoxLayout() hbox.addWidget(self.configurationFileChooserButton) hbox.addWidget(self.runPlusServerButton) self.serverFormLayout.addRow(hbox) GuideletWidget.setup(self) # do specific setup here if _platform == "win32" or showPlusServerWidget: self.launchGuideletButton.setEnabled(False) self.checkExecutableAndArgument() def checkExecutableAndArgument(self): if os.path.exists(self.serverExecutable) and os.path.exists( self.configurationFile): self.runPlusServerButton.setEnabled(True) else: self.runPlusServerButton.setEnabled(False) def getSetting(self, settingName, defaultValue=""): settings = QSettings() value = settings.value(self.moduleName + '/' + settingName, defaultValue) return value if value is not None and value != "" else defaultValue def setSetting(self, settingName, value): settings = QSettings() settings.setValue(self.moduleName + '/' + settingName, value) def addLauncherWidgets(self): GuideletWidget.addLauncherWidgets(self) # add launcher widget here def onServerExecutableSelected(self): executable = QFileDialog.getOpenFileName(self.parent, "PlusServer Executable", self.serverExecutable, "*.exe") if executable != "" and executable.find("PlusServer.exe"): self.serverExecutable = executable self.serverExecutableChooserButton.setText(executable) self.setSetting("PlusServer", executable) self.checkExecutableAndArgument() def onConfigFileSelected(self): self.configurationFile = QFileDialog.getOpenFileName( self.parent, "Choose Configuration File", self.configurationFile, "*.xml") if self.configurationFile != "": self.configurationFileChooserButton.setText( os.path.split(self.configurationFile)[1]) self.setSetting("ConfigurationFile", self.configurationFile) self.checkExecutableAndArgument() def onRunPlusServerButtonClicked(self): if self.runPlusServerButton.isChecked(): command = [ self.serverExecutable, "--config-file=" + self.configurationFile ] logging.info("Executing %s %s" % tuple(command)) self.plusServerProcess = Popen([ self.serverExecutable, "--config-file=" + self.configurationFile ]) if self.plusServerProcess: self.runPlusServerButton.setText("Quit Plus Server") self.launchGuideletButton.setEnabled(True) else: if self.plusServerProcess: self.plusServerProcess.terminate() self.runPlusServerButton.setText("Run PlusServer") self.launchGuideletButton.setEnabled(False) def collectParameterList(self): parameterList = GuideletWidget.collectParameterList(self) if not parameterList: parameterList = dict() parameterList['OfflineVolumeToReconstruct'] = 0, return parameterList def createGuideletInstance(self, parameterList=None): return ProstateTRUSNavGuidelet(None, self.guideletLogic, parameterList) def createGuideletLogic(self): return ProstateTRUSNavLogic()
# -*- coding: utf-8 -*- import sys from qt import QApplication, QPushButton app=QApplication(sys.argv) button=QPushButton(None) button.setText(unicode("Merhaba Dünya")) app.setMainWidget(button) button.show() app.exec_loop()
class ProstateTRUSNavWidget(GuideletWidget): DEFAULT_PLUSSERVER_CHOOSER_TEXT = "Choose PlusServer.exe" DEFAULT_CONFIGURATION_CHOOSER_TEXT = "Select Configuration" def __init__(self, parent = None): GuideletWidget.__init__(self, parent) self.plusServerProcess = None self.configurationFile = self.getSetting('ConfigurationFile', self.DEFAULT_CONFIGURATION_CHOOSER_TEXT) self.serverExecutable = self.getSetting('PlusServer', self.DEFAULT_PLUSSERVER_CHOOSER_TEXT) def cleanup(self): GuideletWidget.cleanup(self) if self.plusServerProcess: self.plusServerProcess.terminate() def setup(self): showPlusServerWidget = True if _platform == "linux" or _platform == "linux2" or _platform == "darwin": #linux or linux or OS X message = "Attention: You are running Slicer on Linux or OS X. Do you have PlusServer installed on the current OS?" result = QMessageBox.question(slicer.util.mainWindow(), 'ProstateTRUSNav', message, QMessageBox.Yes | QMessageBox.No) showPlusServerWidget = result == QMessageBox.Yes if _platform == "win32" or showPlusServerWidget: # Windows... plusServerCollapsibleButton = ctkCollapsibleButton() plusServerCollapsibleButton.text = "PlusServer" self.layout.addWidget(plusServerCollapsibleButton) self.configurationFileChooserButton = QPushButton(self.configurationFile) self.configurationFileChooserButton.connect('clicked()', self.onConfigFileSelected) self.runPlusServerButton = QPushButton("Run PlusServer") self.runPlusServerButton.setCheckable(True) self.runPlusServerButton.connect('clicked()', self.onRunPlusServerButtonClicked) self.serverFormLayout = QFormLayout(plusServerCollapsibleButton) self.serverExecutableChooserButton = QPushButton(self.serverExecutable) self.serverExecutableChooserButton.connect('clicked()', self.onServerExecutableSelected) hbox = QHBoxLayout() hbox.addWidget(self.serverExecutableChooserButton) self.serverFormLayout.addRow(hbox) hbox = QHBoxLayout() hbox.addWidget(self.configurationFileChooserButton) hbox.addWidget(self.runPlusServerButton) self.serverFormLayout.addRow(hbox) GuideletWidget.setup(self) # do specific setup here if _platform == "win32" or showPlusServerWidget: self.launchGuideletButton.setEnabled(False) self.checkExecutableAndArgument() def checkExecutableAndArgument(self): if os.path.exists(self.serverExecutable) and os.path.exists(self.configurationFile): self.runPlusServerButton.setEnabled(True) else: self.runPlusServerButton.setEnabled(False) def getSetting(self, settingName, defaultValue=""): settings = QSettings() value = settings.value(self.moduleName + '/' + settingName, defaultValue) return value if value is not None and value != "" else defaultValue def setSetting(self, settingName, value): settings = QSettings() settings.setValue(self.moduleName + '/'+ settingName, value) def addLauncherWidgets(self): GuideletWidget.addLauncherWidgets(self) # add launcher widget here def onServerExecutableSelected(self): executable = QFileDialog.getOpenFileName(self.parent, "PlusServer Executable", self.serverExecutable, "*.exe") if executable != "" and executable.find("PlusServer.exe"): self.serverExecutable = executable self.serverExecutableChooserButton.setText(executable) self.setSetting("PlusServer", executable) self.checkExecutableAndArgument() def onConfigFileSelected(self): self.configurationFile = QFileDialog.getOpenFileName(self.parent, "Choose Configuration File", self.configurationFile, "*.xml") if self.configurationFile != "": self.configurationFileChooserButton.setText(os.path.split(self.configurationFile)[1]) self.setSetting("ConfigurationFile", self.configurationFile) self.checkExecutableAndArgument() def onRunPlusServerButtonClicked(self): if self.runPlusServerButton.isChecked(): command = [self.serverExecutable, "--config-file="+self.configurationFile] logging.info("Executing %s %s" % tuple(command)) self.plusServerProcess = Popen([self.serverExecutable, "--config-file="+self.configurationFile]) if self.plusServerProcess: self.runPlusServerButton.setText("Quit Plus Server") self.launchGuideletButton.setEnabled(True) else: if self.plusServerProcess: self.plusServerProcess.terminate() self.runPlusServerButton.setText("Run PlusServer") self.launchGuideletButton.setEnabled(False) def collectParameterList(self): parameterList = GuideletWidget.collectParameterList(self) if not parameterList: parameterList = dict() parameterList['OfflineVolumeToReconstruct'] = 0, return parameterList def createGuideletInstance(self, parameterList = None): return ProstateTRUSNavGuidelet(None, self.guideletLogic, parameterList) def createGuideletLogic(self): return ProstateTRUSNavLogic()
class ProstateTRUSNavUltrasound(UltraSound): OFFLINE_VOLUME_FILENAME = "RecVol_Reference.mha" SCOUT_VOLUME_FILENAME = "ScoutScan.mha" LIVE_VOLUME_FILENAME = "LiveReconstructedVolume.mha" RECORDING_FILENAME = "Recording.mha" SCOUT_RECORDING_FILENAME = "ScoutScanRecording.mha" LIVE_RECORDING_FILENAME = LIVE_VOLUME_FILENAME SCOUT_VOLUME_NODE_NAME = "ScoutScan" OFFLINE_VOLUME_NODE_NAME = "RecVol_Reference" LIVE_VOLUME_NODE_NAME = "liveReconstruction" APPLY_HOLE_FILLING_FOR_SNAPSHOT = False SNAPSHOT_INTERVAL = 3 OUTPUT_VOLUME_SPACING = 3 LIVE_OUTPUT_VOLUME_SPACING = 1 @property def roiNode(self): return self._roiNode @roiNode.setter def roiNode(self, node): self._roiNode=node if node is not None: self.startStopLiveReconstructionButton.setEnabled(True) else: self.startStopLiveReconstructionButton.setEnabled(False) def __init__(self, guideletParent): UltraSound.__init__(self, guideletParent) self.parameterNode = guideletParent.parameterNode self.parameterNodeObserver = None self._roiNode = None self.liveOutputSpacingValue = [self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING, self.LIVE_OUTPUT_VOLUME_SPACING] self.outputSpacing = [self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING, self.OUTPUT_VOLUME_SPACING] self.roiOrigin = None self.roiExtent = None self.defaultParameterNode = None self.logic = ProstateTRUSNavUltrasoundLogic() def setupPanel(self, parentWidget): logging.debug('ProstateTRUSNavUltrasound.setupPanel') self.connectorNode = self.guideletParent.connectorNode self.connectorNodeConnected = False collapsibleButton = ctkCollapsibleButton() collapsibleButton.setProperty('collapsedHeight', 20) setButtonStyle(collapsibleButton, 2.0) collapsibleButton.text = "Ultrasound" parentWidget.addWidget(collapsibleButton) ultrasoundLayout = QFormLayout(collapsibleButton) ultrasoundLayout.setContentsMargins(12,4,4,4) ultrasoundLayout.setSpacing(4) self.connectDisconnectButton = QPushButton("Connect") self.connectDisconnectButton.setToolTip("If clicked, connection OpenIGTLink") hbox = QHBoxLayout() hbox.addWidget(self.connectDisconnectButton) ultrasoundLayout.addRow(hbox) self.setupIcons() self.captureIDSelector = QComboBox() self.captureIDSelector.setToolTip("Pick capture device ID") self.captureIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.volumeReconstructorIDSelector = QComboBox() self.volumeReconstructorIDSelector.setToolTip( "Pick volume reconstructor device ID" ) self.volumeReconstructorIDSelector.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopRecordingButton = QPushButton(" Start Recording") self.startStopRecordingButton.setCheckable(True) self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setEnabled(False) self.startStopRecordingButton.setToolTip("If clicked, start recording") self.startStopRecordingButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) recordParametersControlsLayout = QGridLayout() self.filenameLabel = self.createLabel("Filename:", visible=False) recordParametersControlsLayout.addWidget(self.filenameLabel, 1, 0) # Offline Reconstruction self.offlineReconstructButton = QPushButton(" Offline Reconstruction") self.offlineReconstructButton.setCheckable(True) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setEnabled(False) self.offlineReconstructButton.setToolTip("If clicked, reconstruct recorded volume") self.offlineReconstructButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.offlineVolumeToReconstructSelector = QComboBox() self.offlineVolumeToReconstructSelector.setEditable(True) self.offlineVolumeToReconstructSelector.setToolTip( "Pick/set volume to reconstruct" ) self.offlineVolumeToReconstructSelector.visible = False hbox = QHBoxLayout() hbox.addWidget(self.startStopRecordingButton) hbox.addWidget(self.offlineReconstructButton) ultrasoundLayout.addRow(hbox) # Scout scan (record and low resolution reconstruction) and live reconstruction # Scout scan part self.startStopScoutScanButton = QPushButton(" Scout scan\n Start recording") self.startStopScoutScanButton.setCheckable(True) self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setToolTip("If clicked, start recording") self.startStopScoutScanButton.setEnabled(False) self.startStopScoutScanButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.startStopLiveReconstructionButton = QPushButton(" Start live reconstruction") self.startStopLiveReconstructionButton.setCheckable(True) self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip("If clicked, start live reconstruction") self.startStopLiveReconstructionButton.setEnabled(False) self.startStopLiveReconstructionButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.displayRoiButton = QToolButton() self.displayRoiButton.setCheckable(True) self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") hbox = QHBoxLayout() hbox.addWidget(self.startStopScoutScanButton) hbox.addWidget(self.startStopLiveReconstructionButton) # hbox.addWidget(self.displayRoiButton) ultrasoundLayout.addRow(hbox) self.snapshotTimer = QTimer() self.snapshotTimer.setSingleShot(True) self.onParameterSetSelected() return collapsibleButton def setupResliceDriver(self): layoutManager = slicer.app.layoutManager() # Show ultrasound in red view. redSlice = layoutManager.sliceWidget('Red') redSliceLogic = redSlice.sliceLogic() redSliceLogic.GetSliceCompositeNode().SetBackgroundVolumeID(self.liveUltrasoundNode_Reference.GetID()) resliceLogic = slicer.modules.volumereslicedriver.logic() if resliceLogic: redNode = slicer.util.getNode('vtkMRMLSliceNodeRed') redNode.SetSliceResolutionMode(slicer.vtkMRMLSliceNode.SliceResolutionMatchVolumes) resliceLogic.SetDriverForSlice(self.liveUltrasoundNode_Reference.GetID(), redNode) resliceLogic.SetModeForSlice(6, redNode) # Transverse mode, default for PLUS ultrasound. else: logging.warning('Logic not found for Volume Reslice Driver') def createCollapsibleButton(self, text, collapsed=False): collapsibleButton = ctkCollapsibleButton() collapsibleButton.text = text collapsibleButton.collapsed = collapsed return collapsibleButton def createLabel(self, text, visible=True): label = QLabel() label.setText(text) label.visible = visible return label def setupConnections(self): self.startStopRecordingButton.connect('clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.connect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.connect('clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.connect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.connect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.connect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.connect('currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.connect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.connect(self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.connect('clicked(bool)', self.onConnectDisconnectButtonClicked) def disconnect(self): self.startStopRecordingButton.disconnect('clicked(bool)', self.onStartStopRecordingButtonClicked) self.offlineReconstructButton.disconnect('clicked(bool)', self.onReconstVolume) self.startStopScoutScanButton.disconnect('clicked(bool)', self.onStartStopScoutScanButtonClicked) self.startStopLiveReconstructionButton.disconnect('clicked(bool)', self.onStartStopLiveReconstructionButtonClicked) self.displayRoiButton.disconnect('clicked(bool)', self.onDisplayRoiButtonClicked) self.captureIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.volumeReconstructorIDSelector.disconnect('currentIndexChanged(QString)', self.updateParameterNodeFromGui) self.offlineVolumeToReconstructSelector.disconnect('currentIndexChanged(int)', self.updateParameterNodeFromGui) self.displayRoiButton.disconnect('clicked(bool)', self.updateParameterNodeFromGui) self.snapshotTimer.timeout.disconnect(self.onRequestVolumeReconstructionSnapshot) self.connectDisconnectButton.disconnect('clicked(bool)', self.onConnectDisconnectButtonClicked) def setupIcons(self): self.plusRemoteModuleDirectoryPath = slicer.modules.plusremote.path.replace("PlusRemote.py", "") self.recordIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Record.png') self.stopIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Stop.png') self.waitIcon = QIcon(self.plusRemoteModuleDirectoryPath + '/Resources/Icons/icon_Wait.png') self.visibleOffIcon = QIcon(":Icons\VisibleOff.png") self.visibleOnIcon = QIcon(":Icons\VisibleOn.png") def onParameterSetSelected(self): # Set up default values for new nodes if self.parameterNode: self.plusRemoteLogic.setDefaultParameters(self.parameterNode) self.updateGuiFromParameterNode() def updateGuiFromParameterNode(self): self.parameterVolumeList = {'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector} for parameter in self.parameterVolumeList: if self.parameterNode.GetParameter(parameter): self.parameterVolumeList[parameter].blockSignals(True) self.parameterVolumeList[parameter].blockSignals(False) if self.parameterNode.GetParameter('CaptureID'): self.captureIDSelector.blockSignals(True) for i in range(0, self.captureIDSelector.count): if self.parameterNode.GetParameter('CaptureID') == self.captureIDSelector.itemText(i): self.captureIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('CaptureIdIndex'))) self.captureIDSelector.blockSignals(False) if self.parameterNode.GetParameter('VolumeReconstructor'): self.volumeReconstructorIDSelector.blockSignals(True) for i in range(0, self.volumeReconstructorIDSelector.count): if self.parameterNode.GetParameter('VolumeReconstructor') == self.volumeReconstructorIDSelector.itemText(i): self.volumeReconstructorIDSelector.setCurrentIndex(int(self.parameterNode.GetParameter('VolumeReconstructorIndex'))) self.volumeReconstructorIDSelector.blockSignals(False) self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0) def updateParameterNodeFromGui(self): #Update parameter node value to save when user change value in the interface if not self.parameterNode: return self.parametersList = {'CaptureID': self.captureIDSelector.currentText, 'CaptureIdIndex': self.captureIDSelector.currentIndex, 'VolumeReconstructor': self.volumeReconstructorIDSelector.currentText, 'VolumeReconstructorIndex': self.volumeReconstructorIDSelector.currentIndex, 'OfflineVolumeToReconstruct': self.offlineVolumeToReconstructSelector.currentIndex} for parameter in self.parametersList: self.parameterNode.SetParameter(parameter, str(self.parametersList[parameter])) if self.roiNode: roiNodeID = self.roiNode.GetID() self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID) # # Connector observation and actions # def onConnectorNodeConnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeConnected") self.connectorNodeConnected = True self.captureIDSelector.setDisabled(False) self.volumeReconstructorIDSelector.setDisabled(False) self.plusRemoteLogic.getCaptureDeviceIds(self.connectorNode.GetID(), self.onGetCaptureDeviceCommandResponseReceived) self.plusRemoteLogic.getVolumeReconstructorDeviceIds(self.connectorNode.GetID(), self.onGetVolumeReconstructorDeviceCommandResponseReceived) self.connectDisconnectButton.setText("Disconnect") def onConnectorNodeDisconnected(self): logging.debug("ProstateTrusUltrasound:onConnectorNodeDisconnected") self.connectorNodeConnected = False self.startStopRecordingButton.setEnabled(False) self.startStopScoutScanButton.setEnabled(False) self.startStopLiveReconstructionButton.setEnabled(False) self.offlineReconstructButton.setEnabled(False) self.captureIDSelector.setDisabled(True) self.volumeReconstructorIDSelector.setDisabled(True) self.connectDisconnectButton.setText("Connect") def getLiveVolumeRecNode(self): liveVolumeRecNode = slicer.util.getNode(self.LIVE_VOLUME_NODE_NAME) return liveVolumeRecNode def getOfflineVolumeRecNode(self): offlineVolumeRecNode = slicer.util.getNode(self.OFFLINE_VOLUME_NODE_NAME) return offlineVolumeRecNode def getScoutVolumeNode(self): scoutScanVolumeNode = slicer.util.getNode(self.SCOUT_VOLUME_NODE_NAME) return scoutScanVolumeNode def onConnectDisconnectButtonClicked(self): if self.connectorNode.GetState() == slicer.vtkMRMLIGTLConnectorNode.STATE_CONNECTED: self.connectorNode.Stop() else: self.connectorNode.Start() def onStartStopRecordingButtonClicked(self): if self.startStopRecordingButton.isChecked(): self.startStopRecordingButton.setText(" Stop Recording") self.startStopRecordingButton.setIcon(self.stopIcon) self.startStopRecordingButton.setToolTip( "If clicked, stop recording" ) self.onStartRecording(self.generateRecordingOutputFilename()) else: self.startStopRecordingButton.setText(" Start Recording") self.startStopRecordingButton.setIcon(self.recordIcon) self.startStopRecordingButton.setToolTip( "If clicked, start recording" ) self.onStopRecording(self.onVolumeRecorded) def onStartStopScoutScanButtonClicked(self): if self.startStopScoutScanButton.isChecked(): self.startStopScoutScanButton.setText(" Scout Scan\n Stop Recording and Reconstruct Recorded Volume") self.startStopScoutScanButton.setIcon(self.stopIcon) self.startStopScoutScanButton.setToolTip( "If clicked, stop recording and reconstruct recorded volume" ) self.onStartRecording(self.generateScoutRecordingOutputFilename()) else: self.onStopRecording(self.onScoutVolumeRecorded) def onStartStopLiveReconstructionButtonClicked(self): if self.startStopLiveReconstructionButton.isChecked(): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.startStopLiveReconstructionButton.setText(" Stop Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.stopIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, stop live reconstruction" ) self.onStartRecording(self.getLiveRecordingOutputFilename()) self.onStartReconstruction() else: self.startStopLiveReconstructionButton.setText(" Start Live Reconstruction") self.startStopLiveReconstructionButton.setIcon(self.recordIcon) self.startStopLiveReconstructionButton.setToolTip( "If clicked, start live reconstruction" ) self.onStopRecording(self.printCommandResponse) self.onStopReconstruction() def onDisplayRoiButtonClicked(self): if self.displayRoiButton.isChecked(): self.displayRoiButton.setIcon(self.visibleOnIcon) self.displayRoiButton.setToolTip("If clicked, hide ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(1) else: self.displayRoiButton.setIcon(self.visibleOffIcon) self.displayRoiButton.setToolTip("If clicked, display ROI") if self.roiNode: self.roiNode.SetDisplayVisibility(0) def generateRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.RECORDING_FILENAME) def generateScoutRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.SCOUT_RECORDING_FILENAME) def getLiveRecordingOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_RECORDING_FILENAME) def getLiveReconstructionOutputFilename(self): return self.plusRemoteLogic.addTimestampToFilename(self.LIVE_VOLUME_FILENAME) def onStartRecording(self, filename): self.plusRemoteLogic.startRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, filename, self.printCommandResponse) def onStopRecording(self, callback): self.plusRemoteLogic.stopRecording(self.connectorNode.GetID(), self.captureIDSelector.currentText, callback) def onStartReconstruction(self): if self.roiNode: self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) self.plusRemoteLogic.startVolumeReconstuction(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.liveOutputSpacingValue, self.roiOrigin, self.roiExtent, self.printCommandResponse, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) # Set up timer for requesting snapshot self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000) def onStopReconstruction(self): self.snapshotTimer.stop() self.plusRemoteLogic.stopVolumeReconstruction(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.onVolumeLiveReconstructed, self.getLiveReconstructionOutputFilename(), self.LIVE_VOLUME_NODE_NAME) def onReconstVolume(self): self.offlineReconstructButton.setIcon(self.waitIcon) self.offlineReconstructButton.setText(" Offline Reconstruction in progress ...") self.offlineReconstructButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.offlineVolumeToReconstructSelector.currentText, self.outputSpacing, self.onVolumeReconstructed, self.OFFLINE_VOLUME_FILENAME, self.OFFLINE_VOLUME_NODE_NAME) def onScoutScanReconstVolume(self): self.startStopScoutScanButton.setIcon(self.waitIcon) self.startStopScoutScanButton.setText(" Scout Scan\n Reconstruction in progress ...") self.startStopScoutScanButton.setEnabled(False) self.plusRemoteLogic.reconstructRecorded(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.lastScoutRecordingOutputFilename, self.outputSpacing, self.onScoutVolumeReconstructed, self.SCOUT_VOLUME_FILENAME, self.SCOUT_VOLUME_NODE_NAME) def onRequestVolumeReconstructionSnapshot(self, stopFlag = ""): self.plusRemoteLogic.getVolumeReconstructionSnapshot(self.connectorNode.GetID(), self.volumeReconstructorIDSelector.currentText, self.LIVE_VOLUME_FILENAME, self.LIVE_VOLUME_NODE_NAME, self.APPLY_HOLE_FILLING_FOR_SNAPSHOT, self.onSnapshotAcquired) def executeCommandDelayed(self, method, delay=100): # Order of OpenIGTLink message receiving and processing is not guaranteed to be the same # therefore we wait a bit to make sure the image message is processed as well QTimer.singleShot(delay, method) def printCommandResponse(self, command, q): statusText = "Command {0} [{1}]: {2}\n".format(command.GetCommandName(), command.GetID(), command.StatusToString(command.GetStatus())) if command.GetResponseMessage(): statusText = statusText + command.GetResponseMessage() elif command.GetResponseText(): statusText = statusText + command.GetResponseText() logging.debug(statusText) def onGetCaptureDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return captureDeviceIdsListString = command.GetResponseMessage() if captureDeviceIdsListString: captureDevicesIdsList = captureDeviceIdsListString.split(",") else: captureDevicesIdsList = [] for i in range(0,len(captureDevicesIdsList)): if self.captureIDSelector.findText(captureDevicesIdsList[i]) == -1: self.captureIDSelector.addItem(captureDevicesIdsList[i]) def onGetVolumeReconstructorDeviceCommandResponseReceived(self, command, q): self.printCommandResponse(command, q) if command.GetStatus() != command.CommandSuccess: return volumeReconstructorDeviceIdsListString = command.GetResponseMessage() if volumeReconstructorDeviceIdsListString: volumeReconstructorDeviceIdsList = volumeReconstructorDeviceIdsListString.split(",") else: volumeReconstructorDeviceIdsList = [] self.volumeReconstructorIDSelector.clear() self.volumeReconstructorIDSelector.addItems(volumeReconstructorDeviceIdsList) self.startStopRecordingButton.setEnabled(True) self.offlineReconstructButton.setEnabled(True) self.startStopScoutScanButton.setEnabled(True) if self.roiNode: self.startStopLiveReconstructionButton.setEnabled(True) def onVolumeRecorded(self, command, q): self.printCommandResponse(command, q) self.offlineReconstructButton.setEnabled(True) volumeToReconstructFileName = os.path.basename(command.GetResponseMessage()) self.offlineVolumeToReconstructSelector.insertItem(0,volumeToReconstructFileName) self.offlineVolumeToReconstructSelector.setCurrentIndex(0) def onScoutVolumeRecorded(self, command, q): self.printCommandResponse(command,q) self.offlineReconstructButton.setEnabled(True) if command.GetStatus() == command.CommandExpired: logging.fatal("Scout Volume Recording: Timeout while waiting for volume reconstruction result") return if command.GetStatus() == command.CommandSuccess: self.lastScoutRecordingOutputFilename = os.path.basename(command.GetResponseMessage()) self.onScoutScanReconstVolume() def onVolumeReconstructed(self, command, q): self.printCommandResponse(command,q) self.offlineReconstructButton.setIcon(self.recordIcon) self.offlineReconstructButton.setText("Offline Reconstruction") self.offlineReconstructButton.setEnabled(True) self.offlineReconstructButton.setChecked(False) if command.GetStatus() == command.CommandExpired: # volume reconstruction command timed out logging.fatal("Volume Reconstruction: Timeout while waiting for volume reconstruction result") return if command.GetStatus() != command.CommandSuccess: logging.debug("Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onVolumeReconstructedFinalize) def onVolumeReconstructedFinalize(self): applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(self.getOfflineVolumeRecNode()) def onScoutVolumeReconstructed(self, command, q): self.printCommandResponse(command,q) if command.GetStatus() == command.CommandExpired: logging.fatal("Scout Volume Reconstruction: Timeout while waiting for scout volume reconstruction result") return self.startStopScoutScanButton.setIcon(self.recordIcon) self.startStopScoutScanButton.setText(" Scout Scan\n Start Recording") self.startStopScoutScanButton.setEnabled(True) if command.GetStatus() != command.CommandSuccess: logging.debug("Scout Volume Reconstruction: " + command.GetResponseMessage()) return self.executeCommandDelayed(self.onScoutVolumeReconstructedFinalize) def onScoutVolumeReconstructedFinalize(self): # Create and initialize ROI after scout scan because low resolution scout scan is used to set # a smaller ROI for the live high resolution reconstruction self.roiNode = self.logic.onRoiInitialization(self.SCOUT_VOLUME_NODE_NAME, self.roiNode) self.roiOrigin, self.roiExtent = self.logic.updateVolumeOriginAndExtentFromROI(self.LIVE_OUTPUT_VOLUME_SPACING, self.roiNode) scoutScanVolumeNode = self.getScoutVolumeNode() applicationLogic = slicer.app.applicationLogic() applicationLogic.FitSliceToAll() self.guideletParent.showVolumeRendering(scoutScanVolumeNode) def onSnapshotAcquired(self, command, q): self.printCommandResponse(command,q) if not self.startStopLiveReconstructionButton.isChecked(): # live volume reconstruction is not active return self.executeCommandDelayed(self.onSnapshotAcquiredFinalize) def onSnapshotAcquiredFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode()) self.snapshotTimer.start(self.SNAPSHOT_INTERVAL*1000) def onVolumeLiveReconstructed(self, command, q): self.printCommandResponse(command,q) if command.GetStatus() == command.CommandExpired: logging.fatal("LIVE Volume Reconstruction: Failed to stop volume reconstruction") return if command.GetStatus() != command.CommandSuccess: logging.debug("LIVE Volume Reconstruction " + command.GetResponseMessage()) return self.executeCommandDelayed(self.getLiveVolumeRecNode) def onVolumeLiveReconstructedFinalize(self): self.guideletParent.showVolumeRendering(self.getLiveVolumeRecNode())