# Source code for taurus.qt.qtcore.taurusqlistener

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

    """
    """

    __all__ = ["QTaurusBaseListener", "QObjectTaurusListener"]

    __docformat__ = "restructuredtext"

    from taurus.qt.qtcore.util import baseSignal
    from taurus.core.util.log import deprecation_decorator
    from taurus.core.tauruslistener import TaurusListener
    from taurus.external.qt import Qt

    class QTaurusBaseListener(TaurusListener):
        """Base class for QObjects listening to taurus events.

        .. note::
               :meth:`getSignaller` is now unused and deprecated. This is because
               `taurusEvent` is implemented using :func:`baseSignal`, that doesn't
               require the class to inherit from QObject.
        """

        taurusEvent = baseSignal("taurusEvent", object, object, object)

        def __init__(self, name=None, parent=None):
            if name is None:
                name = self.__class__.__name__
            super(QTaurusBaseListener, self).__init__(name, parent=parent)
            self._eventFilters = []
            self.taurusEvent.connect(self.filterEvent)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # Event handling chain
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def eventReceived(self, evt_src, evt_type, evt_value):
            """The basic implementation of the event handling chain is as
            follows:

                - eventReceived just calls :meth:`fireEvent` which emits a
                  "taurusEvent" PyQt signal that is connected
                  (by :meth:`preAttach`) to the :meth:`filterEvent` method.

                - After filtering, :meth:`handleEvent` is invoked with the
                  resulting filtered event

            .. note::
                in the earlier steps of the chain (i.e., in
                :meth:`eventReceived`/:meth:`fireEvent`), the code is executed in a
                Python thread, while from eventFilter ahead, the code is executed
                in a Qt thread. When writing widgets, one should normally work on
                the Qt thread (i.e. reimplementing :meth:`handleEvent`)

            :param evt_src: object that triggered the event
            :type evt_src: object
            :param evt_type: type of event
            :type evt_type: taurus.core.taurusbasetypes.TaurusEventType
            :param evt_value: event value
            :type evt_value: object
            """
            self.fireEvent(evt_src, evt_type, evt_value)

        def fireEvent(self, evt_src=None, evt_type=None, evt_value=None):
            """Emits a "taurusEvent" signal.

            It is unlikely that you may need to reimplement this method in
            subclasses. Consider reimplementing :meth:`eventReceived` or
            :meth:`handleEvent` instead depending on whether you need to execute
            code in the python or Qt threads, respectively

            :param evt_src: object that triggered the event
            :type evt_src: object or None
            :param evt_type: type of event
            :type evt_type: taurus.core.taurusbasetypes.TaurusEventType or None
            :param evt_value: event value
            :type evt_value: object or None
            """
            try:
                self.taurusEvent.emit(evt_src, evt_type, evt_value)
            except Exception:
                pass

        def filterEvent(self, evt_src=-1, evt_type=-1, evt_value=-1):
            """The event is processed by each and all filters in strict order
            unless one of them returns None (in which case the event is discarded)

            :param evt_src: object that triggered the event
            :type evt_src: object
            :param evt_type: type of event
            :type evt_type: taurus.core.taurusbasetypes.TaurusEventType
            :param evt_value: event value
            :type evt_value: object
            """
            r = evt_src, evt_type, evt_value

            if r == (-1, -1, -1):
                # @todo In an ideal world the signature of this method should be
                # (evt_src, evt_type, evt_value). However there's a bug in PyQt:
                # If a signal is disconnected between the moment it is emitted and
                # the moment the slot is called, then the slot is called without
                # parameters (!?). We added this default values to detect if
                # this is the case without printing an error message each time.
                # If this gets fixed, we should remove this line.
                return

            for f in self._eventFilters:
                r = f(*r)
                if r is None:
                    return
            self.handleEvent(*r)

        def handleEvent(self, evt_src, evt_type, evt_value):
            """Event handling. Default implementation does nothing.
            Reimplement as necessary

            :param evt_src: object that triggered the event
            :type evt_src: object or None
            :param evt_type: type of event
            :type evt_type: taurus.core.taurusbasetypes.TaurusEventType or None
            :param evt_value: event value
            :type evt_value: object or None
            """
            pass

        def setEventFilters(self, filters=None):
            """sets the taurus event filters list.

            The filters are run in order, using each output to feed the next
            filter. A filter must be a function that accepts 3 arguments
            ``(evt_src, evt_type, evt_value)`` If the event is to be ignored, the
            filter must return None. If the event is  not to be ignored, filter
            must return a ``(evt_src, evt_type, evt_value)`` tuple which may (or
            not) differ from the input.

            For a library of common filters, see taurus/core/util/eventfilters.py

            :param filters: a sequence of filters
            :type filters: sequence

            See also: insertEventFilter
            """
            if filters is None:
                filters = []
            self._eventFilters = list(filters)

        def getEventFilters(self):
            """Returns the list of event filters for this widget

            :return: the event filters
            :rtype: sequence<callable>
            """
            return self._eventFilters

        def insertEventFilter(self, filter, index=-1):
            """insert a filter in a given position

            :param filter: a filter
            :type filter: callable(evt_src, evt_type, evt_value)
            :param index: index to place the filter (default = -1 meaning place at
                the end)
            :type index: int

            See also: setEventFilters
            """
            self._eventFilters.insert(index, filter)

        @deprecation_decorator(rel="4.0")
        def getSignaller(self):
            return self

    class QObjectTaurusListener(Qt.QObject, QTaurusBaseListener):
        def __init__(self, name=None, parent=None):
            self.call__init__wo_kw(Qt.QObject, parent)
            self.call__init__(QTaurusBaseListener, name=name)

    class ListenerDemo(QObjectTaurusListener):
        def eventReceived(self, evt_src, evt_type, evt_value):
            self.info(
                "New %s event from %s",
                taurus.core.taurusbasetypes.TaurusEventType[evt_type],
                evt_src,
            )
            return super(ListenerDemo, self).eventReceived(
                evt_src, evt_type, evt_value
            )

        def handleEvent(self, evt_src, evt_type, evt_value):
            self.info(
                "New %s event from %s",
                taurus.core.taurusbasetypes.TaurusEventType[evt_type],
                evt_src,
            )

    if __name__ == "__main__":
        import time
        import taurus

        qlistener = ListenerDemo(name="DemoObject")

        attr = taurus.Attribute("sys/tg_test/1/double_scalar")
        attr.addListener(qlistener)

        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("Finished!")