From 3cb8a94c1356c93a961cea76db921d9021f58a56 Mon Sep 17 00:00:00 2001 From: Faraphel Date: Thu, 9 Feb 2023 18:26:03 +0100 Subject: [PATCH] added more widgets --- main.pyw | 11 +- source/gui/scene/MainMenuScene.py | 72 +++++++++ source/gui/scene/__init__.py | 1 + source/gui/scene/abc/AbstractScene.py | 97 +---------- source/gui/scene/debug/FPSScene.py | 2 +- source/gui/scene/debug/__init__.py | 2 +- source/gui/scene/test/TestButtonScene.py | 4 +- source/gui/scene/test/TestLabelScene.py | 4 +- .../gui/widget/{Button.py => WidgetButton.py} | 2 +- .../gui/widget/{Image.py => WidgetImage.py} | 2 +- .../gui/widget/{Input.py => WidgetInput.py} | 2 +- .../gui/widget/{Label.py => WidgetLabel.py} | 22 +-- source/gui/widget/__init__.py | 8 +- source/gui/widget/base/Label.py | 4 + source/gui/widget/base/Sprite.py | 5 + source/gui/window/Window.py | 152 ++++++------------ 16 files changed, 162 insertions(+), 228 deletions(-) create mode 100644 source/gui/scene/MainMenuScene.py rename source/gui/widget/{Button.py => WidgetButton.py} (99%) rename source/gui/widget/{Image.py => WidgetImage.py} (60%) rename source/gui/widget/{Input.py => WidgetInput.py} (98%) rename source/gui/widget/{Label.py => WidgetLabel.py} (83%) diff --git a/main.pyw b/main.pyw index c50036c..1f2348e 100644 --- a/main.pyw +++ b/main.pyw @@ -1,15 +1,16 @@ import pyglet -from source.gui.scene.debug import FPSAbstractScene +from source.gui.scene import MainMenuScene +from source.gui.scene.debug import FPSScene from source.gui.scene.test import TestButtonScene, TestLabelScene from source.gui.window import Window - # Create a new window -window = Window(resizable=True, vsync=False) -window.add_scene(TestButtonScene()) +window = Window(resizable=True, vsync=True) +# window.add_scene(TestButtonScene()) # window.add_scene(TestLabelScene()) -window.add_scene(FPSAbstractScene()) +window.add_scene(MainMenuScene()) +window.add_scene(FPSScene()) # Start the event loop pyglet.app.run(interval=0) diff --git a/source/gui/scene/MainMenuScene.py b/source/gui/scene/MainMenuScene.py new file mode 100644 index 0000000..b488b3d --- /dev/null +++ b/source/gui/scene/MainMenuScene.py @@ -0,0 +1,72 @@ +from typing import TYPE_CHECKING, Optional + +import pyglet + +from source.gui.scene.abc import AbstractScene +from source.gui.widget import WidgetLabel, WidgetButton + +if TYPE_CHECKING: + from source.gui.window import Window + + +class MainMenuScene(AbstractScene): + def __init__(self): + super().__init__() + self._title: Optional[WidgetLabel] = None + self._room_create: Optional[WidgetButton] = None + self._room_join: Optional[WidgetButton] = None + + def on_window_added(self, window: "Window"): + normal_texture = pyglet.image.load("./assets/image/button/test_button_normal.png") + hover_texture = pyglet.image.load("./assets/image/button/test_button_hover.png") + click_texture = pyglet.image.load("./assets/image/button/test_button_clicking.png") + + self.button_atlas = pyglet.image.atlas.TextureAtlas() + normal_region = self.button_atlas.add(normal_texture) + hover_region = self.button_atlas.add(hover_texture) + click_region = self.button_atlas.add(click_texture) + + self.background_batch = pyglet.graphics.Batch() + self.label_batch = pyglet.graphics.Batch() + + self._title = WidgetLabel( + x=0.1, y=0.9, width=0.2, height=0.1, + text="Bataille Navale", + font_size=30, + label_batch=self.label_batch, + ) + self.add_widget(self._title) + + self._room_create = WidgetButton( + x=0.1, y=0.5, width=0.3, height=0.1, + label_text="Créer une salle", + + normal_texture=normal_region, + hover_texture=hover_region, + click_texture=click_region, + + label_batch=self.label_batch, + background_batch=self.background_batch, + ) + self.add_widget(self._room_create) + + self._room_join = WidgetButton( + x=0.1, y=0.4, width=0.3, height=0.1, + label_text="Rejoindre une salle", + + normal_texture=normal_region, + hover_texture=hover_region, + click_texture=click_region, + + label_batch=self.label_batch, + background_batch=self.background_batch, + ) + self.add_widget(self._room_join) + + super().on_window_added(window) + + def on_draw(self, window: "Window"): + super().on_draw(window) + + self.background_batch.draw() + self.label_batch.draw() diff --git a/source/gui/scene/__init__.py b/source/gui/scene/__init__.py index e69de29..0eeaf37 100644 --- a/source/gui/scene/__init__.py +++ b/source/gui/scene/__init__.py @@ -0,0 +1 @@ +from .MainMenuScene import MainMenuScene diff --git a/source/gui/scene/abc/AbstractScene.py b/source/gui/scene/abc/AbstractScene.py index 7f4958d..cd9c18c 100644 --- a/source/gui/scene/abc/AbstractScene.py +++ b/source/gui/scene/abc/AbstractScene.py @@ -12,9 +12,9 @@ class AbstractScene(ABC): Can be used to create a menu, an overlay, ... """ - def __init__(self): + def __init__(self, window: "Window"): self._widgets: list["AbstractWidget"] = [] # the lists of the widgets in the scene - self._window: Optional["Window"] = None # the window where the scene is attached + self._window: "Window" = window # the window where the scene is attached # widget @@ -31,96 +31,3 @@ class AbstractScene(ABC): def clear_widget(self) -> None: self.remove_widget(*self._widgets) - # scene event - - def on_window_added(self, window: "Window"): # when the Scene is added to a window - for widget in self._widgets: widget.on_window_added(window, self) - - def on_window_removed(self, window: "Window"): # when the Scene is removed from a window - for widget in self._widgets: widget.on_window_removed(window, self) - - # window - - @property - def window(self) -> "Window": - return self._window - - @window.setter - def window(self, window: "Window"): # trying to change the window will trigger the event - if self._window is not None: self.on_window_removed(self._window) - self._window = window - if self._window is not None: self.on_window_added(self._window) - - # all the events of the window are directly available here or in the widgets - - def on_draw(self, window: "Window"): - for widget in self._widgets: widget.on_draw(window, self) - - def on_resize(self, window: "Window", width: int, height: int): - for widget in self._widgets: widget.on_resize(window, self, width, height) - - def on_hide(self, window: "Window"): - for widget in self._widgets: widget.on_hide(window, self) - - def on_show(self, window: "Window"): - for widget in self._widgets: widget.on_show(window, self) - - def on_close(self, window: "Window"): - for widget in self._widgets: widget.on_close(window, self) - - def on_expose(self, window: "Window"): - for widget in self._widgets: widget.on_expose(window, self) - - def on_activate(self, window: "Window"): - for widget in self._widgets: widget.on_activate(window, self) - - def on_deactivate(self, window: "Window"): - for widget in self._widgets: widget.on_deactivate(window, self) - - def on_text(self, window: "Window", char: str): - for widget in self._widgets: widget.on_text(window, self, char) - - def on_move(self, window: "Window", x: int, y: int): - for widget in self._widgets: widget.on_move(window, self, x, y) - - def on_context_lost(self, window: "Window"): - for widget in self._widgets: widget.on_context_lost(window, self) - - def on_context_state_lost(self, window: "Window"): - for widget in self._widgets: widget.on_context_state_lost(window, self) - - def on_key_press(self, window: "Window", symbol: int, modifiers: int): - for widget in self._widgets: widget.on_key_press(window, self, symbol, modifiers) - - def on_key_release(self, window: "Window", symbol: int, modifiers: int): - for widget in self._widgets: widget.on_key_release(window, self, symbol, modifiers) - - def on_key_held(self, window: "Window", dt: float, symbol: int, modifiers: int): - for widget in self._widgets: widget.on_key_held(window, self, dt, symbol, modifiers) - - def on_mouse_enter(self, window: "Window", x: int, y: int): - for widget in self._widgets: widget.on_mouse_enter(window, self, x, y) - - def on_mouse_leave(self, window: "Window", x: int, y: int): - for widget in self._widgets: widget.on_mouse_leave(window, self, x, y) - - def on_text_motion(self, window: "Window", motion: int): - for widget in self._widgets: widget.on_text_motion(window, self, motion) - - def on_text_motion_select(self, window: "Window", motion: int): - for widget in self._widgets: widget.on_text_motion_select(window, self, motion) - - def on_mouse_motion(self, window: "Window", x: int, y: int, dx: int, dy: int): - for widget in self._widgets: widget.on_mouse_motion(window, self, x, y, dx, dy) - - def on_mouse_press(self, window: "Window", x: int, y: int, button: int, modifiers: int): - for widget in self._widgets: widget.on_mouse_press(window, self, x, y, button, modifiers) - - def on_mouse_release(self, window: "Window", x: int, y: int, button: int, modifiers: int): - for widget in self._widgets: widget.on_mouse_release(window, self, x, y, button, modifiers) - - def on_mouse_drag(self, window: "Window", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): - for widget in self._widgets: widget.on_mouse_drag(window, self, x, y, dx, dy, buttons, modifiers) - - def on_mouse_scroll(self, window: "Window", x: int, y: int, scroll_x: float, scroll_y: float): - for widget in self._widgets: widget.on_mouse_scroll(window, self, x, y, scroll_x, scroll_y) diff --git a/source/gui/scene/debug/FPSScene.py b/source/gui/scene/debug/FPSScene.py index aed723f..2d5a432 100644 --- a/source/gui/scene/debug/FPSScene.py +++ b/source/gui/scene/debug/FPSScene.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: from source.gui.window import Window -class FPSAbstractScene(AbstractScene): +class FPSScene(AbstractScene): """ A base scene that can be used as an overlay to display the FPS """ diff --git a/source/gui/scene/debug/__init__.py b/source/gui/scene/debug/__init__.py index b5f6215..a8d0b54 100644 --- a/source/gui/scene/debug/__init__.py +++ b/source/gui/scene/debug/__init__.py @@ -1 +1 @@ -from .FPSScene import FPSAbstractScene +from .FPSScene import FPSScene diff --git a/source/gui/scene/test/TestButtonScene.py b/source/gui/scene/test/TestButtonScene.py index 7029a00..59157fa 100644 --- a/source/gui/scene/test/TestButtonScene.py +++ b/source/gui/scene/test/TestButtonScene.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING import pyglet from source.gui.scene.abc import AbstractScene -from source.gui.widget import Button +from source.gui.widget import WidgetButton if TYPE_CHECKING: from source.gui.window import Window @@ -36,7 +36,7 @@ class TestButtonScene(AbstractScene): for x in range(10): for y in range(10): - self.add_widget(Button( + self.add_widget(WidgetButton( x=x*0.1, y=y*0.1, width=0.1, height=0.1, normal_texture=normal_region, diff --git a/source/gui/scene/test/TestLabelScene.py b/source/gui/scene/test/TestLabelScene.py index 6b7ad1b..10009c5 100644 --- a/source/gui/scene/test/TestLabelScene.py +++ b/source/gui/scene/test/TestLabelScene.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING import pyglet from source.gui.scene.abc import AbstractScene -from source.gui.widget import Label +from source.gui.widget import WidgetLabel if TYPE_CHECKING: from source.gui.window import Window @@ -24,7 +24,7 @@ class TestLabelScene(AbstractScene): for x in range(10): for y in range(10): - self.add_widget(Label( + self.add_widget(WidgetLabel( x=x*0.1, y=y*0.1, width=0.1, height=0.1, text=f"{x}.{y}", diff --git a/source/gui/widget/Button.py b/source/gui/widget/WidgetButton.py similarity index 99% rename from source/gui/widget/Button.py rename to source/gui/widget/WidgetButton.py index 45ce227..681934a 100644 --- a/source/gui/widget/Button.py +++ b/source/gui/widget/WidgetButton.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from source.gui.scene.abc import AbstractScene -class Button(AbstractResizableWidget): +class WidgetButton(AbstractResizableWidget): def __init__(self, # position diff --git a/source/gui/widget/Image.py b/source/gui/widget/WidgetImage.py similarity index 60% rename from source/gui/widget/Image.py rename to source/gui/widget/WidgetImage.py index aa917d7..bf1856e 100644 --- a/source/gui/widget/Image.py +++ b/source/gui/widget/WidgetImage.py @@ -1,5 +1,5 @@ from source.gui.widget.abc import AbstractResizableWidget -class Image(AbstractResizableWidget): +class WidgetImage(AbstractResizableWidget): ... diff --git a/source/gui/widget/Input.py b/source/gui/widget/WidgetInput.py similarity index 98% rename from source/gui/widget/Input.py rename to source/gui/widget/WidgetInput.py index 34cefa4..2fe5f61 100644 --- a/source/gui/widget/Input.py +++ b/source/gui/widget/WidgetInput.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from source.gui.scene.abc import AbstractScene -class Input(AbstractResizableWidget): +class WidgetInput(AbstractResizableWidget): def __init__(self, # position diff --git a/source/gui/widget/Label.py b/source/gui/widget/WidgetLabel.py similarity index 83% rename from source/gui/widget/Label.py rename to source/gui/widget/WidgetLabel.py index 870de6f..2a9c562 100644 --- a/source/gui/widget/Label.py +++ b/source/gui/widget/WidgetLabel.py @@ -1,8 +1,8 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional import pyglet.text -from source.gui.widget.base import Sprite +from source.gui.widget.base import Sprite, Label from source.gui.widget.abc import AbstractResizableWidget from source.type import Percentage @@ -11,7 +11,7 @@ if TYPE_CHECKING: from source.gui.scene.abc import AbstractScene -class Label(AbstractResizableWidget): +class WidgetLabel(AbstractResizableWidget): def __init__(self, # position @@ -47,7 +47,7 @@ class Label(AbstractResizableWidget): super().__init__(x, y, width, height) - self._label = None + self._label: Optional[Label] = None self._label_kwargs = { "text": text, "font_name": font_name, @@ -78,7 +78,7 @@ class Label(AbstractResizableWidget): def on_window_added(self, window: "Window", scene: "AbstractScene"): super().on_window_added(window, scene) - self._label = pyglet.text.Label( + self._label = Label( x=self.x, y=self.y, width=self.width, height=self.height, **self._label_kwargs ) @@ -90,13 +90,5 @@ class Label(AbstractResizableWidget): ) def update_size(self): - self._label.x = self.x - self._label.y = self.y - self._label.width = self.width - self._label.height = self.height - - if self._background is not None: - self._background.x = self.x - self._background.y = self.y - self._background.width = self.width - self._background.height = self.height + self._label.update_size(self.x, self.y, self.width, self.height) + if self._background is not None: self._background.update_size(self.x, self.y, self.width, self.height) diff --git a/source/gui/widget/__init__.py b/source/gui/widget/__init__.py index 3c4cb60..d59f93c 100644 --- a/source/gui/widget/__init__.py +++ b/source/gui/widget/__init__.py @@ -1,4 +1,4 @@ -from .Label import Label -from .Input import Input -from .Button import Button -from .Image import Image +from .WidgetLabel import WidgetLabel +from .WidgetInput import WidgetInput +from .WidgetButton import WidgetButton +from .WidgetImage import WidgetImage diff --git a/source/gui/widget/base/Label.py b/source/gui/widget/base/Label.py index d9895e0..69b237e 100644 --- a/source/gui/widget/base/Label.py +++ b/source/gui/widget/base/Label.py @@ -4,6 +4,10 @@ import pyglet.text class Label(pyglet.text.Label): + """ + Similar to the pyglet Label, but allow to update the size from a function + """ + def update_size(self, x: int, y: int, width: Optional[int] = None, height: Optional[int] = None): self.x = x self.y = y diff --git a/source/gui/widget/base/Sprite.py b/source/gui/widget/base/Sprite.py index 0990740..c4a3ea0 100644 --- a/source/gui/widget/base/Sprite.py +++ b/source/gui/widget/base/Sprite.py @@ -2,6 +2,11 @@ import pyglet.sprite class Sprite(pyglet.sprite.Sprite): + """ + Similar to the pyglet Sprite, but allow to change the width and height directly + and update the size from a function + """ + def __init__(self, *args, width: int = None, height: int = None, **kwargs): super().__init__(*args, **kwargs) diff --git a/source/gui/window/Window.py b/source/gui/window/Window.py index 5a32fdd..1a05fd0 100644 --- a/source/gui/window/Window.py +++ b/source/gui/window/Window.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING, Callable, Type import pyglet.window @@ -8,17 +8,17 @@ if TYPE_CHECKING: class Window(pyglet.window.Window): # NOQA """ - Same as the original pyglet class, but allow to set a scene. + Similaire à la fenêtre de base de pyglet, mais permet d'ajouter des scènes. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # scene - self._scenes = [] + self._scenes: list["AbstractScene"] = [] # a dictionary linking a key pressed to the corresponding event function - self._on_key_held_events: dict[(int, int), Callable] = {} + self._on_key_held_events: dict[tuple[int, int], Callable] = {} # keys event handler self.keys_handler = pyglet.window.key.KeyStateHandler() @@ -26,115 +26,67 @@ class Window(pyglet.window.Window): # NOQA # scene system - def set_scene(self, *scenes: "AbstractScene"): + def set_scene(self, scenes_type: Type["AbstractScene"]) -> "AbstractScene": + """ + Clear all the previous scene and add a scene to the window + :param scenes_type: the class of the scene to add + :return: the scene + """ self.clear() - self.add_scene(*scenes) + return self.add_scene(scenes_type) - def add_scene(self, *scenes: "AbstractScene", priority: int = 0): - for scene in scenes: - self._scenes.insert(priority, scene) - scene.on_window_added(self) + def add_scene(self, scene_type: Type["AbstractScene"], priority: int = 0) -> "AbstractScene": + """ + Add a scene to the window + :param scene_type: the class of the scene to add + :param priority: the priority of the scene in the display + :return: the scene + """ + scene: "AbstractScene" = scene_type(self) + self._scenes.insert(priority, scene) + return scene - def remove_scene(self, *scenes: "AbstractScene"): + def remove_scene(self, *scenes: "AbstractScene") -> None: + """ + Remove scenes from the window + :param scenes: the scenes to remove + """ for scene in scenes: - scene.on_window_removed(self) self._scenes.remove(scene) - def clear_scene(self): + def clear_scene(self) -> None: + """ + Remove all the scenes from the window + """ self.remove_scene(*self._scenes) - # NOTE: it is too difficult to refactor all the event because : - # - There is no event "on_any_event" or equivalent - # - The list of all the event is not available in the source - # - Some event need special code like on_draw with the clear, on_resize with the super, ... + # event - def on_draw(self): # NOQA - self.clear() # clear the window to reset it - for scene in self._scenes: scene.on_draw(self) + def scene_event_wrapper(self, name: str, func: Callable) -> Callable: + """ + Un wrapper permettant d'appeler l'événement de toutes les scènes attachées. + :param name: nom de la fonction à appeler dans la scène. + :param func: fonction originale à entourer du wrapper + :return: une fonction appelant l'événement original ainsi que ceux des scènes. + """ - def on_resize(self, width: int, height: int): - super().on_resize(width, height) # this function is already defined and used - for scene in self._scenes: scene.on_resize(self, width, height) + def _func(*args, **kwargs): + func(*args, **kwargs) + for scene in self._scenes: getattr(scene, name, lambda *_, **__: "pass")(*args, **kwargs) - def on_hide(self): - for scene in self._scenes: scene.on_hide(self) + return _func - def on_show(self): - for scene in self._scenes: scene.on_show(self) + def __getattribute__(self, item: str): + """ + 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. + """ - def on_close(self): - super().close() # this function is already defined and used - for scene in self._scenes: scene.on_close(self) + # print(".", end="") - def on_expose(self): - for scene in self._scenes: scene.on_expose(self) + attribute = super().__getattribute__(item) - def on_activate(self): - for scene in self._scenes: scene.on_activate(self) + # si l'attribut est un évenement (commence par "on_"), alors renvoie le dans un wrapper + return self.scene_event_wrapper(item, attribute) if item.startswith("on_") else attribute - def on_deactivate(self): - for scene in self._scenes: scene.on_deactivate(self) - - def on_text(self, char: str): - for scene in self._scenes: scene.on_text(self, char) - - def on_move(self, x: int, y: int): - for scene in self._scenes: scene.on_move(self, x, y) - - def on_context_lost(self): - for scene in self._scenes: scene.on_context_lost(self) - - def on_context_state_lost(self): - for scene in self._scenes: scene.on_context_state_lost(self) - - def on_key_press(self, symbol: int, modifiers: int): - super().on_key_press(symbol, modifiers) # this function is already defined and used - - # this allows the on_key_held event to be called every frame after a press - pyglet.clock.schedule(self.get_on_key_held_function(symbol, modifiers)) - - for scene in self._scenes: scene.on_key_press(self, symbol, modifiers) - - def on_key_release(self, symbol: int, modifiers: int): - # this allows the on_key_held event to stop after the key is released - pyglet.clock.unschedule(self.get_on_key_held_function(symbol, modifiers)) - - for scene in self._scenes: scene.on_key_release(self, symbol, modifiers) - - def get_on_key_held_function(self, symbol: int, modifiers: int): - key: tuple[int, int] = (symbol, modifiers) - - if key not in self._on_key_held_events: - self._on_key_held_events[key] = lambda dt: self.on_key_held(dt, symbol, modifiers) - - return self._on_key_held_events[key] - - def on_key_held(self, dt: float, symbol: int, modifiers: int): - for scene in self._scenes: scene.on_key_held(self, dt, symbol, modifiers) - - def on_mouse_enter(self, x: int, y: int): - for scene in self._scenes: scene.on_mouse_enter(self, x, y) - - def on_mouse_leave(self, x: int, y: int): - for scene in self._scenes: scene.on_mouse_leave(self, x, y) - - def on_text_motion(self, motion: int): - for scene in self._scenes: scene.on_text_motion(self, motion) - - def on_text_motion_select(self, motion: int): - for scene in self._scenes: scene.on_text_motion_select(self, motion) - - def on_mouse_motion(self, x: int, y: int, dx: int, dy: int): - for scene in self._scenes: scene.on_mouse_motion(self, x, y, dx, dy) - - def on_mouse_press(self, x: int, y: int, button: int, modifiers: int): - for scene in self._scenes: scene.on_mouse_press(self, x, y, button, modifiers) - - def on_mouse_release(self, x: int, y: int, button: int, modifiers: int): - for scene in self._scenes: scene.on_mouse_release(self, x, y, button, modifiers) - - def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): - for scene in self._scenes: scene.on_mouse_drag(self, x, y, dx, dy, buttons, modifiers) - - def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float): - for scene in self._scenes: scene.on_mouse_scroll(self, x, y, scroll_x, scroll_y)