/usr/share/pyshared/tvtk/messenger.py is in mayavi2 4.1.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | """
Implements a simple, robust, safe, Messenger class that allows one to
register callbacks for a signal/slot (or event/handler) kind of
messaging system. One can basically register a callback
function/method to be called when an object sends a particular event.
The Messenger class is Borg. So it is easy to instantiate and use.
This module is also reload-safe, so if the module is reloaded the
callback information is not lost. Method callbacks do not have a
reference counting problem since weak references are used.
The main functionality of this module is provided by three functions,
`connect`, `disconnect` and `send`.
Here is example usage with VTK::
>>> import messenger, vtk
>>> def cb(obj, evt):
... print obj.__class__.__name__, evt
...
>>> o = vtk.vtkProperty()
>>> o.AddObserver('ModifiedEvent', messenger.send)
1
>>> messenger.connect(o, 'ModifiedEvent', cb)
>>>
>>> o.SetRepresentation(1)
vtkOpenGLProperty ModifiedEvent
>>> messenger.connect(o, 'AnyEvent', cb)
>>> o.SetRepresentation(2)
vtkOpenGLProperty ModifiedEvent
vtkOpenGLProperty ModifiedEvent
>>>
>>> messenger.send(o, 'foo')
vtkOpenGLProperty foo
>>> messenger.disconnect(o, 'AnyEvent')
>>> messenger.send(o, 'foo')
>>>
This approach is necessary if you don't want to be bitten by reference
cycles. If you have a Python object holding a reference to a VTK
object and pass a method of the object to the AddObserver call, you
will get a reference cycle that cannot be collected by the garbage
collector. Using this messenger module gets around the problem.
Also note that adding a connection for 'AnyEvent' will trigger a
callback no matter what event was generated. The code above also
shows how disconnection works.
"""
# Author: Prabhu Ramachandran
# Copyright (c) 2004-2007, Enthought, Inc.
# License: BSD Style.
__all__ = ['Messenger', 'MessengerError',
'connect', 'disconnect', 'send']
import types
import sys
import weakref
#################################################################
# This code makes the module reload-safe.
#################################################################
_saved = {}
for name in ['messenger', 'tvtk.messenger']:
if sys.modules.has_key(name):
mod = sys.modules[name]
if hasattr(mod, 'Messenger'):
_saved = mod.Messenger._shared_data
del mod
break
#################################################################
# `MessengerError` class for exceptions raised by Messenger.
#################################################################
class MessengerError(Exception):
pass
#################################################################
# `Messenger` class.
#################################################################
class Messenger:
"""Implements a messenger class which deals with something like
signals and slots. Basically, an object can register a signal
that it plans to emit. Any other object can decide to handle that
signal (of that particular object) by registering itself with the
messenger. When a signal is emitted the messenger calls all
handlers. This makes it totally easy to deal with communication
between objects. The class is Borg. Rather than use this class,
please use the 'connect' and 'disconnect' functions.
"""
_shared_data = _saved
def __init__(self):
"""Create the messenger. This class is Borg. So all
instances are the same.
"""
self.__dict__ = self._shared_data
if not hasattr(self, '_signals'):
# First instantiation.
self._signals = {}
self._catch_all = ['AnyEvent', 'all']
#################################################################
# 'Messenger' interface.
#################################################################
def connect(self, obj, event, callback):
""" Registers a slot given an object and its signal to slot
into and also given a bound method in `callback` that should
have two arguments. `send` will call the callback
with the object that emitted the signal and the actual
event/signal as arguments.
Parameters
----------
- obj : Python object
Any Python object that will generate the particular event.
- event : An event (can be anything, usually strings)
The event `obj` will generate. If this is in the list
`self._catch_all`, then any event will call this callback.
- callback : `function` or `method`
This callback will be called when the object generates the
particular event. The object, event and any other arguments
and keyword arguments given by the `obj` are passed along to
the callback.
"""
typ = type(callback)
key = hash(obj)
if not self._signals.has_key(key):
self._signals[key] = {}
signals = self._signals[key]
if not signals.has_key(event):
signals[event] = {}
slots = signals[event]
callback_key = hash(callback)
if typ is types.FunctionType:
slots[callback_key] = (None, callback)
elif typ is types.MethodType:
obj = weakref.ref(callback.im_self)
name = callback.__name__
slots[callback_key] = (obj, name)
else:
raise MessengerError, \
"Callback must be a function or method. "\
"You passed a %s."%(str(callback))
def disconnect(self, obj, event=None, callback=None, obj_is_hash=False):
"""Disconnects the object and its event handlers.
Parameters
----------
- obj : Object
The object that generates events.
- event : The event. (defaults to None)
- callback : `function` or `method`
The event handler.
If `event` and `callback` are None (the default) all the
events and handlers for the object are removed. If only
`callback` is None, only this handler is removed. If `obj`
and 'event' alone are specified, all handlers for the event
are removed.
- obj_is_hash : `bool`
Specifies if the object passed is a hash instead of the object itself.
This is needed if the object is gc'd but only the hash exists and one
wants to disconnect the object.
"""
signals = self._signals
if obj_is_hash:
key = obj
else:
key = hash(obj)
if not signals.has_key(key):
return
if callback is None:
if event is None:
del signals[key]
else:
del signals[key][event]
else:
del signals[key][event][hash(callback)]
def send(self, source, event, *args, **kw_args):
"""To be called by the object `source` that desires to
generate a particular event. This function in turn invokes
all the handlers for the event passing the `source` object,
event and any additional arguments and keyword arguments. If
any connected callback is garbage collected without being
disconnected, it is silently removed from the existing slots.
Parameters
----------
- source : Python object
This is the object that generated the event.
- event : The event.
If there are handlers connected to events called 'AnyEvent'
or 'all', then any event will invoke these.
"""
try:
sigs = self._get_signals(source)
except (MessengerError, KeyError):
return
events = self._catch_all[:]
if event not in events:
events.append(event)
for evt in events:
if sigs.has_key(evt):
slots = sigs[evt]
for key in slots.keys():
obj, meth = slots[key]
if obj: # instance method
inst = obj()
if inst:
getattr(inst, meth)(source, event, *args, **kw_args)
else:
# Oops, dead reference.
del slots[key]
else: # normal function
meth(source, event, *args, **kw_args)
def is_registered(self, obj):
"""Returns if the given object has registered itself with the
messenger.
"""
try:
sigs = self._get_signals(obj)
except MessengerError:
return 0
else:
return 1
def get_signal_names(self, obj):
"""Returns a list of signal names the object passed has
registered.
"""
return self._get_signals(obj).keys()
#################################################################
# Non-public interface.
#################################################################
def _get_signals(self, obj):
"""Given an object `obj` it returns the signals of that
object.
"""
ret = self._signals.get(hash(obj))
if ret is None:
raise MessengerError, \
"No such object: %s, has registered itself "\
"with the messenger."%obj
else:
return ret
#################################################################
# Convenience functions.
#################################################################
_messenger = Messenger()
def connect(obj, event, callback):
_messenger.connect(obj, event, callback)
connect.__doc__ = _messenger.connect.__doc__
def disconnect(obj, event=None, callback=None, obj_is_hash=False):
_messenger.disconnect(obj, event, callback)
disconnect.__doc__ = _messenger.disconnect.__doc__
def send(obj, event, *args, **kw_args):
_messenger.send(obj, event, *args, **kw_args)
send.__doc__ = _messenger.send.__doc__
del _saved
|