
2.3 事件处理

2.3 事件处理
事件是一个操作动作,通常来说,是指Pygame会接受用户的各种操作(比如按键盘、移动鼠标等)。这些操作会产生对应的事件,例如按键盘事件、移动鼠标事件。事件在软件开发中非常重要,Pygame把一系列的事件存放在一个队列里,并逐个进行处理。在本节的内容中,将详细讲解Pygame事件处理的基本知识。
2.3.1 事件检索
在前面的实例2-1中,使用函数pygame.event.get()处理了所有的事件。如果使用pygame.event.wait()函数,Pygame就会等到发生一个事件后才继续下去,而方法pygame.event.poll()一旦被调用,就会根据当前的情形返回一个真实的事件。在表2-3中列出了Pygame中常用的事件。
表2-3 Pygame中常用的事件

(续)

(1)pygame.event.Event对象
在任何时候,当用户按下一个按键或者把鼠标移动到程序的窗口上面等动作时,Pygame库就会创建一个pygame.event.Event对象来记录这个动作,这就是事件。我们可以调用函数pygame.event.get()来获取发生的事件,此函数会返回pygame.event.Event对象(简称为Event对象)的一个列表。这个Event对象的列表包含了自上次调用pygame.event.get()函数之后所发生的所有事件(如果从来没有调用过pygame.event.get(),会包括自程序启动以来所发生的所有事件)。例如在下面的演示代码中,第2行是一个for循环,它会遍历pygame.event.get()所返回的Event对象的列表。在这个for循环的每一次迭代中,一个名为event的变量将会被赋值为列表中的下一个事件对象。函数pygame.event.get()所返回的Event对象的列表,将会按照事件发生的顺序来排序。如果用户单击鼠标并按下键盘按键,鼠标单击的Event对象将会是列表的第一项,键盘按键的Event对象将会是第二项。如果没有事件发生,那么函数pygame.event.get()会返回一个空白的列表。

(2)pygame.quit()函数
在Event对象中有一个名为type的成员变量,其功能是告诉对象表示哪一种事件。针对pygame.locals模块中每一种可能的类型,Pygame都有一个常量变量。例如在下面的代码中可以检查Event对象的type是否等于常量QUIT。

因为在Pygame项目中使用了“from pygame.locals import *”形式的import语句,所以只要输入QUIT就可以了,而不必输入完整形式pygame.locals.QUIT。如果Event对象是一个停止事件,就会调用pygame.quit()和sys.exit()函数。函数pygame.quit()是pygame.init()函数的相反函数,功能是停止Pygame库的工作。在调用函数sys.exit()终止程序之前,需要先调用函数pygame.quit()。因为在程序退出之前Python会关闭Pygame,所以这通常不会出现什么问题。但是,在IDLE中有一个bug,如果一个Pygame程序在调用pygame.quit()之前就终止了,将会导致IDLE挂起。
2.3.2 处理鼠标事件
在Pygame框架中,MOUSEMOTION事件会在鼠标动作的时候发生,它有如下所示的3个参数。
● buttons:一个含有3个数字的元组,3个值分别代表左键、中键和右键,1就说明按下了。
● pos:位置。
● rel:代表现在距离上次产生鼠标事件时的距离。
和MOUSEMOTION类似,常用的鼠标事件还有MOUSEBUTTONDOWN和MOUSEBUTTONUP两个。这两个事件的参数如下所示。
● button:这个值代表了哪个按键被操作。
● pos:位置。

实例文件shubiao.py的具体实现代码如下所示。


执行后可以使用鼠标拖动窗体中的游戏场景图,如图2-4所示。

图2-4 执行效果
2.3.3 处理键盘事件
在Pygame框架中,键盘和游戏手柄的事件比较类似,处理键盘的事件为KEYDOWN和KEYUP。KEYDOWN和KEYUP事件的参数描述如下所示。
● key:按下或者放开的键值,是一个数字,因为很少有人可以记住,所以在Pygame中可以使用K_xxx来表示,比如字母a就是K_a,还有K_SPACE和K_RETURN等。
● mod:包含了组合键信息,如果mod & KMOD_CTRL是真的话,表示用户同时按下了〈Ctrl〉键。类似的还有KMOD_SHIFT和KMOD_ALT。
● unicode:代表按下键对应的Unicode值。例如在下面的实例文件shi.py中,演示了在Pygame框架中处理键盘事件的过程。

实例文件shi.py的具体实现代码如下所示。


执行后可以通过键盘中的方向键移动背景图片,效果如图2-5所示。此处读者需要注意编码的问题,一定要确保系统和程序文件编码的一致性,否则将会出现中文乱码,本书后面的类似实例也是如此。

图2-5 执行效果
2.3.4 事件过滤
在现实应用中,并不是所有的事件都是需要处理的。比如,俄罗斯方块游戏就可能无视鼠标操作,在游戏场景切换的时候按什么键都是徒劳的。应该有一个方法来过滤掉一些不感兴趣的事件,这时需要使用pygame.event.set_blocked(事件名)来完成。如果有好多事件需要过滤,可以传递一个专用列表来实现,比如pygame.event.set_blocked([KEYDOWN, KEYUP]),如果设置参数None,那么所有的事件又被打开了。与之相对应的是,使用pygame.event.set_allowed()函数来设定允许的事件。
在Pygame中,和事件过滤相关的功能函数如下所示。
1)pygame.event.set_blocked():控制哪些事件禁止进入队列。
● set_blocked(type)-> None
● set_blocked(typelist)-> None
● set_blocked(None)-> None
参数指定类型的事件均不允许出现在事件队列中。默认是允许所有事件进入队列的。多次禁止同一类型的事件并不会引发什么问题。如果传入None,则表示允许所有的事件进入队列。
2)pygame.event.set_allowed():控制哪些事件允许进入队列。
● set_allowed(type)-> None
● set_allowed(typelist)-> None
● set_allowed(None)-> None
参数指定类型的事件均允许出现在事件队列中。默认是允许所有事件进入队列。多次允许同一类型的事件并不会引发什么问题。如果传入None,则表示禁止所有的事件进入队列。
3)pygame.event.get_blocked():检测某一类型的事件是否被禁止进入队列。
● get_blocked(type)-> bool
如果参数指定类型的事件被禁止进入队列,则返回True。
2.3.5 产生事件
通常玩家做什么,Pygame框架只需要产生对应的事件即可。但是有的时候需要模拟出一些有用的事件,比如在录像回放时需要把用户的操作再重现一遍。为了产生事件,必须先造一个出来,然后传递它。

甚至可以产生一个完全自定义的全新事件。

