0
\$\begingroup\$

I'm new to pygame and tried to write my own Event Manager because I couldn't find a solution I was satisfied with. So I wrote my own inspired by the observer pattern:

from typing import Callable
from pygame.event import Event
EventHandler = Callable[[Event], None]
_managers = dict()
class EventManager:
 def __init__(self, name):
 self.name = name
 self.handlers: dict[int, list[EventHandler]] = dict()
 def notify(self, event: Event, selector=lambda event: event.type):
 eventType = selector(event)
 if eventType not in self.handlers:
 return
 for handler in self.handlers[eventType]:
 handler(event)
 def register(self, eventType: int, handler: EventHandler):
 print(f"{self.name} registered {eventType}")
 if eventType not in self.handlers:
 self.handlers[eventType] = [handler]
 else:
 self.handlers[eventType].append(handler)
 def deregister(self, eventType: int, handler: EventHandler):
 if not self._is_registered(eventType, handler):
 return
 self.handlers[eventType].remove(handler)
 def _is_registered(self, eventType, handler):
 return eventType in self.handlers and handler in self.handlers[eventType]
 @staticmethod
 def get(id: str):
 if id not in _managers:
 _managers[id] = EventManager(id)
 return _managers[id]
generalEventManager = EventManager.get("General Events")
keyEventManager = EventManager.get("Key Events")

it's used like this:

class Foo:
 _running = True
 def register_events(self):
 generalEventManager.register(pygame.QUIT, lambda _: self.on_quit())
 generalEventManager.register(pygame.KEYDOWN, lambda event: self.on_key_down(event))
 # register key events with
 # keyEventManager.register(pygame.K_RETURN, lambda _: self.on_k_return())
 # ...
 def on_key_down(self, event: Event):
 keyEventManager.notify(event, selector=lambda event: event.key)
 def on_quit(self):
 self._running = False
 def on_cleanup(self):
 pygame.quit()
 def on_execute(self):
 while self._running:
 for event in pygame.event.get():
 self.on_event(event)
 self.on_cleanup()

Is this pythonian? Are there better solutions in pygame? Are there generic python library solutions that I didn't find?

asked Jul 25, 2022 at 21:30
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

This seems fairly pythonic, and a decent use of the observer pattern.

One thing I would say is that you generally use class methods instead of static methods for alternate constructors, and then call the 'cls' attribute instead of calling the class by name. This is important if you ever want to make use of inheritance, in addition you may want to tie your _managers global dict to the class, for the same reason.

@classmethod
def get(cls, id: str):
 if id not in cls._managers:
 cls._managers[id] = cls(id)
 return cls._managers[id]

In addition, you don't need to pass lambdas in every time. You can instead just pass functions themselves.

def register_events(self):
 generalEventManager.register(pygame.QUIT, self.on_quit) # You'll need to modify the call signature here
 generalEventManager.register(pygame.KEYDOWN, self.on_key_down)
 # register key events with
 # keyEventManager.register(pygame.K_RETURN, self.on_k_return)
 # ...

Your names are a little undescriptive, I'd probably call your 'get' method something like 'from_id' for clarity, plus obviously 'Foo' should be something richer.

I'd also look into better ways of handling some of your other higher order functions instead of leaning on lambdas. Maybe try using the operator module, for instance:

from operator import attrgetter
# Code here
 def notify(self, event: Event, selector=attrgetter('type')):
 ...

Also, you might want to use a default dict to simplify the implementation a bit:

from collections import defaultdict
from operator import attrgetter
class EventManager:
 def __init__(self, name):
 self.name = name
 self.handlers: dict[int, list[EventHandler]] = defaultdict(list)
 def notify(self, event: Event, selector=attrgetter('type')):
 for handler in self.handlers[selector(event)]:
 handler(event)
 def register(self, eventType: int, handler: EventHandler):
 print(f"{self.name} registered {eventType}")
 self.handlers[eventType].append(handler)
 def deregister(self, eventType: int, handler: EventHandler):
 if handler in self.handlers[eventType]:
 self.handlers[eventType].remove(handler)
 @classmethod
 def from_id(cls, _id: str):
 if _id not in _managers:
 _managers[_id] = EventManager(_id)
 return _managers[_id]
answered Jul 25, 2022 at 21:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.