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