Kivy A to Z -- Kivy的消息处理机制

时间:2021-01-04 15:57:18

外面一直在下雨,比较无聊,顺便总结了下Kivy的消息的处理过程。

总的来说,在Kivy里,处理的消息一共有四种:按键消息,鼠标消息,触屏消息,还有自定义消息。下面来看下整个消息的处理流程。

 

先来看张图:

 

 Kivy A to Z -- Kivy的消息处理机制


 

   先来解释下这几个类都是干嘛的:

1、EventDispatcher:看名称就知道这是一个消息分发类,在这个类中通过了以下的主要方法:

register_event_type : 注册一种消息类型

bind                :将一个方法绑定到一个消息类型,用于保存方法的是一个list类型。

dispatch            :调用相应的已经注册到指定消息类型的方法。

 

2、WindowBase:Window的基类,定义了Window的一些常用的方法,如add_wdiget,on_draw。

WindowPygame,WindowX11,WindowSDL这三个类是WindowBase的子类,在某一平台上只会初始化一种,并且只有一个实例,主要用于接收消息和处理消息。具体的初始化代码在kivy/core/__init__.py

      #: Instance of a :class:`WindowBase` implementation

Window = core_select_lib('window', (

    ('egl_rpi', 'window_egl_rpi', 'WindowEglRpi'),

    ('pygame', 'window_pygame', 'WindowPygame'),

    ('sdl', 'window_sdl', 'WindowSDL'),

    ('x11', 'window_x11', 'WindowX11'),



), True)


如果没有特别指定,Kivy将会实例化WindowPygame,作为Kivy的主窗口。所有的画图,创建widget的操作都将在这个主窗口上进行。

 

再来解释下主要变量和方法。EventDispatcher.__event_stack,这是一个用于保存所有已经注册的消息。添加一个消息到__event_stack有两种方法:

1、一种是通过调用EventDispatcher.register_event_type

2、另一种方法是在子类中声明一个类变量__events__,如在WindowBase中就有关于__events__的声明:

 

            __events__ = ('on_draw', 'on_flip', 'on_rotate', 'on_resize', 'on_close',

                  'on_motion', 'on_touch_down', 'on_touch_move', 'on_touch_up',

                  'on_mouse_down', 'on_mouse_move', 'on_mouse_up',

                  'on_keyboard', 'on_key_down', 'on_key_up', 'on_dropfile')

并在EventDispatcher.__cinit__时把这些事件类型加到__event_stack变量中。

 

有了事件的类型,接下来要做的事就是通过EventDispatcher.bind添加事件的处理方法:

例如,在input/providers/mouse.py的MouseMotionEventProvider中,就有下面的bind代码:

 

    def start(self):

        '''Start the mouse provider'''

        if not EventLoop.window:

            return

        EventLoop.window.bind(

            on_mouse_move=self.on_mouse_motion,

            on_mouse_down=self.on_mouse_press,

            on_mouse_up=self.on_mouse_release)

 

好,注册了消息类型和消息的处理过程,按下来就要等消息上门了。

前面讲到,Kivy实例化了WindowPygame作为主窗口,并且通过调用App.run最终进入WindowPygame._mainloop这个消息循环方法。接下来看下_mainloop都做了些什么:

 

   def _mainloop(self):
        EventLoop.idle()
        for event in pygame.event.get():
            # kill application (SIG_TERM)
            if event.type == pygame.QUIT:
                EventLoop.quit = True
                self.close()
            # mouse move
            elif event.type == pygame.MOUSEMOTION:
                x, y = event.pos
                self.mouse_pos = x, self.system_size[1] - y
                # don't dispatch motion if no button are pressed
                if event.buttons == (0, 0, 0):
                    continue
                self._mouse_x = x
                self._mouse_y = y
                self._mouse_meta = self.modifiers
                self.dispatch('on_mouse_move', x, y, self.modifiers)

            # mouse action
            elif event.type in (pygame.MOUSEBUTTONDOWN,
                                pygame.MOUSEBUTTONUP):
                self._pygame_update_modifiers()
                x, y = event.pos
                btn = 'left'
                if event.button == 3:
                    btn = 'right'
                elif event.button == 2:
                    btn = 'middle'
                elif event.button == 4:
                    btn = 'scrolldown'
                elif event.button == 5:
                    btn = 'scrollup'
                elif event.button == 6:
                    btn = 'scrollright'
                elif event.button == 7:
                    btn = 'scrollleft'
                eventname = 'on_mouse_down'
                if event.type == pygame.MOUSEBUTTONUP:
                    eventname = 'on_mouse_up'
                self._mouse_x = x
                self._mouse_y = y
                self._mouse_meta = self.modifiers
                self._mouse_btn = btn
                self._mouse_down = eventname == 'on_mouse_down'
                self.dispatch(eventname, x, y, btn, self.modifiers)

            # keyboard action
            elif event.type in (pygame.KEYDOWN, pygame.KEYUP):
                self._pygame_update_modifiers(event.mod)
                # atm, don't handle keyup
                if event.type == pygame.KEYUP:
                    self.dispatch('on_key_up', event.key,
                                  event.scancode)
                    continue
                # don't dispatch more key if down event is accepted
                if self.dispatch('on_key_down', event.key,
                                 event.scancode, event.unicode,
                                 self.modifiers):
                    continue
                self.dispatch('on_keyboard', event.key,
                              event.scancode, event.unicode,
                              self.modifiers)
            # video resize
            elif event.type == pygame.VIDEORESIZE:
                self._size = event.size
                self.update_viewport()
 
            elif event.type == pygame.VIDEOEXPOSE:
                self.canvas.ask_update()
            # ignored event
            elif event.type == pygame.ACTIVEEVENT:
                pass
            # drop file (pygame patch needed)
            elif event.type == pygame.USEREVENT and \
                    hasattr(pygame, 'USEREVENT_DROPFILE') and \
                    event.code == pygame.USEREVENT_DROPFILE:
                self.dispatch('on_dropfile', event.filename)
            '''
            # unhandled event !

            else:

                Logger.debug('WinPygame: Unhandled event %s' % str(event))


 

在这个消息循环里,通过pygame.event.get()获取消息,消息的类型有:按键,鼠标,这两种,通过dispatch将消息发送到相应的方法中去处理,这里有一个疑问:怎么没有看到Kivy对触屏消息的处理?答案是触屏消息的处理是在函数开始的EventLoop.idle()里进行的。这篇讲的就要是消息的处理机制,所以这里就不再展来。其内容留着以后再讲。