Changeset 1157
- Timestamp:
- 03/12/07 12:54:58 (2 years ago)
- Files:
-
- gaphas/trunk/gaphas/state.py (modified) (9 diffs)
- gaphas/trunk/gaphas/view.py (modified) (1 diff)
- gaphas/trunk/state.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphas/trunk/gaphas/state.py
r1153 r1157 3 3 changes. 4 4 5 The State recorder is intended to report fine grained state changes. As a 6 result, individual property changes and method invokations are being 7 reported. 8 9 The StateRecorder is able to record events for: 10 - Canvas (item addition/removal) 11 - properties 12 - request_update() 13 - add() 14 - remove() 15 - tree (actual addition/removal) 16 - Item 17 - Handle 18 - item connect/disconnect 19 - constraint solver 20 21 Given a class: 22 class A(object): 23 def method(self): 24 pass 25 26 If it is decorated as: 27 class A(object): 28 @observed 29 def method(self): 30 pass 31 the method() is refered to as '<function method at 0xXXXXXXXX>'. 32 33 If it is decorated like this: 34 class A(object): 35 def method(self): 36 pass 37 method = observed(method) 38 it is still the function being decorated. 39 40 If decorated like this: 41 class A(object): 42 def method(self): 43 pass 44 A.method = observed(A.method) 45 the method() is refered to as '<unbound method A.method>' 46 47 Problem: 48 Although I can use the first method, I have to return the *decorator* to the 49 dispatch method, not the function being decorated. Since the @observed 50 decorator is called from within the decorator it's hard to find out where 51 the decorated function lives (is it even possible?). 52 Solution: add a special __observer__ attribute to the inner function. 5 Invokations of method and state changing properties are emited to all 6 functions (or bound methods) registered in the `observers' set. 7 Use observers.add() and observers.remove() to add/remove handlers. 8 9 This module also contains a second layer: a state inverser. Instead of 10 emiting the invoked method, it emits a signal (callable, **kwargs) that 11 can be applied to revert the state of the object to the point before the 12 method invokation. 13 14 For this to work the revert_handler has to be added to the observers set: 15 16 gaphas.state.observers.add(gaphas.state.revert_handler) 17 18 53 19 """ 54 20 … … 59 25 OBSERVED_DOCSTRING = \ 60 26 '\n\nThis method is @observed. See gaphas.state for extra info.\n' 27 28 # Tell @observed to dispatch invokation messages by default 29 # May be changed (but be sure to do that right at the start of your 30 # application,otherwise you have no idea what's enabled and what's not!) 31 DISPATCH_BY_DEFAULT = True 61 32 62 33 # Add/remove methods from this subscribers list. … … 65 36 # Since most events originate from methods, it's save to call 66 37 # saveapply(func, keywords) for those functions 67 68 38 subscribers = set() 69 39 … … 71 41 # Subscribe to low-level change events: 72 42 observers = set() 73 74 #75 class funcset(set):76 """77 A set containing only functions.78 >>> fs = funcset()79 >>> def a(): pass80 >>> fs.add(a)81 >>> fs # doctest: +ELLIPSIS82 funcset([<function a at 0x...>])83 >>> class A(object):84 ... def m(self): pass85 >>> fs.add(A.m)86 >>> fs # doctest: +ELLIPSIS87 funcset([<function a at 0x...>, <function m at 0x...>])88 """89 def add(self, item):90 if isinstance(item, types.UnboundMethodType): item = item.im_func91 set.add(self, item)92 43 93 44 def observed(func): … … 100 51 """ 101 52 def wrapper(func, *args, **kwargs): 102 #if func not in silence: 103 dispatch((func.__observer__, args, kwargs), queue=observers) 53 o = func.__observer__ 54 try: 55 if o.__dispatched__: 56 dispatch((o, args, kwargs), queue=observers) 57 except AttributeError: 58 pass 104 59 return func(*args, **kwargs) 105 60 dec = decorator(wrapper, func) … … 107 62 dec.__doc__ = (dec.__doc__ or '') + OBSERVED_DOCSTRING 108 63 func.__observer__ = dec 64 if DISPATCH_BY_DEFAULT: 65 dec.__dispatched__ = True 109 66 return dec 67 68 69 def enable_dispatching(func, enable=True): 70 """ 71 Enable/disable dispatching for a specific function. 72 >>> @observed 73 ... def callme(): 74 ... pass 75 >>> def handler(event): 76 ... print 'event' 77 >>> observers.add(handler) 78 79 By default dispatching is enabled (set DISPATCH_BY_DEFAULT=False to 80 disable this behavior). 81 >>> callme() 82 event 83 >>> enable_dispatching(callme, False) 84 >>> callme() 85 86 ... and enable it again: 87 >>> enable_dispatching(callme) 88 >>> callme() 89 event 90 >>> observers.remove(handler) 91 """ 92 func = getfunction(func) 93 if enable: 94 func.__dispatched__ = True 95 else: 96 try: 97 del func.__dispatched__ 98 except AttributeError: 99 pass 100 101 def disable_dispatching(func, disable=True): 102 """ 103 Inverse operation of enable_dispatching() 104 """ 105 enable_dispatching(func, not disable) 110 106 111 107 … … 147 143 global _reverse 148 144 # We need the function, since that's what's in the events 149 if isinstance(func1, types.UnboundMethodType): func1 = func1.im_func150 if isinstance(func2, types.UnboundMethodType): func2 = func2.im_func145 func1 = getfunction(func1) 146 func2 = getfunction(func2) 151 147 _reverse[func1] = (func2, inspect.getargspec(func2), bind2) 152 148 _reverse[func2] = (func1, inspect.getargspec(func1), bind1) … … 174 170 175 171 argself, argvalue = argnames 176 func = isinstance(fset, types.UnboundMethodType) and fset.im_func or fset172 func = getfunction(fset) 177 173 b = { argvalue: eval("lambda %(self)s: fget(%(self)s)" % {'self': argself }, 178 174 {'fget': fget}) } … … 280 276 281 277 278 def getfunction(func): 279 """ 280 Return the function associated with a class method. 281 """ 282 if isinstance(func, types.UnboundMethodType): 283 return func.im_func 284 return func 285 286 282 287 if __name__ == '__main__': 283 288 import doctest gaphas/trunk/gaphas/view.py
r1133 r1157 13 13 import gobject 14 14 import gtk 15 from cairo import Matrix 15 16 from canvas import Context 16 from geometry import Matrix,Rectangle17 from geometry import Rectangle 17 18 from tool import DefaultTool 18 19 from painter import DefaultPainter, BoundingBoxPainter gaphas/trunk/state.txt
r1153 r1157 149 149 are filtered out later on. 150 150 151 What is Observed 152 ---------------- 153 154 As far as Gaphas is concerned, only properties and methods related to the 155 model (e.g. Canvas, Items) emit state changes. Some extra effort has been taken 156 to monitor the Matrix class (which is from Cairo). 157 158 canvas.py: 159 Canvas: 160 add() and remove() 161 162 item.py: 163 Handle: 164 x, y, connectable, movable, visible, connected_to and disconnect properties 165 Item: 166 canvas and matric properties 167 Element: 168 min_height and min_width properties 169 Line: 170 line_width, fuzzyness, orthogonal and horizontal properties; 171 split_segment() and merge_segment() 172 173 solver.py: 174 Variable: 175 strength and value properties 176 Solver: 177 add_constraint() and remove_constraint() 178 179 tree.py: 180 Tree: 181 add() and remove() 182
