Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 305 lines (226 sloc) 9.89 kb
c2935b7 @baverman removed gsignals dependency
authored
1 import gobject
2 import gobject.constants
3 import weakref
da35d8b @baverman connect signals to base class handlers
authored
4 from inspect import getmembers, ismethod
c2935b7 @baverman removed gsignals dependency
authored
5
6 from weak import weak_connect
1e15924 @baverman again massive signal system refactoring
authored
7 from util import append_attr
c2935b7 @baverman removed gsignals dependency
authored
8
9 SSIGNAL = gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_NO_RECURSE | gobject.SIGNAL_ACTION
10 SACTION = gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION
11
1e15924 @baverman again massive signal system refactoring
authored
12 def attach_signal_connect_info(attr, obj, func, after, idle):
13 """
14 Adds signal connection info to function
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
15
1e15924 @baverman again massive signal system refactoring
authored
16 Used by signal and trigger decorators
17 """
18 connect_params = dict(after=after, idle=idle)
19
20 if func:
21 if not getattr(func, '__call__'):
22 raise Exception('Signal decorator accept callable or connect params')
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
23
1e15924 @baverman again massive signal system refactoring
authored
24 append_attr(func, attr, (obj, connect_params))
25 return func
26 else:
27 def inner(func):
28 append_attr(func, attr, (obj, connect_params))
29 return func
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
30
31 return inner
32
c2935b7 @baverman removed gsignals dependency
authored
33 class Signal(object):
1e15924 @baverman again massive signal system refactoring
authored
34 """
35 Unbounded signal
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
36
1e15924 @baverman again massive signal system refactoring
authored
37 Class holds signal parameters which used to construct correct GObject later.
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
38
39 Instantiating signals::
40
1e15924 @baverman again massive signal system refactoring
authored
41 Signal() # Signal without arguments
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
42
1e15924 @baverman again massive signal system refactoring
authored
43 Signal(object, int) # Signal with two arguments
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
44
1e15924 @baverman again massive signal system refactoring
authored
45 Signal(object, return_type=int) # Signal with return type
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
46
47 Signal(type=gobject.SIGNAL_RUN_FIRST) # default signal type is gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_NO_RECURSE | gobject.SIGNAL_ACTION
1e15924 @baverman again massive signal system refactoring
authored
48
49
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
50 Unbounded signal instances can be used to mark callbacks for automatic signal connecting::
1e15924 @baverman again massive signal system refactoring
authored
51
52 signal = Signal()
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
53
1e15924 @baverman again massive signal system refactoring
authored
54 class Handler(object):
55 @signal
56 def callback(...): pass # Usual (in gobject terms) signal connection
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
57
1e15924 @baverman again massive signal system refactoring
authored
58 @signal(idle=True)
59 def callback(...): pass # Connects signal with idle wrapper
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
60
1e15924 @baverman again massive signal system refactoring
authored
61 @signal(after=True)
62 def callback(...): pass # sender.connect_after(callback) analog
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
63
1e15924 @baverman again massive signal system refactoring
authored
64 @signal(idle=9999)
65 def callback(...): pass # idle wrapper will start callback with specified priority
66 """
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
67
c2935b7 @baverman removed gsignals dependency
authored
68 def __init__(self, *signal_args, **kwargs):
69 allowed_named_arguments = set(('type', 'return_type'))
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
70 if not all(r in allowed_named_arguments for r in kwargs.keys()):
c2935b7 @baverman removed gsignals dependency
authored
71 raise Exception('Signal constructor takes only `type` and `return_type` named arguments')
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
72
c2935b7 @baverman removed gsignals dependency
authored
73 self.signal_type = kwargs.get('type', SSIGNAL)
74 self.return_type = kwargs.get('return_type', None)
75 self.arg_types = tuple(signal_args)
76 self.name = None
77
78 def __call__(self, func=None, after=False, idle=False):
79 return attach_signal_connect_info('signals_to_connect', self, func, after, idle)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
80
c2935b7 @baverman removed gsignals dependency
authored
81 def emit(self):
b62e4ab @baverman Bad python code save preventer plugin
authored
82 """IDE hint"""
c2935b7 @baverman removed gsignals dependency
authored
83 raise Exception('You cannot emit unbounded signals')
84
b62e4ab @baverman Bad python code save preventer plugin
authored
85 def stop_emission(self):
86 """IDE hint"""
87 raise Exception('You cannot stop emission of unbounded signals')
88
c2935b7 @baverman removed gsignals dependency
authored
89
90 class SignalManager(object):
1e15924 @baverman again massive signal system refactoring
authored
91 """
92 Wrapper for inner GObject with signals
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
93
1e15924 @baverman again massive signal system refactoring
authored
94 Example::
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
95
1e15924 @baverman again massive signal system refactoring
authored
96 class Manager(SignalManager):
97 show = Signal()
98 hide = Signal()
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
99
1e15924 @baverman again massive signal system refactoring
authored
100 ``Manager.show`` and ``Manager.hide`` is unbounded signals and can be used as
101 decorators to callbacks. Whereas ``instance.show`` and ``instance.hide`` is bounded and
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
102 can be used to emit signals::
103
1e15924 @baverman again massive signal system refactoring
authored
104 class Plugin(object):
105 def __init__(self):
106 self.signals = Manager()
107 self.signals.connect_signals()
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
108
109 self.signals.hide.emit()
110
1e15924 @baverman again massive signal system refactoring
authored
111 @Manager.show
112 def show(self, sender):
113 pass
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
114
1e15924 @baverman again massive signal system refactoring
authored
115 Inner GObject with necessary __gsignals__ is constructed during instance initialization
116 """
c2935b7 @baverman removed gsignals dependency
authored
117 registered_classes = {}
118
119 def __new__(cls, *args, **kwargs):
120 try:
121 newcls = SignalManager.registered_classes[cls]
122 obj = newcls.__new__(newcls, *args, **kwargs)
123 gobject.GObject.__init__(obj)
124 newcls.__init__(obj, *args, **kwargs)
125
126 return obj
127 except KeyError:
128 pass
129
1e15924 @baverman again massive signal system refactoring
authored
130 def make_signal_prop(signal):
131 def inner(self):
132 return BoundedSignal(self, signal)
133
134 return property(inner)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
135
1e15924 @baverman again massive signal system refactoring
authored
136 newdict = dict(cls.__dict__)
c2935b7 @baverman removed gsignals dependency
authored
137 signals = {}
138 for sname, signal in cls.__dict__.iteritems():
139 if isinstance(signal, Signal):
140 signal.name = sname.replace('_', '-')
141 signals[signal.name] = (signal.signal_type,
142 signal.return_type, signal.arg_types)
1e15924 @baverman again massive signal system refactoring
authored
143
144 newdict[sname] = make_signal_prop(signal)
145
c2935b7 @baverman removed gsignals dependency
authored
146 if not signals:
147 return super(SignalManager, cls).__new__(cls, *args, **kwargs)
148
149 newdict['__gsignals__'] = signals
150 newdict['weak_connect'] = SignalManager.weak_connect
1e15924 @baverman again massive signal system refactoring
authored
151 newdict['connect_signals'] = SignalManager.connect_signals
c2935b7 @baverman removed gsignals dependency
authored
152
153 for k, v in newdict.iteritems():
154 if hasattr(v, 'im_func'):
155 newdict[k] = v.im_func
156
157 newcls = type(cls.__name__, (gobject.GObject,), newdict)
158 gobject.type_register(newcls)
159 SignalManager.registered_classes[cls] = newcls
1e15924 @baverman again massive signal system refactoring
authored
160
c2935b7 @baverman removed gsignals dependency
authored
161 obj = newcls.__new__(newcls, *args, **kwargs)
162 gobject.GObject.__init__(obj)
163 newcls.__init__(obj, *args, **kwargs)
164
165 return obj
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
166
1e15924 @baverman again massive signal system refactoring
authored
167 def connect_signals(self, obj):
168 """
169 Connects marked object methods
170 """
40b553b @baverman inspect only class members not object during signal wiring
authored
171 for attr, value in getmembers(obj.__class__, ismethod):
1e15924 @baverman again massive signal system refactoring
authored
172 for signal, connect_params in getattr(value, 'signals_to_connect', ()):
da35d8b @baverman connect signals to base class handlers
authored
173 id = self.weak_connect(signal, obj, attr, **connect_params)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
174 append_handler_to_object(obj, attr, id, self, signal.name)
c2935b7 @baverman removed gsignals dependency
authored
175
1e15924 @baverman again massive signal system refactoring
authored
176 def weak_connect(self, signal, obj, attr, after, idle):
177 """
178 Connects unbounded signal
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
179
1e15924 @baverman again massive signal system refactoring
authored
180 @param signal: Unbounded signal
181 """
182 return weak_connect(self, signal.name, obj, attr, after=after, idle=idle)
c2935b7 @baverman removed gsignals dependency
authored
183
184
185 class BoundedSignal(object):
1e15924 @baverman again massive signal system refactoring
authored
186 """
187 This class knows about its GObject wrapper and unbounded signal name
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
188
1e15924 @baverman again massive signal system refactoring
authored
189 This allows it to emit signals. Bounded signal weakly connected to its manager so
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
190 you can safely use it in any context
1e15924 @baverman again massive signal system refactoring
authored
191 """
c2935b7 @baverman removed gsignals dependency
authored
192 def __init__(self, manager, signal):
193 self.manager = weakref.ref(manager)
194 self.signal = signal
195
1e15924 @baverman again massive signal system refactoring
authored
196 def connect(self, obj, attr, after=False, idle=False):
197 manager = self.manager()
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
198 if manager:
1e15924 @baverman again massive signal system refactoring
authored
199 manager.weak_connect(self.signal, obj, attr, after=after, idle=idle)
200
c2935b7 @baverman removed gsignals dependency
authored
201 def emit(self, *args):
202 manager = self.manager()
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
203 if manager:
1e15924 @baverman again massive signal system refactoring
authored
204 return manager.emit(self.signal.name, *args)
c2935b7 @baverman removed gsignals dependency
authored
205
b62e4ab @baverman Bad python code save preventer plugin
authored
206 def stop_emission(self):
207 manager = self.manager()
208 if manager:
209 return manager.stop_emission(self.signal.name)
c2935b7 @baverman removed gsignals dependency
authored
210
1e15924 @baverman again massive signal system refactoring
authored
211 def connect_external(sender_name, signal_name, after=False, idle=False):
212 def inner(func):
213 return attach_signal_connect_info('external_signals_to_connect',
214 (sender_name, signal_name), func, after, idle)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
215
1e15924 @baverman again massive signal system refactoring
authored
216 return inner
217
218 def connect_external_signals(obj, **kwargs):
40b553b @baverman inspect only class members not object during signal wiring
authored
219 for attr, value in getmembers(obj.__class__, ismethod):
1e15924 @baverman again massive signal system refactoring
authored
220 for (sender_name, signal_name), connect_params in getattr(value, 'external_signals_to_connect', ()):
221 sender = kwargs[sender_name]
222 id = weak_connect(sender, signal_name, obj, attr, **connect_params)
223 append_handler_to_object(obj, attr, id, sender, signal_name, sender_name)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
224
1e15924 @baverman again massive signal system refactoring
authored
225 def append_handler_to_object(obj, attr, handler_id, sender, signal_name, sender_name=None):
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
226 name = attr + '_handler'
1e15924 @baverman again massive signal system refactoring
authored
227 if not hasattr(obj, name):
228 setattr(obj, name, HandlerHolder())
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
229
1e15924 @baverman again massive signal system refactoring
authored
230 getattr(obj, name).add(handler_id, sender, signal_name, sender_name)
231
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
232 def connect_all(obj, *signal_managers, **external_senders):
1e15924 @baverman again massive signal system refactoring
authored
233 [s.connect_signals(obj) for s in signal_managers]
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
234
1e15924 @baverman again massive signal system refactoring
authored
235 if external_senders:
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
236 connect_external_signals(obj, **external_senders)
1e15924 @baverman again massive signal system refactoring
authored
237
c2935b7 @baverman removed gsignals dependency
authored
238 class Handler(object):
1e15924 @baverman again massive signal system refactoring
authored
239 def __init__(self, handler_id, sender, signal_name, sender_name):
c2935b7 @baverman removed gsignals dependency
authored
240 self.id = handler_id
241 self.sender = weakref.ref(sender)
1e15924 @baverman again massive signal system refactoring
authored
242 self.signal_name = signal_name
243 self.sender_name = sender_name
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
244
1e15924 @baverman again massive signal system refactoring
authored
245 def is_match(self, sender, sender_name, signal_name):
246 result = True
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
247
1e15924 @baverman again massive signal system refactoring
authored
248 result = result and (sender == None or self.sender() is sender)
249 result = result and (sender_name == None or self.sender_name == sender_name)
250 result = result and (signal_name == None or self.signal_name == signal_name)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
251
1e15924 @baverman again massive signal system refactoring
authored
252 return result
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
253
c2935b7 @baverman removed gsignals dependency
authored
254 def block(self):
255 sender = self.sender()
256 if sender:
257 sender.handler_block(self.id)
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
258
c2935b7 @baverman removed gsignals dependency
authored
259 def unblock(self):
260 sender = self.sender()
261 if sender:
262 sender.handler_unblock(self.id)
1e15924 @baverman again massive signal system refactoring
authored
263
264
265 class HandlerHolder(object):
266
267 def __init__(self):
268 self.handlers = []
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
269
270 def add(self, id, sender, signal_name, sender_name=None):
1e15924 @baverman again massive signal system refactoring
authored
271 self.handlers.append(Handler(id, sender, signal_name, sender_name))
272
273 def block(self):
274 try:
275 (handler,) = self.handlers
276 handler.block()
277 except ValueError:
278 raise Exception('There are several signals connected to callback')
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
279
1e15924 @baverman again massive signal system refactoring
authored
280 def unblock(self):
281 try:
282 (handler,) = self.handlers
283 handler.unblock()
284 except ValueError:
285 raise Exception('There are several signals connected to callback')
286
287 @property
288 def id(self):
289 try:
290 (handler,) = self.handlers
291 return handler.id
292 except ValueError:
293 raise Exception('There are several signals connected to callback')
294
295 def __call__(self, sender=None, sender_name=None, signal_name=None):
296 handler = None
297 for h in self.handlers:
298 if h.is_match(sender=sender, sender_name=sender_name, signal_name=signal_name):
299 if handler:
300 raise Exception('Match returns several handlers')
301 else:
302 handler = h
dd77d16 @baverman do not remember larva projects in quick open dialog
authored
303
1e15924 @baverman again massive signal system refactoring
authored
304 return handler
Something went wrong with that request. Please try again.