| 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 |
|
|---|
| 42 |
|
|---|
| 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 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 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 |
|
|---|