File: listening_action.py

package info (click to toggle)
python-pyface 8.0.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,944 kB
  • sloc: python: 54,107; makefile: 82
file content (158 lines) | stat: -rw-r--r-- 5,352 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
#
# Author: Enthought, Inc.
# Description: <Enthought pyface package component>


import logging


from pyface.action.action import Action
from traits.api import Any, Str, observe, Undefined

# Logging.
logger = logging.getLogger(__name__)


class ListeningAction(Action):
    """ An Action that listens and makes a callback to an object.
    """

    # ListeningAction interface ----------------------------------------------

    #: The (extended) name of the method to call. By default, the on_perform
    #: function will be called with the event.
    method = Str()

    #: The (extended) name of the attribute that determines whether the action
    #: is enabled. By default, the action is always enabled when an object is
    #: set.
    enabled_name = Str()

    #: The (extended) name of the attribute that determines whether the action
    #: is visible. By default, the action is always visible.
    visible_name = Str()

    #: The object to which the names above apply.
    object = Any()

    # -------------------------------------------------------------------------
    # 'Action' interface.
    # -------------------------------------------------------------------------

    def destroy(self):
        """ Called when the action is no longer required.

        Removes all the task listeners.
        """

        if self.object:
            if self.enabled_name:
                self.object.observe(
                    self._enabled_update, self.enabled_name, remove=True
                )
            if self.visible_name:
                self.object.observe(
                    self._visible_update, self.visible_name, remove=True
                )

    def perform(self, event=None):
        """ Call the appropriate function.

        This looks for a method to call based on the extended method name
        stored in the :py:attr:`method` trait.  If the method is empty, then
        this follows the usual Action method resolution.
        """
        if self.method != "":
            method = self._get_attr(self.object, self.method)
            if method:
                method()
        else:
            super().perform(event)

    # -------------------------------------------------------------------------
    # Protected interface.
    # -------------------------------------------------------------------------

    def _get_attr(self, obj, name, default=None):
        """ Perform an extended look up of a dotted name. """
        try:
            for attr in name.split("."):
                # Perform the access in the Trait name style: if the object is
                # None, assume it simply hasn't been initialized and don't show
                # the warning.
                if obj is None:
                    return default
                else:
                    obj = getattr(obj, attr)
        except AttributeError:
            logger.error("Did not find name %r on %r" % (attr, obj))
            return default
        return obj

    # Trait change handlers --------------------------------------------------

    @observe('enabled_name')
    def _enabled_name_updated(self, event):
        old, new = event.old, event.new
        obj = self.object
        if obj is not None:
            if old:
                obj.observe(self._enabled_update, old, remove=True)
            if new:
                obj.observe(self._enabled_update, new)
        self._enabled_update()

    @observe('visible_name')
    def _visible_name_updated(self, event):
        old, new = event.old, event.new
        obj = self.object
        if obj is not None:
            if old:
                obj.observe(self._visible_update, old, remove=True)
            if new:
                obj.observe(self._visible_update, new)
        self._visible_update()

    @observe('object')
    def _object_updated(self, event):
        old, new = event.old, event.new
        for kind in ("enabled", "visible"):
            method = getattr(self, "_%s_update" % kind)
            name = getattr(self, "%s_name" % kind)
            if name:
                if old and old is not Undefined:
                    old.observe(method, name, remove=True)
                if new:
                    new.observe(method, name)
                method()

    def _enabled_update(self, event=None):
        if self.enabled_name:
            if self.object:
                self.enabled = bool(
                    self._get_attr(self.object, self.enabled_name, False)
                )
            else:
                self.enabled = False
        else:
            self.enabled = bool(self.object)

    def _visible_update(self, event=None):
        if self.visible_name:
            if self.object:
                self.visible = bool(
                    self._get_attr(self.object, self.visible_name, False)
                )
            else:
                self.visible = False
        else:
            self.visible = True