]>
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
]