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