]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/py/dispatcher.py
   1 """Provides global signal dispatching services.""" 
   3 __author__ 
= "Patrick K. O'Brien <pobrien@orbtech.com>" 
   5 __revision__ 
= "$Revision$"[11:-2] 
  18 class DispatcherError(exceptions
.Exception): 
  19     def __init__(self
, args
=None): 
  24     """Used to represent default parameter values.""" 
  26         return self
.__class
__.__name
__ 
  28 class Any(Parameter
): pass 
  31 class Anonymous(Parameter
): pass 
  32 Anonymous 
= Anonymous() 
  37 _boundMethods 
= weakref
.WeakKeyDictionary() 
  40 def connect(receiver
, signal
=Any
, sender
=Any
, weak
=True): 
  41     """Connect receiver to sender for signal. 
  43     If sender is Any, receiver will receive signal from any sender. 
  44     If signal is Any, receiver will receive any signal from sender. 
  45     If sender is None, receiver will receive signal from Anonymous. 
  46     If signal is Any and sender is None, receiver will receive any  
  47         signal from Anonymous. 
  48     If signal is Any and sender is Any, receiver will receive any  
  49         signal from any sender. 
  50     If weak is true, weak references will be used.""" 
  52         raise DispatcherError
, 'signal cannot be None' 
  54         receiver 
= safeRef(receiver
) 
  55     senderkey 
= id(sender
) 
  57     if connections
.has_key(senderkey
): 
  58         signals 
= connections
[senderkey
] 
  60         connections
[senderkey
] = signals
 
  61         # Keep track of senders for cleanup. 
  62         if sender 
not in (None, Any
): 
  63             def remove(object, senderkey
=senderkey
): 
  64                 _removeSender(senderkey
=senderkey
) 
  65             # Skip objects that can not be weakly referenced, which means 
  66             # they won't be automatically cleaned up, but that's too bad. 
  68                 weakSender 
= weakref
.ref(sender
, remove
) 
  69                 senders
[senderkey
] = weakSender
 
  73     if signals
.has_key(signal
): 
  74         receivers 
= signals
[signal
] 
  76         signals
[signal
] = receivers
 
  78         receivers
.remove(receiver
) 
  81     receivers
.append(receiver
) 
  83 def disconnect(receiver
, signal
=Any
, sender
=Any
, weak
=True): 
  84     """Disconnect receiver from sender for signal. 
  86     Disconnecting is not required. The use of disconnect is the same as for 
  87     connect, only in reverse. Think of it as undoing a previous connection.""" 
  89         raise DispatcherError
, 'signal cannot be None' 
  91         receiver 
= safeRef(receiver
) 
  92     senderkey 
= id(sender
) 
  94         receivers 
= connections
[senderkey
][signal
] 
  96         raise DispatcherError
, \
 
  97               'No receivers for signal %r from sender %s' % (signal
, sender
) 
  99         receivers
.remove(receiver
) 
 101         raise DispatcherError
, \
 
 102               'No connection to receiver %s for signal %r from sender %s' % \
 
 103               (receiver
, signal
, sender
) 
 104     _cleanupConnections(senderkey
, signal
) 
 106 def send(signal
, sender
=Anonymous
, **kwds
): 
 107     """Send signal from sender to all connected receivers. 
 109     Return a list of tuple pairs [(receiver, response), ... ]. 
 110     If sender is not specified, signal is sent anonymously.""" 
 111     senderkey 
= id(sender
) 
 113     # Get receivers that receive *this* signal from *this* sender. 
 116         receivers
.extend(connections
[senderkey
][signal
]) 
 119     # Add receivers that receive *any* signal from *this* sender. 
 122         anyreceivers 
= connections
[senderkey
][Any
] 
 125     for receiver 
in anyreceivers
: 
 126         if receivers
.count(receiver
) == 0: 
 127             receivers
.append(receiver
) 
 128     # Add receivers that receive *this* signal from *any* sender. 
 131         anyreceivers 
= connections
[anykey
][signal
] 
 134     for receiver 
in anyreceivers
: 
 135         if receivers
.count(receiver
) == 0: 
 136             receivers
.append(receiver
) 
 137     # Add receivers that receive *any* signal from *any* sender. 
 140         anyreceivers 
= connections
[anykey
][Any
] 
 143     for receiver 
in anyreceivers
: 
 144         if receivers
.count(receiver
) == 0: 
 145             receivers
.append(receiver
) 
 146     # Call each receiver with whatever arguments it can accept. 
 147     # Return a list of tuple pairs [(receiver, response), ... ]. 
 149     for receiver 
in receivers
: 
 150         if type(receiver
) is weakref
.ReferenceType \
 
 151         or isinstance(receiver
, BoundMethodWeakref
): 
 152             # Dereference the weak reference. 
 153             receiver 
= receiver() 
 155                 # This receiver is dead, so skip it. 
 157         response 
= _call(receiver
, signal
=signal
, sender
=sender
, **kwds
) 
 158         responses 
+= [(receiver
, response
)] 
 161 def _call(receiver
, **kwds
): 
 162     """Call receiver with only arguments it can accept.""" 
 163 ##    if type(receiver) is types.InstanceType: 
 164     if hasattr(receiver
, '__call__') and \
 
 165        (hasattr(receiver
.__call
__, 'im_func') or hasattr(receiver
.__call
__, 'im_code')): 
 166         # receiver is a class instance; assume it is callable. 
 167         # Reassign receiver to the actual method that will be called. 
 168         receiver 
= receiver
.__call
__ 
 169     if hasattr(receiver
, 'im_func'): 
 170         # receiver is a method. Drop the first argument, usually 'self'. 
 171         fc 
= receiver
.im_func
.func_code
 
 172         acceptable 
= fc
.co_varnames
[1:fc
.co_argcount
] 
 173     elif hasattr(receiver
, 'func_code'): 
 174         # receiver is a function. 
 175         fc 
= receiver
.func_code
 
 176         acceptable 
= fc
.co_varnames
[0:fc
.co_argcount
] 
 178         raise DispatcherError
, 'Unknown receiver %s of type %s' % (receiver
, type(receiver
)) 
 179     if not (fc
.co_flags 
& 8): 
 180         # fc does not have a **kwds type parameter, therefore  
 181         # remove unacceptable arguments. 
 182         for arg 
in kwds
.keys(): 
 183             if arg 
not in acceptable
: 
 185     return receiver(**kwds
) 
 189     """Return a *safe* weak reference to a callable object.""" 
 190     if hasattr(object, 'im_self'): 
 191         if object.im_self 
is not None: 
 192             # Turn a bound method into a BoundMethodWeakref instance. 
 193             # Keep track of these instances for lookup by disconnect(). 
 194             selfkey 
= object.im_self
 
 195             funckey 
= object.im_func
 
 196             if not _boundMethods
.has_key(selfkey
): 
 197                 _boundMethods
[selfkey
] = weakref
.WeakKeyDictionary() 
 198             if not _boundMethods
[selfkey
].has_key(funckey
): 
 199                 _boundMethods
[selfkey
][funckey
] = \
 
 200                 BoundMethodWeakref(boundMethod
=object) 
 201             return _boundMethods
[selfkey
][funckey
] 
 202     return weakref
.ref(object, _removeReceiver
) 
 205 class BoundMethodWeakref
: 
 206     """BoundMethodWeakref class.""" 
 208     def __init__(self
, boundMethod
): 
 209         """Return a weak-reference-like instance for a bound method.""" 
 211         def remove(object, self
=self
): 
 212             """Set self.isDead to true when method or instance is destroyed.""" 
 214             _removeReceiver(receiver
=self
) 
 215         self
.weakSelf 
= weakref
.ref(boundMethod
.im_self
, remove
) 
 216         self
.weakFunc 
= weakref
.ref(boundMethod
.im_func
, remove
) 
 219         """Return the closest representation.""" 
 220         return '<bound method weakref for %s.%s>' % (self
.weakSelf
, self
.weakFunc
) 
 223         """Return a strong reference to the bound method.""" 
 227             object = self
.weakSelf() 
 228             method 
= self
.weakFunc().__name
__ 
 229             try:  # wxPython hack to handle wxDead objects. 
 230                 return getattr(object, method
) 
 231             except AttributeError: 
 232 ##                 _removeReceiver(receiver=self) 
 236 def _removeReceiver(receiver
): 
 237     """Remove receiver from connections.""" 
 238     for senderkey 
in connections
.keys(): 
 239         for signal 
in connections
[senderkey
].keys(): 
 240             receivers 
= connections
[senderkey
][signal
] 
 242                 receivers
.remove(receiver
) 
 245             _cleanupConnections(senderkey
, signal
) 
 247 def _cleanupConnections(senderkey
, signal
): 
 248     """Delete any empty signals for senderkey. Delete senderkey if empty.""" 
 249     receivers 
= connections
[senderkey
][signal
] 
 251         # No more connected receivers. Therefore, remove the signal. 
 252         signals 
= connections
[senderkey
] 
 255             # No more signal connections. Therefore, remove the sender. 
 256             _removeSender(senderkey
) 
 258 def _removeSender(senderkey
): 
 259     """Remove senderkey from connections.""" 
 260     del connections
[senderkey
] 
 261     # Senderkey will only be in senders dictionary if sender  
 262     # could be weakly referenced. 
 264         del senders
[senderkey
]