class TaurusMacroExecutorWidget(TaurusWidget): def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setObjectName(self.__class__.__name__) self._doorName = "" self._macroId = None self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.addToFavouritesAction = Qt.QAction( getThemeIcon("software-update-available"), "Add to favourites", self) self.connect(self.addToFavouritesAction, Qt.SIGNAL("triggered()"), self.onAddToFavourites) self.addToFavouritesAction.setToolTip("Add to favourites") self.stopMacroAction = Qt.QAction( getIcon(":/actions/media_playback_stop.svg"), "Stop macro", self) self.connect(self.stopMacroAction, Qt.SIGNAL("triggered()"), self.onStopMacro) self.stopMacroAction.setToolTip("Stop macro") self.pauseMacroAction = Qt.QAction( getIcon(":/actions/media_playback_pause.svg"), "Pause macro", self) self.connect(self.pauseMacroAction, Qt.SIGNAL("triggered()"), self.onPauseMacro) self.pauseMacroAction.setToolTip("Pause macro") self.playMacroAction = Qt.QAction( getIcon(":/actions/media_playback_start.svg"), "Start macro", self) self.connect(self.playMacroAction, Qt.SIGNAL("triggered()"), self.onPlayMacro) self.playMacroAction.setToolTip("Start macro") actionsLayout = Qt.QHBoxLayout() actionsLayout.setContentsMargins(0, 0, 0, 0) addToFavouritsButton = Qt.QToolButton() addToFavouritsButton.setDefaultAction(self.addToFavouritesAction) self.addToFavouritesAction.setEnabled(False) actionsLayout.addWidget(addToFavouritsButton) self.macroComboBox = MacroComboBox(self) self.macroComboBox.setUseParentModel(True) self.macroComboBox.setModelColumn(0) actionsLayout.addWidget(self.macroComboBox) stopMacroButton = Qt.QToolButton() stopMacroButton.setDefaultAction(self.stopMacroAction) actionsLayout.addWidget(stopMacroButton) pauseMacroButton = Qt.QToolButton() pauseMacroButton.setDefaultAction(self.pauseMacroAction) actionsLayout.addWidget(pauseMacroButton) self.playMacroButton = Qt.QToolButton() self.playMacroButton.setDefaultAction(self.playMacroAction) actionsLayout.addWidget(self.playMacroButton) self.disableControlActions() self.doorStateLed = TaurusLed(self) actionsLayout.addWidget(self.doorStateLed) self.layout().addLayout(actionsLayout) splitter = Qt.QSplitter(self) self.layout().addWidget(splitter) splitter.setOrientation(Qt.Qt.Vertical) self._paramEditorModel = ParamEditorModel() self.stackedWidget = Qt.QStackedWidget() self.standardMacroParametersEditor = StandardMacroParametersEditor( self.stackedWidget) self.stackedWidget.addWidget(self.standardMacroParametersEditor) self.customMacroParametersEditor = None splitter.addWidget(self.stackedWidget) self._favouritesBuffer = None self.favouritesMacrosEditor = FavouritesMacrosEditor(self) self.registerConfigDelegate(self.favouritesMacrosEditor) self.favouritesMacrosEditor.setUseParentModel(True) self.favouritesMacrosEditor.setFocusPolicy(Qt.Qt.NoFocus) self._historyBuffer = None self.historyMacrosViewer = HistoryMacrosViewer(self) self.registerConfigDelegate(self.historyMacrosViewer) self.historyMacrosViewer.setUseParentModel(True) self.historyMacrosViewer.setFocusPolicy(Qt.Qt.NoFocus) self.tabMacroListsWidget = Qt.QTabWidget(self) self.tabMacroListsWidget.addTab(self.favouritesMacrosEditor, "Favourite list") self.tabMacroListsWidget.addTab(self.historyMacrosViewer, "History Viewer") splitter.addWidget(self.tabMacroListsWidget) self._isHistoryMacro = False self.macroProgressBar = MacroProgressBar(self) self.layout().addWidget(self.macroProgressBar) #spockCommandLabel = Qt.QLabel("Spock command:", self) #spockCommandLabel.setFont(Qt.QFont("Courier",9)) self.spockCommand = SpockCommandWidget("Spock", self) self.spockCommand.setSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) self.spockCommand.setUseParentModel(True) spockCommandLayout = Qt.QHBoxLayout() spockCommandLayout.setContentsMargins(0, 0, 0, 0) #spockCommandLayout.addWidget(spockCommandLabel) spockCommandLayout.addWidget(self.spockCommand) self.layout().addLayout(spockCommandLayout) self.connect(self.macroComboBox, Qt.SIGNAL("currentIndexChanged(QString)"), self.onMacroComboBoxChanged) self.connect(self.favouritesMacrosEditor.list, Qt.SIGNAL("favouriteSelected"), self.onFavouriteSelected) self.connect(self.historyMacrosViewer.list, Qt.SIGNAL("historySelected"), self.onHistorySelected) self.connect(self.spockCommand, Qt.SIGNAL("pressedReturn"), self.onPlayMacro) self.connect(self.spockCommand, Qt.SIGNAL("spockComboBox"), self.setComboBoxItem) self.connect(self.spockCommand, Qt.SIGNAL("elementUp"), self.setHistoryUp) self.connect(self.spockCommand, Qt.SIGNAL("elementDown"), self.setHistoryDown) self.connect(self.spockCommand, Qt.SIGNAL("setHistoryFocus"), self.setHistoryFocus) self.connect(self.spockCommand, Qt.SIGNAL("expandTree"), self.standardMacroParametersEditor.tree.expandAll) def macroId(self): return self._macroId def contextMenuEvent(self, event): menu = Qt.QMenu() action = menu.addAction(getThemeIcon("view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) self.stopMacroAction.setEnabled(True) elif doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM: self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(True) def setMacroId(self, macroId): self._macroId = macroId def doorName(self): return self._doorName def setDoorName(self, doorName): self._doorName = doorName def setFavouritesBuffer(self, favouritesMacro): self._favouritesBuffer = favouritesMacro #History Widget def setHistoryUp(self): self.setHistoryFocus() self.historyMacrosViewer.listElementUp() def setHistoryDown(self): self.setHistoryFocus() self.historyMacrosViewer.listElementDown() def setHistoryFocus(self): self.tabMacroListsWidget.setCurrentWidget(self.historyMacrosViewer) #self.historyMacrosViewer.setFocus() def historyBuffer(self): return self._historyBuffer def setHistoryBuffer(self, favouritesMacro): self._historyBuffer = favouritesMacro def favouritesBuffer(self): return self._favouritesBuffer def paramEditorModel(self): return self._paramEditorModel def setParamEditorModel(self, paramEditorModel): self._paramEditorModel = paramEditorModel def setComboBoxItem(self, macroName): self.macroComboBox.selectMacro(macroName) def onMacroComboBoxChanged(self, macroName): macroName = str(macroName) if macroName == "": macroName, macroNode = None, None # macroNode = macro.MacroNode(name="") self.playMacroAction.setEnabled(False) self.addToFavouritesAction.setEnabled(False) else: if self._isHistoryMacro: macroNode = self.historyBuffer() self.setHistoryBuffer(None) self.favouritesMacrosEditor.list.clearSelection() else: macroNode = self.favouritesBuffer() self.setFavouritesBuffer(None) self.historyMacrosViewer.list.clearSelection() self._isHistoryMacro = False if macroNode is None: macroNode = self.getModelObj().getMacroNodeObj(macroName) self.playMacroAction.setEnabled(True) self.addToFavouritesAction.setEnabled(True) self.paramEditorModel().setRoot(macroNode) self.spockCommand.setModel(self.paramEditorModel()) if self.stackedWidget.count() == 2: self.stackedWidget.removeWidget(self.customMacroParametersEditor) self.customMacroParametersEditor.setParent(None) self.customMacroParametersEditor = ParamEditorManager().getMacroEditor( macroName, self.stackedWidget) if self.customMacroParametersEditor: self.customMacroParametersEditor.setModel(self.paramEditorModel()) self.stackedWidget.addWidget(self.customMacroParametersEditor) self.stackedWidget.setCurrentWidget( self.customMacroParametersEditor) else: self.standardMacroParametersEditor.setModel( self.paramEditorModel()) self.emit(Qt.SIGNAL("macroNameChanged"), macroName) def onFavouriteSelected(self, macroNode): self.setFavouritesBuffer(macroNode) name = "" if not macroNode is None: name = macroNode.name() self._isHistoryMacro = False self.macroComboBox.selectMacro(name) def onHistorySelected(self, macroNode): self.setHistoryBuffer(macroNode) name = "" if not macroNode is None: name = macroNode.name() self._isHistoryMacro = True self.macroComboBox.selectMacro(name) def onAddToFavourites(self): self.favouritesMacrosEditor.addMacro( deepcopy(self.paramEditorModel().root())) def addToHistory(self): self.historyMacrosViewer.addMacro( deepcopy(self.paramEditorModel().root())) def onDoorChanged(self, doorName): self.setDoorName(doorName) if self.doorName() == "": self.doorStateLed.setModel(None) return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON: self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") elif doorState == PyTango.DevState.STANDBY: self.playMacroAction.setText("Resume macro") self.playMacroAction.setToolTip("Resume macro") def onPlayMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM: self.setFocus() paramEditorModel = self.paramEditorModel() macroNode = paramEditorModel.root() id = macroNode.assignId() self.setMacroId(id) params, alerts = macroNode.toRun() xmlString = paramEditorModel.toXmlString() if len(alerts) > 0: Qt.QMessageBox.warning(self, "Macro parameters warning", alerts) return door.runMacro(xmlString) self.addToHistory() # door.runMacro(str(macroNode.name()), params) elif doorState == PyTango.DevState.STANDBY: door.command_inout("ResumeMacro") else: Qt.QMessageBox.warning( self, "Error while starting/resuming macro", "It was not possible to start/resume macro, because state of the door was different than ON/STANDBY" ) def onStopMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") else: Qt.QMessageBox.warning( self, "Error while stopping macro", "It was not possible to stop macro, because state of the door was different than RUNNING or STANDBY" ) def onPauseMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") else: Qt.QMessageBox.warning( self, "Error while pausing macro", "It was not possible to pause macro, because state of the door was different than RUNNING" ) def onMacroStatusUpdated(self, data): macro = data[0] if macro is None: return data = data[1][0] state, range, step, id = data["state"], data["range"], data[ "step"], data["id"] if id is None: return id = int(id) if id != self.macroId(): return macroName = macro.name shortMessage = "" if state == "start": self.emit(Qt.SIGNAL("macroStarted"), "DoorOutput") self.macroProgressBar.setRange(range[0], range[1]) self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) self.stopMacroAction.setEnabled(True) self.emit(Qt.SIGNAL("plotablesFilterChanged"), None) self.emit(Qt.SIGNAL("plotablesFilterChanged"), standardPlotablesFilter) shortMessage = "Macro %s started." % macroName elif state == "pause": self.playMacroAction.setText("Resume macro") self.playMacroAction.setToolTip("Resume macro") self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) shortMessage = "Macro %s paused." % macroName elif state == "resume": self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) shortMessage = "Macro %s resumed." % macroName elif state == "stop" or state == "finish": self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s finished." % macroName elif state == "exception": self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s error." % macroName exc_value, exc_stack = data['exc_value'], data['exc_stack'] exceptionDialog = TaurusMessageBox(MacroRunException, exc_value, exc_stack) exceptionDialog.exec_() elif state == "abort": self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s stopped." % macroName elif state == "step": shortMessage = "Macro %s at %d %% of progress." % (macroName, step) self.emit(Qt.SIGNAL("shortMessageEmitted"), shortMessage) self.macroProgressBar.setValue(step) def disableControlActions(self): self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) self.playMacroAction.setEnabled(False) def setModel(self, model): oldModelObj = self.getModelObj() if oldModelObj is not None: self.disconnect(oldModelObj, Qt.SIGNAL("macrosUpdated"), self.macroComboBox.onMacrosUpdated) TaurusWidget.setModel(self, model) newModelObj = self.getModelObj() self.connect(newModelObj, Qt.SIGNAL("macrosUpdated"), self.macroComboBox.onMacrosUpdated) @classmethod def getQtDesignerPluginInfo(cls): return { 'container': False, 'group': 'Taurus Sardana', 'module': 'taurus.qt.qtgui.extra_macroexecutor', 'icon': ':/designer/frame.png' }
class TaurusSequencerWidget(TaurusWidget): macroStarted = Qt.pyqtSignal('QString') plotablesFilterChanged = Qt.pyqtSignal(object) currentMacroChanged = Qt.pyqtSignal(object) macroNameChanged = Qt.pyqtSignal('QString') shortMessageEmitted = Qt.pyqtSignal('QString') sequenceEmpty = Qt.pyqtSignal() def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) # list representing all macros ids (all from sequence) currently # executed self._macroIds = [] self._sequencesPath = str(Qt.QDir.homePath()) self._sequenceModel = MacroSequenceTreeModel() self.registerConfigProperty( "sequencesPath", "setSequencesPath", "sequencesPath") self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) splitter = Qt.QSplitter() self.layout().addWidget(splitter) splitter.setOrientation(Qt.Qt.Vertical) sequenceEditor = TaurusWidget() splitter.addWidget(sequenceEditor) sequenceEditor.setUseParentModel(True) sequenceEditor.setLayout(Qt.QVBoxLayout()) sequenceEditor.layout().setContentsMargins(0, 0, 0, 0) self.tree = MacroSequenceTree(sequenceEditor) self.sequenceProxyModel = MacroSequenceProxyModel() self.sequenceProxyModel.setSourceModel(self._sequenceModel) self.tree.setModel(self.sequenceProxyModel) self.tree.setItemDelegate(SequenceEditorDelegate(self.tree)) actionsLayout = Qt.QHBoxLayout() actionsLayout.setContentsMargins(0, 0, 0, 0) self.newSequenceAction = Qt.QAction( getThemeIcon("document-new"), "New", self) self.newSequenceAction.triggered.connect(self.onNewSequence) self.newSequenceAction.setToolTip("New sequence") self.newSequenceAction.setEnabled(False) newSequenceButton = Qt.QToolButton() newSequenceButton.setDefaultAction(self.newSequenceAction) actionsLayout.addWidget(newSequenceButton) self.openSequenceAction = Qt.QAction( getThemeIcon("document-open"), "Open...", self) self.openSequenceAction.triggered.connect(self.onOpenSequence) self.openSequenceAction.setToolTip("Open sequence...") openSequenceButton = Qt.QToolButton() openSequenceButton.setDefaultAction(self.openSequenceAction) actionsLayout.addWidget(openSequenceButton) self.saveSequenceAction = Qt.QAction( getThemeIcon("document-save"), "Save...", self) self.saveSequenceAction.triggered.connect(self.onSaveSequence) self.saveSequenceAction.setToolTip("Save sequence...") self.saveSequenceAction.setEnabled(False) saveSequenceButton = Qt.QToolButton() saveSequenceButton.setDefaultAction(self.saveSequenceAction) actionsLayout.addWidget(saveSequenceButton) self.stopSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_stop.svg"), "Stop", self) self.stopSequenceAction.triggered.connect(self.onStopSequence) self.stopSequenceAction.setToolTip("Stop sequence") stopSequenceButton = Qt.QToolButton() stopSequenceButton.setDefaultAction(self.stopSequenceAction) actionsLayout.addWidget(stopSequenceButton) self.pauseSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_pause.svg"), "Pause", self) self.pauseSequenceAction.triggered.connect(self.onPauseSequence) self.pauseSequenceAction.setToolTip("Pause sequence") pauseSequenceButton = Qt.QToolButton() pauseSequenceButton.setDefaultAction(self.pauseSequenceAction) actionsLayout.addWidget(pauseSequenceButton) self.playSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_start.svg"), "Play", self) self.playSequenceAction.triggered.connect(self.onPlaySequence) self.playSequenceAction.setToolTip("Play sequence") playSequenceButton = Qt.QToolButton() playSequenceButton.setDefaultAction(self.playSequenceAction) actionsLayout.addWidget(playSequenceButton) self.doorStateLed = TaurusLed(self) actionsLayout.addWidget(self.doorStateLed) #@todo this feature will be replaced by checkboxes in the # sequence tree view indicating clearing of the plot after execution self.fullSequencePlotCheckBox = Qt.QCheckBox( "Full sequence plot", self) self.fullSequencePlotCheckBox.toggled.connect(self.setFullSequencePlot) self.fullSequencePlotCheckBox.setChecked(True) actionsLayout.addWidget(self.fullSequencePlotCheckBox) spacerItem = Qt.QSpacerItem( 0, 0, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed) actionsLayout.addItem(spacerItem) sequenceEditor.layout().addLayout(actionsLayout) macroLayout = Qt.QHBoxLayout() macroLayout.setContentsMargins(0, 0, 0, 0) macroLabel = Qt.QLabel("Macro:") macroLayout.addWidget(macroLabel) self.macroComboBox = MacroComboBox(self) self.macroComboBox.setUseParentModel(True) self.macroComboBox.setModelColumn(0) self.macroComboBox.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) macroLayout.addWidget(self.macroComboBox) self.addMacroAction = Qt.QAction( getThemeIcon("list-add"), "Add macro...", self) self.addMacroAction.triggered.connect(self.onAdd) self.addMacroAction.setToolTip( "Clicking this button will add selected macro") self.addMacroAction.setEnabled(False) addButton = Qt.QToolButton() addButton.setDefaultAction(self.addMacroAction) macroLayout.addWidget(addButton) sequenceEditor.layout().addLayout(macroLayout) sequenceLayout = Qt.QHBoxLayout() sequenceLayout.addWidget(self.tree) layout = Qt.QVBoxLayout() delButton = Qt.QToolButton() delButton.setDefaultAction(self.tree.deleteAction) delButton.setEnabled(False) layout.addWidget(delButton) upButton = Qt.QToolButton() upButton.setDefaultAction(self.tree.moveUpAction) upButton.setEnabled(False) layout.addWidget(upButton) downButton = Qt.QToolButton() downButton.setDefaultAction(self.tree.moveDownAction) downButton.setEnabled(False) layout.addWidget(downButton) leftButton = Qt.QToolButton() leftButton.setDefaultAction(self.tree.moveLeftAction) leftButton.setEnabled(False) layout.addWidget(leftButton) rightButton = Qt.QToolButton() rightButton.setDefaultAction(self.tree.moveRightAction) rightButton.setEnabled(False) layout.addWidget(rightButton) spacerItem = Qt.QSpacerItem( 0, 40, Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Expanding) layout.addItem(spacerItem) sequenceLayout.addLayout(layout) sequenceEditor.layout().addLayout(sequenceLayout) self.parametersProxyModel = MacroParametersProxyModel() self.parametersProxyModel.setSourceModel(self._sequenceModel) self.stackedWidget = Qt.QStackedWidget() splitter.addWidget(self.stackedWidget) self.standardMacroParametersEditor = StandardMacroParametersEditor( self.stackedWidget) self.standardMacroParametersEditor.setModel(self.parametersProxyModel) self.standardMacroParametersEditor.tree.setItemDelegate( ParamEditorDelegate(self.standardMacroParametersEditor.tree)) self.stackedWidget.addWidget(self.standardMacroParametersEditor) self.customMacroParametersEditor = None self.macroComboBox.currentIndexChanged.connect( self.onMacroComboBoxChanged) self.tree.macroChanged.connect(self.setMacroParametersRootIndex) def contextMenuEvent(self, event): menu = Qt.QMenu() action = menu.addAction(getThemeIcon( "view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): """Method used by "Check door state" action (available in the context menu). It is a workaround for situations when the event notification about the macro status does not reach the sequencer widget.""" door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) self.stopSequenceAction.setEnabled(True) elif doorState in (PyTango.DevState.ON, PyTango.DevState.ALARM): self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(True) def doorName(self): return self._doorName def setDoorName(self, doorName): self._doorName = doorName def firstMacroId(self): return self._firstMacroId def setFirstMacroId(self, firstMacroId): self._firstMacroId = firstMacroId def lastMacroId(self): return self._lastMacroId def setLastMacroId(self, lastMacroId): self._lastMacroId = lastMacroId def macroIds(self): return self._macroIds def setMacroIds(self, macroIds): self._macroIds = macroIds def emitExecutionStarted(self): return self._emitExecutionStarted def setEmitExecutionStarted(self, yesNo): self._emitExecutionStarted = yesNo def sequencesPath(self): return self._sequencesPath def setSequencesPath(self, sequencesPath): self._sequencesPath = sequencesPath def isFullSequencePlot(self): return self._fullSequencePlot def setFullSequencePlot(self, fullSequencePlot): self._fullSequencePlot = fullSequencePlot def onNewSequence(self): if Qt.QMessageBox.question(self, "New sequence", "Do you want to save existing sequence?", Qt.QMessageBox.Yes, Qt.QMessageBox.No) == Qt.QMessageBox.Yes: self.onSaveSequence() self.tree.clearTree() self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) self.currentMacroChanged.emit(None) def loadFile(self, fileName): if fileName == "": return #@todo: reset macroComboBox to index 0 try: file = open(fileName, 'r') string = file.read() if fileName.endswith('.xml'): root = self.fromXmlString(string) else: root = self.fromPlainText(string) self._sequenceModel.setRoot(root) self.sequenceProxyModel.invalidateFilter() self.tree.expandAll() self.tree.expanded() self.parametersProxyModel.setMacroIndex(None) self.parametersProxyModel.invalidateFilter() if not self._sequenceModel.isEmpty(): self.newSequenceAction.setEnabled(True) self.saveSequenceAction.setEnabled(True) self.playSequenceAction.setEnabled(True) except IOError: Qt.QMessageBox.warning( self, "Error while loading macros sequence", "There was a problem while reading from file: %s" % fileName) file = None self.tree.clearTree() self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) except: self.tree.clearTree() self.playSequenceAction.setEnabled(False) self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) raise finally: if not file is None: file.close() self.setSequencesPath(str.join("/", fileName.rsplit("/")[:-1])) self.currentMacroChanged.emit(None) def onOpenSequence(self): if not self._sequenceModel.isEmpty(): if Qt.QMessageBox.question( self, "Open sequence", "Do you want to save existing sequence?", Qt.QMessageBox.Yes, Qt.QMessageBox.No) == Qt.QMessageBox.Yes: self.onSaveSequence() self.tree.clearTree() sequencesPath = self.sequencesPath() fileName, _ = compat.getOpenFileName( self, "Choose a sequence to open...", sequencesPath, "*") self.loadFile(fileName) def onSaveSequence(self): sequencesPath = self.sequencesPath() if sequencesPath == "": sequencesPath = str(Qt.QDir.homePath()) sequencesPath = os.path.join(sequencesPath, "Untitled.xml") fileName, _ = compat.getSaveFileName( self, "Choose a sequence file name...", sequencesPath, "*.xml") if fileName == "": return try: file = open(fileName, "w") file.write(self.tree.toXmlString(pretty=True, withId=False)) self.setSequencesPath(str.join("/", fileName.rsplit("/")[:-1])) except Exception as e: Qt.QMessageBox.warning( self, "Error while saving macros sequence", "There was a problem while writing to the file: %s" % fileName) print e finally: if not file is None: file.close() def onPlaySequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if (doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM): first, last, ids = self.tree.prepareMacroIds() self.setFirstMacroId(first) self.setLastMacroId(last) self.setMacroIds(ids) self.tree.prepareMacroProgresses() self.setEmitExecutionStarted(True) door.runMacro(self.tree.toXmlString()) elif doorState == PyTango.DevState.STANDBY: door.command_inout("ResumeMacro") else: Qt.QMessageBox.warning( self, "Error while starting/resuming sequence", "It was not possible to start/resume sequence, " "because state of the door was different than ON/STANDBY") def onStopSequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") else: Qt.QMessageBox.warning( self, "Error while stopping sequence", "It was not possible to stop sequence, " "because state of the door was different than " "RUNNING or STANDBY") def onPauseSequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") else: Qt.QMessageBox.warning( self, "Error while pausing sequence", "It was not possible to pause sequence, " "because state of the door was different than RUNNING") def onMacroStatusUpdated(self, data): macro = data[0] if macro is None: return data = data[1][0] state, range, step, id = str(data["state"]), data[ "range"], data["step"], data["id"] if id is None: return id = int(id) if not id in self.macroIds(): return macroName = macro.name shortMessage = "" if state == "start": #@todo: Check this signal because it doesn't work, # emitExecutionStarted is not set!!! if self.emitExecutionStarted(): self.macroStarted.emit("DoorOutput") self.tree.setRangeForMacro(id, range) self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) self.stopSequenceAction.setEnabled(True) if id == self.firstMacroId(): self.plotablesFilterChanged.emit(None) self.plotablesFilterChanged.emit(standardPlotablesFilter) shortMessage = "Sequence started." elif not self.isFullSequencePlot(): self.plotablesFilterChanged.emit(None) shortMessage += " Macro %s started." % macroName elif state == "pause": self.playSequenceAction.setText("Resume sequence") self.playSequenceAction.setToolTip("Resume sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) shortMessage = "Macro %s paused." % macroName elif state == "resume": self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) shortMessage = "Macro %s resumed." % macroName elif state == "stop" or state == "finish": shortMessage = "Macro %s finished." % macroName if id == self.lastMacroId(): self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage += " Sequence finished." elif state == 'exception': self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage = "Macro %s error." % macroName exc_value, exc_stack = data['exc_value'], data['exc_stack'] exceptionDialog = TaurusMessageBox( MacroRunException, exc_value, exc_stack) exceptionDialog.exec_() elif state == 'abort': self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage = "Macro %s stopped." % macroName elif state == "step": shortMessage = "Macro %s at %d %% of progress." % (macroName, step) self.shortMessageEmitted.emit(shortMessage) self.tree.setProgressForMacro(id, step) def onDoorChanged(self, doorName): self.setDoorName(doorName) if self.doorName() == "": self.doorStateLed.setModel(None) return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON: self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playSequenceAction.setText("Resume sequence") self.playSequenceAction.setToolTip("Resume sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(True) def setMacroParametersRootIndex(self, sourceIndex): parametersModel = self.standardMacroParametersEditor.tree.model() parametersModel.setMacroIndex(sourceIndex) parametersModel.invalidateFilter() proxyIndex = parametersModel.mapFromSource(sourceIndex) macroNode = sourceIndex.internalPointer() macroName = macroNode.name() if self.stackedWidget.count() == 2: self.stackedWidget.removeWidget(self.customMacroParametersEditor) self.customMacroParametersEditor.setParent(None) self.customMacroParametersEditor = \ ParamEditorManager().getMacroEditor(macroName) if self.customMacroParametersEditor: self.customMacroParametersEditor.setModel(parametersModel) self.customMacroParametersEditor.setRootIndex(proxyIndex) self.stackedWidget.addWidget(self.customMacroParametersEditor) self.stackedWidget.setCurrentWidget( self.customMacroParametersEditor) else: self.standardMacroParametersEditor.tree.setRootIndex(proxyIndex) self.standardMacroParametersEditor.tree.expandAll() def onMacroComboBoxChanged(self): macroName = str(self.macroComboBox.currentText()) if macroName == "": self.addMacroAction.setEnabled(False) else: self.addMacroAction.setEnabled(True) self.macroNameChanged.emit(macroName) def onAdd(self): macroName = str(self.macroComboBox.currentText()) macroNode = self.getModelObj().getMacroNodeObj(macroName) self.tree.addMacro(macroNode) self.saveSequenceAction.setEnabled(True) self.playSequenceAction.setEnabled(True) def isEmptySequence(self): return len(self.tree.root()) == 0 def isMacroSelected(self): return len(self.tree.selectedIndexes()) == 2 def emptySequence(self): self.tree.clearTree() self.disableButtons() self.currentMacroChanged.emit(None) self.sequenceEmpty.emit() def fromXmlString(self, xmlString): newRoot = self.tree.fromXmlString(xmlString) macroServerObj = self.getModelObj() for macroNode in newRoot.allMacros(): macroServerObj.fillMacroNodeAdditionalInfos(macroNode) return newRoot def fromPlainText(self, plainText): newRoot = self.tree.fromPlainText(plainText) macroServerObj = self.getModelObj() error_line = 0 for macroNode in newRoot.allMacros(): error_line += 1 try: macroServerObj.recreateMacroNodeAndFillAdditionalInfos( macroNode) except Exception as e: Qt.QMessageBox.warning(self, "Error while parsing the sequence", "Sequence line number %d contains " "the following error:\n %s\n " "The sequence will not be loaded" % (error_line, e)) raise e return newRoot def setModel(self, model): oldModelObj = self.getModelObj() if oldModelObj is not None: oldModelObj.macrosUpdated.disconnect( self.macroComboBox.onMacrosUpdated) TaurusWidget.setModel(self, model) newModelObj = self.getModelObj() newModelObj.macrosUpdated.connect(self.macroComboBox.onMacrosUpdated) @classmethod def getQtDesignerPluginInfo(cls): return {'container': False, 'group': 'Taurus Sardana', 'module': 'taurus.qt.qtgui.extra_macroexecutor', 'icon': ':/designer/frame.png'}
class TaurusSequencerWidget(TaurusWidget): macroStarted = Qt.pyqtSignal('QString') plotablesFilterChanged = Qt.pyqtSignal(object) currentMacroChanged = Qt.pyqtSignal(object) macroNameChanged = Qt.pyqtSignal('QString') shortMessageEmitted = Qt.pyqtSignal('QString') sequenceEmpty = Qt.pyqtSignal() def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) # list representing all macros ids (all from sequence) currently # executed self._macroIds = [] self._sequencesPath = str(Qt.QDir.homePath()) self._sequenceModel = MacroSequenceTreeModel() self.registerConfigProperty("sequencesPath", "setSequencesPath", "sequencesPath") self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) splitter = Qt.QSplitter() self.layout().addWidget(splitter) splitter.setOrientation(Qt.Qt.Vertical) sequenceEditor = TaurusWidget() splitter.addWidget(sequenceEditor) sequenceEditor.setUseParentModel(True) sequenceEditor.setLayout(Qt.QVBoxLayout()) sequenceEditor.layout().setContentsMargins(0, 0, 0, 0) self.tree = MacroSequenceTree(sequenceEditor) self.sequenceProxyModel = MacroSequenceProxyModel() self.sequenceProxyModel.setSourceModel(self._sequenceModel) self.tree.setModel(self.sequenceProxyModel) self.tree.setItemDelegate(SequenceEditorDelegate(self.tree)) actionsLayout = Qt.QHBoxLayout() actionsLayout.setContentsMargins(0, 0, 0, 0) self.newSequenceAction = Qt.QAction(getThemeIcon("document-new"), "New", self) self.newSequenceAction.triggered.connect(self.onNewSequence) self.newSequenceAction.setToolTip("New sequence") self.newSequenceAction.setEnabled(False) newSequenceButton = Qt.QToolButton() newSequenceButton.setDefaultAction(self.newSequenceAction) actionsLayout.addWidget(newSequenceButton) self.openSequenceAction = Qt.QAction(getThemeIcon("document-open"), "Open...", self) self.openSequenceAction.triggered.connect(self.onOpenSequence) self.openSequenceAction.setToolTip("Open sequence...") openSequenceButton = Qt.QToolButton() openSequenceButton.setDefaultAction(self.openSequenceAction) actionsLayout.addWidget(openSequenceButton) self.saveSequenceAction = Qt.QAction(getThemeIcon("document-save"), "Save...", self) self.saveSequenceAction.triggered.connect(self.onSaveSequence) self.saveSequenceAction.setToolTip("Save sequence...") self.saveSequenceAction.setEnabled(False) saveSequenceButton = Qt.QToolButton() saveSequenceButton.setDefaultAction(self.saveSequenceAction) actionsLayout.addWidget(saveSequenceButton) self.stopSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_stop.svg"), "Stop", self) self.stopSequenceAction.triggered.connect(self.onStopSequence) self.stopSequenceAction.setToolTip("Stop sequence") stopSequenceButton = Qt.QToolButton() stopSequenceButton.setDefaultAction(self.stopSequenceAction) actionsLayout.addWidget(stopSequenceButton) self.pauseSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_pause.svg"), "Pause", self) self.pauseSequenceAction.triggered.connect(self.onPauseSequence) self.pauseSequenceAction.setToolTip("Pause sequence") pauseSequenceButton = Qt.QToolButton() pauseSequenceButton.setDefaultAction(self.pauseSequenceAction) actionsLayout.addWidget(pauseSequenceButton) self.playSequenceAction = Qt.QAction( getIcon(":/actions/media_playback_start.svg"), "Play", self) self.playSequenceAction.triggered.connect(self.onPlaySequence) self.playSequenceAction.setToolTip("Play sequence") playSequenceButton = Qt.QToolButton() playSequenceButton.setDefaultAction(self.playSequenceAction) actionsLayout.addWidget(playSequenceButton) self.doorStateLed = TaurusLed(self) actionsLayout.addWidget(self.doorStateLed) #@todo this feature will be replaced by checkboxes in the # sequence tree view indicating clearing of the plot after execution self.fullSequencePlotCheckBox = Qt.QCheckBox("Full sequence plot", self) self.fullSequencePlotCheckBox.toggled.connect(self.setFullSequencePlot) self.fullSequencePlotCheckBox.setChecked(True) actionsLayout.addWidget(self.fullSequencePlotCheckBox) spacerItem = Qt.QSpacerItem(0, 0, Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Fixed) actionsLayout.addItem(spacerItem) sequenceEditor.layout().addLayout(actionsLayout) macroLayout = Qt.QHBoxLayout() macroLayout.setContentsMargins(0, 0, 0, 0) macroLabel = Qt.QLabel("Macro:") macroLayout.addWidget(macroLabel) self.macroComboBox = MacroComboBox(self) self.macroComboBox.setUseParentModel(True) self.macroComboBox.setModelColumn(0) self.macroComboBox.setSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) macroLayout.addWidget(self.macroComboBox) self.addMacroAction = Qt.QAction(getThemeIcon("list-add"), "Add macro...", self) self.addMacroAction.triggered.connect(self.onAdd) self.addMacroAction.setToolTip( "Clicking this button will add selected macro") self.addMacroAction.setEnabled(False) addButton = Qt.QToolButton() addButton.setDefaultAction(self.addMacroAction) macroLayout.addWidget(addButton) sequenceEditor.layout().addLayout(macroLayout) sequenceLayout = Qt.QHBoxLayout() sequenceLayout.addWidget(self.tree) layout = Qt.QVBoxLayout() delButton = Qt.QToolButton() delButton.setDefaultAction(self.tree.deleteAction) delButton.setEnabled(False) layout.addWidget(delButton) upButton = Qt.QToolButton() upButton.setDefaultAction(self.tree.moveUpAction) upButton.setEnabled(False) layout.addWidget(upButton) downButton = Qt.QToolButton() downButton.setDefaultAction(self.tree.moveDownAction) downButton.setEnabled(False) layout.addWidget(downButton) leftButton = Qt.QToolButton() leftButton.setDefaultAction(self.tree.moveLeftAction) leftButton.setEnabled(False) layout.addWidget(leftButton) rightButton = Qt.QToolButton() rightButton.setDefaultAction(self.tree.moveRightAction) rightButton.setEnabled(False) layout.addWidget(rightButton) spacerItem = Qt.QSpacerItem(0, 40, Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Expanding) layout.addItem(spacerItem) sequenceLayout.addLayout(layout) sequenceEditor.layout().addLayout(sequenceLayout) self.parametersProxyModel = MacroParametersProxyModel() self.parametersProxyModel.setSourceModel(self._sequenceModel) self.stackedWidget = Qt.QStackedWidget() splitter.addWidget(self.stackedWidget) self.standardMacroParametersEditor = StandardMacroParametersEditor( self.stackedWidget) self.standardMacroParametersEditor.setModel(self.parametersProxyModel) self.standardMacroParametersEditor.tree.setItemDelegate( ParamEditorDelegate(self.standardMacroParametersEditor.tree)) self.stackedWidget.addWidget(self.standardMacroParametersEditor) self.customMacroParametersEditor = None self.macroComboBox.currentIndexChanged.connect( self.onMacroComboBoxChanged) self.tree.macroChanged.connect(self.setMacroParametersRootIndex) def contextMenuEvent(self, event): menu = Qt.QMenu() action = menu.addAction(getThemeIcon("view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): """Method used by "Check door state" action (available in the context menu). It is a workaround for situations when the event notification about the macro status does not reach the sequencer widget.""" door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) self.stopSequenceAction.setEnabled(True) elif doorState in (PyTango.DevState.ON, PyTango.DevState.ALARM): self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(True) def doorName(self): return self._doorName def setDoorName(self, doorName): self._doorName = doorName def firstMacroId(self): return self._firstMacroId def setFirstMacroId(self, firstMacroId): self._firstMacroId = firstMacroId def lastMacroId(self): return self._lastMacroId def setLastMacroId(self, lastMacroId): self._lastMacroId = lastMacroId def macroIds(self): return self._macroIds def setMacroIds(self, macroIds): self._macroIds = macroIds def emitExecutionStarted(self): return self._emitExecutionStarted def setEmitExecutionStarted(self, yesNo): self._emitExecutionStarted = yesNo def sequencesPath(self): return self._sequencesPath def setSequencesPath(self, sequencesPath): self._sequencesPath = sequencesPath def isFullSequencePlot(self): return self._fullSequencePlot def setFullSequencePlot(self, fullSequencePlot): self._fullSequencePlot = fullSequencePlot def onNewSequence(self): if Qt.QMessageBox.question(self, "New sequence", "Do you want to save existing sequence?", Qt.QMessageBox.Yes, Qt.QMessageBox.No) == Qt.QMessageBox.Yes: self.onSaveSequence() self.tree.clearTree() self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) self.currentMacroChanged.emit(None) def loadFile(self, fileName): if fileName == "": return #@todo: reset macroComboBox to index 0 try: file = open(fileName, 'r') string = file.read() if fileName.endswith('.xml'): root = self.fromXmlString(string) else: root = self.fromPlainText(string) self._sequenceModel.setRoot(root) self.sequenceProxyModel.invalidateFilter() self.tree.expandAll() self.tree.expanded() self.parametersProxyModel.setMacroIndex(None) self.parametersProxyModel.invalidateFilter() if not self._sequenceModel.isEmpty(): self.newSequenceAction.setEnabled(True) self.saveSequenceAction.setEnabled(True) self.playSequenceAction.setEnabled(True) except IOError: Qt.QMessageBox.warning( self, "Error while loading macros sequence", "There was a problem while reading from file: %s" % fileName) file = None self.tree.clearTree() self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) except: self.tree.clearTree() self.playSequenceAction.setEnabled(False) self.newSequenceAction.setEnabled(False) self.saveSequenceAction.setEnabled(False) raise finally: if not file is None: file.close() self.setSequencesPath(str.join("/", fileName.rsplit("/")[:-1])) self.currentMacroChanged.emit(None) def onOpenSequence(self): if not self._sequenceModel.isEmpty(): if Qt.QMessageBox.question( self, "Open sequence", "Do you want to save existing sequence?", Qt.QMessageBox.Yes, Qt.QMessageBox.No) == Qt.QMessageBox.Yes: self.onSaveSequence() self.tree.clearTree() sequencesPath = self.sequencesPath() fileName, _ = compat.getOpenFileName(self, "Choose a sequence to open...", sequencesPath, "*") self.loadFile(fileName) def onSaveSequence(self): sequencesPath = self.sequencesPath() if sequencesPath == "": sequencesPath = str(Qt.QDir.homePath()) sequencesPath = os.path.join(sequencesPath, "Untitled.xml") fileName, _ = compat.getSaveFileName(self, "Choose a sequence file name...", sequencesPath, "*.xml") if fileName == "": return try: file = open(fileName, "w") file.write(self.tree.toXmlString(pretty=True, withId=False)) self.setSequencesPath(str.join("/", fileName.rsplit("/")[:-1])) except Exception as e: Qt.QMessageBox.warning( self, "Error while saving macros sequence", "There was a problem while writing to the file: %s" % fileName) print e finally: if not file is None: file.close() def onPlaySequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if (doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM): first, last, ids = self.tree.prepareMacroIds() self.setFirstMacroId(first) self.setLastMacroId(last) self.setMacroIds(ids) self.tree.prepareMacroProgresses() self.setEmitExecutionStarted(True) door.runMacro(self.tree.toXmlString()) elif doorState == PyTango.DevState.STANDBY: door.command_inout("ResumeMacro") else: Qt.QMessageBox.warning( self, "Error while starting/resuming sequence", "It was not possible to start/resume sequence, " "because state of the door was different than ON/STANDBY") def onStopSequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") else: Qt.QMessageBox.warning( self, "Error while stopping sequence", "It was not possible to stop sequence, " "because state of the door was different than " "RUNNING or STANDBY") def onPauseSequence(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") else: Qt.QMessageBox.warning( self, "Error while pausing sequence", "It was not possible to pause sequence, " "because state of the door was different than RUNNING") def onMacroStatusUpdated(self, data): macro = data[0] if macro is None: return data = data[1][0] state, range, step, id = str( data["state"]), data["range"], data["step"], data["id"] if id is None: return id = int(id) if not id in self.macroIds(): return macroName = macro.name shortMessage = "" if state == "start": #@todo: Check this signal because it doesn't work, # emitExecutionStarted is not set!!! if self.emitExecutionStarted(): self.macroStarted.emit("DoorOutput") self.tree.setRangeForMacro(id, range) self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) self.stopSequenceAction.setEnabled(True) if id == self.firstMacroId(): self.plotablesFilterChanged.emit(None) self.plotablesFilterChanged.emit(standardPlotablesFilter) shortMessage = "Sequence started." elif not self.isFullSequencePlot(): self.plotablesFilterChanged.emit(None) shortMessage += " Macro %s started." % macroName elif state == "pause": self.playSequenceAction.setText("Resume sequence") self.playSequenceAction.setToolTip("Resume sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) shortMessage = "Macro %s paused." % macroName elif state == "resume": self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(True) shortMessage = "Macro %s resumed." % macroName elif state == "stop" or state == "finish": shortMessage = "Macro %s finished." % macroName if id == self.lastMacroId(): self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage += " Sequence finished." elif state == 'exception': self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage = "Macro %s error." % macroName exc_value, exc_stack = data['exc_value'], data['exc_stack'] exceptionDialog = TaurusMessageBox(MacroRunException, exc_value, exc_stack) exceptionDialog.exec_() elif state == 'abort': self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) shortMessage = "Macro %s stopped." % macroName elif state == "step": shortMessage = "Macro %s at %d %% of progress." % (macroName, step) self.shortMessageEmitted.emit(shortMessage) self.tree.setProgressForMacro(id, step) def onDoorChanged(self, doorName): self.setDoorName(doorName) if self.doorName() == "": self.doorStateLed.setModel(None) return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON: self.playSequenceAction.setText("Start sequence") self.playSequenceAction.setToolTip("Start sequence") self.playSequenceAction.setEnabled(False) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playSequenceAction.setText("Resume sequence") self.playSequenceAction.setToolTip("Resume sequence") self.playSequenceAction.setEnabled(True) self.pauseSequenceAction.setEnabled(False) self.stopSequenceAction.setEnabled(True) def setMacroParametersRootIndex(self, sourceIndex): parametersModel = self.standardMacroParametersEditor.tree.model() parametersModel.setMacroIndex(sourceIndex) parametersModel.invalidateFilter() proxyIndex = parametersModel.mapFromSource(sourceIndex) macroNode = sourceIndex.internalPointer() macroName = macroNode.name() if self.stackedWidget.count() == 2: self.stackedWidget.removeWidget(self.customMacroParametersEditor) self.customMacroParametersEditor.setParent(None) self.customMacroParametersEditor = \ ParamEditorManager().getMacroEditor(macroName) if self.customMacroParametersEditor: self.customMacroParametersEditor.setModel(parametersModel) self.customMacroParametersEditor.setRootIndex(proxyIndex) self.stackedWidget.addWidget(self.customMacroParametersEditor) self.stackedWidget.setCurrentWidget( self.customMacroParametersEditor) else: self.standardMacroParametersEditor.tree.setRootIndex(proxyIndex) self.standardMacroParametersEditor.tree.expandAll() def onMacroComboBoxChanged(self): macroName = str(self.macroComboBox.currentText()) if macroName == "": self.addMacroAction.setEnabled(False) else: self.addMacroAction.setEnabled(True) self.macroNameChanged.emit(macroName) def onAdd(self): macroName = str(self.macroComboBox.currentText()) macroNode = self.getModelObj().getMacroNodeObj(macroName) self.tree.addMacro(macroNode) self.saveSequenceAction.setEnabled(True) self.playSequenceAction.setEnabled(True) def isEmptySequence(self): return len(self.tree.root()) == 0 def isMacroSelected(self): return len(self.tree.selectedIndexes()) == 2 def emptySequence(self): self.tree.clearTree() self.disableButtons() self.currentMacroChanged.emit(None) self.sequenceEmpty.emit() def fromXmlString(self, xmlString): newRoot = self.tree.fromXmlString(xmlString) macroServerObj = self.getModelObj() for macroNode in newRoot.allMacros(): macroServerObj.fillMacroNodeAdditionalInfos(macroNode) return newRoot def fromPlainText(self, plainText): newRoot = self.tree.fromPlainText(plainText) macroServerObj = self.getModelObj() error_line = 0 for macroNode in newRoot.allMacros(): error_line += 1 try: macroServerObj.recreateMacroNodeAndFillAdditionalInfos( macroNode) except Exception as e: Qt.QMessageBox.warning( self, "Error while parsing the sequence", "Sequence line number %d contains " "the following error:\n %s\n " "The sequence will not be loaded" % (error_line, e)) raise e return newRoot def setModel(self, model): oldModelObj = self.getModelObj() if oldModelObj is not None: oldModelObj.macrosUpdated.disconnect( self.macroComboBox.onMacrosUpdated) TaurusWidget.setModel(self, model) newModelObj = self.getModelObj() newModelObj.macrosUpdated.connect(self.macroComboBox.onMacrosUpdated) @classmethod def getQtDesignerPluginInfo(cls): return { 'container': False, 'group': 'Taurus Sardana', 'module': 'taurus.qt.qtgui.extra_macroexecutor', 'icon': ':/designer/frame.png' }
class TaurusMacroExecutorWidget(TaurusWidget): doorChanged = Qt.pyqtSignal('QString') macroNameChanged = Qt.pyqtSignal('QString') macroStarted = Qt.pyqtSignal('QString') plotablesFilterChanged = Qt.pyqtSignal(object) shortMessageEmitted = Qt.pyqtSignal('QString') def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setObjectName(self.__class__.__name__) self._doorName = "" self._macroId = None self.setLayout(Qt.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.addToFavouritesAction = Qt.QAction(getThemeIcon( "software-update-available"), "Add to favourites", self) self.addToFavouritesAction.triggered.connect(self.onAddToFavourites) self.addToFavouritesAction.setToolTip("Add to favourites") self.stopMacroAction = Qt.QAction( getIcon(":/actions/media_playback_stop.svg"), "Stop macro", self) self.stopMacroAction.triggered.connect(self.onStopMacro) self.stopMacroAction.setToolTip("Stop macro") self.pauseMacroAction = Qt.QAction( getIcon(":/actions/media_playback_pause.svg"), "Pause macro", self) self.pauseMacroAction.triggered.connect(self.onPauseMacro) self.pauseMacroAction.setToolTip("Pause macro") self.playMacroAction = Qt.QAction( getIcon(":/actions/media_playback_start.svg"), "Start macro", self) self.playMacroAction.triggered.connect(self.onPlayMacro) self.playMacroAction.setToolTip("Start macro") actionsLayout = Qt.QHBoxLayout() actionsLayout.setContentsMargins(0, 0, 0, 0) addToFavouritsButton = Qt.QToolButton() addToFavouritsButton.setDefaultAction(self.addToFavouritesAction) self.addToFavouritesAction.setEnabled(False) actionsLayout.addWidget(addToFavouritsButton) self.macroComboBox = MacroComboBox(self) self.macroComboBox.setUseParentModel(True) self.macroComboBox.setModelColumn(0) actionsLayout.addWidget(self.macroComboBox) stopMacroButton = Qt.QToolButton() stopMacroButton.setDefaultAction(self.stopMacroAction) actionsLayout.addWidget(stopMacroButton) pauseMacroButton = Qt.QToolButton() pauseMacroButton.setDefaultAction(self.pauseMacroAction) actionsLayout.addWidget(pauseMacroButton) self.playMacroButton = Qt.QToolButton() self.playMacroButton.setDefaultAction(self.playMacroAction) actionsLayout.addWidget(self.playMacroButton) self.disableControlActions() self.doorStateLed = TaurusLed(self) actionsLayout.addWidget(self.doorStateLed) self.layout().addLayout(actionsLayout) splitter = Qt.QSplitter(self) self.layout().addWidget(splitter) splitter.setOrientation(Qt.Qt.Vertical) self._paramEditorModel = ParamEditorModel() self.stackedWidget = Qt.QStackedWidget() self.standardMacroParametersEditor = StandardMacroParametersEditor( self.stackedWidget) self.stackedWidget.addWidget(self.standardMacroParametersEditor) self.customMacroParametersEditor = None splitter.addWidget(self.stackedWidget) self._favouritesBuffer = None self.favouritesMacrosEditor = FavouritesMacrosEditor(self) self.registerConfigDelegate(self.favouritesMacrosEditor) self.favouritesMacrosEditor.setUseParentModel(True) self.favouritesMacrosEditor.setFocusPolicy(Qt.Qt.NoFocus) self._historyBuffer = None self.historyMacrosViewer = HistoryMacrosViewer(self) self.registerConfigDelegate(self.historyMacrosViewer) self.historyMacrosViewer.setUseParentModel(True) self.historyMacrosViewer.setFocusPolicy(Qt.Qt.NoFocus) self.tabMacroListsWidget = Qt.QTabWidget(self) self.tabMacroListsWidget.addTab( self.favouritesMacrosEditor, "Favourite list") self.tabMacroListsWidget.addTab( self.historyMacrosViewer, "History Viewer") splitter.addWidget(self.tabMacroListsWidget) # Due to a limitation in the useParentModel architecture of Taurus, # the parent of historyMacrosViewer and favouritesMacrosEditor # must be recalculated. See more details in the taurus snippet code [1] # [1] https://raw.githubusercontent.com/taurus-org/taurus/develop/doc/source/devel/examples/parentmodel_issue_demo.py self.historyMacrosViewer.recheckTaurusParent() self.favouritesMacrosEditor.recheckTaurusParent() self._isHistoryMacro = False self.macroProgressBar = MacroProgressBar(self) self.layout().addWidget(self.macroProgressBar) #spockCommandLabel = Qt.QLabel("Spock command:", self) # spockCommandLabel.setFont(Qt.QFont("Courier",9)) self.spockCommand = SpockCommandWidget("Spock", self) self.spockCommand.setSizePolicy( Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Minimum) self.spockCommand.setUseParentModel(True) spockCommandLayout = Qt.QHBoxLayout() spockCommandLayout.setContentsMargins(0, 0, 0, 0) # spockCommandLayout.addWidget(spockCommandLabel) spockCommandLayout.addWidget(self.spockCommand) self.layout().addLayout(spockCommandLayout) self.macroComboBox.currentIndexChanged['QString'].connect( self.onMacroComboBoxChanged) self.favouritesMacrosEditor.list.favouriteSelected.connect( self.onFavouriteSelected) self.historyMacrosViewer.list.historySelected.connect( self.onHistorySelected) self.spockCommand.pressedReturn.connect(self.onPlayMacro) self.spockCommand.spockComboBox.connect(self.setComboBoxItem) self.spockCommand.elementUp.connect(self.setHistoryUp) self.spockCommand.elementDown.connect(self.setHistoryDown) self.spockCommand.expandTree.connect( self.standardMacroParametersEditor.tree.expandAll) def macroId(self): return self._macroId def contextMenuEvent(self, event): menu = Qt.QMenu() action = menu.addAction(getThemeIcon( "view-refresh"), "Check door state", self.checkDoorState) menu.exec_(event.globalPos()) def checkDoorState(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) self.stopMacroAction.setEnabled(True) elif doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM: self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) elif doorState == PyTango.DevState.STANDBY: self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(True) def setMacroId(self, macroId): self._macroId = macroId def doorName(self): return self._doorName def setDoorName(self, doorName): self._doorName = doorName def setFavouritesBuffer(self, favouritesMacro): self._favouritesBuffer = favouritesMacro # History Widget def setHistoryUp(self): self.setHistoryFocus() self.historyMacrosViewer.listElementUp() def setHistoryDown(self): self.setHistoryFocus() self.historyMacrosViewer.listElementDown() def setHistoryFocus(self): self.tabMacroListsWidget.setCurrentWidget(self.historyMacrosViewer) # self.historyMacrosViewer.setFocus() def historyBuffer(self): return self._historyBuffer def setHistoryBuffer(self, favouritesMacro): self._historyBuffer = favouritesMacro def favouritesBuffer(self): return self._favouritesBuffer def paramEditorModel(self): return self._paramEditorModel def setParamEditorModel(self, paramEditorModel): self._paramEditorModel = paramEditorModel def setComboBoxItem(self, macroName): self.macroComboBox.selectMacro(macroName) @Qt.pyqtSlot('QString') def onMacroComboBoxChanged(self, macroName): macroName = str(macroName) if macroName == "": macroName, macroNode = None, None # macroNode = macro.MacroNode(name="") self.playMacroAction.setEnabled(False) self.addToFavouritesAction.setEnabled(False) else: if self._isHistoryMacro: macroNode = self.historyBuffer() self.setHistoryBuffer(None) self.favouritesMacrosEditor.list.clearSelection() else: macroNode = self.favouritesBuffer() self.setFavouritesBuffer(None) self.historyMacrosViewer.list.clearSelection() self._isHistoryMacro = False if macroNode is None: macroNode = self.getModelObj().getMacroNodeObj(macroName) self.playMacroAction.setEnabled(True) self.addToFavouritesAction.setEnabled(True) self.paramEditorModel().setRoot(macroNode) self.spockCommand.setModel(self.paramEditorModel()) if self.stackedWidget.count() == 2: self.stackedWidget.removeWidget(self.customMacroParametersEditor) self.customMacroParametersEditor.setParent(None) self.customMacroParametersEditor = ParamEditorManager( ).getMacroEditor(macroName, self.stackedWidget) if self.customMacroParametersEditor: self.customMacroParametersEditor.setModel(self.paramEditorModel()) self.stackedWidget.addWidget(self.customMacroParametersEditor) self.stackedWidget.setCurrentWidget( self.customMacroParametersEditor) else: self.standardMacroParametersEditor.setModel( self.paramEditorModel()) self.macroNameChanged.emit(macroName) def onFavouriteSelected(self, macroNode): self.setFavouritesBuffer(macroNode) name = "" if not macroNode is None: name = macroNode.name() self._isHistoryMacro = False self.macroComboBox.selectMacro(name) def onHistorySelected(self, macroNode): self.setHistoryBuffer(macroNode) name = "" if not macroNode is None: name = macroNode.name() self._isHistoryMacro = True self.macroComboBox.selectMacro(name) def onAddToFavourites(self): self.favouritesMacrosEditor.addMacro( deepcopy(self.paramEditorModel().root())) def addToHistory(self): self.historyMacrosViewer.addMacro( deepcopy(self.paramEditorModel().root())) def onDoorChanged(self, doorName): self.setDoorName(doorName) if self.doorName() == "": self.doorStateLed.setModel(None) return self.doorStateLed.setModel(self.doorName() + "/State") door = Device(doorName) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON: self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") elif doorState == PyTango.DevState.STANDBY: self.playMacroAction.setText("Resume macro") self.playMacroAction.setToolTip("Resume macro") def onPlayMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM: self.setFocus() paramEditorModel = self.paramEditorModel() macroNode = paramEditorModel.root() id = macroNode.assignId() self.setMacroId(id) params, alerts = macroNode.toRun() xmlString = paramEditorModel.toXmlString() if len(alerts) > 0: Qt.QMessageBox.warning( self, "Macro parameters warning", alerts) return door.runMacro(xmlString) self.addToHistory() # door.runMacro(str(macroNode.name()), params) elif doorState == PyTango.DevState.STANDBY: door.command_inout("ResumeMacro") else: Qt.QMessageBox.warning(self, "Error while starting/resuming macro", "It was not possible to start/resume macro, because state of the door was different than ON/STANDBY") def onStopMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState in (PyTango.DevState.RUNNING, PyTango.DevState.STANDBY): door.command_inout("StopMacro") else: Qt.QMessageBox.warning(self, "Error while stopping macro", "It was not possible to stop macro, because state of the door was different than RUNNING or STANDBY") def onPauseMacro(self): door = Device(self.doorName()) try: doorState = door.state() except TypeError: # TODO: For Taurus 4 adaptation doorState = door.getState() if doorState == PyTango.DevState.RUNNING: door.command_inout("PauseMacro") else: Qt.QMessageBox.warning(self, "Error while pausing macro", "It was not possible to pause macro, because state of the door was different than RUNNING") def onMacroStatusUpdated(self, data): macro = data[0] if macro is None: return data = data[1][0] state, range, step, id = data["state"], data[ "range"], data["step"], data["id"] if id is None: return id = int(id) if id != self.macroId(): return macroName = macro.name shortMessage = "" if state == "start": self.macroStarted.emit("DoorOutput") self.macroProgressBar.setRange(range[0], range[1]) self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) self.stopMacroAction.setEnabled(True) self.plotablesFilterChanged.emit(None) self.plotablesFilterChanged.emit(standardPlotablesFilter) shortMessage = "Macro %s started." % macroName elif state == "pause": self.playMacroAction.setText("Resume macro") self.playMacroAction.setToolTip("Resume macro") self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) shortMessage = "Macro %s paused." % macroName elif state == "resume": self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") self.playMacroAction.setEnabled(False) self.pauseMacroAction.setEnabled(True) shortMessage = "Macro %s resumed." % macroName elif state == "stop" or state == "finish": self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s finished." % macroName elif state == "exception": self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s error." % macroName exc_value, exc_stack = data['exc_value'], data['exc_stack'] exceptionDialog = TaurusMessageBox( MacroRunException, exc_value, exc_stack) exceptionDialog.exec_() elif state == "abort": self.playMacroAction.setText("Start macro") self.playMacroAction.setToolTip("Start macro") self.playMacroAction.setEnabled(True) self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) shortMessage = "Macro %s stopped." % macroName elif state == "step": shortMessage = "Macro %s at %d %% of progress." % (macroName, step) self.shortMessageEmitted.emit(shortMessage) self.macroProgressBar.setValue(step) def disableControlActions(self): self.pauseMacroAction.setEnabled(False) self.stopMacroAction.setEnabled(False) self.playMacroAction.setEnabled(False) def setModel(self, model): oldModelObj = self.getModelObj() if oldModelObj is not None: # TODO: check if macrosUpdated signal exists oldModelObj.macrosUpdated.disconnect( self.macroComboBox.onMacrosUpdated) TaurusWidget.setModel(self, model) newModelObj = self.getModelObj() newModelObj.macrosUpdated.connect( self.macroComboBox.onMacrosUpdated) @classmethod def getQtDesignerPluginInfo(cls): return {'container': False, 'group': 'Taurus Sardana', 'module': 'taurus.qt.qtgui.extra_macroexecutor', 'icon': ':/designer/frame.png'}