# Source code for taurus.qt.qtgui.base.tauruscontroller

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    # ###########################################################################
    #
    # 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 the set of base class taurus controllers.
    """

    import weakref

    from taurus.external.qt import Qt

    from taurus.core.taurusbasetypes import DataFormat, TaurusEventType

    from taurus.qt.qtgui.base import TaurusBaseComponent
    from taurus.qt.qtgui.util import QT_ATTRIBUTE_QUALITY_PALETTE
    from taurus.qt.qtgui.util import QT_DEVICE_STATE_PALETTE

    __docformat__ = "restructuredtext"

    class TaurusBaseController(object):
        """Base class for all taurus controllers"""

        def __init__(self, widget, updateAsPalette=True):
            self._widget = weakref.ref(widget)
            self._updateAsPalette = updateAsPalette
            self._stateObj = None
            self._last_value = None
            self._last_config_value = None
            self._last_error_value = None
            self._setStyle()

        def _setStyle(self):
            pass

        def usePalette(self):
            return self._updateAsPalette

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

        def modelObj(self):
            return self.widget().getModelObj()

        attrObj = configObj = deviceObj = modelObj

        def valueObj(self):
            value = self._last_value
            if value is None:
                modelObj = self.modelObj()
                if modelObj is None:
                    return None
                value = modelObj.getValueObj()
            return value

        def value(self):
            valueObj = self.valueObj()
            return getattr(valueObj, "value", None)

        def w_value(self):  # TODO: adapt to tep14
            valueObj = self.valueObj()
            return getattr(valueObj, "w_value", None)

        def quality(self):
            valueObj = self.valueObj()
            return getattr(valueObj, "quality", None)

        def state(self):
            return self._stateObj.state

        def getDisplayValue(self, write=False, **kwargs):
            return self.widget().getDisplayValue(**kwargs)

        def handleEvent(self, evt_src, evt_type, evt_value):
            # update the "_last" values only if the event source is the model
            # (it could be the background...)
            if evt_src == self.modelObj():
                if evt_type in (TaurusEventType.Change, TaurusEventType.Periodic):
                    if self._last_value is None:
                        # reset the format so that it gets updated by displayValue
                        self.widget().resetFormat()
                    self._last_value = evt_value
                elif evt_type == TaurusEventType.Config:  # TODO: adapt to tep14
                    self._last_config_value = evt_value
                    self.widget().resetFormat()
                else:
                    self._last_error_value = evt_value
                    # In case of error, modify the last_value as well
                    try:
                        self._last_value = self.modelObj().getValueObj()
                    except Exception:
                        self._last_value = None
            self.update()

        def eventReceived(self, evt_src, evt_type, evt_value):
            # should handle the state event here. Because this is invoked by a
            # random thread, we pass it to the widget, which will forward to the
            # proper thread

            # @todo: sometimes we get this method called but self.widget() is None.
            #        Check why. For the moment I just protect it by substituting
            #        the following line by the ones after it
            # self.widget().eventReceived(evt_src, evt_type, evt_value)
            w = self.widget()
            if w is not None:
                w.eventReceived(evt_src, evt_type, evt_value)

        def update(self):
            widget = self.widget()
            self._updateConnections(widget)
            self._updateForeground(widget)
            self._updateBackground(widget)
            self._updateToolTip(widget)

        def _needsStateConnection(self):
            return False

        def _updateConnections(self, widget):
            stateObj, newStateObj = self._stateObj, None
            if self._needsStateConnection():
                newStateObj = self.deviceObj()
                if stateObj != newStateObj:
                    if stateObj is not None:
                        stateObj.removeListener(self)
                    if newStateObj is not None:
                        newStateObj.addListener(self)
            else:
                if stateObj is not None:
                    stateObj.removeListener(self)
            self._stateObj = newStateObj

        def _updateForeground(self, widget):
            pass

        def _updateBackground(self, widget):
            pass

        def _updateToolTip(self, widget):
            if widget.getAutoTooltip():
                widget.setToolTip(widget.getFormatedToolTip())

    class TaurusAttributeControllerHelper(object):

        def configObj(self):
            return self.attrObj()  # they are the same object since tep14

            # attrObj = self.attrObj()
            # if attrObj is None: return None
            # return attrObj.getConfig()

        def deviceObj(self):
            attrObj = self.attrObj()
            if attrObj is None:
                return None
            return attrObj.getParentObj()

    class TaurusScalarAttributeControllerHelper(TaurusAttributeControllerHelper):

        def getDisplayValue(self, write=False, **kwargs):
            valueObj = self.valueObj()
            widget = self.widget()
            if valueObj is None or valueObj.rvalue is None:
                return widget.getDisplayValue(**kwargs)

            format = self.attrObj().data_format
            if format == DataFormat._0D:
                return self._getDisplayValue(widget, valueObj, None, write)

            idx = widget.getModelIndexValue()
            return self._getDisplayValue(widget, valueObj, idx, write)

        def _getDisplayValue(self, widget, valueObj, idx, write):
            try:
                if write:
                    value = valueObj.wvalue
                else:
                    value = valueObj.rvalue
                if idx is not None and len(idx):
                    for i in idx:
                        value = value[i]
                return widget.displayValue(value)
            except Exception:
                return widget.getNoneValue()

        def displayValue(self, value):
            if value is None:
                return None
            ret = None
            try:
                if self.isScalar():
                    format = self.getFormat()
                    if self.isNumeric() and format is not None:
                        format = self.getFormat()
                        ret = self.getFormat() % value
                    else:
                        ret = str(value)
                elif self.isSpectrum():
                    ret = str(value)
                else:
                    ret = str(value)
            except Exception:
                # if cannot calculate value based on the format just return the
                # value
                raise
                ret = str(value)
            return ret

    class TaurusConfigurationControllerHelper(object):
        # TODO: Check if this is  used. If so, rename to avoid "Configuration"

        def __init__(self):
            self._configParam = None

        def attrObj(self):
            return self.configObj()  # they are the same object since tep14

        def deviceObj(self):
            attrObj = self.attrObj()
            if attrObj is None:
                return None
            return attrObj.getParentObj()

        @property
        def configParam(self):
            if self._configParam is None:
                self._configParam = self.widget().modelFragmentName or ""
            return self._configParam

        def getDisplayValue(self, write=False, **kwargs):
            widget = self.widget()
            model = self.getModelObj(**kwargs)
            if model is None:
                return widget.getNoneValue()
            param = self.configParam
            try:
                val = widget.getModelFragmentObj(**kwargs)
                try:
                    no_val = getattr(model, "no_" + param)  # TODO: Tango-centric
                    if val.lower() == no_val.lower():
                        val = widget.getNoneValue()
                except Exception:
                    pass
            except AttributeError:
                if param:
                    val = str(param)
                    attr = self.attrObj()
                    if attr is not None:
                        val = val.replace("<label>", attr.label or "---")
                        val = val.replace("<attr_name>", attr.name or "---")
                        val = val.replace(
                            "<attr_fullname>", attr.getFullName() or "---"
                        )
                    dev = self.deviceObj()
                    if dev is not None:
                        val = val.replace(
                            "<dev_fullname>", dev.getFullName() or "---"
                        )
                        val = val.replace(
                            "<dev_alias>", dev.getSimpleName() or "---"
                        )
                        val = val.replace(
                            "<dev_name>", dev.getNormalName() or "---"
                        )
                else:
                    val = widget.getNoneValue()
            except Exception:
                widget.debug("Invalid configuration parameter '%s'" % param)
                val = widget.getNoneValue()
            if val is None:
                val = widget.getNoneValue()
            return val

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

    def _updatePaletteColors(widget, bgBrush, fgBrush, frameBrush):
        qt_palette = widget.palette()
        qt_palette.setBrush(Qt.QPalette.Window, bgBrush)
        qt_palette.setBrush(Qt.QPalette.Base, bgBrush)
        qt_palette.setBrush(Qt.QPalette.WindowText, fgBrush)
        qt_palette.setBrush(Qt.QPalette.Light, frameBrush)
        qt_palette.setBrush(Qt.QPalette.Dark, frameBrush)
        widget.setPalette(qt_palette)

    def updateLabelBackground(ctrl, widget):
        """Helper method to setup background of taurus labels and lcds"""
        bgRole = widget.bgRole

        if ctrl.usePalette():
            widget.setAutoFillBackground(True)
            if bgRole in ("", "none", "None"):
                transparentBrush = Qt.QBrush(Qt.Qt.transparent)
                frameBrush = transparentBrush
                bgBrush, fgBrush = transparentBrush, Qt.QBrush(Qt.Qt.black)
            else:
                frameBrush = Qt.QBrush(Qt.QColor(255, 255, 255, 128))
                bgItem, palette = None, QT_DEVICE_STATE_PALETTE
                if bgRole == "quality":
                    palette = QT_ATTRIBUTE_QUALITY_PALETTE
                    bgItem = ctrl.quality()
                elif bgRole == "tango_state":
                    modelObj = widget.getModelObj()
                    try:
                        bgItem = modelObj.getParent().stateObj.rvalue
                    except AttributeError:
                        pass
                elif bgRole == "state":
                    try:
                        bgItem = ctrl.state()
                    except AttributeError:
                        pass  # protect against calls with state not instantiated
                elif bgRole == "value":
                    bgItem = ctrl.value()
                else:
                    # TODO: this is an *experimental* extension of the bgRole API
                    # added in v 4.1.2-alpha. It may change in future versions
                    modelObj = widget.getModelObj()
                    try:
                        bgItem = modelObj.getFragmentObj(bgRole)
                    except Exception:
                        widget.warning('Invalid bgRole "%s"', bgRole)
                bgBrush, fgBrush = palette.qbrush(bgItem)
            _updatePaletteColors(widget, bgBrush, fgBrush, frameBrush)
        else:
            if bgRole in ("", "none", "None"):
                ss = StyleSheetTemplate.format("rgba(0,0,0,0)", "")
            else:
                bgItem, palette = None, QT_DEVICE_STATE_PALETTE
                if bgRole == "quality":
                    palette = QT_ATTRIBUTE_QUALITY_PALETTE
                    bgItem = ctrl.quality()
                elif bgRole == "state":
                    bgItem = ctrl.state()
                elif bgRole == "value":
                    bgItem = ctrl.value()
                else:
                    # TODO: this is an *experimental* extension of the bgRole API
                    # added in v 4.1.2-alpha. It may change in future versions
                    modelObj = widget.getModelObj()
                    try:
                        bgItem = modelObj.getFragmentObj(bgRole)
                    except Exception:
                        widget.warning('Invalid bgRole "%s"', bgRole)
                color_ss = palette.qtStyleSheet(bgItem)
                ss = StyleSheetTemplate.format("rgba(255,255,255,128)", color_ss)
            widget.setStyleSheet(ss)
        widget.update()  # necessary in pyqt <= 4.4

    class TaurusBgRoleSubscriber(TaurusBaseComponent):

        def __init__(self, parent):
            super().__init__(parent=parent)
            self.parent = parent

        def handleEvent(self, evt_src, evt_type, evt_value):
            super().handleEvent(evt_src, evt_type, evt_value)
            self.parent.controllerUpdate()