root/gaphor/tags/gaphor-0.12.3/gaphor/action.py

Revision 1306, 5.6 kB (checked in by arj..@yirdis.nl, 2 years ago)

Make buil_action_group() work better and prevent vague exceptions from being thrown due to services being initialized too early.

Line 
1 """
2 Support for actions in generic files.
3
4 See also gaphor/service/actionmanager.py for the management module.
5 """
6
7 class action(object):
8     """
9     Decorator. Turns a regular function (/method) into a full blown
10     Action class.
11
12     >>> class A(object):
13     ...     @action(name="my_action", label="my action")
14     ...     def myaction(self):
15     ...         print 'action called'
16     >>> a = A()
17     >>> a.myaction()
18     action called
19     >>> is_action(a.myaction)
20     True
21     >>> for method in dir(A):
22     ...     if is_action(getattr(A, method)):
23     ...         print method
24     myaction
25     >>> A.myaction.__action__.name
26     'my_action'
27     >>> A.myaction.__action__.label
28     'my action'
29     """
30
31     def __init__(self, name, label=None, tooltip=None, stock_id=None, accel=None, **kwargs):
32         self.name = name
33         self.label = label
34         self.tooltip = tooltip
35         self.stock_id = stock_id
36         self.accel = accel
37         self.__dict__.update(kwargs)
38
39     def __call__(self, func):
40         func.__action__ = self
41         #for n, v in self.kwargs.items():
42         #    setattr(func, n, v)
43         return func
44        
45
46 class toggle_action(action):
47     """
48     A toggle button can be switched on and off.
49     An extra 'active' attribute is provided than gives the initial status.
50     """
51     def __init__(self, name, label=None, accel=None, active=False):
52         super(toggle_action, self).__init__(name, label, accel=accel, active=active)
53
54
55 class radio_action(action):
56     """
57     Radio buttons take a list of names, a list of labels and a list of
58     tooltips (and optionally, a list of stock_ids).
59     The callback function should have an extra value property, which is
60     given the index number of the activated radio button action.
61     """
62     def __init__(self, names, labels=None, tooltips=None, stock_ids=None, accels=None, active=0):
63         super(radio_action, self).__init__(names[0], names=names, labels=labels, tooltips=tooltips, stock_ids=stock_ids, accels=accels, active=active)
64
65
66 def is_action(func):
67     return bool(getattr(func, '__action__', False))
68
69
70 def build_action_group(obj, name=None):
71     """
72     Build actions and an ActionGroup for each Action instance found in obj()
73     (that's why Action is a class ;) ). This function requires GTK+.
74
75     >>> class A(object):
76     ...     @action(name='bar')
77     ...     def bar(self): print 'Say bar'
78     ...     @toggle_action(name='foo')
79     ...     def foo(self, active): print 'Say foo', active
80     ...     @radio_action(names=('baz', 'beer'), labels=('Baz', 'Beer'))
81     ...     def baz(self, value):
82     ...         print 'Say', value, (value and 'beer' or 'baz')
83     >>> group = build_action_group(A())
84     Say 0 baz
85     >>> len(group.list_actions())
86     4
87     >>> a = group.get_action('bar')
88     >>> a.activate()
89     Say bar
90     >>> group.get_action('foo').activate()
91     Say foo True
92     >>> group.get_action('beer').activate()
93     Say 1 beer
94     >>> group.get_action('baz').activate()
95     Say 0 baz
96     """
97     import gtk
98     group = gtk.ActionGroup(name or obj)
99     objtype = type(obj)
100
101     for attrname in dir(obj):
102         try:
103             # Fetch the methods from the object's type instead of the object
104             # itself. This prevents some desciptors (mainly gaphor.core.inject)
105             # from executing.
106             # Otherwise stuff like dependency resolving (=inject) may kick in
107             # too early.
108             method = getattr(objtype, attrname)
109         except:
110             continue
111         act = getattr(method, '__action__', None)
112         if isinstance(act, radio_action):
113             actgroup = None
114             if not act.labels: act.labels = [None] * len(act.names)
115             if not act.tooltips: act.tooltips = [None] * len(act.names)
116             if not act.stock_ids: act.stock_ids = [None] * len(act.names)
117             if not act.accels: act.accels = [None] * len(act.names)
118             assert len(act.names) == len(act.labels)
119             assert len(act.names) == len(act.tooltips)
120             assert len(act.names) == len(act.stock_ids)
121             assert len(act.names) == len(act.accels)
122             for i, n in enumerate(act.names):
123                 gtkact = gtk.RadioAction(n, act.labels[i], act.tooltips[i], act.stock_ids[i], value=i)
124
125                 if not actgroup:
126                     actgroup = gtkact
127                 else:
128                     gtkact.props.group = actgroup
129                 group.add_action_with_accel(gtkact, act.accels[i])
130
131             actgroup.connect('changed', _radio_action_changed, obj, attrname)
132             actgroup.set_current_value(act.active)
133
134         elif isinstance(act, toggle_action):
135             gtkact = gtk.ToggleAction(act.name, act.label, act.tooltip, act.stock_id)
136             gtkact.set_property('active', act.active)
137             gtkact.connect('activate', _toggle_action_activate, obj, attrname)
138             group.add_action_with_accel(gtkact, act.accel)
139
140         elif isinstance(act, action):
141             gtkact = gtk.Action(act.name, act.label, act.tooltip, act.stock_id)
142             gtkact.connect('activate', _action_activate, obj, attrname)
143             group.add_action_with_accel(gtkact, act.accel)
144
145         elif act is not None:
146             raise TypeError, 'Invalid action type: %s' % action
147     return group
148
149
150 def _action_activate(action, obj, name):
151     method = getattr(obj, name)
152     method()
153
154
155 def _toggle_action_activate(action, obj, name):
156     method = getattr(obj, name)
157     method(action.props.active)
158
159
160 def _radio_action_changed(action, current_action, obj, name):
161     method = getattr(obj, name)
162     method(current_action.props.value)
163
164
165 if __name__ == '__main__':
166     import doctest
167     doctest.testmod()
168
169 # vim:sw=4:et:ai
Note: See TracBrowser for help on using the browser.