]>
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] 
  12 class DispatcherError(exceptions
.Exception): 
  13     def __init__(self
, args
=None): 
  18     """Used to represent default parameter values.""" 
  20         return self
.__class
__.__name
__ 
  22 class Any(Parameter
): pass 
  25 class Anonymous(Parameter
): pass 
  26 Anonymous 
= Anonymous() 
  31 _boundMethods 
= weakref
.WeakKeyDictionary() 
  34 def connect(receiver
, signal
=Any
, sender
=Any
, weak
=True): 
  35     """Connect receiver to sender for signal. 
  37     If sender is Any, receiver will receive signal from any sender. 
  38     If signal is Any, receiver will receive any signal from sender. 
  39     If sender is None, receiver will receive signal from Anonymous. 
  40     If signal is Any and sender is None, receiver will receive any  
  41         signal from Anonymous. 
  42     If signal is Any and sender is Any, receiver will receive any  
  43         signal from any sender. 
  44     If weak is true, weak references will be used.""" 
  46         raise DispatcherError
, 'signal cannot be None' 
  48         receiver 
= safeRef(receiver
) 
  49     senderkey 
= id(sender
) 
  51     if connections
.has_key(senderkey
): 
  52         signals 
= connections
[senderkey
] 
  54         connections
[senderkey
] = signals
 
  55         # Keep track of senders for cleanup. 
  56         if sender 
not in (None, Any
): 
  57             def remove(object, senderkey
=senderkey
): 
  58                 _removeSender(senderkey
=senderkey
) 
  59             # Skip objects that can not be weakly referenced, which means 
  60             # they won't be automatically cleaned up, but that's too bad. 
  62                 weakSender 
= weakref
.ref(sender
, remove
) 
  63                 senders
[senderkey
] = weakSender
 
  67     if signals
.has_key(signal
): 
  68         receivers 
= signals
[signal
] 
  70         signals
[signal
] = receivers
 
  72         receivers
.remove(receiver
) 
  75     receivers
.append(receiver
) 
  77 def disconnect(receiver
, signal
=Any
, sender
=Any
, weak
=True): 
  78     """Disconnect receiver from sender for signal. 
  80     Disconnecting is not required. The use of disconnect is the same as for 
  81     connect, only in reverse. Think of it as undoing a previous connection.""" 
  83         raise DispatcherError
, 'signal cannot be None' 
  85         receiver 
= safeRef(receiver
) 
  86     senderkey 
= id(sender
) 
  88         receivers 
= connections
[senderkey
][signal
] 
  90         raise DispatcherError
, \
 
  91               'No receivers for signal %r from sender %s' % (signal
, sender
) 
  93         receivers
.remove(receiver
) 
  95         raise DispatcherError
, \
 
  96               'No connection to receiver %s for signal %r from sender %s' % \
 
  97               (receiver
, signal
, sender
) 
  98     _cleanupConnections(senderkey
, signal
) 
 100 def send(signal
, sender
=Anonymous
, **kwds
): 
 101     """Send signal from sender to all connected receivers. 
 103     Return a list of tuple pairs [(receiver, response), ... ]. 
 104     If sender is not specified, signal is sent anonymously.""" 
 105     senderkey 
= id(sender
) 
 107     # Get receivers that receive *this* signal from *this* sender. 
 110         receivers
.extend(connections
[senderkey
][signal
]) 
 113     # Add receivers that receive *any* signal from *this* sender. 
 116         anyreceivers 
= connections
[senderkey
][Any
] 
 119     for receiver 
in anyreceivers
: 
 120         if receivers
.count(receiver
) == 0: 
 121             receivers
.append(receiver
) 
 122     # Add receivers that receive *this* signal from *any* sender. 
 125         anyreceivers 
= connections
[anykey
][signal
] 
 128     for receiver 
in anyreceivers
: 
 129         if receivers
.count(receiver
) == 0: 
 130             receivers
.append(receiver
) 
 131     # Add receivers that receive *any* signal from *any* sender. 
 134         anyreceivers 
= connections
[anykey
][Any
] 
 137     for receiver 
in anyreceivers
: 
 138         if receivers
.count(receiver
) == 0: 
 139             receivers
.append(receiver
) 
 140     # Call each receiver with whatever arguments it can accept. 
 141     # Return a list of tuple pairs [(receiver, response), ... ]. 
 143     for receiver 
in receivers
: 
 144         if type(receiver
) is weakref
.ReferenceType \
 
 145         or isinstance(receiver
, BoundMethodWeakref
): 
 146             # Dereference the weak reference. 
 147             receiver 
= receiver() 
 149                 # This receiver is dead, so skip it. 
 151         response 
= _call(receiver
, signal
=signal
, sender
=sender
, **kwds
) 
 152         responses 
+= [(receiver
, response
)] 
 155 def _call(receiver
, **kwds
): 
 156     """Call receiver with only arguments it can accept.""" 
 157 ##    if type(receiver) is types.InstanceType: 
 158     if hasattr(receiver
, '__call__') and \
 
 159        (hasattr(receiver
.__call
__, 'im_func') or hasattr(receiver
.__call
__, 'im_code')): 
 160         # receiver is a class instance; assume it is callable. 
 161         # Reassign receiver to the actual method that will be called. 
 162         receiver 
= receiver
.__call
__ 
 163     if hasattr(receiver
, 'im_func'): 
 164         # receiver is a method. Drop the first argument, usually 'self'. 
 165         fc 
= receiver
.im_func
.func_code
 
 166         acceptable 
= fc
.co_varnames
[1:fc
.co_argcount
] 
 167     elif hasattr(receiver
, 'func_code'): 
 168         # receiver is a function. 
 169         fc 
= receiver
.func_code
 
 170         acceptable 
= fc
.co_varnames
[0:fc
.co_argcount
] 
 172         raise DispatcherError
, 'Unknown receiver %s of type %s' % (receiver
, type(receiver
)) 
 173     if not (fc
.co_flags 
& 8): 
 174         # fc does not have a **kwds type parameter, therefore  
 175         # remove unacceptable arguments. 
 176         for arg 
in kwds
.keys(): 
 177             if arg 
not in acceptable
: 
 179     return receiver(**kwds
) 
 183     """Return a *safe* weak reference to a callable object.""" 
 184     if hasattr(object, 'im_self'): 
 185         if object.im_self 
is not None: 
 186             # Turn a bound method into a BoundMethodWeakref instance. 
 187             # Keep track of these instances for lookup by disconnect(). 
 188             selfkey 
= object.im_self
 
 189             funckey 
= object.im_func
 
 190             if not _boundMethods
.has_key(selfkey
): 
 191                 _boundMethods
[selfkey
] = weakref
.WeakKeyDictionary() 
 192             if not _boundMethods
[selfkey
].has_key(funckey
): 
 193                 _boundMethods
[selfkey
][funckey
] = \
 
 194                 BoundMethodWeakref(boundMethod
=object) 
 195             return _boundMethods
[selfkey
][funckey
] 
 196     return weakref
.ref(object, _removeReceiver
) 
 199 class BoundMethodWeakref
: 
 200     """BoundMethodWeakref class.""" 
 202     def __init__(self
, boundMethod
): 
 203         """Return a weak-reference-like instance for a bound method.""" 
 205         def remove(object, self
=self
): 
 206             """Set self.isDead to true when method or instance is destroyed.""" 
 208             _removeReceiver(receiver
=self
) 
 209         self
.weakSelf 
= weakref
.ref(boundMethod
.im_self
, remove
) 
 210         self
.weakFunc 
= weakref
.ref(boundMethod
.im_func
, remove
) 
 213         """Return the closest representation.""" 
 214         return '<bound method weakref for %s.%s>' % (self
.weakSelf
, self
.weakFunc
) 
 217         """Return a strong reference to the bound method.""" 
 221             object = self
.weakSelf() 
 222             method 
= self
.weakFunc().__name
__ 
 223             try:  # wxPython hack to handle wxDead objects. 
 224                 return getattr(object, method
) 
 225             except AttributeError: 
 226 ##                 _removeReceiver(receiver=self) 
 230 def _removeReceiver(receiver
): 
 231     """Remove receiver from connections.""" 
 232     for senderkey 
in connections
.keys(): 
 233         for signal 
in connections
[senderkey
].keys(): 
 234             receivers 
= connections
[senderkey
][signal
] 
 236                 receivers
.remove(receiver
) 
 239             _cleanupConnections(senderkey
, signal
) 
 241 def _cleanupConnections(senderkey
, signal
): 
 242     """Delete any empty signals for senderkey. Delete senderkey if empty.""" 
 243     receivers 
= connections
[senderkey
][signal
] 
 245         # No more connected receivers. Therefore, remove the signal. 
 246         signals 
= connections
[senderkey
] 
 249             # No more signal connections. Therefore, remove the sender. 
 250             _removeSender(senderkey
) 
 252 def _removeSender(senderkey
): 
 253     """Remove senderkey from connections.""" 
 254     del connections
[senderkey
] 
 255     # Senderkey will only be in senders dictionary if sender  
 256     # could be weakly referenced. 
 258         del senders
[senderkey
]