Changeset 1157

Show
Ignore:
Timestamp:
03/12/07 12:54:58 (2 years ago)
Author:
arjanmol
Message:

Added selective dispatching, updated docs

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphas/trunk/gaphas/state.py

    r1153 r1157  
    33changes. 
    44 
    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. 
     5Invokations of method and state changing properties are emited to all 
     6functions (or bound methods) registered in the `observers' set. 
     7Use observers.add() and observers.remove() to add/remove handlers. 
     8 
     9This module also contains a second layer: a state inverser. Instead of 
     10emiting the invoked method, it emits a signal (callable, **kwargs) that 
     11can be applied to revert the state of the object to the point before the 
     12method invokation. 
     13 
     14For 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 
    5319""" 
    5420 
     
    5925OBSERVED_DOCSTRING = \ 
    6026        '\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!) 
     31DISPATCH_BY_DEFAULT = True 
    6132 
    6233# Add/remove methods from this subscribers list. 
     
    6536# Since most events originate from methods, it's save to call 
    6637# saveapply(func, keywords) for those functions 
    67  
    6838subscribers = set() 
    6939 
     
    7141# Subscribe to low-level change events: 
    7242observers = set() 
    73  
    74 #  
    75 class funcset(set): 
    76     """ 
    77     A set containing only functions. 
    78     >>> fs = funcset() 
    79     >>> def a(): pass 
    80     >>> fs.add(a) 
    81     >>> fs # doctest: +ELLIPSIS 
    82     funcset([<function a at 0x...>]) 
    83     >>> class A(object): 
    84     ...    def m(self): pass 
    85     >>> fs.add(A.m) 
    86     >>> fs # doctest: +ELLIPSIS 
    87     funcset([<function a at 0x...>, <function m at 0x...>]) 
    88     """ 
    89     def add(self, item): 
    90         if isinstance(item, types.UnboundMethodType): item = item.im_func 
    91         set.add(self, item) 
    9243 
    9344def observed(func): 
     
    10051    """ 
    10152    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 
    10459        return func(*args, **kwargs) 
    10560    dec = decorator(wrapper, func) 
     
    10762    dec.__doc__ = (dec.__doc__ or '') + OBSERVED_DOCSTRING 
    10863    func.__observer__ = dec 
     64    if DISPATCH_BY_DEFAULT: 
     65        dec.__dispatched__ = True 
    10966    return dec 
     67 
     68 
     69def 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 
     101def disable_dispatching(func, disable=True): 
     102    """ 
     103    Inverse operation of enable_dispatching() 
     104    """ 
     105    enable_dispatching(func, not disable) 
    110106 
    111107 
     
    147143    global _reverse 
    148144    # We need the function, since that's what's in the events 
    149     if isinstance(func1, types.UnboundMethodType): func1 = func1.im_func 
    150     if isinstance(func2, types.UnboundMethodType): func2 = func2.im_func 
     145    func1 = getfunction(func1) 
     146    func2 = getfunction(func2) 
    151147    _reverse[func1] = (func2, inspect.getargspec(func2), bind2) 
    152148    _reverse[func2] = (func1, inspect.getargspec(func1), bind1) 
     
    174170 
    175171        argself, argvalue = argnames 
    176         func = isinstance(fset, types.UnboundMethodType) and fset.im_func or fset 
     172        func = getfunction(fset) 
    177173        b = { argvalue: eval("lambda %(self)s: fget(%(self)s)" % {'self': argself }, 
    178174                    {'fget': fget}) } 
     
    280276 
    281277 
     278def 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 
    282287if __name__ == '__main__': 
    283288    import doctest 
  • gaphas/trunk/gaphas/view.py

    r1133 r1157  
    1313import gobject 
    1414import gtk 
     15from cairo import Matrix 
    1516from canvas import Context 
    16 from geometry import Matrix, Rectangle 
     17from geometry import Rectangle 
    1718from tool import DefaultTool 
    1819from painter import DefaultPainter, BoundingBoxPainter 
  • gaphas/trunk/state.txt

    r1153 r1157  
    149149are filtered out later on. 
    150150 
     151What is Observed  
     152---------------- 
     153 
     154As far as Gaphas is concerned, only properties and methods related to the 
     155model (e.g. Canvas, Items) emit state changes. Some extra effort has been taken 
     156to 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