I’m some-what new to pygame, and while trying to get back into it, I found the advice “replace your conditionals with polymorphism.” Most pygame tutorials recommend using the “for event in pygame.event.get()” loop and then using a conditional to determine if any particular event has occured.
1: How do I write that code using a polymorphic function 2: Is it worth it to re-factor the code, or just leave the conditional as-is
Below is what most tutorials recommend
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()
Below is how I want to approach this
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)
Advertisement
Answer
In pygame the event type is an enumerator constant. You have to map this constant to the corresponding class. Use a dictionary
:
def Exit(event): # [...] def Keydown(): # [...] eventhandler = {pygame.QUIT: Exit, pygame.KEYDOWN: Keydown}
Of course, the dictionary can be generated or even extended dynamically, too. (e.g.: eventhandler[pygame.MOUSEBUTTONDOWN] = MouseDown
)
The events must be delegated to the appropriate actions assigned in the event handler:
while True: for event in pygame.event.get(): if event.type in eventhandler: eventhandler[event.type](event)
Example:
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)
For a more general approach, the handlers can be managed in a list. So multiple actions can be associated to an event type:
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)
Example:
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)
Or even a combination of both answers (see answer of @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)