Welcome, guest | Sign In | My Account | Store | Cart

This code snippet was based on the nice recipe 439356 made by Patrick Chasco. My implementation supports only class methods callbacks. I'm keeping the idea of use weakrefs to avoid the interpreter keep the object allocated because the signal is registered (i.e. the signal object holds a reference to callback method). IMO the usage of WeakValueDictionary made the code smaller and clear and also are maintenance-free (when the object is collect by the garbage collector the signal is automatically unregistered).

Python, 68 lines
 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
"""
File:    signal.py
Author:  Thiago Marcos P. Santos
Created: August 28, 2008

Purpose: A signal/slot implementation
"""

from weakref import WeakValueDictionary


class Signal(object):
    def __init__(self):
        self.__slots = WeakValueDictionary()

    def __call__(self, *args, **kargs):
        for key in self.__slots:
            func, _ = key
            func(self.__slots[key], *args, **kargs)

    def connect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        self.__slots[key] = slot.im_self

    def disconnect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        if key in self.__slots:
            self.__slots.pop(key)

    def clear(self):
        self.__slots.clear()


# Sample usage:
class Model(object):
    def __init__(self, value):
        self.__value = value
        self.changed = Signal()

    def set_value(self, value):
        self.__value = value
        self.changed() # Emit signal

    def get_value(self):
        return self.__value


class View(object):
    def __init__(self, model):
        self.model = model
        model.changed.connect(self.model_changed)

    def model_changed(self):
        print "New value:", self.model.get_value()


model = Model(10)
view1 = View(model)
view2 = View(model)
view3 = View(model)

model.set_value(20)

del view1
model.set_value(30)

model.changed.clear()
model.set_value(40)

Structures in weakref module such as WeakKeyDictionary and WeakValueDictionary are very powerful for object caching. You can use the object id() as key in your WeakValueDictionary. This id is unique for each instance.

2 comments

yang weiqin 13 years, 2 months ago  # | flag

Good.But how to disconnect when calling the signal.

Thiago Marcos P. Santos (author) 12 years, 11 months ago  # | flag

model.changed.disconnect(view1.model_changed)

i.e: You must call the signal's disconnect method using the slot as parameter.