python:signal/slot机制

作者: 核桃啊 | 来源:发表于2016-05-10 16:38 被阅读1331次

    观察者模式

    观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统

    观察者模式观察者模式结构如下:

    observer

    分析

    根据其定义可以得出几条重要的结论:

    • 目标对象会保存其订阅者的引用
    • 状态改变,即满足条件时,目标会拿到订阅者的引用,并且调用订阅者的方法接口

    Observer是观察者的接口,提供notify接口,由Subject来调用,由具体的concreteObserver实现。
    Subject是订阅的主题,提供3个接口,通知、增加订阅者和删除订阅者。
    贴出维基上Python的实现:

    class AbstractSubject(object):
        def register(self, listener):
            raise NotImplementedError("Must subclass me")
     
        def deregister(self, listener):
            raise NotImplementedError("Must subclass me")
     
        def notify_listeners(self, event):
            raise NotImplementedError("Must subclass me")
     
    class Listener(object):
        def __init__(self, name, subject):
            self.name = name
            subject.register(self)
     
        def notify(self, event):
            print self.name, "received event", event
     
    class Subject(AbstractSubject):
        def __init__(self):
            self.listeners = []
            self.data = None
    
        def getUserAction(self):
            self.data = raw_input('Enter something to do:')
            return self.data
    
        # Implement abstract Class AbstractSubject
    
        def register(self, listener):
            self.listeners.append(listener)
     
        def deregister(self, listener):
            self.listeners.remove(listener)
     
        def notify_listeners(self, event):
            for listener in self.listeners:
                listener.notify(event)
    
     
    if __name__=="__main__":
        # make a subject object to spy on
        subject = Subject()
     
        # register two listeners to monitor it.
        listenerA = Listener("<listener A>", subject)
        listenerB = Listener("<listener B>", subject)
     
        # simulated event
        subject.notify_listeners ("<event 1>")
        # outputs:
        #     <listener A> received event <event 1>
        #     <listener B> received event <event 1>
     
        action = subject.getUserAction()
        subject.notify_listeners(action)
        #Enter something to do:hello
        # outputs:
        #     <listener A> received event hello
        #     <listener B> received event hello
    

    signal/slot 信号槽

    信号槽似乎来源于QT,相关介绍qt信号槽,其结构是所有的控件都继承自signalApp,signalApp实现了3个通用接口,产生信号、产生槽和连接信号和槽

    信号和槽的关系

    当信号发送出去时,会调用信号相关联的槽函数。比如:

    button = Button()
    lable = Label()
    signalAPP.connect(button, SIGNAL(clicked), lable, SLOT(setValue))
    

    将button的点击信号和label的槽函数进行连接,当信号发送时,会调用label的槽函数,这种机制将信号与槽分离,可以信号与信号进行连接,可以一个信号对应多个槽,可以一个槽对应多个信号。

    一个简单的python信号槽实现

    # -*- coding: utf-8 -*-
    
    class CSignal():
        def __init__(self):
            self.slot = []
        def emit(self, *arg, **kw):
            for pFunc in self.slot:
                pFunc(*arg, **kw)
        def connect(self, cbfunc):
            self.slot.append(cbfunc)
    
    class memberFuc():
        def __init__(self):
            pass
        def test(self, *arg, **kw):
            print 'i am memberFuc!'
    
    def test(*arg, **kw):
        print "i am test", arg, kw
    
    if __name__ == "__main__":
        testSignal = CSignal()
        testSignal.connect(test)
    
        testOb = memberFuc()
        testSignal.connect(testOb.test)
    
        testSignal.emit()
        # output:
        #   i am test
        #   i am memberfunc
        del testOb
        testSignal.emit()
        # output:
        #   i am test
        #   i am memberfunc
    
    

    上面代码和QT的信号槽有几点不同:

    • 一个信号只关联一个槽
    • 信号对象独立出来,不是控件的基类

    注意:当connect一个method时,会增加对象的引用计数,导致del 对象时,对象引用计数不为0,仍然存在,解决的方法时利用弱引用。下面是网上的实现:

    
    """
    File:    signals.py
    Author:  Patrick Chasco
    Created: July 26, 2005
    
    Purpose: A signals implementation
    """
    
    
    #========================================================
    # Implementation
    #========================================================
    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 member of a class, 
        a global, or a 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[i]
                if slot != None:
                    slot(*args, **kwargs)
                else:
                    del self.slots[i]
                    
        def call(self, *args, **kwargs):
            self.__call__(*args, **kwargs)
    
        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[i]
                    if inspect.ismethod(slot):
                        if wm.f == slot.im_func and wm.c() == slot.im_self:
                            del self.slots[i]
                            return
                    else:
                        if wm.c().hostedFunction == slot:
                            del self.slots[i]
                            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)
    
    
    #========================================================
    # Example usage
    #========================================================
    if __name__ == "__main__":
        class Button:
            def __init__(self):
                # Creating a signal as a member of a class
                self.sigClick = Signal()
    
        class Listener:
            # a sample method that will be connected to the signal
            def onClick(self):
                print "onClick ", repr(self)
        
        # a sample function to connect to the signal
        def listenFunction():
            print "listenFunction"
       
        # a function that accepts arguments
        def listenWithArgs(text):
            print "listenWithArgs: ", text
    
        b = Button()
        l = Listener()
        
        # Demonstrating connecting and calling signals
        print
        print "should see one message"
        b.sigClick.connect(l.onClick)
        b.sigClick()
    
        # Disconnecting all signals
        print
        print "should see no messages"
        b.sigClick.disconnectAll()
        b.sigClick()
    
        # connecting multiple functions to a signal
        print
        print "should see two messages"
        l2 = Listener()
        b.sigClick.connect(l.onClick)
        b.sigClick.connect(l2.onClick)
        b.sigClick()
        
        # disconnecting individual functions
        print
        print "should see two messages"
        b.sigClick.disconnect(l.onClick)
        b.sigClick.connect(listenFunction)
        b.sigClick()
        
        # signals disconnecting automatically
        print
        print "should see one message"
        b.sigClick.disconnectAll()
        b.sigClick.connect(l.onClick)
        b.sigClick.connect(l2.onClick)
        del l2    
        b.sigClick()
        
        # example with arguments and a local signal
        print
        print "should see one message"
        sig = Signal()
        sig.connect(listenWithArgs)
        sig("Hello, World!")
    

    参考

    python signal/slot

    相关文章

      网友评论

        本文标题:python:signal/slot机制

        本文链接:https://www.haomeiwen.com/subject/jifyrttx.html