File: action.py

package info (click to toggle)
gaphor 0.13.0-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,692 kB
  • ctags: 2,971
  • sloc: python: 19,981; xml: 247; makefile: 54; sh: 40
file content (169 lines) | stat: -rw-r--r-- 5,732 bytes parent folder | download | duplicates (2)
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
159
160
161
162
163
164
165
166
167
168
169
"""
Support for actions in generic files.

See also gaphor/service/actionmanager.py for the management module.
"""

class action(object):
    """
    Decorator. Turns a regular function (/method) into a full blown
    Action class.

    >>> class A(object):
    ...     @action(name="my_action", label="my action")
    ...     def myaction(self):
    ...         print 'action called'
    >>> a = A()
    >>> a.myaction()
    action called
    >>> is_action(a.myaction)
    True
    >>> for method in dir(A):
    ...     if is_action(getattr(A, method)):
    ...         print method
    myaction
    >>> A.myaction.__action__.name
    'my_action'
    >>> A.myaction.__action__.label
    'my action'
    """

    def __init__(self, name, label=None, tooltip=None, stock_id=None, accel=None, **kwargs):
        self.name = name
        self.label = label
        self.tooltip = tooltip
        self.stock_id = stock_id
        self.accel = accel
        self.__dict__.update(kwargs)

    def __call__(self, func):
        func.__action__ = self
        #for n, v in self.kwargs.items():
        #    setattr(func, n, v)
        return func
        

class toggle_action(action):
    """
    A toggle button can be switched on and off.
    An extra 'active' attribute is provided than gives the initial status.
    """
    def __init__(self, name, label=None, accel=None, active=False):
        super(toggle_action, self).__init__(name, label, accel=accel, active=active)


class radio_action(action):
    """
    Radio buttons take a list of names, a list of labels and a list of
    tooltips (and optionally, a list of stock_ids).
    The callback function should have an extra value property, which is
    given the index number of the activated radio button action.
    """
    def __init__(self, names, labels=None, tooltips=None, stock_ids=None, accels=None, active=0):
        super(radio_action, self).__init__(names[0], names=names, labels=labels, tooltips=tooltips, stock_ids=stock_ids, accels=accels, active=active)


def is_action(func):
    return bool(getattr(func, '__action__', False))


def build_action_group(obj, name=None):
    """
    Build actions and an ActionGroup for each Action instance found in obj()
    (that's why Action is a class ;) ). This function requires GTK+.

    >>> class A(object):
    ...     @action(name='bar')
    ...     def bar(self): print 'Say bar'
    ...     @toggle_action(name='foo')
    ...     def foo(self, active): print 'Say foo', active
    ...     @radio_action(names=('baz', 'beer'), labels=('Baz', 'Beer'))
    ...     def baz(self, value):
    ...         print 'Say', value, (value and 'beer' or 'baz')
    >>> group = build_action_group(A())
    Say 0 baz
    >>> len(group.list_actions())
    4
    >>> a = group.get_action('bar')
    >>> a.activate()
    Say bar
    >>> group.get_action('foo').activate()
    Say foo True
    >>> group.get_action('beer').activate()
    Say 1 beer
    >>> group.get_action('baz').activate()
    Say 0 baz
    """
    import gtk
    group = gtk.ActionGroup(name or obj)
    objtype = type(obj)

    for attrname in dir(obj):
        try:
            # Fetch the methods from the object's type instead of the object
            # itself. This prevents some desciptors (mainly gaphor.core.inject)
            # from executing.
            # Otherwise stuff like dependency resolving (=inject) may kick in
            # too early.
            method = getattr(objtype, attrname)
        except:
            continue
        act = getattr(method, '__action__', None)
        if isinstance(act, radio_action):
            actgroup = None
            if not act.labels: act.labels = [None] * len(act.names)
            if not act.tooltips: act.tooltips = [None] * len(act.names)
            if not act.stock_ids: act.stock_ids = [None] * len(act.names)
            if not act.accels: act.accels = [None] * len(act.names)
            assert len(act.names) == len(act.labels)
            assert len(act.names) == len(act.tooltips)
            assert len(act.names) == len(act.stock_ids)
            assert len(act.names) == len(act.accels)
            for i, n in enumerate(act.names):
                gtkact = gtk.RadioAction(n, act.labels[i], act.tooltips[i], act.stock_ids[i], value=i)

                if not actgroup:
                    actgroup = gtkact
                else:
                    gtkact.props.group = actgroup
                group.add_action_with_accel(gtkact, act.accels[i])

            actgroup.connect('changed', _radio_action_changed, obj, attrname)
            actgroup.set_current_value(act.active)

        elif isinstance(act, toggle_action):
            gtkact = gtk.ToggleAction(act.name, act.label, act.tooltip, act.stock_id)
            gtkact.set_property('active', act.active)
            gtkact.connect('activate', _toggle_action_activate, obj, attrname)
            group.add_action_with_accel(gtkact, act.accel)

        elif isinstance(act, action):
            gtkact = gtk.Action(act.name, act.label, act.tooltip, act.stock_id)
            gtkact.connect('activate', _action_activate, obj, attrname)
            group.add_action_with_accel(gtkact, act.accel)

        elif act is not None:
            raise TypeError, 'Invalid action type: %s' % action
    return group


def _action_activate(action, obj, name):
    method = getattr(obj, name)
    method()


def _toggle_action_activate(action, obj, name):
    method = getattr(obj, name)
    method(action.props.active)


def _radio_action_changed(action, current_action, obj, name):
    method = getattr(obj, name)
    method(current_action.props.value)


if __name__ == '__main__':
    import doctest
    doctest.testmod()

# vim:sw=4:et:ai