def __init__(self, parent=None, designmode=False): TaurusWidget.__init__(self, parent, designmode) self._modelcommands = [] self._buttons = [] self.setLayout(Qt.QGridLayout()) self.frame = TaurusWidget(self) self.frame.setUseParentModel(True) self.frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setUseParentModel(True) self.scrollArea.setWidget(self.frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) # Adding right click edit action self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseCommandsAction = Qt.QAction('Modify commands', self) self.addAction(self.chooseCommandsAction) self.connect(self.chooseCommandsAction, Qt.SIGNAL("triggered()"), self.chooseCommands) self.setModifiableByUser(True) # Make it saveable self.registerConfigProperty(self.commandsToConfig, self.commandsFromConfig, 'commands')
def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setLayout(Qt.QVBoxLayout()) self._splitter = Qt.QSplitter() self._splitter.setOrientation(Qt.Qt.Vertical) self.layout().addWidget(self._splitter) self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) self._scrollArea = TaurusScrollArea(self) self._scrollArea.setWidget(self._frame) self._scrollArea.setWidgetResizable(True) self._splitter.addWidget(self._scrollArea) self._outputTE = Qt.QTextEdit() self._outputTE.setReadOnly(True) self._splitter.addWidget(self._outputTE) #self._splitter.moveSplitter(self._splitter.getRange(0)[-1], 0) self._cmdWidgets = [] self._paramWidgets = [] self._viewFilters = [] self._defaultParameters = [] self._sortKey = lambda x: x.cmd_name self._operatorViewFilter = lambda x: x.disp_level == DisplayLevel.OPERATOR # self.setLayout(Qt.QGridLayout()) self.modelChanged.connect(self._updateCommandWidgets)
def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setLayout(Qt.QVBoxLayout()) self._splitter = Qt.QSplitter() self._splitter.setOrientation(Qt.Qt.Vertical) self.layout().addWidget(self._splitter) self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) self._scrollArea = TaurusScrollArea(self) self._scrollArea.setWidget(self._frame) self._scrollArea.setWidgetResizable(True) self._splitter.addWidget(self._scrollArea) self._outputTE = Qt.QTextEdit() self._outputTE.setReadOnly(True) self._splitter.addWidget(self._outputTE) #self._splitter.moveSplitter(self._splitter.getRange(0)[-1], 0) self._cmdWidgets = [] self._paramWidgets = [] self._viewFilters = [] self._defaultParameters = [] self._sortKey = lambda x: x.cmd_name self._operatorViewFilter = lambda x: x.disp_level == PyTango.DispLevel.OPERATOR # self.setLayout(Qt.QGridLayout()) self.modelChanged.connect(self._updateCommandWidgets)
class TaurusForm(TaurusWidget): '''A form containing specific widgets for interacting with a given list of taurus attributes and/or devices. Its model is a list of attribute and/or device names to be shown. Each item is represented in a row consisting of a label, a read widget, a write widget, a units widget and an "extra" widget (some of them may not be shown) which are vertically aligned with their counterparts from other items. By default a :class:`TaurusValue` object is used for each item, but this can be changed and specific mappings can be defined using the :meth:`setCustomWidgetMap` method. Item objects can be accessed by index using a list-like notation:: form = TaurusForm() form.model = ['sys/tg_test/1'+a for a in ('short_image','/float_scalar','/double_scalar')] form[0].labelConfig = 'dev_alias' form[-1].writeWidgetClass = 'TaurusWheelEdit' print(len(form)) # --> outputs '3' (the length of the form is the number of items) By default, the form provides global Apply and Cancel buttons. You can also see some code that exemplifies the use of TaurusForm in :ref:`Taurus coding examples <examples>` ''' def __init__(self, parent=None, formWidget=None, buttons=None, withButtons=True, designMode=False): self._children = [] TaurusWidget.__init__(self, parent, designMode) if buttons is None: buttons = Qt.QDialogButtonBox.Apply | \ Qt.QDialogButtonBox.Reset self._customWidgetMap = {} self._model = [] # self._children = [] self.setFormWidget(formWidget) self.setLayout(Qt.QVBoxLayout()) frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setWidget(frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) self.__modelChooserDlg = None self.buttonBox = QButtonBox(buttons=buttons, parent=self) self.layout().addWidget(self.buttonBox) self._connectButtons() # Actions (they automatically populate the context menu) self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseModelsAction = Qt.QAction('Modify Contents', self) self.addAction(self.chooseModelsAction) self.chooseModelsAction.triggered.connect(self.chooseModels) self.showButtonsAction = Qt.QAction('Show Buttons', self) self.showButtonsAction.setCheckable(True) self.addAction(self.showButtonsAction) self.showButtonsAction.triggered[bool].connect(self.setWithButtons) self.setWithButtons(withButtons) self.changeLabelsAction = Qt.QAction('Change labels (all items)', self) self.addAction(self.changeLabelsAction) self.changeLabelsAction.triggered.connect(self.onChangeLabelsAction) self.compactModeAction = Qt.QAction('Compact mode (all items)', self) self.compactModeAction.setCheckable(True) self.addAction(self.compactModeAction) self.compactModeAction.triggered[bool].connect(self.setCompact) self.setFormatterAction = Qt.QAction('Set formatter (all items)', self) self.addAction(self.setFormatterAction) self.setFormatterAction.triggered.connect(self.onSetFormatter) self.resetModifiableByUser() self.setSupportedMimeTypes([TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, 'text/plain']) self.resetCompact() # properties self.registerConfigProperty( self.isWithButtons, self.setWithButtons, 'withButtons') self.registerConfigProperty(self.isCompact, self.setCompact, 'compact') def __getitem__(self, key): '''provides a list-like interface: items of the form can be accessed using slice notation''' return self.getItemByIndex(key) def __len__(self): '''returns the number of items contained by the form''' return len(self.getItems()) def _splitModel(self, modelNames): '''convert str to list if needed (commas and whitespace are considered as separators)''' if isinstance(modelNames, binary_type): modelNames = modelNames.decode() if isinstance(modelNames, string_types): modelNames = str(modelNames).replace(',', ' ') modelNames = modelNames.split() return modelNames def setCustomWidgetMap(self, cwmap): '''Sets a map map for custom widgets. :param cwmap: (dict<str,tuple>) a dictionary whose keys are device type strings (i.e. see :class:`PyTango.DeviceInfo`) and whose values are tuples of classname,args,kwargs ''' # TODO: tango-centric self._customWidgetMap = cwmap def getCustomWidgetMap(self): '''Returns the map used to create custom widgets. :return: (dict<str,tuple>) a dictionary whose keys are device type strings (i.e. see :class:`PyTango.DeviceInfo`) and whose values are tuples of classname,args,kwargs ''' # TODO: tango-centric return self._customWidgetMap @Qt.pyqtSlot('QString', name='modelChanged') def parentModelChanged(self, parentmodel_name): self.info("Parent model changed to '%s'" % parentmodel_name) parentmodel_name = str(parentmodel_name) if self.getUseParentModel(): # reset the model of childs for obj, model in zip(self.getItems(), self.getModel()): obj.setModel('%s/%s' % (parentmodel_name, str(model))) else: self.debug( "received event from parent although not using parent model") def chooseModels(self): '''launches a model chooser dialog to modify the contents of the form''' if self.__modelChooserDlg is None: self.__modelChooserDlg = Qt.QDialog(self) self.__modelChooserDlg.setWindowTitle( "%s - Model Chooser" % str(self.windowTitle())) self.__modelChooserDlg.modelChooser = TaurusModelChooser() layout = Qt.QVBoxLayout() layout.addWidget(self.__modelChooserDlg.modelChooser) self.__modelChooserDlg.setLayout(layout) self.__modelChooserDlg.modelChooser.updateModels.connect(self.setModel) models_and_labels = [] indexdict = {} for m in self.getModel(): key = _normalize_model_name_case(m) indexdict[key] = indexdict.get(key, -1) + 1 item = self.getItemByModel(m, indexdict[key]) if item is None: label = None else: try: # this assumes that the readwidget is a QLabel subclass (or # something that has text()) label = str(item.labelWidget().text()) except: label = None models_and_labels.append((m, label)) self.__modelChooserDlg.modelChooser.setListedModels(models_and_labels) self.__modelChooserDlg.show() self.__modelChooserDlg.raise_() def chooseAttrs(self): self.info( 'TaurusForm.chooseAttrs() ahs been deprecated. Use TaurusForm.chooseModels() instead') self.chooseModels() def sizeHint(self): return Qt.QWidget.sizeHint(self) def _connectButtons(self): self.buttonBox.applyClicked.connect(self.apply) self.buttonBox.resetClicked.connect(self.reset) def getModel(self): return self._model @Qt.pyqtSlot('QStringList') def addModels(self, modelNames): '''Adds models to the existing ones: :param modelNames: (sequence<str>) the names of the models to be added .. seealso:: :meth:`removeModels` ''' modelNames = self._splitModel(modelNames) self.setModel(self.getModel() + modelNames) @Qt.pyqtSlot('QStringList') def removeModels(self, modelNames): '''Removes models from those already in the form. :param modelNames: (sequence<str>) the names of the models to be removed .. seealso:: :meth:`addModels` ''' modelNames = self._splitModel(modelNames) currentModels = self.getModel() for name in modelNames: try: currentModels.remove(name) except: self.warning("'%s' not in model list" % name) self.setModel(currentModels) def setModelCheck(self, model, check=True): if model is None: model = [] model = [str(m or '') for m in self._splitModel(model)] self.destroyChildren() self._model = model self.fillWithChildren() # update the modelchooser list if self.__modelChooserDlg is not None: self.__modelChooserDlg.modelChooser.setListedModels(self._model) def resetModel(self): self.destroyChildren() self._model = [] def getFormWidget(self, model=None): '''Returns a tuple that can be used for creating a widget for a given model. :param model: (str) a taurus model name for which the new item of the form will be created :return: (tuple<type,list,dict>) a tuple containing a class, a list of args and a dict of keyword args. The args and the keyword args can be passed to the class constructor ''' if model is None: return self._defaultFormWidget, (), {} # If a model is given, check if is in the custom widget map try: # if it is not an attribute, it will get an exception here obj = taurus.Attribute(model) return self._defaultFormWidget, (), {} except: try: obj = taurus.Device(model) except: self.warning( 'Cannot handle model "%s". Using default widget.' % (model)) return self._defaultFormWidget, (), {} try: key = obj.getDeviceProxy().info().dev_class # TODO: Tango-centric except: return self._defaultFormWidget, (), {} #value = self._formWidgetsMap.get(key, self._defaultFormWidget) cwmap = self.getCustomWidgetMap() value = cwmap.get(key, self._defaultFormWidget) if isinstance(value, type): # for backwards compatibility if issubclass(value, self._defaultFormWidget): return value, (), {} else: return self._defaultFormWidget, (), {'customWidgetMap': {key: value}} # we expect a tuple of str,list,dict --> # (classname_including_full_module, args, kwargs) name, args, kwargs = value pkgname, klassname = name.rsplit('.', 1) try: pkg = __import__(pkgname, fromlist=[klassname]) klass = getattr(pkg, klassname) except: self.warning( 'Cannot import "%s". Using default widget for "%s".' % (name, model)) return self._defaultFormWidget, (), {} if not issubclass(klass, self._defaultFormWidget): cwmap = kwargs.get('customWidgetMap', {}) cwmap.update({key: klass}) kwargs['customWidgetMap'] = cwmap klass = self._defaultFormWidget return klass, args, kwargs def setFormWidget(self, formWidget): if formWidget is None: from taurus.qt.qtgui.panel import TaurusValue self._defaultFormWidget = TaurusValue elif issubclass(formWidget, Qt.QWidget): self._defaultFormWidget = formWidget else: raise TypeError( 'formWidget must be one of None, QWidget. %s passed' % repr(type(formWidget))) def resetFormWidget(self): self.setFormWidget(self, None) def isWithButtons(self): return self._withButtons def setWithButtons(self, trueFalse): self._withButtons = trueFalse self.buttonBox.setVisible(self._withButtons) self.showButtonsAction.setChecked(self._withButtons) def resetWithButtons(self): self.setWithButtons(True) def onSetFormatter(self): """Reimplemented from TaurusBaseWidget""" # Form delegates se to the taurusvalues format = TaurusWidget.onSetFormatter(self) if format is not None: for item in self.getItems(): rw = item.readWidget(followCompact=True) if hasattr(rw, 'setFormat'): rw.setFormat(format) return format def setFormat(self, format): """ Reimplemented to call setFormat on the taurusvalues """ TaurusWidget.setFormat(self, format) for item in self.getItems(): if hasattr(item, 'setFormat'): item.setFormat(format) def setCompact(self, compact): self._compact = compact for item in self.getItems(): item.setCompact(compact) self.compactModeAction.setChecked(compact) def isCompact(self): return self._compact def resetCompact(self): from taurus import tauruscustomsettings self.setCompact(getattr(tauruscustomsettings, 'T_FORM_COMPACT', {})) def dropEvent(self, event): '''reimplemented to support dropping of modelnames in forms''' mtype = self.handleMimeData(event.mimeData(), self.addModels) if mtype is None: self.info('Invalid model in dropped data') else: event.acceptProposedAction() def setModifiableByUser(self, modifiable): ''' sets whether the user can change the contents of the form (e.g., via Modify Contents in the context menu) Reimplemented from :meth:`TaurusWidget.setModifiableByUser` :param modifiable: (bool) .. seealso:: :meth:`TaurusWidget.setModifiableByUser` ''' TaurusWidget.setModifiableByUser(self, modifiable) self.chooseModelsAction.setEnabled(modifiable) self.showButtonsAction.setEnabled(modifiable) self.changeLabelsAction.setEnabled(modifiable) self.compactModeAction.setEnabled(modifiable) for item in self.getItems(): try: item.setModifiableByUser(modifiable) except: pass def setRegExp(self, regExp): pass def getRegExp(self): pass def destroyChildren(self): for child in self._children: self.unregisterConfigurableItem(child) # child.destroy() child.setModel(None) child.deleteLater() self._children = [] def fillWithChildren(self): frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) frame.layout().addItem(Qt.QSpacerItem( 0, 0, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.MinimumExpanding)) parent_name = None if self.getUseParentModel(): parent_model = self.getParentModelObj() if parent_model: parent_name = parent_model.getFullName() for i, model in enumerate(self.getModel()): if not model: continue if parent_name: # @todo: Change this (it assumes tango model naming!) model = "%s/%s" % (parent_name, model) klass, args, kwargs = self.getFormWidget(model=model) widget = klass(frame, *args, **kwargs) # @todo UGLY... See if this can be done in other ways... (this causes trouble with widget that need more vertical space , like PoolMotorTV) widget.setMinimumHeight(20) try: widget.setCompact(self.isCompact()) widget.setModel(model) widget.setParent(frame) except: # raise self.warning( 'an error occurred while adding the child "%s". Skipping' % model) self.traceback(level=taurus.Debug) try: widget.setModifiableByUser(self.isModifiableByUser()) except: pass try: widget.setFormat(self.getFormat()) except Exception: self.debug('Cannot set format %s to child %s', self.getFormat(), model) widget.setObjectName("__item%i" % i) self.registerConfigDelegate(widget) self._children.append(widget) frame.layout().addItem(Qt.QSpacerItem( 0, 0, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.MinimumExpanding)) self.scrollArea.setWidget(frame) # self.scrollArea.setWidgetResizable(True) self.scrollArea.setMinimumWidth(frame.layout().sizeHint().width() + 20) def getItemByModel(self, model, index=0): '''returns the child item with given model. If there is more than one item with the same model, the index parameter can be used to distinguish among them Please note that his index is only relative to same-model items!''' for child in self._children: if (_normalize_model_name_case(child.getModel()) == _normalize_model_name_case(model)): if index <= 0: return child else: index -= 1 def getItemByIndex(self, index): '''returns the child item with at the given index position. ''' return self.getItems()[index] def getItems(self): '''returns a list of the objects that have been created as childs of the form''' return self._children # def _manageButtonBox(self): # if self.isWithButtons(): # self.buttonBox.setVisible(True) # else: # self.buttonBox.setVisible(False) def onChangeLabelsAction(self): '''changes the labelConfig of all its items''' keys = ['{attr.label}', '{attr.name}', '{attr.fullname}', '{dev.name}', '{dev.fullname}'] msg = 'Choose the label format. \n' + \ 'You may use Python format() syntax. The TaurusDevice object\n' + \ 'can be referenced as "dev" and the TaurusAttribute object\n' + \ 'as "attr"' labelConfig, ok = Qt.QInputDialog.getItem(self, 'Change Label', msg, keys, 0, True) if ok: for item in self.getItems(): item.labelConfig = (str(labelConfig)) @Qt.pyqtSlot() def apply(self): self.safeApplyOperations() @Qt.pyqtSlot() def reset(self): self.resetPendingOperations() @classmethod def getQtDesignerPluginInfo(cls): ret = TaurusWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.panel' ret['container'] = False return ret #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # QT properties #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- model = Qt.pyqtProperty("QStringList", getModel, TaurusWidget.setModel, resetModel) useParentModel = Qt.pyqtProperty("bool", TaurusWidget.getUseParentModel, TaurusWidget.setUseParentModel, TaurusWidget.resetUseParentModel) withButtons = Qt.pyqtProperty("bool", isWithButtons, setWithButtons, resetWithButtons) modifiableByUser = Qt.pyqtProperty("bool", TaurusWidget.isModifiableByUser, setModifiableByUser, TaurusWidget.resetModifiableByUser) compact = Qt.pyqtProperty("bool", isCompact, setCompact, resetCompact)
class TaurusCommandsForm(TaurusWidget): '''A form that shows commands available for a Device Server''' # TODO: tango-centric def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setLayout(Qt.QVBoxLayout()) self._splitter = Qt.QSplitter() self._splitter.setOrientation(Qt.Qt.Vertical) self.layout().addWidget(self._splitter) self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) self._scrollArea = TaurusScrollArea(self) self._scrollArea.setWidget(self._frame) self._scrollArea.setWidgetResizable(True) self._splitter.addWidget(self._scrollArea) self._outputTE = Qt.QTextEdit() self._outputTE.setReadOnly(True) self._splitter.addWidget(self._outputTE) #self._splitter.moveSplitter(self._splitter.getRange(0)[-1], 0) self._cmdWidgets = [] self._paramWidgets = [] self._viewFilters = [] self._defaultParameters = [] self._sortKey = lambda x: x.cmd_name self._operatorViewFilter = lambda x: x.disp_level == DisplayLevel.OPERATOR # self.setLayout(Qt.QGridLayout()) self.modelChanged.connect(self._updateCommandWidgets) def createConfig(self, allowUnpickable=False): ''' extending :meth:`TaurusBaseWidget.createConfig` :param alllowUnpickable: (bool) :return: (dict<str,object>) configurations (which can be loaded with :meth:`applyConfig`). .. seealso: :meth:`TaurusBaseWidget.createConfig`, :meth:`applyConfig` ''' # get the basic config configdict = TaurusWidget.createConfig( self, allowUnpickable=allowUnpickable) # store the splitter config configdict['splitter/state'] = self._splitter.saveState().data() return configdict def applyConfig(self, configdict, **kwargs): """extending :meth:`TaurusBaseWidget.applyConfig` to restore the splitter config :param configdict: (dict) .. seealso:: :meth:`TaurusBaseWidget.applyConfig`, :meth:`createConfig` """ # first do the basic stuff... TaurusWidget.applyConfig(self, configdict, **kwargs) # restore the splitter config self._splitter.restoreState( Qt.QByteArray(configdict['splitter/state'])) def getModelClass(self): '''see :meth:`TaurusBaseComponent.getModelClass`''' return taurus.core.taurusdevice.TaurusDevice def _updateCommandWidgets(self, *args): ''' Inserts command buttons and parameter widgets in the layout, according to the commands from the model ''' dev = self.getModelObj() if dev is None: self._clearFrame() return try: commands = sorted(dev.command_list_query(), key=self._sortKey) except Exception as e: self.warning('Problem querying commands from %s. Reason: %s', dev, e) self._clearFrame() return for f in self.getViewFilters(): commands = list(filter(f, commands)) self._clearFrame() layout = self._frame.layout() model = self.getFullModelName() for row, c in enumerate(commands): self.debug('Adding button for command %s' % c.cmd_name) button = TaurusCommandButton(command=c.cmd_name, text=c.cmd_name) layout.addWidget(button, row, 0) button.setModel(model) self._cmdWidgets.append(button) button.commandExecuted.connect(self._onCommandExecuted) import taurus.core.tango.util.tango_taurus as tango_taurus in_type = tango_taurus.FROM_TANGO_TO_TAURUS_TYPE[c.in_type] if in_type is not None: self.debug('Adding arguments for command %s' % c.cmd_name) pwidget = ParameterCB() if c.cmd_name.lower() in self._defaultParameters: for par in self._defaultParameters.get(c.cmd_name.lower(), []): pwidget.addItem(par) #pwidget.setEditable( (self._defaultParameters[c.cmd_name.lower()] or [''])[0] == '' ) if (self._defaultParameters[c.cmd_name.lower()] or [''])[0] == '': pwidget.setEditable(True) else: pwidget.setEditable(False) button.setParameters(self._defaultParameters[ c.cmd_name.lower()][0]) pwidget.editTextChanged.connect(button.setParameters) pwidget.currentIndexChanged['QString'].connect(button.setParameters) pwidget.activated.connect(button.setFocus) button.commandExecuted.connect(pwidget.rememberCurrentText) layout.addWidget(pwidget, row, 1) self._paramWidgets.append(pwidget) def _clearFrame(self): '''destroys all widgets in the scroll area frame''' self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) # hack because useParentModel does not work! self._frame.setModel(self.getModelName()) self._scrollArea.setWidget(self._frame) self._cmdWidgets = [] self._paramWidgets = [] def _onCommandExecuted(self, result): '''Slot called when the command is executed, to manage the output :param result: return value from the command. The type depends on the command ''' timestamp = datetime.now() cmdbutton = self.sender() output = '<i>%s</i><br>' % timestamp.strftime('%Y-%m-%d %H:%M:%S') +\ '<b>Command:</b> %s<br>' % cmdbutton.getCommand() +\ '<b>Pars:</b> %s<br>' % repr(cmdbutton.getParameters()) +\ '<b>Return Value:</b><br>%s' % str(result) self._outputTE.append(output) # ugly workaround for bug in html rendering in Qt separator = '<table width=\"100%\"><tr><td><hr /></td></tr></table>' # see http://lists.trolltech.com/qt-interest/2008-04/thread00224-0.html self._outputTE.append(separator) # self._outputTE.append('%s'%timestamp) #self._outputTE.append('<b>Command:</b> "%s"'%cmdbutton.getCommand()) #self._outputTE.append('<b>Pars:</b> %s'%repr(cmdbutton.getParameters())) #self._outputTE.append('<b>Return Value:</b><br>%s<hr>'%str(result)) def setSortKey(self, sortkey): '''sets the method used to sort the commands (cmd_name by default) :param sortkey: (callable) a function that takes a :class:`CommandInfo` as argument and returns a key to use for sorting purposes (e.g. the default sortKey is ``lambda x:x.cmd_name``) ''' self._sortKey = sortkey self._updateCommandWidgets() def setDefaultParameters(self, params): '''sets the values that will appear by default in the parameters combo box, the command combo box for the command will be editable only if the first parameter is an empty string :param params: (dict<str,list>) { 'cmd_name': ['parameters string 1', 'parameters string 2' ] } ''' self._defaultParameters = dict((k.lower(), v) for k, v in params.items()) self._updateCommandWidgets() def setViewFilters(self, filterlist): '''sets the filters to be applied when displaying the commands :param filterlist: (sequence<callable>) a sequence of command filters. All filters will be applied to each command to decide whether to display it or not. for a command to be plotted, the following condition must be true for all filters: bool(filter(command))==True ''' self._viewFilters = filterlist self._updateCommandWidgets() def getViewFilters(self): '''returns the filters used in deciding which commands are displayed :return: (sequence<callable>) a sequence of filters ''' return self._viewFilters @Qt.pyqtSlot(bool, name="setCommand") def setExpertView(self, expert): '''sets the expert view mode :param expert: (bool) If expert is True, commands won't be filtered. If it is False, commands with display level Expert won't be shown ''' currentfilters = self.getViewFilters() if expert: if self._operatorViewFilter in currentfilters: currentfilters.remove(self._operatorViewFilter) else: if self._operatorViewFilter not in currentfilters: currentfilters.insert(0, self._operatorViewFilter) self.setViewFilters(currentfilters) self._expertView = expert def getSplitter(self): '''returns the splitter that separates the command buttons area from the output area :return: (QSplitter)''' return self._splitter @classmethod def getQtDesignerPluginInfo(cls): ret = TaurusWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.panel' ret['container'] = False return ret
def __init__(self, parent=None, formWidget=None, buttons=None, withButtons=True, designMode=False): self._children = [] TaurusWidget.__init__(self, parent, designMode) if buttons is None: buttons = Qt.QDialogButtonBox.Apply | \ Qt.QDialogButtonBox.Reset self._customWidgetMap = {} self._model = [] # self._children = [] self.setFormWidget(formWidget) self.setLayout(Qt.QVBoxLayout()) frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setWidget(frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) self.__modelChooserDlg = None self.buttonBox = QButtonBox(buttons=buttons, parent=self) self.layout().addWidget(self.buttonBox) self._connectButtons() # Actions (they automatically populate the context menu) self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseModelsAction = Qt.QAction('Modify Contents', self) self.addAction(self.chooseModelsAction) self.chooseModelsAction.triggered.connect(self.chooseModels) self.showButtonsAction = Qt.QAction('Show Buttons', self) self.showButtonsAction.setCheckable(True) self.addAction(self.showButtonsAction) self.showButtonsAction.triggered[bool].connect(self.setWithButtons) self.setWithButtons(withButtons) self.changeLabelsAction = Qt.QAction('Change labels (all items)', self) self.addAction(self.changeLabelsAction) self.changeLabelsAction.triggered.connect(self.onChangeLabelsAction) self.compactModeAction = Qt.QAction('Compact mode (all items)', self) self.compactModeAction.setCheckable(True) self.addAction(self.compactModeAction) self.compactModeAction.triggered[bool].connect(self.setCompact) self.setFormatterAction = Qt.QAction('Set formatter (all items)', self) self.addAction(self.setFormatterAction) self.setFormatterAction.triggered.connect(self.onSetFormatter) self.resetModifiableByUser() self.setSupportedMimeTypes([TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, 'text/plain']) self.resetCompact() # properties self.registerConfigProperty( self.isWithButtons, self.setWithButtons, 'withButtons') self.registerConfigProperty(self.isCompact, self.setCompact, 'compact')
def __init__(self, parent=None, formWidget=None, buttons=None, withButtons=True, designMode=False): TaurusWidget.__init__(self, parent, designMode) if buttons is None: buttons = Qt.QDialogButtonBox.Apply | \ Qt.QDialogButtonBox.Reset self._customWidgetMap = {} self._model = [] self._children = [] self.setFormWidget(formWidget) self.setLayout(Qt.QVBoxLayout()) frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setWidget(frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) self.__modelChooserDlg = None self.buttonBox = QButtonBox(buttons=buttons, parent=self) self.layout().addWidget(self.buttonBox) self._connectButtons() # Actions (they automatically populate the context menu) self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseModelsAction = Qt.QAction('Modify Contents', self) self.addAction(self.chooseModelsAction) self.chooseModelsAction.triggered[()].connect(self.chooseModels) self.showButtonsAction = Qt.QAction('Show Buttons', self) self.showButtonsAction.setCheckable(True) self.addAction(self.showButtonsAction) self.showButtonsAction.triggered[bool].connect(self.setWithButtons) self.setWithButtons(withButtons) self.changeLabelsAction = Qt.QAction('Change labels (all items)', self) self.addAction(self.changeLabelsAction) self.changeLabelsAction.triggered[()].connect(self.onChangeLabelsAction) self.compactModeAction = Qt.QAction('Compact mode (all items)', self) self.compactModeAction.setCheckable(True) self.addAction(self.compactModeAction) self.compactModeAction.triggered[bool].connect(self.setCompact) self.resetModifiableByUser() self.setSupportedMimeTypes([TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, 'text/plain']) self.resetCompact() # properties self.registerConfigProperty( self.isWithButtons, self.setWithButtons, 'withButtons') self.registerConfigProperty(self.isCompact, self.setCompact, 'compact')
class TaurusForm(TaurusWidget): '''A form containing specific widgets for interacting with a given list of taurus attributes and/or devices. Its model is a list of attribute and/or device names to be shown. Each item is represented in a row consisting of a label, a read widget, a write widget, a units widget and an "extra" widget (some of them may not be shown) which are vertically aligned with their counterparts from other items. By default a :class:`TaurusValue` object is used for each item, but this can be changed and specific mappings can be defined using the :meth:`setCustomWidgetMap` method. Item objects can be accessed by index using a list-like notation:: form = TaurusForm() form.model = ['sys/tg_test/1'+a for a in ('short_image','/float_scalar','/double_scalar')] form[0].labelConfig = 'dev_alias' form[-1].writeWidgetClass = 'TaurusWheelEdit' print(len(form)) # --> outputs '3' (the length of the form is the number of items) By default, the form provides global Apply and Cancel buttons. You can also see some code that exemplifies the use of TaurusForm in :ref:`Taurus coding examples <examples>` ''' def __init__(self, parent=None, formWidget=None, buttons=None, withButtons=True, designMode=False): TaurusWidget.__init__(self, parent, designMode) if buttons is None: buttons = Qt.QDialogButtonBox.Apply | \ Qt.QDialogButtonBox.Reset self._customWidgetMap = {} self._model = [] self._children = [] self.setFormWidget(formWidget) self.setLayout(Qt.QVBoxLayout()) frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setWidget(frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) self.__modelChooserDlg = None self.buttonBox = QButtonBox(buttons=buttons, parent=self) self.layout().addWidget(self.buttonBox) self._connectButtons() # Actions (they automatically populate the context menu) self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseModelsAction = Qt.QAction('Modify Contents', self) self.addAction(self.chooseModelsAction) self.chooseModelsAction.triggered[()].connect(self.chooseModels) self.showButtonsAction = Qt.QAction('Show Buttons', self) self.showButtonsAction.setCheckable(True) self.addAction(self.showButtonsAction) self.showButtonsAction.triggered[bool].connect(self.setWithButtons) self.setWithButtons(withButtons) self.changeLabelsAction = Qt.QAction('Change labels (all items)', self) self.addAction(self.changeLabelsAction) self.changeLabelsAction.triggered[()].connect(self.onChangeLabelsAction) self.compactModeAction = Qt.QAction('Compact mode (all items)', self) self.compactModeAction.setCheckable(True) self.addAction(self.compactModeAction) self.compactModeAction.triggered[bool].connect(self.setCompact) self.resetModifiableByUser() self.setSupportedMimeTypes([TAURUS_MODEL_LIST_MIME_TYPE, TAURUS_DEV_MIME_TYPE, TAURUS_ATTR_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, 'text/plain']) self.resetCompact() # properties self.registerConfigProperty( self.isWithButtons, self.setWithButtons, 'withButtons') self.registerConfigProperty(self.isCompact, self.setCompact, 'compact') def __getitem__(self, key): '''provides a list-like interface: items of the form can be accessed using slice notation''' return self.getItemByIndex(key) def __len__(self): '''returns the number of items contained by the form''' return len(self.getItems()) def _splitModel(self, modelNames): '''convert str to list if needed (commas and whitespace are considered as separators)''' if isinstance(modelNames, (basestring, Qt.QString)): modelNames = str(modelNames).replace(',', ' ') modelNames = modelNames.split() return modelNames def setCustomWidgetMap(self, cwmap): '''Sets a map map for custom widgets. :param cwmap: (dict<str,tuple>) a dictionary whose keys are device type strings (i.e. see :class:`PyTango.DeviceInfo`) and whose values are tuples of classname,args,kwargs ''' self._customWidgetMap = cwmap def getCustomWidgetMap(self): '''Returns the map used to create custom widgets. :return: (dict<str,tuple>) a dictionary whose keys are device type strings (i.e. see :class:`PyTango.DeviceInfo`) and whose values are tuples of classname,args,kwargs ''' return self._customWidgetMap @Qt.pyqtSlot('QString', name='modelChanged') def parentModelChanged(self, parentmodel_name): self.info("Parent model changed to '%s'" % parentmodel_name) parentmodel_name = str(parentmodel_name) if self.getUseParentModel(): # reset the model of childs for obj, model in zip(self.getItems(), self.getModel()): obj.setModel('%s/%s' % (parentmodel_name, str(model))) else: self.debug( "received event from parent although not using parent model") def chooseModels(self): '''launches a model chooser dialog to modify the contents of the form''' if self.__modelChooserDlg is None: self.__modelChooserDlg = Qt.QDialog(self) self.__modelChooserDlg.setWindowTitle( "%s - Model Chooser" % unicode(self.windowTitle())) self.__modelChooserDlg.modelChooser = TaurusModelChooser() layout = Qt.QVBoxLayout() layout.addWidget(self.__modelChooserDlg.modelChooser) self.__modelChooserDlg.setLayout(layout) self.__modelChooserDlg.modelChooser.updateModels.connect(self.setModel) models_and_labels = [] models = [m.lower() for m in self.getModel()] indexdict = {} for m in models: indexdict[m] = indexdict.get(m, -1) + 1 item = self.getItemByModel(m, indexdict[m]) if item is None: label = None else: try: # this assumes that the readwidget is a QLabel subclass (or # something that has text()) label = str(item.labelWidget().text()) except: label = None models_and_labels.append((m, label)) self.__modelChooserDlg.modelChooser.setListedModels(models_and_labels) self.__modelChooserDlg.show() self.__modelChooserDlg.raise_() def chooseAttrs(self): self.info( 'TaurusForm.chooseAttrs() ahs been deprecated. Use TaurusForm.chooseModels() instead') self.chooseModels() def sizeHint(self): return Qt.QWidget.sizeHint(self) def _connectButtons(self): self.buttonBox.applyClicked.connect(self.apply) self.buttonBox.resetClicked.connect(self.reset) def getModel(self): return self._model @Qt.pyqtSlot('QStringList') def addModels(self, modelNames): '''Adds models to the existing ones: :param modelNames: (sequence<str>) the names of the models to be added .. seealso:: :meth:`removeModels` ''' modelNames = self._splitModel(modelNames) self.setModel(self.getModel() + modelNames) @Qt.pyqtSlot('QStringList') def removeModels(self, modelNames): '''Removes models from those already in the form. :param modelNames: (sequence<str>) the names of the models to be removed .. seealso:: :meth:`addModels` ''' modelNames = self._splitModel(modelNames) currentModels = self.getModel() for name in modelNames: try: currentModels.remove(name) except: self.warning("'%s' not in model list" % name) self.setModel(currentModels) def setModelCheck(self, model, check=True): if model is None: model = [] model = [str(m or '') for m in self._splitModel(model)] self.destroyChildren() self._model = model self.fillWithChildren() # update the modelchooser list if self.__modelChooserDlg is not None: self.__modelChooserDlg.modelChooser.setListedModels(self._model) def resetModel(self): self.destroyChildren() self._model = [] def getFormWidget(self, model=None): '''Returns a tuple that can be used for creating a widget for a given model. :param model: (str) a taurus model name for which the new item of the form will be created :return: (tuple<type,list,dict>) a tuple containing a class, a list of args and a dict of keyword args. The args and the keyword args can be passed to the class constructor ''' if model is None: return self._defaultFormWidget, (), {} # If a model is given, check if is in the custom widget map try: # if it is not an attribute, it will get an exception here obj = taurus.Attribute(model) return self._defaultFormWidget, (), {} except: try: obj = taurus.Device(model) except: self.warning( 'Cannot handle model "%s". Using default widget.' % (model)) return self._defaultFormWidget, (), {} try: key = obj.getDeviceProxy().info().dev_class # TODO: Tango-centric except: return self._defaultFormWidget, (), {} #value = self._formWidgetsMap.get(key, self._defaultFormWidget) cwmap = self.getCustomWidgetMap() value = cwmap.get(key, self._defaultFormWidget) if isinstance(value, type): # for backwards compatibility if issubclass(value, self._defaultFormWidget): return value, (), {} else: return self._defaultFormWidget, (), {'customWidgetMap': {key: value}} # we expect a tuple of str,list,dict --> # (classname_including_full_module, args, kwargs) name, args, kwargs = value pkgname, klassname = name.rsplit('.', 1) try: pkg = __import__(pkgname, fromlist=[klassname]) klass = getattr(pkg, klassname) except: self.warning( 'Cannot import "%s". Using default widget for "%s".' % (name, model)) return self._defaultFormWidget, (), {} if not issubclass(klass, self._defaultFormWidget): cwmap = kwargs.get('customWidgetMap', {}) cwmap.update({key: klass}) kwargs['customWidgetMap'] = cwmap klass = self._defaultFormWidget return klass, args, kwargs def setFormWidget(self, formWidget): if formWidget is None: from taurus.qt.qtgui.panel import TaurusValue self._defaultFormWidget = TaurusValue elif issubclass(formWidget, Qt.QWidget): self._defaultFormWidget = formWidget else: raise TypeError( 'formWidget must be one of None, QWidget. %s passed' % repr(type(formWidget))) def resetFormWidget(self): self.setFormWidget(self, None) def isWithButtons(self): return self._withButtons def setWithButtons(self, trueFalse): self._withButtons = trueFalse self.buttonBox.setVisible(self._withButtons) self.showButtonsAction.setChecked(self._withButtons) def resetWithButtons(self): self.setWithButtons(True) def setCompact(self, compact): self._compact = compact for item in self.getItems(): item.setCompact(compact) self.compactModeAction.setChecked(compact) def isCompact(self): return self._compact def resetCompact(self): from taurus import tauruscustomsettings self.setCompact(getattr(tauruscustomsettings, 'T_FORM_COMPACT', {})) def dropEvent(self, event): '''reimplemented to support dropping of modelnames in forms''' mtype = self.handleMimeData(event.mimeData(), self.addModels) if mtype is None: self.info('Invalid model in dropped data') else: event.acceptProposedAction() def setModifiableByUser(self, modifiable): ''' sets whether the user can change the contents of the form (e.g., via Modify Contents in the context menu) Reimplemented from :meth:`TaurusWidget.setModifiableByUser` :param modifiable: (bool) .. seealso:: :meth:`TaurusWidget.setModifiableByUser` ''' TaurusWidget.setModifiableByUser(self, modifiable) self.chooseModelsAction.setEnabled(modifiable) self.showButtonsAction.setEnabled(modifiable) self.changeLabelsAction.setEnabled(modifiable) self.compactModeAction.setEnabled(modifiable) for item in self.getItems(): try: item.setModifiableByUser(modifiable) except: pass def setRegExp(self, regExp): pass def getRegExp(self): pass def destroyChildren(self): for child in self._children: self.unregisterConfigurableItem(child) # child.destroy() child.setModel(None) child.deleteLater() self._children = [] def fillWithChildren(self): frame = TaurusWidget() frame.setLayout(Qt.QGridLayout()) frame.layout().addItem(Qt.QSpacerItem( 0, 0, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.MinimumExpanding)) parent_name = None if self.getUseParentModel(): parent_model = self.getParentModelObj() if parent_model: parent_name = parent_model.getFullName() for i, model in enumerate(self.getModel()): if not model: continue if parent_name: # @todo: Change this (it assumes tango model naming!) model = "%s/%s" % (parent_name, model) klass, args, kwargs = self.getFormWidget(model=model) widget = klass(frame, *args, **kwargs) # @todo UGLY... See if this can be done in other ways... (this causes trouble with widget that need more vertical space , like PoolMotorTV) widget.setMinimumHeight(20) try: widget.setCompact(self.isCompact()) widget.setModel(model) widget.setParent(frame) except: # raise self.warning( 'an error occurred while adding the child "%s". Skipping' % model) self.traceback(level=taurus.Debug) try: widget.setModifiableByUser(self.isModifiableByUser()) except: pass widget.setObjectName("__item%i" % i) self.registerConfigDelegate(widget) self._children.append(widget) frame.layout().addItem(Qt.QSpacerItem( 0, 0, Qt.QSizePolicy.Minimum, Qt.QSizePolicy.MinimumExpanding)) self.scrollArea.setWidget(frame) # self.scrollArea.setWidgetResizable(True) self.scrollArea.setMinimumWidth(frame.layout().sizeHint().width() + 20) def getItemByModel(self, model, index=0): '''returns the child item with given model. If there is more than one item with the same model, the index parameter can be used to distinguish among them Please note that his index is only relative to same-model items!''' for child in self._children: if child.getModel().lower() == model.lower(): if index <= 0: return child else: index -= 1 def getItemByIndex(self, index): '''returns the child item with at the given index position. ''' return self.getItems()[index] def getItems(self): '''returns a list of the objects that have been created as childs of the form''' return self._children # def _manageButtonBox(self): # if self.isWithButtons(): # self.buttonBox.setVisible(True) # else: # self.buttonBox.setVisible(False) def onChangeLabelsAction(self): '''changes the labelConfig of all its items''' keys = ['{attr.label}', '{attr.name}', '{attr.fullname}', '{dev.name}', '{dev.fullname}'] msg = 'Choose the label format. \n' + \ 'You may use Python format() syntax. The TaurusDevice object\n' + \ 'can be referenced as "dev" and the TaurusAttribute object\n' + \ 'as "attr"' labelConfig, ok = Qt.QInputDialog.getItem(self, 'Change Label', msg, keys, 0, True) if ok: for item in self.getItems(): item.labelConfig = (str(labelConfig)) @Qt.pyqtSlot() def apply(self): self.safeApplyOperations() @Qt.pyqtSlot() def reset(self): self.resetPendingOperations() @classmethod def getQtDesignerPluginInfo(cls): ret = TaurusWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.panel' ret['container'] = False return ret #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # QT properties #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- model = Qt.pyqtProperty("QStringList", getModel, TaurusWidget.setModel, resetModel) useParentModel = Qt.pyqtProperty("bool", TaurusWidget.getUseParentModel, TaurusWidget.setUseParentModel, TaurusWidget.resetUseParentModel) withButtons = Qt.pyqtProperty("bool", isWithButtons, setWithButtons, resetWithButtons) modifiableByUser = Qt.pyqtProperty("bool", TaurusWidget.isModifiableByUser, setModifiableByUser, TaurusWidget.resetModifiableByUser) compact = Qt.pyqtProperty("bool", isCompact, setCompact, resetCompact)
class TaurusCommandsForm(TaurusWidget): '''A form that shows commands available for a Device Server''' def __init__(self, parent=None, designMode=False): TaurusWidget.__init__(self, parent, designMode) self.setLayout(Qt.QVBoxLayout()) self._splitter = Qt.QSplitter() self._splitter.setOrientation(Qt.Qt.Vertical) self.layout().addWidget(self._splitter) self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) self._scrollArea = TaurusScrollArea(self) self._scrollArea.setWidget(self._frame) self._scrollArea.setWidgetResizable(True) self._splitter.addWidget(self._scrollArea) self._outputTE = Qt.QTextEdit() self._outputTE.setReadOnly(True) self._splitter.addWidget(self._outputTE) #self._splitter.moveSplitter(self._splitter.getRange(0)[-1], 0) self._cmdWidgets = [] self._paramWidgets = [] self._viewFilters = [] self._defaultParameters = [] self._sortKey = lambda x: x.cmd_name self._operatorViewFilter = lambda x: x.disp_level == PyTango.DispLevel.OPERATOR # self.setLayout(Qt.QGridLayout()) self.modelChanged.connect(self._updateCommandWidgets) def createConfig(self, allowUnpickable=False): ''' extending :meth:`TaurusBaseWidget.createConfig` :param alllowUnpickable: (bool) :return: (dict<str,object>) configurations (which can be loaded with :meth:`applyConfig`). .. seealso: :meth:`TaurusBaseWidget.createConfig`, :meth:`applyConfig` ''' # get the basic config configdict = TaurusWidget.createConfig( self, allowUnpickable=allowUnpickable) # store the splitter config configdict['splitter/state'] = self._splitter.saveState().data() return configdict def applyConfig(self, configdict, **kwargs): """extending :meth:`TaurusBaseWidget.applyConfig` to restore the splitter config :param configdict: (dict) .. seealso:: :meth:`TaurusBaseWidget.applyConfig`, :meth:`createConfig` """ # first do the basic stuff... TaurusWidget.applyConfig(self, configdict, **kwargs) # restore the splitter config self._splitter.restoreState( Qt.QByteArray(configdict['splitter/state'])) def getModelClass(self): '''see :meth:`TaurusBaseComponent.getModelClass`''' return taurus.core.taurusdevice.TaurusDevice def _updateCommandWidgets(self, *args): ''' Inserts command buttons and parameter widgets in the layout, according to the commands from the model ''' #self.debug('In TaurusCommandsForm._updateCommandWidgets())') dev = self.getModelObj() if dev is None or dev.state != TaurusDevState.Ready: self.debug('Cannot connect to device') self._clearFrame() return commands = sorted(dev.command_list_query(), key=self._sortKey) for f in self.getViewFilters(): commands = filter(f, commands) self._clearFrame() layout = self._frame.layout() for row, c in enumerate(commands): self.debug('Adding button for command %s' % c.cmd_name) button = TaurusCommandButton(command=c.cmd_name, text=c.cmd_name) layout.addWidget(button, row, 0) button.setUseParentModel(True) self._cmdWidgets.append(button) button.commandExecuted.connect(self._onCommandExecuted) if c.in_type != PyTango.CmdArgType.DevVoid: self.debug('Adding arguments for command %s' % c.cmd_name) pwidget = ParameterCB() if c.cmd_name.lower() in self._defaultParameters: for par in self._defaultParameters.get(c.cmd_name.lower(), []): pwidget.addItem(par) #pwidget.setEditable( (self._defaultParameters[c.cmd_name.lower()] or [''])[0] == '' ) if (self._defaultParameters[c.cmd_name.lower()] or [''])[0] == '': pwidget.setEditable(True) else: pwidget.setEditable(False) button.setParameters(self._defaultParameters[ c.cmd_name.lower()][0]) pwidget.editTextChanged.connect(button.setParameters) pwidget.currentIndexChanged.connect(button.setParameters) pwidget.activated.connect(button.setFocus) button.commandExecuted.connect(pwidget.rememberCurrentText) layout.addWidget(pwidget, row, 1) self._paramWidgets.append(pwidget) def _clearFrame(self): '''destroys all widgets in the scroll area frame''' self._frame = TaurusWidget(self) self._frame.setLayout(Qt.QGridLayout()) # hack because useParentModel does not work! self._frame.setModel(self.getModelName()) self._scrollArea.setWidget(self._frame) self._cmdWidgets = [] self._paramWidgets = [] def _onCommandExecuted(self, result): '''Slot called when the command is executed, to manage the output :param result: return value from the command. The type depends on the command ''' timestamp = datetime.now() cmdbutton = self.sender() output = '<i>%s</i><br>' % timestamp.strftime('%Y-%m-%d %H:%M:%S') +\ '<b>Command:</b> %s<br>' % cmdbutton.getCommand() +\ '<b>Pars:</b> %s<br>' % repr(cmdbutton.getParameters()) +\ '<b>Return Value:</b><br>%s' % str(result) self._outputTE.append(output) # ugly workaround for bug in html rendering in Qt separator = '<table width=\"100%\"><tr><td><hr /></td></tr></table>' # see http://lists.trolltech.com/qt-interest/2008-04/thread00224-0.html self._outputTE.append(separator) # self._outputTE.append('%s'%timestamp) #self._outputTE.append('<b>Command:</b> "%s"'%cmdbutton.getCommand()) #self._outputTE.append('<b>Pars:</b> %s'%repr(cmdbutton.getParameters())) #self._outputTE.append('<b>Return Value:</b><br>%s<hr>'%str(result)) def setSortKey(self, sortkey): '''sets the method used to sort the commands (cmd_name by default) :param sortkey: (callable) a function that takes a :class:`CommandInfo` as argument and returns a key to use for sorting purposes (e.g. the default sortKey is ``lambda x:x.cmd_name``) ''' self._sortKey = sortkey self._updateCommandWidgets() def setDefaultParameters(self, params): '''sets the values that will appear by default in the parameters combo box, the command combo box for the command will be editable only if the first parameter is an empty string :param params: (dict<str,list>) { 'cmd_name': ['parameters string 1', 'parameters string 2' ] } ''' self._defaultParameters = dict((k.lower(), v) for k, v in params.items()) self._updateCommandWidgets() def setViewFilters(self, filterlist): '''sets the filters to be applied when displaying the commands :param filterlist: (sequence<callable>) a sequence of command filters. All filters will be applied to each command to decide whether to display it or not. for a command to be plotted, the following condition must be true for all filters: bool(filter(command))==True ''' self._viewFilters = filterlist self._updateCommandWidgets() def getViewFilters(self): '''returns the filters used in deciding which commands are displayed :return: (sequence<callable>) a sequence of filters ''' return self._viewFilters @Qt.pyqtSlot(bool, name="setCommand") def setExpertView(self, expert): '''sets the expert view mode :param expert: (bool) If expert is True, commands won't be filtered. If it is False, commands with display level Expert won't be shown ''' currentfilters = self.getViewFilters() if expert: if self._operatorViewFilter in currentfilters: currentfilters.remove(self._operatorViewFilter) else: if self._operatorViewFilter not in currentfilters: currentfilters.insert(0, self._operatorViewFilter) self.setViewFilters(currentfilters) self._expertView = expert def getSplitter(self): '''returns the splitter that separates the command buttons area from the output area :return: (QSplitter)''' return self._splitter @classmethod def getQtDesignerPluginInfo(cls): ret = TaurusWidget.getQtDesignerPluginInfo() ret['module'] = 'taurus.qt.qtgui.panel' ret['container'] = False return ret
class TaurusCommandButtonPanel(TaurusWidget): """ A taurus panel containing tauruscommandbuttons. Creates a button that can be configured by rightclicking on the panel area. One button is stored as a dictionary containing the following keys: model: Device to run command on command: Command to run customtext: Custom text for button dangermessage: Danger message that popups before executing the command parameters: timeout: If model is not set the button will use parentmodel i.e. If all commands in the panel are connected to the same device one could setModel on the panel instead. """ def __init__(self, parent=None, designmode=False): TaurusWidget.__init__(self, parent, designmode) self._modelcommands = [] self._buttons = [] self.setLayout(Qt.QGridLayout()) self.frame = TaurusWidget(self) self.frame.setUseParentModel(True) self.frame.setLayout(Qt.QGridLayout()) self.scrollArea = TaurusScrollArea(self) self.scrollArea.setUseParentModel(True) self.scrollArea.setWidget(self.frame) self.scrollArea.setWidgetResizable(True) self.layout().addWidget(self.scrollArea) # Adding right click edit action self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu) self.chooseCommandsAction = Qt.QAction('Modify commands', self) self.addAction(self.chooseCommandsAction) self.connect(self.chooseCommandsAction, Qt.SIGNAL("triggered()"), self.chooseCommands) self.setModifiableByUser(True) # Make it saveable self.registerConfigProperty(self.commandsToConfig, self.commandsFromConfig, 'commands') def setModelCommands(self, modelcommands): """ Set which commandbutton that show up based on dictionary of model and command in a list """ self._modelcommands = modelcommands self.clearButtons() self.populateButtons() def clearButtons(self): """ Remove all the tauruscommandbuttons from gui """ for button in self._buttons: self.frame.layout().removeWidget(button) button.deleteLater() self._buttons = [] def populateButtons(self): """ Create taurus commandbuttons for everything in modelcommand """ for modelcommand in self._modelcommands: button = TaurusCommandButton(self.frame) button.setCommand(modelcommand['command']) if 'model' in modelcommand: button.setModel(modelcommand['model']) else: button.setUseParentModel(True) if 'customtext' in modelcommand: button.setCustomText(modelcommand['customtext']) if 'dangermessage' in modelcommand: button.setDangerMessage(modelcommand['dangermessage']) if 'parameters' in modelcommand: button.setParameters(modelcommand['parameters']) if 'timeout' in modelcommand: button.setTimeout(modelcommand['timeout']) self.connect(button, Qt.SIGNAL('commandExecuted'), self.onCommandExectued) # Make button expand vertically button.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.frame.layout().addWidget(button) self._buttons.append(button) def chooseCommands(self): """ Display a dialog that allows user to select which commands to be represented in the panel. If this panel gets used we could make a nicer dialog """ if len(self._modelcommands) > 0: modelcommands = self._modelcommands else: modelcommands = [{'model': 'sys/tg_test/1', 'command': 'DevVoid'}] cmdstr = json.dumps(modelcommands) text, ok = QtGui.QInputDialog.getText(None, 'Enter commands json dict', 'Enter dicts in a list: <br>' + 'Dict contents:<br>' + 'model, command <br>' + 'Optional dict keys: ' + 'customtext, dangermessage, ' + 'parameters, timeout', QtGui.QLineEdit.Normal, cmdstr) if ok: newmodelcommands = json.loads(text) self.setModelCommands(newmodelcommands) def commandsToConfig(self): """ Serialize and the commands dict """ return json.dumps(self._modelcommands) def commandsFromConfig(self, config): """ Recreate the commands dictionary """ self.setModelCommands(json.loads(config)) def onCommandExectued(self, result): """ Slot called when the command is executed """ if result is None: print "Command executed and returned None" return cmdbutton = self.sender() output = ('<b>Command:</b> ' + cmdbutton.getCommand() + '<br>' + '<b>Pars:</b> ' + repr(cmdbutton.getParameters()) + '<br>' + '<b>Return Value:</b><br>' + str(result)) QtGui.QMessageBox.information(None, "Return", output)