Jump to content
  • Advertisement
Sign in to follow this  
smr

Critique my Python Signal/Slot implementation

This topic is 4833 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I wrote this in Signal/Slot implementation last night for use in a project I'm working on. There's always another way of doing something in python, and I'm pretty clueless on a lot of the advanced python topics (itertools...). So I was wondering if any of the local GDNet gurus could critique it for me and perhaps even point out some places where there's a python construct that could be used instead of my code. If you like what I've done, please feel free to use it yourself. Just give me some props in your source =)
"""
File:    Signal.py
Author:  Patrick Chasco
Created: July 26, 2005

Purpose: A signals implementation
"""

# This code is public domain with one condition: If you use it,
# please give credit to the Author in your source.

from weakref import *
import inspect

class Signal:
    """
    class Signal

    A simple implementation of the Signal/Slot pattern. To use, simply 
    create a Signal instance. The instance may be a part of a class, 
    global, or local; it makes no difference what scope it resides 
    within. Connect slots to the signal using the "connect()" method. 
    The slot may be a member of a class or a simple function. If the 
    slot is a member of a class, Signal will automatically detect when
    the method's class instance has been deleted and remove it from 
    its list of connected slots.
    """
    def __init__(self):
        self.slots = []

        # for keeping references to _WeakMethod_FuncHost objects.
        # If we didn't, then the weak references would die for
        # non-method slots that we've created.
        self.funchost = []

    def __call__(self, *args, **kwargs):
        for i in range(len(self.slots)):
            slot = self.slots
            if slot != None:
                slot(*args, **kwargs)
            else:
                del self.slots

    def connect(self, slot):
        self.disconnect(slot)
        if inspect.ismethod(slot):
            self.slots.append(WeakMethod(slot))
        else:
            o = _WeakMethod_FuncHost(slot)
            self.slots.append(WeakMethod(o.func))
            # we stick a copy in here just to keep the instance alive
            self.funchost.append(o)

    def disconnect(self, slot):
        try:
            for i in range(len(self.slots)):
                wm = self.slots
                if inspect.ismethod(slot):
                    if wm.f == slot.im_func and wm.c() == slot.im_self:
                        del self.slots
                        return
                else:
                    if wm.c().hostedFunction == slot:
                        del self.slots
                        return
        except:
            pass

    def disconnectAll(self):
        del self.slots
        del self.funchost
        self.slots = []
        self.funchost = []

class _WeakMethod_FuncHost:
    def __init__(self, func):
        self.hostedFunction = func
    def func(self, *args, **kwargs):
        self.hostedFunction(*args, **kwargs)

# this class was generously donated by a poster on ASPN (aspn.activestate.com)
class WeakMethod:
	def __init__(self, f):
            self.f = f.im_func
            self.c = ref(f.im_self)
	def __call__(self, *args, **kwargs):
            if self.c() == None : return
            self.f(self.c(), *args, **kwargs)


if __name__ == "__main__":
    class Alarm:
        def __init__(self):
            self.SigRing = Signal()

    class Sleeper:
        def __init__(self, name):
            self.name = name

        def OnRing(self, time):
            print self.name, "heard the alarm at", time

    def Listener(time):
        print "Listener received the signal at", time

    alarm = Alarm()
    patrick = Sleeper("Patrick")
    katie = Sleeper("Katie")
    billy = Sleeper("Billy")

    print "ring patrick, katie, billy"
    alarm.SigRing.connect(patrick.OnRing)
    alarm.SigRing.connect(katie.OnRing)
    alarm.SigRing.connect(billy.OnRing)
    alarm.SigRing(time="12:00 AM")
    print

    print "ring katie, billy"
    alarm.SigRing.disconnect(patrick.OnRing)
    alarm.SigRing("12:01 AM")
    print

    del katie

    print "ring billy"
    alarm.SigRing("12:02 AM")
    print

    print "ring patrick, billy"
    alarm.SigRing.connect(patrick.OnRing)
    alarm.SigRing.connect(billy.OnRing)    
    alarm.SigRing("12:03 AM")
    print

    print "ring none"
    alarm.SigRing.disconnectAll()
    alarm.SigRing()
    print


    print "ring listener"
    alarm.SigRing.connect(Listener)
    alarm.SigRing("12:04 AM")
    print

    print "ring none"
    alarm.SigRing.disconnect(Listener)
    alarm.SigRing("12:05 AM")
    print

    print "ring patrick, katie, billy, listener"
    alarm.SigRing.connect(patrick.OnRing)
    alarm.SigRing.connect(billy.OnRing)
    alarm.SigRing.connect(Listener)
    alarm.SigRing(time="12:05 AM")
    print    

    print "ring none"
    alarm.SigRing.disconnectAll()
    alarm.SigRing("12:06 AM")
    print






And the result when ran:
>>> ================================ RESTART ================================
>>> 
ring patrick, katie, billy
Patrick heard the alarm at 12:00 AM
Katie heard the alarm at 12:00 AM
Billy heard the alarm at 12:00 AM

ring katie, billy
Katie heard the alarm at 12:01 AM
Billy heard the alarm at 12:01 AM

ring billy
Billy heard the alarm at 12:02 AM

ring patrick, billy
Patrick heard the alarm at 12:03 AM
Billy heard the alarm at 12:03 AM

ring none

ring listener
Listener received the signal at 12:04 AM

ring none

ring patrick, billy, listener
Patrick heard the alarm at 12:05 AM
Billy heard the alarm at 12:05 AM
Listener received the signal at 12:05 AM

ring none

>>> 
[Edited by - smr on July 27, 2005 9:36:54 AM]

Share this post


Link to post
Share on other sites
Advertisement
I've worked out a few bugs in the implementation of the Signal class.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!