Pygame: как полиморфно писать событийный цикл

avatar
user9825338
26 февраля 2020 в 04:09
241
3
2

Я новичок в pygame, и, пытаясь вернуться к нему, я нашел совет "замените ваши условные операторы полиморфизмом". В большинстве руководств по pygame рекомендуется использовать цикл «for event in pygame.event.get()», а затем использовать условное выражение, чтобы определить, произошло ли какое-либо конкретное событие.

1: Как мне написать этот код, используя полиморфную функцию 2: Стоит ли рефакторить код или просто оставить условие как есть

Ниже приведены рекомендации большинства руководств

def gameloop():
"""
Stuff that comes before the event-loop
"""

for event in pygame.event.get():
    if event.type == pygame.QUIT:   # This is the conditional I want to remove
        quit_functions()

Ниже я хочу подойти к этому

from abc import ABC, abstractmethod
import pygame


def detect_event(event_class_name):
    for event in pygame.event.get():
        # This is how I want to get the event and then assign it a function
        event_class_name.determine_event(event)


# Above: I want to use this function to detect events and call their functions

# Below: This is how I want to assign functions to the events


class EventHandler(ABC):
    @staticmethod
    @abstractmethod
    def determine_event(event): pass


class Exit(EventHandler):
    @staticmethod
    # How do I identify this with the event-type "pygame.QUIT," for example
    def determine_event(event):
        pygame.quit()
        quit(0)
Источник
furas
26 февраля 2020 в 05:13
1

Я бы сохранил его в словаре как pygame.QUIT: function_name. И тогда цикл for event может использовать словарь для выполнения функции. И я бы создал декоратор @event(pygame.QUIT), который добавляет функцию в этот словарь.

Ответы (3)

avatar
Rabbid76
26 февраля 2020 в 07:39
2

В pygame тип события является константой перечислителя. Вы должны сопоставить эту константу с соответствующим классом. Используйте dictionary:

def Exit(event):
    # [...]

def Keydown():
    # [...]

eventhandler = {pygame.QUIT: Exit, pygame.KEYDOWN: Keydown}

Конечно, словарь можно создавать или даже расширять динамически. (например: eventhandler[pygame.MOUSEBUTTONDOWN] = MouseDown)

События должны быть делегированы соответствующим действиям, назначенным в обработчике событий:

while True:
    for event in pygame.event.get():
        if event.type in eventhandler:
            eventhandler[event.type](event)

Пример:

class EventHandler():
    @staticmethod
    def determine_event(event): pass

class Exit(EventHandler):
    @staticmethod
    def determine_event(event):
        pygame.quit()
        quit(0)

class Keydown(EventHandler):
    @staticmethod
    def determine_event(event):
        print(event.key)

eventhandler = {pygame.QUIT: Exit, pygame.KEYDOWN: Keydown}

while True:
    for event in pygame.event.get():
        if event.type in eventhandler:
            eventhandler[event.type].determine_event(event)

Для более общего подхода обработчиками можно управлять в виде списка. Таким образом, с типом события можно связать несколько действий:

.
def Exit(event):
    # [...]

def Keydown1():
    # [...]

def Keydown2():
    # [...]

eventhandler = {pygame.QUIT: [Exit], pygame.KEYDOWN: [Keydown1, Keydown2]}
while True:
    for event in pygame.event.get():
        if event.type in eventhandler:
            for target in eventhandler[event.type]:
                target(event)

Пример:

class EventHandler():
    targets = {}
    @staticmethod
    def add(type, event):
        EventHandler.targets.setdefault(type, []).append(event) 
    @staticmethod
    def notify(event):  
        if event.type in EventHandler.targets:
            for target in EventHandler.targets[event.type]:
                target.determine_event(event)
    @staticmethod
    def determine_event(event): pass

class Exit(EventHandler):
    @staticmethod
    def determine_event(event):
        pygame.quit()
        quit(0)

class KeydownPrint(EventHandler):
    @staticmethod
    def determine_event(event):
        print(event.key)

class KeydownAction(EventHandler):
    @staticmethod
    def determine_event(event):
        print("action")

EventHandler.add(pygame.QUIT, Exit)
EventHandler.add(pygame.KEYDOWN, KeydownPrint)
EventHandler.add(pygame.KEYDOWN, KeydownAction)

while True:
    for event in pygame.event.get():
        EventHandler.notify(event)

Или даже комбинация обоих ответов (см. ответ @AKX):

class EventHandler:
    targets = {}
    def register(type):
        def decorator(fn):
            EventHandler.targets.setdefault(type, []).append(fn)   
        return decorator
    def notify(event):
        fnl = EventHandler.targets[event.type] if event.type in EventHandler.targets else []  
        for fn in fnl: 
          fn(event)

@EventHandler.register(pygame.QUIT)
def onExit(event):
    pygame.quit()
    quit(0)

@EventHandler.register(pygame.KEYDOWN)
def keydownPrint(event):
    print(event.key)

@EventHandler.register(pygame.KEYDOWN)
def keydownAction(event):
    print("action")

while True:
    for event in pygame.event.get():
        EventHandler.notify(event)
avatar
Walber Franklin
22 августа 2021 в 00:57
0

Возможно, вы могли бы использовать eval() со строкой:

EVENTS = {
pygame.QUIT : "quit_handler()" ,
pygame.MOUSEBUTTONDOWN: "click_handler(event)"
}

for event in pygame.event.get:
    eval (EVENTS.get(event.type))
avatar
AKX
26 февраля 2020 в 07:45
2

Вам не нужно использовать классы со статическими методами, и, опираясь на ответ Rabbid76, (imho) элегантный способ сделать это — декоратор для регистрации каждого обработчика событий в dict:

import pygame

event_handler_registry = {}


def register_event_handler(event):
    def decorator(fn):
        event_handler_registry[event] = fn
        return fn

    return decorator


@register_event_handler(pygame.QUIT)
def on_exit(event):
    pygame.quit()
    quit(0)


@register_event_handler(pygame.KEYDOWN)
def on_key_down(event):
    print(event.key)


while True:
    for event in pygame.event.get():
        event_handler = event_handler_registry.get(event.type)
        if event_handler:
            event_handler(event)
        else:
            print(f"No event handler for {event}")
user9825338
26 февраля 2020 в 23:56
0

Спасибо, это сработало. Мне пришлось узнать, что такое декораторы и как их делать, так что, думаю, я получил больше, чем рассчитывал. Но это хорошо.