Source code for taurus.qt.qtgui.panel.taurusvalue

#!/usr/bin/env python

# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
#
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taurus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
#
# ###########################################################################

"""
taurusvalue.py:
"""

__docformat__ = "restructuredtext"

import importlib
import re
import weakref

import taurus.core
from taurus.core import DataFormat, DataType, TaurusEventType
from taurus.core.taurusbasetypes import TaurusElementType
from taurus.core.util.log import deprecation_decorator
from taurus.external.qt import Qt
from taurus.qt.qtcore.configuration import BaseConfigurableClass
from taurus.qt.qtcore.mimetypes import (
    TAURUS_ATTR_MIME_TYPE,
    TAURUS_DEV_MIME_TYPE,
    TAURUS_MODEL_MIME_TYPE,
)
from taurus.qt.qtcore.tango.tangocombobox import TangoValueComboBox
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.button import TaurusLauncherButton
from taurus.qt.qtgui.compact import TaurusReadWriteSwitcher
from taurus.qt.qtgui.container import TaurusFrame
from taurus.qt.qtgui.display import TaurusLabel, TaurusLed
from taurus.qt.qtgui.input import (
    TaurusValueCheckBox,
    TaurusValueLineEdit,
    TaurusValueSpinBox,
    TaurusWheelEdit,
)
from taurus.qt.qtgui.util import ConfigurationMenu


class DefaultTaurusEnumComboBox(TangoValueComboBox):
    """This implementation is Tango centric"""

    def __init__(self, parent=None, designMode=False):
        super().__init__(parent, designMode)
        self._autoApply = False


[docs] class DefaultTaurusValueCheckBox(TaurusValueCheckBox): def __init__(self, *args): TaurusValueCheckBox.__init__(self, *args) self.setShowText(False)
[docs] @classmethod def getQtDesignerPluginInfo(cls): return None
[docs] class DefaultLabelWidget(TaurusLabel): """ The base class used by default for showing the label of a TaurusValue. .. note:: It only makes sense to use this class as a part of a TaurusValue, since it assumes that it can get a reference to a TaurusValue via the :meth:`getTaurusValueBuddy` member """ _dragEnabled = True def __init__(self, *args): TaurusLabel.__init__(self, *args) self.setAlignment(Qt.Qt.AlignRight) self.setSizePolicy(Qt.QSizePolicy.Preferred, Qt.QSizePolicy.Maximum) self.setBgRole(None) self.autoTrim = False self.setStyleSheet( "DefaultLabelWidget {border-style: solid; " + "border-width: 1px; " + "border-color: transparent; " + "border-radius: 4px;}" )
[docs] def setModel(self, model, **kwargs): if model is None or model == "": return TaurusLabel.setModel(self, None) try: config = self.taurusValueBuddy().getLabelConfig() except Exception: config = "{attr.label}" elementtype = self.taurusValueBuddy().getModelType() fullname = self.taurusValueBuddy().getModelObj().getFullName() if elementtype == TaurusElementType.Attribute: config = self.taurusValueBuddy().getLabelConfig() TaurusLabel.setModel(self, "%s#%s" % (fullname, config)) elif elementtype == TaurusElementType.Device: devName = self.taurusValueBuddy().getModelObj().getSimpleName() TaurusLabel.setModel(self, model) self.setText(devName)
_BCK_COMPAT_TAGS = { "<attr_name>": "{attr.name}", "<attr_fullname>": "{attr.fullname}", "<dev_alias>": "{dev.name}", "<dev_name>": "{dev.name}", "<dev_fullname>": "{dev.fullname}", }
[docs] def getDisplayValue(self, cache=True, fragmentName=None, **kwargs): """Reimplementation of getDisplayValue""" if self.modelObj is None or fragmentName is None: return self.getNoneValue() # support bck-compat tags for old in re.findall("<.+?>", fragmentName): new = self._BCK_COMPAT_TAGS.get(old, "{attr.%s}" % old) self.deprecated(dep=old, alt=new) fragmentName = fragmentName.replace(old, new) return TaurusLabel.displayValue(self, fragmentName, **kwargs)
[docs] def sizeHint(self): return Qt.QSize(Qt.QLabel.sizeHint(self).width(), 18)
[docs] def contextMenuEvent(self, event): """The label widget will be used for handling the actions of the whole TaurusValue see :meth:`QWidget.contextMenuEvent` """ menu = Qt.QMenu(self) # @todo: This should be done more Taurus-ish menu.addMenu(ConfigurationMenu(self.taurusValueBuddy())) if hasattr( self.taurusValueBuddy().writeWidget(followCompact=True), "resetPendingOperations", ): r_action = menu.addAction( "reset write value", self.taurusValueBuddy() .writeWidget(followCompact=True) .resetPendingOperations, ) r_action.setEnabled(self.taurusValueBuddy().hasPendingOperations()) if self.taurusValueBuddy().isModifiableByUser(): menu.addAction("Change label", self.taurusValueBuddy()._onChangeLabelText) menu.addAction( "Change Read Widget", self.taurusValueBuddy().onChangeReadWidget, ) menu.addAction("Set Formatter", self.taurusValueBuddy().onSetFormatter) cw_action = menu.addAction( "Change Write Widget", self.taurusValueBuddy().onChangeWriteWidget, ) # disable the action if the taurusValue is readonly cw_action.setEnabled(not self.taurusValueBuddy().isReadOnly()) cm_action = menu.addAction("Compact") cm_action.setCheckable(True) cm_action.setChecked(self.taurusValueBuddy().isCompact()) cm_action.toggled.connect(self.taurusValueBuddy().setCompact) menu.exec_(event.globalPos()) event.accept()
[docs] def getModelMimeData(self): """ reimplemented to use the taurusValueBuddy model instead of its own model """ mimeData = TaurusLabel.getModelMimeData(self) _modelname = str(self.taurusValueBuddy().getModelName()) modelname = _modelname.encode(encoding="utf8") mimeData.setData(TAURUS_MODEL_MIME_TYPE, modelname) if self.taurusValueBuddy().getModelType() == TaurusElementType.Device: mimeData.setData(TAURUS_DEV_MIME_TYPE, modelname) elif self.taurusValueBuddy().getModelType() == TaurusElementType.Attribute: mimeData.setData(TAURUS_ATTR_MIME_TYPE, modelname) return mimeData
[docs] @classmethod def getQtDesignerPluginInfo(cls): return None
class ExpandingLabel(TaurusLabel): """just a expanding TaurusLabel""" def __init__(self, *args): TaurusLabel.__init__(self, *args) self.setSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Preferred)
[docs] class DefaultReadWidgetLabel(ExpandingLabel): """A customised label for the read widget"""
[docs] def setModel(self, m, **kwargs): TaurusLabel.setModel(self, m) fgrole = "rvalue" model_obj = self.getModelObj() if model_obj is None: return if model_obj.getType() in (DataType.Integer, DataType.Float): fgrole += ".magnitude" self.setFgRole(fgrole)
class CenteredLed(TaurusLed): """just a centered TaurusLed""" DefaultAlignment = Qt.Qt.AlignHCenter | Qt.Qt.AlignVCenter class UnitLessLineEdit(TaurusValueLineEdit): """A customised TaurusValueLineEdit that always shows the magnitude""" def setModel(self, model, **kwargs): if model is None or model == "": return TaurusValueLineEdit.setModel(self, None) return TaurusValueLineEdit.setModel(self, model + "#wvalue.magnitude")
[docs] class DefaultUnitsWidget(TaurusLabel): FORMAT = "{}" def __init__(self, *args): TaurusLabel.__init__(self, *args) self.setNoneValue("") self.setSizePolicy(Qt.QSizePolicy.Preferred, Qt.QSizePolicy.Maximum) self.autoTrim = False self.setBgRole(None) self.setAlignment(Qt.Qt.AlignLeft)
[docs] def setModel(self, model, **kwargs): if model is None or model == "": return TaurusLabel.setModel(self, None) TaurusLabel.setModel(self, model + "#rvalue.units")
[docs] def sizeHint(self): # self.minimumSizeHint().width(), # Qt.QLabel.minimumSizeHint(self).width() return Qt.QSize(Qt.QLabel.sizeHint(self).width(), 24)
[docs] @classmethod def getQtDesignerPluginInfo(cls): return None
class _AbstractTaurusValueButton(TaurusLauncherButton): _deleteWidgetOnClose = True _text = "Show" def __init__(self, parent=None, designMode=False): TaurusLauncherButton.__init__(self, parent=parent, designMode=designMode) self.setSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Maximum) @classmethod def getQtDesignerPluginInfo(cls): return None
[docs] class TaurusPlotButton(_AbstractTaurusValueButton): """A button that launches a TaurusPlot""" _widgetClassName = "taurus_pyqtgraph:TaurusPlot" _icon = "designer:qwtplot.png"
[docs] class TaurusArrayEditorButton(_AbstractTaurusValueButton): """A button that launches a TaurusArrayEditor .. note:: the TaurusArrayEditor widget was removed (with qwt5). Until a better replacement is implemented, this will use a TaurusValuesTable """ _widgetClassName = "taurus.qt.qtgui.table:TaurusValuesTable" _kwargs = {"defaultWriteMode": "w"} _icon = "designer:arrayedit.png" _text = "Edit"
[docs] class TaurusImageButton(_AbstractTaurusValueButton): """A button that launches an ImageDialog""" _widgetClassName = "taurus.qt.qtgui.extra_guiqwt:TaurusImageDialog" _icon = "mimetypes:image-x-generic.svg"
[docs] class TaurusValuesTableButton(_AbstractTaurusValueButton): """A button that launches a TaurusValuesTable""" _widgetClassName = "taurus.qt.qtgui.table:TaurusValuesTable" _icon = "designer:table.png" _kwargs = {"defaultWriteMode": "r"}
[docs] class TaurusValuesTableButton_W(TaurusValuesTableButton): """A button that launches a TaurusValuesTable""" _text = "Edit" _kwargs = {"defaultWriteMode": "w"}
[docs] class TaurusDevButton(_AbstractTaurusValueButton): """A button that launches a TaurusAttrForm""" _widgetClassName = "taurus.qt.qtgui.panel:TaurusDevicePanel" _icon = "places:folder-remote.svg" _text = "Show Device"
class TaurusStatusLabel(TaurusLabel): """just a taurusLabel but showing the state as its background by default""" def __init__(self, parent=None, designMode=False): TaurusLabel.__init__(self, parent=parent, designMode=designMode) self.setBgRole("state") self.setSizePolicy(Qt.QSizePolicy.Expanding, Qt.QSizePolicy.Maximum) @classmethod def getQtDesignerPluginInfo(cls): return None
[docs] class TaurusValue(Qt.QWidget, TaurusBaseWidget): """ Internal TaurusValue class .. warning:: :class:`TaurusValue` (and any derived class from it) should never be instantiated directly. It is designed to be instantiated by a :class:`TaurusForm` class, since it breaks some conventions on the way it manages layouts of its parent model.""" _compact = False def __init__(self, parent=None, designMode=False, customWidgetMap=None): name = self.__class__.__name__ self.call__init__wo_kw(Qt.QWidget, parent) self.call__init__(TaurusBaseWidget, name, designMode=designMode) self.__error = False self.__modelClass = None self._designMode = designMode # This is a hack to show something usable when in designMode if self._designMode: layout = Qt.QHBoxLayout(self) dummy = ExpandingLabel() layout.addWidget(dummy) # dummy.setUseParentModel(True) dummy.setModel("#attr_fullname") dummy.setPrefixText("< TaurusValue: ") dummy.setSuffixText(" >") else: self.setFixedSize(0, 0) self._labelWidget = None self._readWidget = None self._writeWidget = None self._unitsWidget = None self._customWidget = None # deprecated self._extraWidget = None if customWidgetMap is None: customWidgetMap = {} else: self.deprecated( dep="customWidgetMap arg", alt="Form item factories", rel="4.6.5", ) self._customWidgetMap = customWidgetMap # deprecated self.labelWidgetClassID = "Auto" self.readWidgetClassID = "Auto" self.writeWidgetClassID = "Auto" self.unitsWidgetClassID = "Auto" self.customWidgetClassID = "Auto" # deprecated self.extraWidgetClassID = "Auto" self.setPreferredRow(-1) self._row = None self._allowWrite = True self._minimumHeight = None self._labelConfig = "{attr.label}" self.setModifiableByUser(False) if parent is not None: self.setParent(parent) self.registerConfigProperty( self.getLabelConfig, self.setLabelConfig, "labelConfig" ) self.registerConfigProperty(self.isCompact, self.setCompact, "compact") def _get_class(self, spec): """return class from a pkg_resources-like specification: "modulename:classname". It falls-back to assuming that the spec is a Taurus class name and uses the deprecated TaurusWidgetFactory().getWidgetClass """ if ":" in spec: mod_name, class_name = spec.split(":") return getattr(importlib.import_module(mod_name), class_name) else: # fall back to using TaurusWidgetFactory, deprecated from taurus.qt.qtgui.util import TaurusWidgetFactory _qt_widgets = TaurusWidgetFactory()._qt_widgets _modname, _cls = _qt_widgets[spec] self.deprecated( dep="specifying a class without module ('{}')".format(spec), alt="'{}:{}'".format(_modname, _cls), rel="5.0.0", ) return _cls
[docs] def setVisible(self, visible): for w in ( self.labelWidget(), self.readWidget(), self.writeWidget(), self.unitsWidget(), self._customWidget, self.extraWidget(), ): if w is not None: w.setVisible(visible) Qt.QWidget.setVisible(self, visible)
[docs] def labelWidget(self): """Returns the label widget""" return self._labelWidget
[docs] def readWidget(self, followCompact=False): """ Returns the read widget. If followCompact=True, and compact mode is used, it returns the switcher's readWidget instead of the switcher itself. """ if followCompact and self.isCompact(): return getattr(self._readWidget, "readWidget", self._readWidget) return self._readWidget
[docs] def writeWidget(self, followCompact=False): """ Returns the write widget. If followCompact=True, and compact mode is used, it returns the switcher's writeWidget instead of None. """ if followCompact and self.isCompact(): return getattr(self._readWidget, "writeWidget", None) return self._writeWidget
[docs] def unitsWidget(self): """Returns the units widget""" return self._unitsWidget
@deprecation_decorator(alt="item factories", rel="4.6.5") def customWidget(self): """Returns the custom widget""" return self._customWidget
[docs] def extraWidget(self): """Returns the extra widget""" return self._extraWidget
[docs] def setParent(self, parent): # make sure that the parent has a QGriLayout pl = parent.layout() if pl is None: pl = Qt.QGridLayout(parent) # creates AND sets the parent layout if not isinstance(pl, Qt.QGridLayout): raise ValueError("layout must be a QGridLayout (got %s)" % type(pl)) if self._row is None: # @TODO we should check that the Preferred row is empty in pl self._row = self.getPreferredRow() if self._row < 0: self._row = pl.rowCount() # insert self into the 0-column # this widget is invisible (except in design mode) pl.addWidget(self, self._row, 0) # Create/update the subwidgets (this also inserts them in the layout) if not self._designMode: # in design mode, no subwidgets are created self.updateLabelWidget() self.updateReadWidget() self.updateWriteWidget() self.updateUnitsWidget() self.updateExtraWidget() # do the base class stuff too Qt.QWidget.setParent(self, parent)
[docs] def onSetFormatter(self): """ Reimplemented to call onSetFormatter of the read widget (if provided) """ rw = self.readWidget(followCompact=True) if hasattr(rw, "onSetFormatter"): return rw.onSetFormatter()
[docs] def setFormat(self, format): """ Reimplemented to call setFormat of the read widget (if provided) """ TaurusBaseWidget.setFormat(self, format) try: rw = self.readWidget(followCompact=True) except AttributeError: return if hasattr(rw, "setFormat"): rw.setFormat(format)
[docs] def getAllowWrite(self): return self._allowWrite
[docs] @Qt.pyqtSlot(bool) def setAllowWrite(self, mode): self._allowWrite = mode
[docs] def resetAllowWrite(self): self._allowWrite = True
[docs] def getPreferredRow(self): return self._preferredRow
[docs] @Qt.pyqtSlot(int) def setPreferredRow(self, row): self._preferredRow = row
[docs] def resetPreferredRow(self): self.setPreferredRow(-1)
[docs] def getRow(self): return self._row
[docs] def setMinimumHeight(self, minimumHeight): self._minimumHeight = minimumHeight
[docs] def minimumHeight(self): return self._minimumHeight
[docs] def getDefaultLabelWidgetClass(self): return DefaultLabelWidget
[docs] def getDefaultReadWidgetClass(self, returnAll=False): """ Returns the default class (or classes) to use as read widget for the current model. :param returnAll: if True, the return value is a list of valid classes instead of just one class :type returnAll: bool :return: the default class to use for the read widget (or, if returnAll==True, a list of classes that can show the attribute ). If a list is returned, it will be loosely ordered by preference, being the first element always the default one. :rtype: class or list<class> """ modelobj = self.getModelObj() if modelobj is None: if returnAll: return [DefaultReadWidgetLabel] else: return DefaultReadWidgetLabel modeltype = self.getModelType() if modeltype == TaurusElementType.Attribute: # The model is an attribute try: if modelobj.isBoolean(): result = [CenteredLed, DefaultReadWidgetLabel] except Exception: pass if modelobj.data_format == DataFormat._0D: if modelobj.type == DataType.Boolean: result = [CenteredLed, DefaultReadWidgetLabel] elif modelobj.type == DataType.DevState: result = [CenteredLed, DefaultReadWidgetLabel] elif modelobj.type == DataType.DevEnum: result = [DefaultReadWidgetLabel] elif ( str(self.getModel()).lower().endswith("/status") ): # @todo: tango-centric!! result = [TaurusStatusLabel, DefaultReadWidgetLabel] else: result = [DefaultReadWidgetLabel] elif modelobj.data_format == DataFormat._1D: if modelobj.type in (DataType.Float, DataType.Integer): result = [ TaurusPlotButton, TaurusValuesTableButton, DefaultReadWidgetLabel, ] else: result = [TaurusValuesTableButton, DefaultReadWidgetLabel] elif modelobj.data_format == DataFormat._2D: if modelobj.type in (DataType.Float, DataType.Integer): try: # unused import but useful to determine if # TaurusImageButton should be added from taurus.qt.qtgui.extra_guiqwt import ( # noqa TaurusImageDialog, ) result = [ TaurusImageButton, TaurusValuesTableButton, DefaultReadWidgetLabel, ] except ImportError: result = [ TaurusValuesTableButton, DefaultReadWidgetLabel, ] else: result = [TaurusValuesTableButton, DefaultReadWidgetLabel] else: self.warning("Unsupported attribute type %s" % modelobj.type) result = [None] elif modeltype == TaurusElementType.Device: result = [TaurusDevButton] else: msg = "Unsupported model type ('%s')" % modeltype self.warning(msg) raise ValueError(msg) if returnAll: return result else: return result[0]
[docs] def getDefaultWriteWidgetClass(self, returnAll=False): """ Returns the default class (or classes) to use as write widget for the current model. :param returnAll: if True, the return value is a list of valid classes instead of just one class :type returnAll: bool :return: the default class to use for the write widget (or, if returnAll==True, a list of classes that can show the attribute ). If a list is returned, it will be loosely ordered by preference, being the first element always the default one. :rtype: class or list<class> """ modelclass = self.getModelClass() if self.isReadOnly() or ( modelclass and modelclass.getTaurusElementType() != TaurusElementType.Attribute ): if returnAll: return [] else: return None modelobj = self.getModelObj() if modelobj is None: if returnAll: return [UnitLessLineEdit] else: return UnitLessLineEdit modelType = modelobj.getType() if modelobj.data_format == DataFormat._0D: if modelType == DataType.Boolean: result = [DefaultTaurusValueCheckBox, TaurusValueLineEdit] elif modelType == DataType.DevEnum: result = [DefaultTaurusEnumComboBox] else: result = [ UnitLessLineEdit, TaurusValueSpinBox, TaurusWheelEdit, ] elif modelobj.data_format == DataFormat._1D: result = [TaurusValuesTableButton_W, TaurusValueLineEdit] elif modelobj.data_format == DataFormat._2D: result = [TaurusValuesTableButton_W] else: self.debug( "Unsupported attribute type for writing: %s" % str(DataType.whatis(modelType)) ) result = [None] if returnAll: return result else: return result[0]
[docs] def getDefaultUnitsWidgetClass(self): return DefaultUnitsWidget
@deprecation_decorator(alt="item factories", rel="4.6.5") def getDefaultCustomWidgetClass(self): self.__getDefaultCustomWidgetClass() def __getDefaultCustomWidgetClass(self): """ renamed from __getDefaultCustomWidgetClass to avoid deprecation warnings. To be removed. """ modelclass = self.getModelClass() if modelclass and modelclass.getTaurusElementType() != TaurusElementType.Device: return None try: key = self.getModelObj().getDeviceProxy().info().dev_class except Exception: return None return self._customWidgetMap.get(key, None)
[docs] def getDefaultExtraWidgetClass(self): return None
@deprecation_decorator(alt="item factories", rel="4.6.5") def setCustomWidgetMap(self, cwmap): """Sets a map map for custom widgets. :param cwmap: a dictionary whose keys are device class strings (see :class:`tango.DeviceInfo`) and whose values are widget classes to be used :type cwmap: dict<str,Qt.QWidget> """ self._customWidgetMap = cwmap @deprecation_decorator(alt="item factories", rel="4.6.5") def getCustomWidgetMap(self): """Returns the map used to create custom widgets. :return: a dictionary whose keys are device type strings (i.e. see :class:`tango.DeviceInfo`) and whose values are widgets to be used :rtype: dict<str,Qt.QWidget> """ return self._customWidgetMap
[docs] def onChangeLabelConfig(self): self.deprecated(msg="onChangeLabelConfig is deprecated", rel="Jan2018") self._onChangeLabelText()
def _onChangeLabelText(self): keys = [ "{attr.label}", "{attr.name}", "{attr.fullname}", "{dev.name}", "{dev.fullname}", ] try: current = keys.index(self.labelConfig) except Exception: current = len(keys) keys.append(self.labelConfig) 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, current, True ) if ok: self.labelConfig = labelConfig
[docs] def onChangeReadWidget(self): classnames = ["None", "Auto"] + [ c.__name__ for c in self.getDefaultReadWidgetClass(returnAll=True) ] cname, ok = Qt.QInputDialog.getItem( self, "Change Read Widget", "Choose a new read widget class", classnames, 1, True, ) if ok: self.setReadWidgetClass(str(cname))
[docs] def onChangeWriteWidget(self): classnames = ["None", "Auto"] + [ c.__name__ for c in self.getDefaultWriteWidgetClass(returnAll=True) ] cname, ok = Qt.QInputDialog.getItem( self, "Change Write Widget", "Choose a new write widget class", classnames, 1, True, ) if ok: self.setWriteWidgetClass(str(cname))
def _destroyWidget(self, widget): """get rid of a widget in a safe way""" if widget is None: self.getTaurusFactory().removeExistingAttribute(self.getModel()) raise Exception("Destroying a None widget: Attribute listener removed.") widget.hide() widget.setParent(None) if hasattr(widget, "setModel"): widget.setModel(None) # COULD NOT INVESTIGATE DEEPER, BUT THE STARTUP-HANGING # HAPPENS WITH SOME SIGNALS RELATED WITH THE LINEEDIT... # MAYBE OTHER 'WRITE WIDGETS' HAVE THE SAME PROBLEM ?!?!?! if isinstance(widget, Qt.QLineEdit): widget.blockSignals(True) # THIS HACK REDUCES THE STARTUP-HANGING RATE widget.deleteLater() def _newSubwidget(self, oldWidget, newClass): """eliminates oldWidget and returns a new one. If newClass is None, None is returned If newClass is the same as the olWidget class, nothing happens """ if oldWidget.__class__ == newClass: return oldWidget if oldWidget is not None: self._destroyWidget(oldWidget) if newClass is None: result = None else: result = newClass() return result
[docs] def labelWidgetClassFactory(self, classID): if self._customWidget is not None: return None if classID is None or classID == "None": return None classID = globals().get(classID, classID) if isinstance(classID, type): return classID elif str(classID) == "Auto": return self.getDefaultLabelWidgetClass() else: self._get_class(classID)
[docs] def readWidgetClassFactory(self, classID): if self._customWidget is not None: return None if classID is None or classID == "None": return None classID = globals().get(classID, classID) if isinstance(classID, type): ret = classID elif str(classID) == "Auto": ret = self.getDefaultReadWidgetClass() else: ret = self._get_class(classID) if self._compact: R = ret W = self.writeWidgetClassFactory( self.writeWidgetClassID, ignoreCompact=True ) if W is None: return R switcherClass = self.getSwitcherClass() switcherClass.readWClass = R switcherClass.writeWClass = W return switcherClass return ret
[docs] def writeWidgetClassFactory(self, classID, ignoreCompact=False): if self._customWidget is not None: return None if classID is None or classID == "None": return None if self._compact and not ignoreCompact: return None classID = globals().get(classID, classID) if isinstance(classID, type): return classID elif str(classID) == "Auto": return self.getDefaultWriteWidgetClass() else: return self._get_class(classID)
[docs] def unitsWidgetClassFactory(self, classID): if self._customWidget is not None: return None if classID is None or classID == "None": return None classID = globals().get(classID, classID) if isinstance(classID, type): return classID elif str(classID) == "Auto": return self.getDefaultUnitsWidgetClass() else: return self._get_class(classID)
@deprecation_decorator(alt="item factories", rel="4.6.5") def customWidgetClassFactory(self, classID): return self.__customWidgetClassFactory(self, classID) def __customWidgetClassFactory(self, classID): """ renamed from customWidgetClassFactory to avoid deprecation warnings. To be removed. """ if classID is None or classID == "None": return None classID = globals().get(classID, classID) if isinstance(classID, type): return classID elif str(classID) == "Auto": return self.__getDefaultCustomWidgetClass() else: return self._get_class(classID)
[docs] def extraWidgetClassFactory(self, classID): if self._customWidget is not None: return None if classID is None or classID == "None": return None classID = globals().get(classID, classID) if isinstance(classID, type): return classID elif str(classID) == "Auto": return self.getDefaultExtraWidgetClass() else: return self._get_class(classID)
[docs] def updateLabelWidget(self): # get the class for the widget and replace it if necessary klass = self.labelWidgetClassFactory(self.labelWidgetClassID) self._labelWidget = self._newSubwidget(self._labelWidget, klass) # take care of the layout self.addLabelWidgetToLayout() if self._labelWidget is not None: # give the new widget a reference to its buddy TaurusValue object self._labelWidget.taurusValueBuddy = weakref.ref(self) # tweak the new widget if self.minimumHeight() is not None: self._labelWidget.setMinimumHeight(self.minimumHeight()) # set the model for the subwidget if hasattr(self._labelWidget, "setModel"): self._labelWidget.setModel(self.getFullModelName())
[docs] def updateReadWidget(self): # get the class for the widget and replace it if necessary try: klass = self.readWidgetClassFactory(self.readWidgetClassID) if klass is None: raise Exception("Read widget class is None") self._readWidget = self._newSubwidget(self._readWidget, klass) except Exception as e: self._destroyWidget(self._readWidget) self._readWidget = Qt.QLabel("[Error]") msg = "Error creating read widget:\n" + str(e) self._readWidget.setToolTip(msg) self.error(msg) # self.traceback(30) # warning level=30 # take care of the layout self.addReadWidgetToLayout() if self._readWidget is not None: # give the new widget a reference to its buddy TaurusValue object self._readWidget.taurusValueBuddy = weakref.ref(self) if isinstance(self._readWidget, TaurusReadWriteSwitcher): self._readWidget.readWidget.taurusValueBuddy = weakref.ref(self) self._readWidget.writeWidget.taurusValueBuddy = weakref.ref(self) # tweak the new widget if self.minimumHeight() is not None: self._readWidget.setMinimumHeight(self.minimumHeight()) # set the model for the subwidget if hasattr(self._readWidget, "setModel"): self._readWidget.setModel(self.getFullModelName())
[docs] def updateWriteWidget(self): oldWriteWidgetID = id(self._writeWidget) # get the class for the widget and replace it if necessary klass = self.writeWidgetClassFactory(self.writeWidgetClassID) self._writeWidget = self._newSubwidget(self._writeWidget, klass) # take care of the layout # this is needed because the writeWidget affects to the readWritget # layout self.addReadWidgetToLayout() self.addWriteWidgetToLayout() if self._writeWidget is not None: # give the new widget a reference to its buddy TaurusValue object self._writeWidget.taurusValueBuddy = weakref.ref(self) if oldWriteWidgetID != id(self._writeWidget): self._writeWidget.valueChangedSignal.connect(self.updatePendingOpsStyle) self._writeWidget.setDangerMessage(self.getDangerMessage()) self._writeWidget.setForceDangerousOperations( self.getForceDangerousOperations() ) if self.minimumHeight() is not None: self._writeWidget.setMinimumHeight(self.minimumHeight()) # set the model for the subwidget if hasattr(self._writeWidget, "setModel"): self._writeWidget.setModel(self.getFullModelName())
[docs] def updateUnitsWidget(self): # get the class for the widget and replace it if necessary klass = self.unitsWidgetClassFactory(self.unitsWidgetClassID) self._unitsWidget = self._newSubwidget(self._unitsWidget, klass) # take care of the layout self.addUnitsWidgetToLayout() if self._unitsWidget is not None: # give the new widget a reference to its buddy TaurusValue object self._unitsWidget.taurusValueBuddy = weakref.ref(self) # tweak the new widget if self.minimumHeight() is not None: self._unitsWidget.setMinimumHeight(self.minimumHeight()) # set the model for the subwidget if hasattr(self._unitsWidget, "setModel"): self._unitsWidget.setModel(self.getFullModelName())
@deprecation_decorator(alt="item factories", rel="4.6.5") def updateCustomWidget(self): self.__updateCustomWidget() def __updateCustomWidget(self): # get the class for the widget and replace it if necessary klass = self.__customWidgetClassFactory(self.customWidgetClassID) self._customWidget = self._newSubwidget(self._customWidget, klass) # take care of the layout self.__addCustomWidgetToLayout() if self._customWidget is not None: # set the model for the subwidget if hasattr(self._customWidget, "setModel"): self._customWidget.setModel(self.getFullModelName())
[docs] def updateExtraWidget(self): # get the class for the widget and replace it if necessary klass = self.extraWidgetClassFactory(self.extraWidgetClassID) self._extraWidget = self._newSubwidget(self._extraWidget, klass) # take care of the layout self.addExtraWidgetToLayout() if self._extraWidget is not None: # give the new widget a reference to its buddy TaurusValue object self._extraWidget.taurusValueBuddy = weakref.ref(self) # set the model for the subwidget if hasattr(self._extraWidget, "setModel"): self._extraWidget.setModel(self.getFullModelName())
[docs] def addLabelWidgetToLayout(self): if self._labelWidget is not None and self.parent() is not None: alignment = getattr( self._labelWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0) ) self.parent().layout().addWidget( self._labelWidget, self._row, 1, 1, 1, alignment )
[docs] def addReadWidgetToLayout(self): if self._readWidget is not None and self.parent() is not None: alignment = getattr( self._readWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0) ) if self._writeWidget is None: self.parent().layout().addWidget( self._readWidget, self._row, 2, 1, 2, alignment ) else: self.parent().layout().addWidget( self._readWidget, self._row, 2, 1, 1, alignment )
[docs] def addWriteWidgetToLayout(self): if self._writeWidget is not None and self.parent() is not None: alignment = getattr( self._writeWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0) ) self.parent().layout().addWidget( self._writeWidget, self._row, 3, 1, 1, alignment )
[docs] def addUnitsWidgetToLayout(self): if self._unitsWidget is not None and self.parent() is not None: alignment = getattr( self._unitsWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0) ) self.parent().layout().addWidget( self._unitsWidget, self._row, 4, 1, 1, alignment )
@deprecation_decorator(alt="item factories", rel="4.6.5") def addCustomWidgetToLayout(self): self.__addCustomWidgetToLayout() def __addCustomWidgetToLayout(self): """ Renamed from addCustomWidgetToLayout to avoid deprecation warnings. To be removed. """ if self._customWidget is not None and self.parent() is not None: alignment = getattr( self._customWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0) ) self.parent().layout().addWidget( self._customWidget, self._row, 1, 1, -1, alignment )
[docs] def addExtraWidgetToLayout(self): parent = self.parent() if parent is not None: if self._extraWidget is not None: alignment = getattr( self._extraWidget, "layoutAlignment", Qt.Qt.AlignmentFlag(0), ) parent.layout().addWidget( self._extraWidget, self._row, 5, 1, 1, alignment )
[docs] @Qt.pyqtSlot("QString") def parentModelChanged(self, parentmodel_name): """Invoked when the parent model changes :param parentmodel_name: the new name of the parent model :type parentmodel_name: str """ TaurusBaseWidget.parentModelChanged(self, parentmodel_name) if not self._designMode: # in design mode, no subwidgets are created self.__updateCustomWidget() self.updateLabelWidget() self.updateReadWidget() self.updateWriteWidget() self.updateUnitsWidget() self.updateExtraWidget()
[docs] @Qt.pyqtSlot("QString", name="setLabelWidget") def setLabelWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.labelWidgetClassID = classID self.updateLabelWidget()
[docs] def getLabelWidgetClass(self): return self.labelWidgetClassID
[docs] def resetLabelWidgetClass(self): self.labelWidgetClassID = "Auto"
[docs] @Qt.pyqtSlot("QString", name="setReadWidget") def setReadWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.readWidgetClassID = classID self.updateReadWidget()
[docs] def getReadWidgetClass(self): return self.readWidgetClassID
[docs] def resetReadWidgetClass(self): self.readWidgetClassID = "Auto"
[docs] @Qt.pyqtSlot("QString", name="setWriteWidget") def setWriteWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.writeWidgetClassID = classID self.updateWriteWidget()
[docs] def getWriteWidgetClass(self): return self.writeWidgetClassID
[docs] def resetWriteWidgetClass(self): self.writeWidgetClassID = "Auto"
[docs] @Qt.pyqtSlot("QString", name="setUnitsWidget") def setUnitsWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.unitsWidgetClassID = classID self.updateUnitsWidget()
[docs] def getUnitsWidgetClass(self): return self.unitsWidgetClassID
[docs] def resetUnitsWidgetClass(self): self.unitsWidgetClassID = "Auto"
@deprecation_decorator(alt="item factories", rel="4.6.5") @Qt.pyqtSlot("QString", name="setCustomWidget") def setCustomWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.customWidgetClassID = classID self.__updateCustomWidget() @deprecation_decorator(alt="item factories", rel="4.6.5") def getCustomWidgetClass(self): return self.customWidgetClassID @deprecation_decorator(alt="item factories", rel="4.6.5") def resetCustomWidgetClass(self): self.customWidgetClassID = "Auto"
[docs] @Qt.pyqtSlot("QString", name="setExtraWidget") def setExtraWidgetClass(self, classID): """substitutes the current widget by a new one. classID can be one of: None, 'Auto', a TaurusWidget class name, or any class """ self.extraWidgetClassID = classID self.updateExtraWidget()
[docs] def getExtraWidgetClass(self): return self.extraWidgetClassID
[docs] def resetExtraWidgetClass(self): self.extraWidgetClassID = "Auto"
[docs] def setCompact(self, compact): # don't do anything if it is already done if compact == self._compact: return # do not switch to compact mode if the write widget is None if compact and self.writeWidget() is None: self.debug("No write widget. Ignoring setCompact(True)") return # Backup the current RW format rw = self.readWidget(followCompact=True) format = rw.getFormat() self._compact = compact if self.getModel(): self.updateReadWidget() self.updateWriteWidget() # Apply the format to the new RW rw = self.readWidget(followCompact=True) rw.setFormat(format)
[docs] def isCompact(self): return self._compact
[docs] def isReadOnly(self): if not self.getAllowWrite(): return True modelObj = self.getModelObj() if modelObj is None: return False return not modelObj.isWritable()
[docs] def getModelClass(self, **kwargs): return self.__modelClass
[docs] def createConfig(self, allowUnpickable=False): """ extending :meth:`TaurusBaseWidget.createConfig` to store also the class names for subwidgets :param alllowUnpickable: :type alllowUnpickable: bool :return: configurations (which can be loaded with :meth:`applyConfig`). :rtype: dict<str,object> .. seealso: :meth:`TaurusBaseWidget.createConfig`, :meth:`applyConfig` """ configdict = TaurusBaseWidget.createConfig( self, allowUnpickable=allowUnpickable ) # store the subwidgets classIDs and configs for key in ( "LabelWidget", "ReadWidget", "WriteWidget", "UnitsWidget", "ExtraWidget", ): # calls self.getLabelWidgetClass, self.getReadWidgetClass,... classID = getattr(self, "get%sClass" % key)() if isinstance(classID, type): # If the class is not from taurus, it doesn't have classid, # so we generate it. classID = "{0.__module__}:{0.__name__}".format(classID) if isinstance(classID, str) or allowUnpickable: configdict[key] = {"classid": classID} widget = getattr(self, key[0].lower() + key[1:])() if isinstance(widget, BaseConfigurableClass): configdict[key]["delegate"] = widget.createConfig() else: self.info( "createConfig: %s not saved because " + "it is not Pickable (%s)", key, str(classID), ) return configdict
[docs] def applyConfig(self, configdict, **kwargs): """extending :meth:`TaurusBaseWidget.applyConfig` to restore the subwidget's classes :param configdict: :type configdict: dict .. seealso:: :meth:`TaurusBaseWidget.applyConfig`, :meth:`createConfig` """ # first do the basic stuff... TaurusBaseWidget.applyConfig(self, configdict, **kwargs) # restore the subwidgets classIDs for key in ( "LabelWidget", "ReadWidget", "WriteWidget", "UnitsWidget", "ExtraWidget", ): if key in configdict: widget_configdict = configdict[key] classID = widget_configdict.get("classid", None) if classID is not None and ":" in classID: # classid is of type "module:type" instead of # a taurus class name import importlib module, name = classID.split(":") module = importlib.import_module(module) classID = getattr(module, name) getattr(self, "set%sClass" % key)(classID) if "delegate" in widget_configdict: widget = getattr(self, key[0].lower() + key[1:])() if isinstance(widget, BaseConfigurableClass): widget.applyConfig(widget_configdict["delegate"], **kwargs)
[docs] @Qt.pyqtSlot("QString") def setModel(self, model, **kwargs): """extending :meth:`TaurusBaseWidget.setModel` to change the modelclass dynamically and to update the subwidgets """ self.__modelClass = taurus.Manager().findObjectClass(model or "") TaurusBaseWidget.setModel(self, model, **kwargs) if not self._designMode: # in design mode, no subwidgets are created self.__updateCustomWidget() self.updateLabelWidget() self.updateReadWidget() self.updateWriteWidget() self.updateUnitsWidget() self.updateExtraWidget()
[docs] def handleEvent(self, evt_src, evt_type, evt_value): """Reimplemented from :meth:`TaurusBaseWidget.handleEvent` to update subwidgets on config events """ if self._designMode: return if self.__error or evt_type == TaurusEventType.Config: self.__updateCustomWidget() self.updateLabelWidget() self.updateReadWidget() self.updateWriteWidget() self.updateUnitsWidget() self.updateExtraWidget() # set/unset the error flag self.__error = evt_type == TaurusEventType.Error
[docs] def isValueChangedByUser(self): try: return self._writeWidget.isValueChangedByUser() except AttributeError: return False
[docs] def setDangerMessage(self, dangerMessage=None): TaurusBaseWidget.setDangerMessage(self, dangerMessage) try: return self._writeWidget.setDangerMessage(dangerMessage) except AttributeError: pass
[docs] def setForceDangerousOperations(self, yesno): TaurusBaseWidget.setForceDangerousOperations(self, yesno) try: return self._writeWidget.setForceDangerousOperations(yesno) except AttributeError: pass
[docs] def hasPendingOperations(self): """self.getPendingOperations will always return an empty list, but still self.hasPendingOperations will look at the writeWidget's operations. If you want to ask the TaurusValue for its pending operations, call self.writeWidget().getPendingOperations() """ w = self.writeWidget(followCompact=True) if w is None: return False return w.hasPendingOperations()
[docs] def updatePendingOpsStyle(self): if self._labelWidget is None: return if self.hasPendingOperations(): newstyle = ( "%s {border-style: solid ; border-width: 1px; " "border-color: blue; color: blue; border-radius:4px;}" % self._labelWidget.__class__.__name__ ) oldstyle = self._labelWidget.styleSheet() if newstyle != oldstyle: self._labelWidget.setStyleSheet(newstyle) else: newstyle = ( "%s {border-style: solid; border-width: 1px; " "border-color: transparent; color: black; " "border-radius:4px;}" % self._labelWidget.__class__.__name__ ) oldstyle = self._labelWidget.styleSheet() if newstyle != oldstyle: self._labelWidget.setStyleSheet(newstyle)
[docs] def getLabelConfig(self): return self._labelConfig
[docs] @Qt.pyqtSlot("QString") def setLabelConfig(self, config): """Sets fragment configuration to the label widget. :param config: fragment :type config: str """ self._labelConfig = config # backwards compatibility: this method used to work for setting # an arbitrary text to the label widget try: self.getModelFragmentObj(config) self._labelWidget._permanentText = None except Exception: try: for old in re.findall("<.+?>", config): new = self._BCK_COMPAT_TAGS.get(old, old) self.deprecated(dep=old, alt=new) config = config.replace(old, new) self._labelWidget.setText(config) except Exception: self.debug("Setting permanent text to the label widget failed") return self.updateLabelWidget()
[docs] def resetLabelConfig(self): self._labelConfig = "{attr.label}" self.updateLabelWidget()
[docs] def getSwitcherClass(self): """Returns the TaurusValue switcher class (used in compact mode). Override this method if you want to use a custom switcher in TaurusValue subclasses. """ class TVSwitcher(TaurusReadWriteSwitcher): pass return TVSwitcher
[docs] @classmethod def getQtDesignerPluginInfo(cls): return None
# ret = TaurusBaseWidget.getQtDesignerPluginInfo() # ret['module'] = 'taurus.qt.qtgui.panel' # ret['icon'] = "designer:label.png" # return ret ######################################################## # Qt properties (for designer) model = Qt.pyqtProperty( "QString", TaurusBaseWidget.getModel, setModel, TaurusBaseWidget.resetModel, ) preferredRow = Qt.pyqtProperty( "int", getPreferredRow, setPreferredRow, resetPreferredRow ) labelWidgetClass = Qt.pyqtProperty( "QString", getLabelWidgetClass, setLabelWidgetClass, resetLabelWidgetClass, ) readWidgetClass = Qt.pyqtProperty( "QString", getReadWidgetClass, setReadWidgetClass, resetReadWidgetClass ) writeWidgetClass = Qt.pyqtProperty( "QString", getWriteWidgetClass, setWriteWidgetClass, resetWriteWidgetClass, ) unitsWidgetClass = Qt.pyqtProperty( "QString", getUnitsWidgetClass, setUnitsWidgetClass, resetUnitsWidgetClass, ) extraWidgetClass = Qt.pyqtProperty( "QString", getExtraWidgetClass, setExtraWidgetClass, resetExtraWidgetClass, ) labelConfig = Qt.pyqtProperty( "QString", getLabelConfig, setLabelConfig, resetLabelConfig ) allowWrite = Qt.pyqtProperty("bool", getAllowWrite, setAllowWrite, resetAllowWrite) modifiableByUser = Qt.pyqtProperty( "bool", TaurusBaseWidget.isModifiableByUser, TaurusBaseWidget.setModifiableByUser, TaurusBaseWidget.resetModifiableByUser, )
[docs] class TaurusValuesFrame(TaurusFrame): """This is a container specialized into containing TaurusValue widgets. It should be used Only for TaurusValues """ _model = []
[docs] @Qt.pyqtSlot("QStringList") def setModel(self, model, **kwargs): self._model = model for tv in self.getTaurusValues(): tv.destroy() for m in self._model: taurusvalue = TaurusValue(self, self.designMode) taurusvalue.setMinimumHeight(20) taurusvalue.setModel(str(m)) taurusvalue.setModifiableByUser(self.isModifiableByUser())
[docs] def getModel(self, **kwargs): return self._model
[docs] def resetModel(self, **kwargs): self.setModel([])
[docs] def getTaurusValueByIndex(self, index): """returns the TaurusValue item at the given index position""" return self.getTaurusValues()[index]
[docs] def getTaurusValues(self): """returns the list of TaurusValue Objects contained by this frame""" return [obj for obj in self.children() if isinstance(obj, TaurusValue)]
[docs] @classmethod def getQtDesignerPluginInfo(cls): """we don't want this widget in designer""" return None
# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ # QT properties # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ model = Qt.pyqtProperty("QStringList", getModel, setModel, resetModel)
if __name__ == "__main__": import sys from taurus.qt.qtgui.application import TaurusApplication from taurus.qt.qtgui.panel import TaurusForm app = TaurusApplication(cmd_line_parser=None) w = TaurusForm() models = ["sys/tg_test/1/short_scalar"] * 4 w.setModel(models) w[0].writeWidget().setDangerMessage("BOOO") w[1].setWriteWidgetClass(TaurusValueLineEdit) w[2].setWriteWidgetClass("None") class DummyCW(Qt.QLabel): def setModel(self, name, **kwargs): Qt.QLabel.setText(self, name) w[3].setCustomWidgetClass(DummyCW) w.setModifiableByUser(True) w.show() sys.exit(app.exec_())