diff --git a/NOTE.md b/NOTE.md index e65d7ac..c80ca67 100644 --- a/NOTE.md +++ b/NOTE.md @@ -1,9 +1,10 @@ A faire : +- Ecran de configuration de la partie +- Nom dans les options - Prévisualisation des bateaux sur la grille - Faire une scène incluant par défaut les boutons "Retour" - Police d'écriture -- Combiner le _event_wrapper de Window et Scene (?) Bug : -- / \ No newline at end of file +- / diff --git a/source/event/signal/StopEventScene.py b/source/event/signal/StopEventScene.py deleted file mode 100644 index 81595c0..0000000 --- a/source/event/signal/StopEventScene.py +++ /dev/null @@ -1,2 +0,0 @@ -class StopEventScene(Exception): - pass diff --git a/source/event/signal/StopEventWidget.py b/source/event/signal/StopEventWidget.py deleted file mode 100644 index 3d04bb7..0000000 --- a/source/event/signal/StopEventWidget.py +++ /dev/null @@ -1,2 +0,0 @@ -class StopEventWidget(Exception): - pass diff --git a/source/event/signal/__init__.py b/source/event/signal/__init__.py deleted file mode 100644 index f49c3b9..0000000 --- a/source/event/signal/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .StopEventScene import StopEventScene -from .StopEventWidget import StopEventWidget diff --git a/source/gui/event/EventPropagationMixin.py b/source/gui/event/EventPropagationMixin.py new file mode 100644 index 0000000..812a146 --- /dev/null +++ b/source/gui/event/EventPropagationMixin.py @@ -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) diff --git a/source/gui/event/StopEvent.py b/source/gui/event/StopEvent.py new file mode 100644 index 0000000..f04da2b --- /dev/null +++ b/source/gui/event/StopEvent.py @@ -0,0 +1,2 @@ +class StopEvent(Exception): + pass diff --git a/source/gui/event/__init__.py b/source/gui/event/__init__.py new file mode 100644 index 0000000..4bbff34 --- /dev/null +++ b/source/gui/event/__init__.py @@ -0,0 +1,2 @@ +from .StopEvent import StopEvent +from .EventPropagationMixin import EventPropagationMixin diff --git a/source/gui/scene/Settings.py b/source/gui/scene/Settings.py index 075c16c..118427f 100644 --- a/source/gui/scene/Settings.py +++ b/source/gui/scene/Settings.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING import pyglet -from source.event.signal import StopEventScene +from source.gui.event import StopEvent from source.gui.scene.abc import Scene from source.gui import widget, texture @@ -69,7 +69,7 @@ class Settings(Scene): self.batch_label.draw() 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): - raise StopEventScene() + raise StopEvent() diff --git a/source/gui/scene/abc/Scene.py b/source/gui/scene/abc/Scene.py index f90e072..6ae9a69 100644 --- a/source/gui/scene/abc/Scene.py +++ b/source/gui/scene/abc/Scene.py @@ -1,15 +1,14 @@ from abc import ABC -from functools import lru_cache -from typing import TYPE_CHECKING, Callable, Type, Any +from typing import TYPE_CHECKING, Type -from source.event.signal import StopEventWidget +from source.gui.event import EventPropagationMixin if TYPE_CHECKING: from source.gui.window import Window from source.gui.widget.abc import Widget -class Scene(ABC): +class Scene(ABC, EventPropagationMixin): """ A scene that can be attached to a window. 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._widgets: list["Widget"] = list() + # Event propagation + + @property + def childs(self): + return self._widgets + # Widget Managing def add_widget(self, widget_class: Type["Widget"], **widget_kwargs): @@ -50,45 +55,3 @@ class Scene(ABC): """ 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) diff --git a/source/gui/window/Window.py b/source/gui/window/Window.py index 6eae773..a386626 100644 --- a/source/gui/window/Window.py +++ b/source/gui/window/Window.py @@ -1,15 +1,14 @@ -from functools import lru_cache -from typing import Type, Callable, TYPE_CHECKING, Any +from typing import Type, TYPE_CHECKING import pyglet -from source.event.signal import StopEventScene +from source.gui.event import EventPropagationMixin if TYPE_CHECKING: 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. Scene can be added to the window @@ -20,6 +19,12 @@ class Window(pyglet.window.Window): # NOQA self._scenes: list["Scene"] = list() + # Event Propagation + + @property + def childs(self): + return self._scenes + # Scene Managing 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 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)