factorised the event_wrapper from Window and Scene to a Mixin

This commit is contained in:
Faraphel 2023-02-19 18:50:04 +01:00
parent f38f440e9d
commit 8a385a75bf
10 changed files with 87 additions and 110 deletions

View file

@ -1,9 +1,10 @@
A faire : A faire :
- Ecran de configuration de la partie
- Nom dans les options
- Prévisualisation des bateaux sur la grille - Prévisualisation des bateaux sur la grille
- Faire une scène incluant par défaut les boutons "Retour" - Faire une scène incluant par défaut les boutons "Retour"
- Police d'écriture - Police d'écriture
- Combiner le _event_wrapper de Window et Scene (?)
Bug : Bug :
- / - /

View file

@ -1,2 +0,0 @@
class StopEventScene(Exception):
pass

View file

@ -1,2 +0,0 @@
class StopEventWidget(Exception):
pass

View file

@ -1,2 +0,0 @@
from .StopEventScene import StopEventScene
from .StopEventWidget import StopEventWidget

View file

@ -0,0 +1,59 @@
from abc import abstractmethod
from functools import lru_cache
from typing import Callable, Any
from source.gui.event import StopEvent
class EventPropagationMixin:
@property
@abstractmethod
def childs(self):
pass
@lru_cache
def _event_wrapper(self, item: str) -> Callable:
"""
Un wrapper permettant d'appeler l'événement de toutes les scènes attachées.
:param item: nom de la fonction à appeler dans la scène.
:return: une fonction appelant l'événement original ainsi que ceux des scènes.
"""
# if the event is the drawing of the objects, reverse the order of the scenes
# so that the last drawn are the one on top
child_transform = (
(lambda child: reversed(child)) if item == "on_draw" else
(lambda child: child)
)
# try to get the original function
func = None
try: func = super().__getattribute__(item)
except AttributeError: pass
# try to get a function that would get executed after everything else
func_after = None
try: func_after = super().__getattribute__(item + "_after")
except AttributeError: pass
def _func(*args, **kwargs) -> None:
if func is not None: func(*args, **kwargs)
for child in child_transform(self.childs):
try: getattr(child, item, lambda *_, **__: "pass")(*args, **kwargs)
# si l'erreur StopEventScene est détecté, les autres scènes ne recevront pas l'event
except StopEvent: break
if func_after is not None: func_after(*args, **kwargs)
return _func
def __getattribute__(self, item: str) -> Any:
"""
Fonction appelée dès que l'on essaye d'accéder à l'un des attributs de l'objet.
:param item: nom de l'attribut recherché
:return: l'attribut de l'objet correspondant.
"""
# si l'attribut est un événement (commence par "on_"), alors renvoie le dans un wrapper
return self._event_wrapper(item) if item.startswith("on_") else super().__getattribute__(item)

View file

@ -0,0 +1,2 @@
class StopEvent(Exception):
pass

View file

@ -0,0 +1,2 @@
from .StopEvent import StopEvent
from .EventPropagationMixin import EventPropagationMixin

View file

@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
import pyglet import pyglet
from source.event.signal import StopEventScene from source.gui.event import StopEvent
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
from source.gui import widget, texture from source.gui import widget, texture
@ -69,7 +69,7 @@ class Settings(Scene):
self.batch_label.draw() self.batch_label.draw()
def on_mouse_press_after(self, x: int, y: int, button: int, modifiers: int): def on_mouse_press_after(self, x: int, y: int, button: int, modifiers: int):
raise StopEventScene() raise StopEvent()
def on_mouse_motion_after(self, x: int, y: int, button: int, modifiers: int): def on_mouse_motion_after(self, x: int, y: int, button: int, modifiers: int):
raise StopEventScene() raise StopEvent()

View file

@ -1,15 +1,14 @@
from abc import ABC from abc import ABC
from functools import lru_cache from typing import TYPE_CHECKING, Type
from typing import TYPE_CHECKING, Callable, Type, Any
from source.event.signal import StopEventWidget from source.gui.event import EventPropagationMixin
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
from source.gui.widget.abc import Widget from source.gui.widget.abc import Widget
class Scene(ABC): class Scene(ABC, EventPropagationMixin):
""" """
A scene that can be attached to a window. A scene that can be attached to a window.
It allows to switch the whole comportment of the window in a simpler way. It allows to switch the whole comportment of the window in a simpler way.
@ -21,6 +20,12 @@ class Scene(ABC):
self.window = window self.window = window
self._widgets: list["Widget"] = list() self._widgets: list["Widget"] = list()
# Event propagation
@property
def childs(self):
return self._widgets
# Widget Managing # Widget Managing
def add_widget(self, widget_class: Type["Widget"], **widget_kwargs): def add_widget(self, widget_class: Type["Widget"], **widget_kwargs):
@ -50,45 +55,3 @@ class Scene(ABC):
""" """
self._widgets.clear() self._widgets.clear()
# Event Handling
@lru_cache
def _event_wrapper(self, item: str) -> Callable:
"""
Un wrapper permettant d'appeler l'événement de tous les widgets attachées.
:param item: nom de la fonction à appeler dans le widget.
:return: une fonction appelant l'événement original ainsi que ceux des scènes.
"""
# Récupère la fonction originale. S'il n'y en a pas, renvoie une fonction sans effet.
func = None
try: func = super().__getattribute__(item)
except AttributeError: pass
# Récupère une fonction qui devra s'exécuter après tout le reste
func_after = None
try: func_after = super().__getattribute__(item + "_after")
except AttributeError: pass
def _func(*args, **kwargs) -> None:
if func is not None: func(*args, **kwargs)
for widget in self._widgets:
try: getattr(widget, item, lambda *_, **__: "pass")(*args, **kwargs)
# si l'erreur StopEventWidget est détecté, les autres scènes ne recevront pas l'event
except StopEventWidget: break
if func_after is not None: func_after(*args, **kwargs)
return _func
def __getattribute__(self, item: str) -> Any:
"""
Fonction appelée dès que l'on essaye d'accéder à l'un des attributs de l'objet.
:param item: nom de l'attribut recherché
:return: l'attribut de l'objet correspondant.
"""
# si l'attribut est un événement (commence par "on_"), alors renvoie le dans un wrapper
return self._event_wrapper(item) if item.startswith("on_") else super().__getattribute__(item)

View file

@ -1,15 +1,14 @@
from functools import lru_cache from typing import Type, TYPE_CHECKING
from typing import Type, Callable, TYPE_CHECKING, Any
import pyglet import pyglet
from source.event.signal import StopEventScene from source.gui.event import EventPropagationMixin
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
class Window(pyglet.window.Window): # NOQA class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
""" """
A window. Based on the pyglet window object. A window. Based on the pyglet window object.
Scene can be added to the window Scene can be added to the window
@ -20,6 +19,12 @@ class Window(pyglet.window.Window): # NOQA
self._scenes: list["Scene"] = list() self._scenes: list["Scene"] = list()
# Event Propagation
@property
def childs(self):
return self._scenes
# Scene Managing # Scene Managing
def set_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene": def set_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene":
@ -66,52 +71,3 @@ class Window(pyglet.window.Window): # NOQA
def on_draw(self): # NOQA def on_draw(self): # NOQA
self.clear() self.clear()
# Event Handling
@lru_cache
def _event_wrapper(self, item: str) -> Callable:
"""
Un wrapper permettant d'appeler l'événement de toutes les scènes attachées.
:param item: nom de la fonction à appeler dans la scène.
:return: une fonction appelant l'événement original ainsi que ceux des scènes.
"""
# if the event is the drawing of the objects, reverse the order of the scenes
# so that the last drawn are the one on top
scene_transform = (
(lambda scenes: reversed(scenes)) if item == "on_draw" else
(lambda scenes: scenes)
)
# try to get the original function
func = None
try: func = super().__getattribute__(item)
except AttributeError: pass
# try to get a function that would get executed after everything else
func_after = None
try: func_after = super().__getattribute__(item + "_after")
except AttributeError: pass
def _func(*args, **kwargs) -> None:
if func is not None: func(*args, **kwargs)
for scene in scene_transform(self._scenes):
try: getattr(scene, item)(*args, **kwargs)
# si l'erreur StopEventScene est détecté, les autres scènes ne recevront pas l'event
except StopEventScene: break
if func_after is not None: func_after(*args, **kwargs)
return _func
def __getattribute__(self, item: str) -> Any:
"""
Fonction appelée dès que l'on essaye d'accéder à l'un des attributs de l'objet.
:param item: nom de l'attribut recherché
:return: l'attribut de l'objet correspondant.
"""
# si l'attribut est un événement (commence par "on_"), alors renvoie le dans un wrapper
return self._event_wrapper(item) if item.startswith("on_") else super().__getattribute__(item)