Source code for taurus.qt.qtgui.display.tauruslabel

#!/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/>.
##
#############################################################################

"""This module provides a set of basic Taurus widgets based on QLabel"""
from __future__ import absolute_import

from builtins import str
from builtins import object

import collections
import re

from taurus.core.taurusbasetypes import (TaurusElementType, TaurusEventType,
                                         AttrQuality, TaurusDevState)
from taurus.external.qt import Qt
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.base import TaurusBaseController
from taurus.qt.qtgui.base import TaurusScalarAttributeControllerHelper
from taurus.qt.qtgui.base import TaurusConfigurationControllerHelper
from taurus.qt.qtgui.base import updateLabelBackground


__all__ = ["TaurusLabel"]

__docformat__ = 'restructuredtext'

_QT_PLUGIN_INFO = {
    'module': 'taurus.qt.qtgui.display',
    'group': 'Taurus Display',
    'icon': "designer:label.png",
}

TaurusModelType = TaurusElementType
EventType = TaurusEventType


class TaurusLabelController(TaurusBaseController):

    StyleSheetTemplate = "border-style: outset; border-width: 2px; border-color: {0}; {1}"

    def __init__(self, label):
        self._text = ''
        self._trimmedText = False
        self._trimPattern = re.compile('<[^<]*>')
        TaurusBaseController.__init__(self, label)

    def _setStyle(self):
        TaurusBaseController._setStyle(self)
        label = self.label()
        # if update as palette
        if self.usePalette():
            label.setFrameShape(Qt.QFrame.Box)
            label.setFrameShadow(Qt.QFrame.Raised)
            label.setLineWidth(1)

    def label(self):
        return self.widget()

    def showValueDialog(self, label):
        Qt.QMessageBox.about(label,  "Full text", self._text)

    def _needsStateConnection(self):
        label = self.label()
        ret = 'state' in (label.fgRole, label.bgRole)
        return ret

    def _updateForeground(self, label):
        fgRole, value = label.fgRole, ''

        # handle special cases (that are not covered with fragment)
        if fgRole.lower() == 'state':
            value = self.state().name
        elif fgRole.lower() in ('', 'none'):
            pass
        else:
            value = label.getDisplayValue(fragmentName=fgRole)
        self._text = text = label.prefixText + value + label.suffixText

        # Checks that the display fits in the widget and sets it to "..." if
        # it does not fit the widget
        self._trimmedText = self._shouldTrim(label, text)
        if self._trimmedText:
            text = "<a href='...'>...</a>"
        label.setText_(text)

    def _shouldTrim(self, label, text):
        if not label.autoTrim:
            return False
        text = re.sub(self._trimPattern, '', text)
        font_metrics = Qt.QFontMetrics(label.font())
        size, textSize = label.size().width(), font_metrics.width(text)
        return textSize > size

    def _updateToolTip(self, label):
        if not label.getAutoTooltip():
            return
        toolTip = label.getFormatedToolTip()
        if self._trimmedText:
            toolTip = u"<p><b>Value:</b> %s</p><hr>%s" % (self._text, toolTip)
        label.setToolTip(toolTip)

    _updateBackground = updateLabelBackground


class TaurusLabelControllerAttribute(TaurusScalarAttributeControllerHelper, TaurusLabelController):

    def __init__(self, label):
        TaurusScalarAttributeControllerHelper.__init__(self)
        TaurusLabelController.__init__(self, label)

    def _setStyle(self):
        TaurusLabelController._setStyle(self)
        label = self.label()
        label.setDynamicTextInteractionFlags(
            Qt.Qt.TextSelectableByMouse | Qt.Qt.LinksAccessibleByMouse)


class TaurusLabelControllerConfiguration(TaurusConfigurationControllerHelper, TaurusLabelController):

    def __init__(self, label):
        TaurusConfigurationControllerHelper.__init__(self)
        TaurusLabelController.__init__(self, label)

    def _setStyle(self):
        TaurusLabelController._setStyle(self)
        label = self.label()
        label.setDynamicTextInteractionFlags(Qt.Qt.NoTextInteraction)


#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Design time controllers for label
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-

class TaurusLabelControllerDesignMode(object):

    def _updateLength(self, lcd):
        lcd.setNumDigits(6)

    def getDisplayValue(self, write=False):
        v = self.w_value()
        if not write:
            v = self.value()
        return "%6.2f" % v

    def value(self):
        return 99.99

    def w_value(self):
        return 0.0

    def quality(self):
        return AttrQuality.ATTR_VALID

    def state(self):
        return TaurusDevState.Ready

    def _updateToolTip(self, lcd):
        lcd.setToolTip("Some random value for design purposes only")


class TaurusLabelControllerAttributeDesignMode(TaurusLabelControllerDesignMode, TaurusLabelControllerAttribute):

    def __init__(self, label):
        TaurusLabelControllerDesignMode.__init__(self)
        TaurusLabelControllerAttribute.__init__(self, label)


class TaurusLabelControllerConfigurationDesignMode(TaurusLabelControllerDesignMode, TaurusLabelControllerConfiguration):

    def __init__(self, label):
        TaurusLabelControllerDesignMode.__init__(self)
        TaurusLabelControllerConfiguration.__init__(self, label)

    def getDisplayValue(self, write=False):
        return "%6.2f" % -99.99

    def _updateToolTip(self, lcd):
        lcd.setToolTip(
            "Some random configuration value for design purposes only")


_CONTROLLER_MAP = {
    None: None,
    TaurusModelType.Unknown: None,
    TaurusModelType.Attribute: TaurusLabelControllerAttribute,
    TaurusModelType.Configuration: TaurusLabelControllerConfiguration,
}

_DESIGNER_CONTROLLER_MAP = {
    None: TaurusLabelControllerAttributeDesignMode,
    TaurusModelType.Unknown: TaurusLabelControllerAttributeDesignMode,
    TaurusModelType.Attribute: TaurusLabelControllerAttributeDesignMode,
    TaurusModelType.Configuration: TaurusLabelControllerConfigurationDesignMode,
}


[docs]class TaurusLabel(Qt.QLabel, TaurusBaseWidget): DefaultPrefix = '' DefaultSuffix = '' DefaultBgRole = 'quality' DefaultFgRole = 'rvalue' DefaultShowText = True DefaultModelIndex = None DefaultAutoTrim = True DefaultAlignment = Qt.Qt.AlignRight | Qt.Qt.AlignVCenter _deprecatedRoles = dict(value='rvalue', w_value='wvalue') def __init__(self, parent=None, designMode=False): self._prefix = self.DefaultPrefix self._suffix = self.DefaultSuffix self._permanentText = None self._bgRole = self.DefaultBgRole self._fgRole = self.DefaultFgRole self._modelIndex = self.DefaultModelIndex self._autoTrim = self.DefaultAutoTrim self._modelIndexStr = '' self._controller = None self._dynamicTextInteractionFlags = True name = self.__class__.__name__ self.call__init__wo_kw(Qt.QLabel, parent) self.call__init__(TaurusBaseWidget, name, designMode=designMode) self.setAlignment(self.DefaultAlignment) self.linkActivated.connect(self.showValueDialog) # if we are in design mode there will be no events so we force the # creation of a controller object if self._designMode: self.controllerUpdate() # register configurable properties self.registerConfigProperty( self.getPermanentText, self._setPermanentText, "permanentText" ) def _calculate_controller_class(self): ctrl_map = _CONTROLLER_MAP if self._designMode: ctrl_map = _DESIGNER_CONTROLLER_MAP model_type = self.getModelType() # ugly workaround to adapt TaurusLabel to tep14 without refactoring # TODO: proper refactoring of TaurusValue to supress the Conf API if model_type == TaurusModelType.Attribute and self.modelFragmentName: model_type = TaurusModelType.Configuration ctrl_klass = ctrl_map.get(model_type, TaurusLabelController) return ctrl_klass
[docs] def controller(self): ctrl = self._controller # if there is a controller object and it is not the base controller... if ctrl is not None and not ctrl.__class__ == TaurusLabelController: return ctrl # if there is a controller object and it is still the same class... ctrl_klass = self._calculate_controller_class() if ctrl_klass is None: return None elif ctrl.__class__ == ctrl_klass: return ctrl self._controller = ctrl = ctrl_klass(self) return ctrl
[docs] def controllerUpdate(self): ctrl = self.controller() if ctrl is not None: ctrl.update()
[docs] def showValueDialog(self, *args): ctrl = self.controller() if ctrl is not None: ctrl.showValueDialog(self)
[docs] def resizeEvent(self, event): # # recheck the display every time we resize to make sure the text should # # become trimmed or not if not getattr(self, '_inResize', False): self._inResize = True self.controllerUpdate() self._inResize = False Qt.QLabel.resizeEvent(self, event)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # TaurusBaseWidget overwriting #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def handleEvent(self, evt_src, evt_type, evt_value): ctrl = self.controller() if ctrl is not None: ctrl.handleEvent(evt_src, evt_type, evt_value)
[docs] def isReadOnly(self): return True
[docs] def setModel(self, m): # force to build another controller self._controller = None self._permanentText = None TaurusBaseWidget.setModel(self, m) if self.modelFragmentName: self.setFgRole(self.modelFragmentName)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- # QT property definition #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def getModelIndexValue(self): return self._modelIndex
[docs] def getModelIndex(self): return self._modelIndexStr
[docs] def setModelIndex(self, modelIndex): mi = str(modelIndex) if len(mi) == 0: self._modelIndex = None else: try: mi_value = eval(str(mi)) except: return if type(mi_value) == int: mi_value = mi_value, if not isinstance(mi_value, collections.Sequence): return self._modelIndex = mi_value self._modelIndexStr = mi self.controllerUpdate()
[docs] def getModelMimeData(self): mimeData = TaurusBaseWidget.getModelMimeData(self) mimeData.setText(self.text()) return mimeData
[docs] def resetModelIndex(self): self.setModelIndex(self.DefaultModelIndex)
[docs] def getBgRole(self): return self._bgRole
[docs] def setBgRole(self, bgRole): """ Set the background role. The label background will be set according to the current palette and the role. Valid roles are: - 'none' : no background - 'state' a color depending on the device state - 'quality' a color depending on the attribute quality - 'rvalue' a color depending on the rvalue of the attribute - <arbitrary member name> a color based on the value of an arbitrary member of the model object (warning: experimental feature!) .. warning:: the <arbitrary member name> support is still experimental and its API may change in future versions """ self._bgRole = str(bgRole) self.controllerUpdate()
[docs] def resetBgRole(self): """Reset the background role to its default value""" self.setBgRole(self.DefaultBgRole)
[docs] def getFgRole(self): """get the foreground role for this label (see :meth:`setFgRole`)""" return self._fgRole
[docs] def setFgRole(self, fgRole): """Set what is shown as the foreground (the text) of the label Valid Roles are: - 'rvalue' the read value of the attribute - 'wvalue' the write value of the attribute - 'none' : no text - 'quality' - the quality of the attribute is displayed - 'state' - the device state """ # warn about deprecated roles role = self._deprecatedRoles.get(fgRole, fgRole) if fgRole != role: self.deprecated(rel='4.0', dep='setFgRole(%s)' % fgRole, alt='setFgRole(%s)' % role) self._fgRole = str(role) self.controllerUpdate()
[docs] def resetFgRole(self): """Reset the foreground role to its default value""" self.setFgRole(self.DefaultFgRole)
[docs] def getPrefixText(self): return self._prefix
[docs] def setPrefixText(self, prefix): self._prefix = str(prefix) self.controllerUpdate()
[docs] def resetPrefixText(self): self.setPrefixText(self.DefaultPrefix)
[docs] def getSuffixText(self): return self._suffix
[docs] def setSuffixText(self, suffix): self._suffix = str(suffix) self.controllerUpdate()
[docs] def resetSuffixText(self): self.setSuffixText(self.DefaultSuffix)
[docs] def getPermanentText(self): return self._permanentText
def _setPermanentText(self, text): self._permanentText = text if text is not None: self.setText_(text)
[docs] def setText_(self, text): """Method to expose QLabel.setText""" Qt.QLabel.setText(self, text)
[docs] def setText(self, text): """Reimplementation of setText to set permanentText""" self._setPermanentText(text)
[docs] def setAutoTrim(self, trim): """Enable/disable auto-trimming of the text. If trim is True, the text in the label will be trimmed when it doesn't fit in the available space :param trim: (bool) """ self._autoTrim = trim self.controllerUpdate()
[docs] def setDynamicTextInteractionFlags(self, flags): if self.hasDynamicTextInteractionFlags(): Qt.QLabel.setTextInteractionFlags(self, flags)
[docs] def hasDynamicTextInteractionFlags(self): return self._dynamicTextInteractionFlags
[docs] def setTextInteractionFlags(self, flags): Qt.QLabel.setTextInteractionFlags(self, flags) self._dynamicTextInteractionFlags = False
[docs] def resetTextInteractionFlags(self): Qt.QLabel.resetTextInteractionFlags(self) self.dynamicTextInteractionFlags = True
[docs] def getAutoTrim(self): """ Whether auto-trimming of the text is enabled. :return: (bool) """ return self._autoTrim
[docs] def resetAutoTrim(self): """Reset auto-trimming to its default value""" self.setAutoTrim(self.DefaultAutoTrim)
[docs] def displayValue(self, v): """Reimplementation of displayValue for TaurusLabel""" if self._permanentText is None: value = TaurusBaseWidget.displayValue(self, v) else: value = self._permanentText dev = None attr = None modeltype = self.getModelType() if modeltype == TaurusElementType.Device: dev = self.getModelObj() elif modeltype == TaurusElementType.Attribute: attr = self.getModelObj() dev = attr.getParent() try: v = value.format(dev=dev, attr=attr) except Exception as e: self.warning( "Error formatting display (%r). Reverting to raw string", e) v = value return v
[docs] @classmethod def getQtDesignerPluginInfo(cls): d = TaurusBaseWidget.getQtDesignerPluginInfo() d.update(_QT_PLUGIN_INFO) return d
#: This property holds the unique URI string representing the model name #: with which this widget will get its data from. The convention used for #: the string can be found :ref:`here <model-concept>`. #: #: **Access functions:** #: #: * :meth:`TaurusBaseWidget.getModel` #: * :meth:`TaurusLabel.setModel` #: * :meth:`TaurusBaseWidget.resetModel` #: #: .. seealso:: :ref:`model-concept` model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel, setModel, TaurusBaseWidget.resetModel) #: This property holds whether or not this widget should search in the #: widget hierarchy for a model prefix in a parent widget. #: #: **Access functions:** #: #: * :meth:`TaurusBaseWidget.getUseParentModel` #: * :meth:`TaurusBaseWidget.setUseParentModel` #: * :meth:`TaurusBaseWidget.resetUseParentModel` #: #: .. seealso:: :ref:`model-concept` useParentModel = Qt.pyqtProperty("bool", TaurusBaseWidget.getUseParentModel, TaurusBaseWidget.setUseParentModel, TaurusBaseWidget.resetUseParentModel) #: This property holds the index inside the model value that should be #: displayed #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getModelIndex` #: * :meth:`TaurusLabel.setModelIndex` #: * :meth:`TaurusLabel.resetModelIndex` #: #: .. seealso:: :ref:`model-concept` modelIndex = Qt.pyqtProperty( "QString", getModelIndex, setModelIndex, resetModelIndex) #: This property holds a prefix text #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getPrefixText` #: * :meth:`TaurusLabel.setPrefixText` #: * :meth:`TaurusLabel.resetPrefixText` prefixText = Qt.pyqtProperty("QString", getPrefixText, setPrefixText, resetPrefixText, doc="prefix text") #: This property holds a suffix text #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getSuffixText` #: * :meth:`TaurusLabel.setSuffixText` #: * :meth:`TaurusLabel.resetSuffixText` suffixText = Qt.pyqtProperty("QString", getSuffixText, setSuffixText, resetSuffixText, doc="suffix text") #: This property holds the foreground role (the text). #: Valid values are: #: #: #. ''/'None' - no value is displayed #: #. 'rvalue' - the value is displayed #: #. 'wvalue' - the write value is displayed #: #. 'quality' - the quality is displayed #: #. 'state' - the device state is displayed #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getFgRole` #: * :meth:`TaurusLabel.setFgRole` #: * :meth:`TaurusLabel.resetFgRole` fgRole = Qt.pyqtProperty("QString", getFgRole, setFgRole, resetFgRole, doc="foreground role") #: This property holds the background role. #: Valid values are ''/'None', 'quality', 'state' #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getBgRole` #: * :meth:`TaurusLabel.setBgRole` #: * :meth:`TaurusLabel.resetBgRole` bgRole = Qt.pyqtProperty("QString", getBgRole, setBgRole, resetBgRole, doc="background role") #: Specifies wether the text will be trimmed when it doesn't fit in the #: available space #: #: **Access functions:** #: #: * :meth:`TaurusLabel.getAutoTrim` #: * :meth:`TaurusLabel.setAutoTrim` #: * :meth:`TaurusLabel.resetAutoTrim` autoTrim = Qt.pyqtProperty("bool", getAutoTrim, setAutoTrim, resetAutoTrim, doc="auto trim text") #: Specifies whether the user can drag data from this widget #: #: **Access functions:** #: #: * :meth:`TaurusLabel.isDragEnabled` #: * :meth:`TaurusLabel.setDragEnabled` #: * :meth:`TaurusLabel.resetDragEnabled` dragEnabled = Qt.pyqtProperty("bool", TaurusBaseWidget.isDragEnabled, TaurusBaseWidget.setDragEnabled, TaurusBaseWidget.resetDragEnabled, doc="enable dragging") #: Specifies how the label should interact with user input if it displays #: text. #: #: **Access functions:** #: #: * :meth:`TaurusLabel.textInteractionFlags` #: * :meth:`TaurusLabel.setTextInteractionFlags` #: * :meth:`TaurusLabel.resetTextInteractionFlags try: textInteractionFlags = Qt.pyqtProperty(Qt.Qt.TextInteractionFlag, Qt.QLabel.textInteractionFlags, setTextInteractionFlags, resetTextInteractionFlags, doc="Specifies how the label should interact with user input if it displays text.") except TypeError: # Old PyQt4 version only accept strings for the type arg textInteractionFlags = Qt.pyqtProperty("int", Qt.QLabel.textInteractionFlags, setTextInteractionFlags, resetTextInteractionFlags, doc="Specifies how the label should interact with user input if it displays text.")
def demo(): "Label" from .demo import tauruslabeldemo return tauruslabeldemo.main() def main(): import sys import taurus.qt.qtgui.application Application = taurus.qt.qtgui.application.TaurusApplication app = Application.instance() owns_app = app is None if owns_app: import taurus.core.util.argparse parser = taurus.core.util.argparse.get_taurus_parser() parser.usage = "%prog [options] <full_attribute_name(s)>" app = Application(sys.argv, cmd_line_parser=parser, app_name="Taurus label demo", app_version="1.0", org_domain="Taurus", org_name="Tango community") args = app.get_command_line_args() if len(args) == 0: w = demo() else: w = Qt.QWidget() layout = Qt.QGridLayout() w.setLayout(layout) for model in args: label = TaurusLabel() label.model = model layout.addWidget(label) w.show() if owns_app: sys.exit(app.exec_()) else: return w if __name__ == '__main__': main()