]>
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): 
  36     Connect receiver to sender for signal. 
  38     * If sender is Any, receiver will receive signal from any sender. 
  39     * If signal is Any, receiver will receive any signal from sender. 
  40     * If sender is None, receiver will receive signal from Anonymous. 
  41     * If signal is Any and sender is None, receiver will receive any  
  42       signal from Anonymous. 
  43     * If signal is Any and sender is Any, receiver will receive any  
  44       signal from any sender. 
  45     * If weak is true, weak references will be used. 
  48         raise DispatcherError
, 'signal cannot be None' 
  50         receiver 
= safeRef(receiver
) 
  51     senderkey 
= id(sender
) 
  53     if connections
.has_key(senderkey
): 
  54         signals 
= connections
[senderkey
] 
  56         connections
[senderkey
] = signals
 
  57         # Keep track of senders for cleanup. 
  58         if sender 
not in (None, Any
): 
  59             def remove(object, senderkey
=senderkey
): 
  60                 _removeSender(senderkey
=senderkey
) 
  61             # Skip objects that can not be weakly referenced, which means 
  62             # they won't be automatically cleaned up, but that's too bad. 
  64                 weakSender 
= weakref
.ref(sender
, remove
) 
  65                 senders
[senderkey
] = weakSender
 
  69     if signals
.has_key(signal
): 
  70         receivers 
= signals
[signal
] 
  72         signals
[signal
] = receivers
 
  74         receivers
.remove(receiver
) 
  77     receivers
.append(receiver
) 
  79 def disconnect(receiver
, signal
=Any
, sender
=Any
, weak
=True): 
  80     """Disconnect receiver from sender for signal. 
  82     Disconnecting is not required. The use of disconnect is the same as for 
  83     connect, only in reverse. Think of it as undoing a previous connection.""" 
  85         raise DispatcherError
, 'signal cannot be None' 
  87         receiver 
= safeRef(receiver
) 
  88     senderkey 
= id(sender
) 
  90         receivers 
= connections
[senderkey
][signal
] 
  92         raise DispatcherError
, \
 
  93               'No receivers for signal %r from sender %s' % (signal
, sender
) 
  95         receivers
.remove(receiver
) 
  97         raise DispatcherError
, \
 
  98               'No connection to receiver %s for signal %r from sender %s' % \
 
  99               (receiver
, signal
, sender
) 
 100     _cleanupConnections(senderkey
, signal
) 
 102 def send(signal
, sender
=Anonymous
, **kwds
): 
 103     """Send signal from sender to all connected receivers. 
 105     Return a list of tuple pairs [(receiver, response), ... ]. 
 106     If sender is not specified, signal is sent anonymously.""" 
 107     senderkey 
= id(sender
) 
 109     # Get receivers that receive *this* signal from *this* sender. 
 112         receivers
.extend(connections
[senderkey
][signal
]) 
 115     # Add receivers that receive *any* signal from *this* sender. 
 118         anyreceivers 
= connections
[senderkey
][Any
] 
 121     for receiver 
in anyreceivers
: 
 122         if receivers
.count(receiver
) == 0: 
 123             receivers
.append(receiver
) 
 124     # Add receivers that receive *this* signal from *any* sender. 
 127         anyreceivers 
= connections
[anykey
][signal
] 
 130     for receiver 
in anyreceivers
: 
 131         if receivers
.count(receiver
) == 0: 
 132             receivers
.append(receiver
) 
 133     # Add receivers that receive *any* signal from *any* sender. 
 136         anyreceivers 
= connections
[anykey
][Any
] 
 139     for receiver 
in anyreceivers
: 
 140         if receivers
.count(receiver
) == 0: 
 141             receivers
.append(receiver
) 
 142     # Call each receiver with whatever arguments it can accept. 
 143     # Return a list of tuple pairs [(receiver, response), ... ]. 
 145     for receiver 
in receivers
: 
 146         if type(receiver
) is weakref
.ReferenceType \
 
 147         or isinstance(receiver
, BoundMethodWeakref
): 
 148             # Dereference the weak reference. 
 149             receiver 
= receiver() 
 151                 # This receiver is dead, so skip it. 
 153         response 
= _call(receiver
, signal
=signal
, sender
=sender
, **kwds
) 
 154         responses 
+= [(receiver
, response
)] 
 157 def _call(receiver
, **kwds
): 
 158     """Call receiver with only arguments it can accept.""" 
 159 ##    if type(receiver) is types.InstanceType: 
 160     if hasattr(receiver
, '__call__') and \
 
 161        (hasattr(receiver
.__call
__, 'im_func') or hasattr(receiver
.__call
__, 'im_code')): 
 162         # receiver is a class instance; assume it is callable. 
 163         # Reassign receiver to the actual method that will be called. 
 164         receiver 
= receiver
.__call
__ 
 165     if hasattr(receiver
, 'im_func'): 
 166         # receiver is a method. Drop the first argument, usually 'self'. 
 167         fc 
= receiver
.im_func
.func_code
 
 168         acceptable 
= fc
.co_varnames
[1:fc
.co_argcount
] 
 169     elif hasattr(receiver
, 'func_code'): 
 170         # receiver is a function. 
 171         fc 
= receiver
.func_code
 
 172         acceptable 
= fc
.co_varnames
[0:fc
.co_argcount
] 
 174         raise DispatcherError
, 'Unknown receiver %s of type %s' % (receiver
, type(receiver
)) 
 175     if not (fc
.co_flags 
& 8): 
 176         # fc does not have a **kwds type parameter, therefore  
 177         # remove unacceptable arguments. 
 178         for arg 
in kwds
.keys(): 
 179             if arg 
not in acceptable
: 
 181     return receiver(**kwds
) 
 185     """Return a *safe* weak reference to a callable object.""" 
 186     if hasattr(object, 'im_self'): 
 187         if object.im_self 
is not None: 
 188             # Turn a bound method into a BoundMethodWeakref instance. 
 189             # Keep track of these instances for lookup by disconnect(). 
 190             selfkey 
= object.im_self
 
 191             funckey 
= object.im_func
 
 192             if not _boundMethods
.has_key(selfkey
): 
 193                 _boundMethods
[selfkey
] = weakref
.WeakKeyDictionary() 
 194             if not _boundMethods
[selfkey
].has_key(funckey
): 
 195                 _boundMethods
[selfkey
][funckey
] = \
 
 196                 BoundMethodWeakref(boundMethod
=object) 
 197             return _boundMethods
[selfkey
][funckey
] 
 198     return weakref
.ref(object, _removeReceiver
) 
 201 class BoundMethodWeakref
: 
 202     """BoundMethodWeakref class.""" 
 204     def __init__(self
, boundMethod
): 
 205         """Return a weak-reference-like instance for a bound method.""" 
 207         def remove(object, self
=self
): 
 208             """Set self.isDead to true when method or instance is destroyed.""" 
 210             _removeReceiver(receiver
=self
) 
 211         self
.weakSelf 
= weakref
.ref(boundMethod
.im_self
, remove
) 
 212         self
.weakFunc 
= weakref
.ref(boundMethod
.im_func
, remove
) 
 215         """Return the closest representation.""" 
 216         return '<bound method weakref for %s.%s>' % (self
.weakSelf
, self
.weakFunc
) 
 219         """Return a strong reference to the bound method.""" 
 223             object = self
.weakSelf() 
 224             method 
= self
.weakFunc().__name
__ 
 225             try:  # wxPython hack to handle wxDead objects. 
 226                 return getattr(object, method
) 
 227             except AttributeError: 
 228 ##                 _removeReceiver(receiver=self) 
 232 def _removeReceiver(receiver
): 
 233     """Remove receiver from connections.""" 
 234     for senderkey 
in connections
.keys(): 
 235         for signal 
in connections
[senderkey
].keys(): 
 236             receivers 
= connections
[senderkey
][signal
] 
 238                 receivers
.remove(receiver
) 
 241             _cleanupConnections(senderkey
, signal
) 
 243 def _cleanupConnections(senderkey
, signal
): 
 244     """Delete any empty signals for senderkey. Delete senderkey if empty.""" 
 245     receivers 
= connections
[senderkey
][signal
] 
 247         # No more connected receivers. Therefore, remove the signal. 
 248         signals 
= connections
[senderkey
] 
 251             # No more signal connections. Therefore, remove the sender. 
 252             _removeSender(senderkey
) 
 254 def _removeSender(senderkey
): 
 255     """Remove senderkey from connections.""" 
 256     del connections
[senderkey
] 
 257     # Senderkey will only be in senders dictionary if sender  
 258     # could be weakly referenced. 
 260         del senders
[senderkey
]