This is a thread-safe dispatcher.
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 | #idle_queue.py
import Queue
#Global queue, import it from anywhere, you get the same object instance.
idle_loop = Queue.Queue()
def idle_add(func, *args, **kwargs):
def idle():
func(*args, **kwargs)
return False
idle_loop.put(idle)
#weak_ref.py
import weakref
class _BoundMethodWeakref:
def __init__(self, func):
self.func_name = func.__name__
self.wref = weakref.ref(func.__self__) #__self__ returns the class
def __call__(self):
func_cls = self.wref()
if func_cls is None: #lost reference
return None
else:
func = getattr(func_cls, self.func_name)
return func
def weak_ref(callback):
if hasattr(callback, '__self__') and callback.__self__ is not None: #is a bound method?
return _BoundMethodWeakref(callback)
else:
return weakref.ref(callback)
#event.py
import threading
#import logging
#logger = logging.getLogger(__name__)
import idle_queue
from weak_ref import weak_ref
class Event:
def __init__(self, name):
self.name = name
self.callbacks = []
self.lock = threading.Lock()
def connect(self, callback):
with self.lock:
callback = weak_ref(callback)
self.callbacks.append(callback)
def disconnect(self, callback):
with self.lock:
for index, weakref_callback in enumerate(self.callbacks):
if callback == weakref_callback():
del self.callbacks[index]
break
def emit(self, *args, **kwargs):
with self.lock:
#logger.debug("Event emitted: {}".format(self.name))
for weakref_callback in self.callbacks[:]:
callback = weakref_callback()
if callback is not None:
idle_queue.idle_add(callback, *args, **kwargs)
else: #lost reference
self.callbacks.remove(weakref_callback)
#if not self.callbacks:
#logger.debug("No signals assosiated to: {}".format(self.name))
#events.py
import Event
class _Events:
#add some signals, example:
#do_something = Event('do something') #args: my_arg1, my_arg_list2, my_arg_str3
quit_app = Event('quit app') #args: some_arg
events = _Events()
if __name__ == "__main__":
def quit(some_arg):
print some_arg
sys.exit(0)
events.quit_app.connect(quit) #connect the callback/slot
#events.quit_app.disconnect(quit) #disconnect
something = "goodbye"
events.quit_app.emit(something) #emit the signal
#this should go in your main thread loop if your are using a gui.
#example: http://code.activestate.com/recipes/578299-pyqt-pyside-thread-safe-global-queue-main-loop-int/
callback = idle_loop.get()
callback() #dispatch the event
|
For anyone interested, I created a repository on github, called python-signals.