事件机制资料(全,详细,认真)-看完这一篇让你了解事件处理机制

事件处理机制的目标之一是实现一个类似反射弧的反射.
即我在一个感受器给出特定的刺激之后会得到特定的响应.
翻译成计算机语言:
产生刺激的事物就是事件源
特定的刺激就是外部事件
特定的响应就是内部事件.

在来说说具体实现:
*.特定的响应我们肯定用函数来做,
而且响应肯定和刺激有关,所以也确定了它的两个参数:
刺激发生地和刺激内容.
我们至此部分的确定了响应函数,谁来调用它还是个问题
*.刺激内容,这是个变化的,生动的,由人来给出,而且是以参数的形式给出,
而且是给到一个函数中,这个函数我暂且命名为刺激函数.
但是谁来调用刺激函数,刺激函数的效果是啥?
*.基本猜想1:由感受器调用刺激函数(?),刺激函数的效果是改变感受器的参数
*.基本问题:感受器和响应部分是如何练习到一起的.
*.联系已有模型,观察者模式:
被观察者类,又名Observable,watched,subject,model
刺激就是notifyObserver()的参数args,所以说,其实是你通过被观察者这个类产生刺激
所以说被观察者类其实是真实刺激源(你)和神经系统(这个软件)的中介,
所以说被观察者类才是真正的感受器.
所以说notifyObserver()方法其实就是感受方法,连接方式就是该方法中调用观察者的
update()方法,直接将感受到的args当作全部的刺激参数传了过去.
从生物角度来说:观察者模式对感受器的区别不敏感,它的基本模型中并不能区分刺激是哪里传来的?
其次,这个感受器你刺激它一下它动一下.
就像你拿针扎一下自己,然后你和该部位连接的效应器就都收到了被针扎了的信息.
*联系已有的委托事件模型:
这里感受器很明显就是EventSource的实例对象,
刺激和感受器的信息都被放到了Event对象中,
这种模型下(如果只按照我写的代码)刺激的方式其实被限制了,
因为你的刺激方式其实就是调用EventSource的各种刺激函数,
你多少个刺激函数,本质上就有多少种刺激函数,这就很适合GUI这种了,
反正你对一个控件可能的刺激手段也比较固定和有限.
再来说一下Listener,也就是监听器,这种东西设计出来就是就是把感受器给模块化了,
否则你每有一个感受器就得新写神经中枢,就好比你的左右手感受方式如簇相似,神经中枢应该是相同的.
以后再有类似的反射我直接使用相同的感受器就好,也就是一个Listener.
*.简单来说,无论是观察者模式还是委托事件模式并不像神经的反射,反而像是激素的作用方式,
1.委托事件模式,这个EventSource就像是某个分泌激素的腺体,不同的刺激方法使它分泌出不同
的激素(event),它的notifyListener()方法,就像是激素随体液传播到身体的各个器官,
每个器官细胞表面上的受体如果和激素匹配(判断event的一些条件),那么就产生效应.
这里的EventListener就好比身体的一些效应器官.总之这种一对多的关系并不像反射
2.不过我今天一想,感觉观察者模式其实和委托事件模式大同小异,只不过把工作量(记忆量/熵),
由决定给什么刺激转变为决定调用什么刺激参数,不过是把刺激给封装了一下而已.
3.还有Java中的事件监听机制(在GUI那块,这里懒得写了),那里是一个监听器中有多个效应函数,
从刺激到效应产生,要经过两次匹配过程,首先把刺激信息传给匹配的监听器,监听器再根据刺激信息
调用匹配的刺激函数,这也是一种思路.
调用匹配的刺激函数,这也是一种思路.
本质都是经过判断下的函数调用链

 以下是相关的学习所用文件:

# -*- encoding: UTF-8 -*-

#就是为了理解这个
# 系统模块
from queue import Queue, Empty
from threading import *


class EventManager:
    def __init__(self):
        """初始化事件管理器"""
        # 事件对象列表
        self.__eventQueue = Queue()
        # 事件管理器开关
        self.__active = False
        # 事件处理线程
        self.__thread = Thread(target=self.__Run)

        # 这里的__handlers是一个字典,用来保存对应的事件的响应函数
        # 其中每个键对应的值是一个列表,列表中保存了对该事件监听的响应函数,一对多
        self.__handlers = {}  # {事件类型:[处理事件的方法]}

    def __Run(self):
        """引擎运行"""
        while self.__active == True:
            try:
                # 获取事件的阻塞时间设为1秒
                event = self.__eventQueue.get(block=True, timeout=1)
                self.__EventProcess(event)
            except Empty:
                pass

    def __EventProcess(self, event):
        """处理事件"""
        # 检查是否存在对该事件进行监听的处理函数
        if event.type_ in self.__handlers:
            # 若存在,则按顺序将事件传递给处理函数执行
            for handler in self.__handlers[event.type_]:
                handler(event)

    def Start(self):
        """启动"""
        # 将事件管理器设为启动
        self.__active = True
        # 启动事件处理线程
        self.__thread.start()

    def Stop(self):
        """停止"""
        # 将事件管理器设为停止
        self.__active = False
        # 等待事件处理线程退出
        self.__thread.join()

    def AddEventListener(self, type_, handler):
        """绑定事件和监听器处理函数"""
        # 尝试获取该事件类型对应的处理函数列表,若无则创建
        try:
            handlerList = self.__handlers[type_]
        except KeyError:
            handlerList = []

        self.__handlers[type_] = handlerList
        # 若要注册的处理器不在该事件的处理器列表中,则注册该事件
        if handler not in handlerList:
            handlerList.append(handler)

    def RemoveEventListener(self, handler):
        """移除监听器的处理函数"""
        # 读者自己试着实现

    def SendEvent(self, event):
        """发送事件,向事件队列中存入事件"""
        self.__eventQueue.put(event)


"""事件对象"""


class Event:
    def __init__(self, type_=None):
        self.type_ = type_  # 事件类型
        self.dict = {}  # 字典用于保存具体的事件数据
#委托事件模型:
class Event:
    def __init__(self,info):
        self.sourceInfo=info
        self.status=None

    def setStatus(self,condition):
        self.status=condition

class EventListener:
    def __init__(self):
        pass

    def changeStatus(self,event):
        #event.sourceInfo
        #event.status
        pass

class EventSource:
    def __init__(self,info):
        self.listenerList=[]
        self.info=info

    def addListener(self,listener):
        self.listenerList.append(listener)

    def condition1(self):
        event=Event(self.info)
        event.setStatus("1")
        self.notifyListener(event)

    def condition2(self):
        event=Event(self.info)
        event.setStatus("2")
        self.notifyListener(event)

    def condition3(self):
        event=Event(self.info)
        event.setStatus("3")
        self.notifyListener(event)

    def notifyListener(self,event):
        for listener in self.listenerList:
            listener.changeStatus()
#观察者模式总结
#这个就是被观察者的父亲类,基本类
class Observable1:
    def __init__(self):
        self.Observer=[]
        self.changed=False

    #添加观察者
    def addObserver(self,observer):
        self.Observer.append(observer)

    def deleteObserver(self,observer):
        self.Observer.remove(observer)

   #通知观察者,要给一个参数,给的参数就是观察者做出变化的依据
    def notifyObserver(self,args):
        for o in self.Observer:
            o.update1(args)

    def setChangd(self):
        self.changed=True

    def clearChanged(self):
        self.changed=False

#这本来应该写成一个接口,将来所有观察者就实现它的方法就好
#准确的来说是如果你要观察Obervable就实现它好了
#如果你的类想要拥有观察Observable1及其子类的能力,就继承本Oberver类吧

class Observer1:
    def __init__(self):
        pass
    def update1(self,args):
        #这里应该就是update的最终实现了
        #但我也只是做演示,所以用了空方法
        pass

#这里应该是你将来被观察的具体类
#有你定义的变化量,也就是将来要往update1()传递的参数args
class Watched1(Observable1):
    def __init__(self):
        Observable1.__init__(self)
        #你还应该在这里声明你的特殊变量
        #假如你是微信公众号,那你要加一个文章变量
        #假如你是一个红灯,那你应该加灯的颜色变量
        #加如你是一个控件,那你应该加event变量
        pass

    def myNotifyObserver(self,args):
        #你还可能要自己重写通知方法,
        #再加点花样,比如红绿灯会在绿灯后删掉所有观察者
        pass

#这就是你的实际用的类
#他要具有观察Observable1及其子类并做出反应的能力
#其实不应该由我来定义,是你已经定义好的类
class Watcher(Observer):
    pass

#GUI中的事件绑定
from tkinter import *
#关于事件处理这个事情,你可能在GUI的事件绑定中听说过
#它的基本格式为:
def handler(event):
    #通过调用event对象的属性实现事件处理
    pass
#widget.bind(<[modifier-]-type[-detail]>,handler)
#至于<[modifier-]-type[-detail]>具体怎么写,event对象具体有什么属性我就不赘述了.
#queue模块
#首先说一下queue模块
#这个模块其实就是实现了一个叫队列的数据结构,但是它是线程安全的
#而且严格来说不只是队列,还能实现栈.

import queue

#先进先出,也就是传统意义上的队列,也就是滑滑梯
q=queue.Queue(2)#在构造的时候可以给定队列的最大容量,不给的话默认是没有上限
"""
如果队列当前已满且 block 为 True,put() 方法就使调用线程暂停,直到空出一个数据单元。
如果block为 False,put 方法将引发 Full 异常
"""
q.put(1)
q.put(2)
"""
调用队列对象的 get() 方法从队头删除并返回一个项目。可选参数为 block,默认为 True。
如果队列满了且 block 为 True,get() 就使调用线程暂停,直至有项目可用。
如果队列满了且 block 为 False,队列将引发 Empty 异常。
常用的方法是先判断一个队列是否为空,如果不为空则取值。"""
print(q.get())
print(q.qsize())#注意,在取出一个元素后,队列显示为只剩下一个元素了
# 后进先出队列,也就是填弹夹
q1= queue.LifoQueue()
q1.put(1)
q1.put(2)
print(q1.get())

# 优先级队列
# 当优先级相同时,按放数据顺序取数据
q2 = queue.PriorityQueue()
q2.put((1, "本元素优先级为1"))
q2.put((2, "本元素优先级为2"))
print(q2.get())#(1, '本元素优先级为1')
#自己刚开始写的实例
class 红绿灯:
    def __init__(self):
        self.看灯的人=[]
        self.灯的颜色="初始颜色"

    def 人看灯(self,张三):
        self.看灯的人.append(张三)

    def 人不看灯(self,李四):
        self.看灯的人.remove(李四)

    def 灯的光线传到人的眼里被人看到(self):
        for 人 in self.看灯的人:
            人.看到了灯的颜色(self.灯的颜色)

    def 变颜色(self,新颜色):
        self.灯的颜色=新颜色
        self.灯的光线传到人的眼里被人看到()
        if 新颜色=="绿":
            self.看灯的人=[]

class 路人:
    def __init__(self,名字):
        self.路人名字=名字
        self.看到的颜色="初始颜色"

    def 看到了灯的颜色(self,灯的颜色):
        self.看到的颜色=灯的颜色
        if self.看到的颜色=="红":
            print(self,"看到了",self.看到的颜色,"正在等待")
        if self.看到的颜色=="绿灯":
            print(self, "看到了", self.看到的颜色, "正在通行" )

import uuid
class 造人工厂:
    def __init__(self):
        pass
    def 造人(self):
        人=路人(uuid.uuid1())
        return 人

#本来还有个测试文件,但是找不到了
#当的一个观察者模式的例子
# -*- encoding: UTF-8 -*-

from threading import *
from EventManager import *
import time

#事件名称  新文章
EVENT_ARTICAL = "Event_Artical"


#事件源 公众号
class PublicAccounts:
    def __init__(self,eventManager):
        self.__eventManager = eventManager

    def WriteNewArtical(self):
        #事件对象,写了新文章
        event = Event(type_=EVENT_ARTICAL)
        event.dict["artical"] = u'如何写出更优雅的代码\n'
        #发送事件
        self.__eventManager.SendEvent(event)
        print(u'公众号发送新文章')


#监听器 订阅者
class Listener:
    def __init__(self,username):
        self.__username = username

    #监听器的处理函数 读文章
    def ReadArtical(self,event):
        print(u'%s 收到新文章' % self.__username)
        print(u'正在阅读新文章内容:%s'  % event.dict["artical"])


"""测试函数"""
def test():
    listner1 = Listener("thinkroom") #订阅者1
    listner2 = Listener("steve")#订阅者2

    eventManager = EventManager()

    #绑定事件和监听器响应函数(新文章)
    eventManager.AddEventListener(EVENT_ARTICAL, listner1.ReadArtical)
    eventManager.AddEventListener(EVENT_ARTICAL, listner2.ReadArtical)
    eventManager.Start()

    publicAcc = PublicAccounts(eventManager)
    while True:
        publicAcc.WriteNewArtical()
        time.sleep(2)

if __name__ == '__main__':
    test()

最后是我的一些理解:


事件处理机制的目标之一是实现一个类似反射弧的反射.
即我在一个感受器给出特定的刺激之后会得到特定的响应.
翻译成计算机语言:
产生刺激的事物就是事件源
特定的刺激就是外部事件
特定的响应就是内部事件.

在来说说具体实现:
*.特定的响应我们肯定用函数来做,
而且响应肯定和刺激有关,所以也确定了它的两个参数:
刺激发生地和刺激内容.
我们至此部分的确定了响应函数,谁来调用它还是个问题
*.刺激内容,这是个变化的,生动的,由人来给出,而且是以参数的形式给出,
而且是给到一个函数中,这个函数我暂且命名为刺激函数.
但是谁来调用刺激函数,刺激函数的效果是啥?
*.基本猜想1:由感受器调用刺激函数(?),刺激函数的效果是改变感受器的参数
*.基本问题:感受器和响应部分是如何练习到一起的.
*.联系已有模型,观察者模式:
被观察者类,又名Observable,watched,subject,model
刺激就是notifyObserver()的参数args,所以说,其实是你通过被观察者这个类产生刺激
所以说被观察者类其实是真实刺激源(你)和神经系统(这个软件)的中介,
所以说被观察者类才是真正的感受器.
所以说notifyObserver()方法其实就是感受方法,连接方式就是该方法中调用观察者的
update()方法,直接将感受到的args当作全部的刺激参数传了过去.
从生物角度来说:观察者模式对感受器的区别不敏感,它的基本模型中并不能区分刺激是哪里传来的?
其次,这个感受器你刺激它一下它动一下.
就像你拿针扎一下自己,然后你和该部位连接的效应器就都收到了被针扎了的信息.
*联系已有的委托事件模型:
这里感受器很明显就是EventSource的实例对象,
刺激和感受器的信息都被放到了Event对象中,
这种模型下(如果只按照我写的代码)刺激的方式其实被限制了,
因为你的刺激方式其实就是调用EventSource的各种刺激函数,
你多少个刺激函数,本质上就有多少种刺激函数,这就很适合GUI这种了,
反正你对一个控件可能的刺激手段也比较固定和有限.
再来说一下Listener,也就是监听器,这种东西设计出来就是就是把感受器给模块化了,
否则你每有一个感受器就得新写神经中枢,就好比你的左右手感受方式如簇相似,神经中枢应该是相同的.
以后再有类似的反射我直接使用相同的感受器就好,也就是一个Listener.
*.简单来说,无论是观察者模式还是委托事件模式并不像神经的反射,反而像是激素的作用方式,
1.委托事件模式,这个EventSource就像是某个分泌激素的腺体,不同的刺激方法使它分泌出不同
的激素(event),它的notifyListener()方法,就像是激素随体液传播到身体的各个器官,
每个器官细胞表面上的受体如果和激素匹配(判断event的一些条件),那么就产生效应.
这里的EventListener就好比身体的一些效应器官.总之这种一对多的关系并不像反射
2.不过我今天一想,感觉观察者模式其实和委托事件模式大同小异,只不过把工作量(记忆量/熵),
由决定给什么刺激转变为决定调用什么刺激参数,不过是把刺激给封装了一下而已.
3.还有Java中的事件监听机制(在GUI那块,这里懒得写了),那里是一个监听器中有多个效应函数,
从刺激到效应产生,要经过两次匹配过程,首先把刺激信息传给匹配的监听器,监听器再根据刺激信息
调用匹配的刺激函数,这也是一种思路.
调用匹配的刺激函数,这也是一种思路.
本质都是经过判断下的函数调用链

看我这么认真,关注,点赞鼓励一下呗

发布了33 篇原创文章 · 获赞 9 · 访问量 5396

猜你喜欢

转载自blog.csdn.net/realliyuhao/article/details/104389109