rewrote the base of the Window, Scene and Widget class to make them as clean as possible

This commit is contained in:
Faraphel 2023-02-09 23:56:04 +01:00
parent 3cb8a94c13
commit 432a311b76
31 changed files with 228 additions and 843 deletions

14
NOTE.md Normal file
View file

@ -0,0 +1,14 @@
- Connexion entre les joueurs
- Envoie des paramètres à l'autre joueur (largeur longueur du plateau, nombre de bateau et leur taille, ...)
- Phase de placement des bateaux
- Confirmer l'emplacement des bateaux (envoie des données signalant la confirmation)
- Joueur 1 sélectionne une case (envoie des données à l'autre joueur)
- Joueur 2 envoie la valeur de retour de board.bomb après les données de joueur 1
- Joueur 2 sélectionne une case
- Joueur 1 envoie la valeur de retour également
...
- Joueur 1 gagne (envoie des données de victoire à l'autre joueur)
- Joueur 1 et Joueur 2 s'envoie mutuellement leurs plateaux pour voir les cases restantes de l'autre
- Proposition de rejouer ?

View file

@ -1,16 +1,23 @@
import pyglet import pyglet
from source.gui.scene import MainMenuScene from source.gui.scene import Scene
from source.gui.scene.debug import FPSScene from source.gui.widget import Text, FPSDisplay
from source.gui.scene.test import TestButtonScene, TestLabelScene
from source.gui.window import Window from source.gui.window import Window
# Test Scene
class TestScene(Scene):
def __init__(self, window: "Window", *args, **kwargs):
super().__init__(window, *args, **kwargs)
self.add_widget(FPSDisplay)
self.add_widget(Text, text="Hello World !")
# Create a new window # Create a new window
window = Window(resizable=True, vsync=True) window = Window(resizable=True, vsync=False)
# window.add_scene(TestButtonScene()) window.add_scene(TestScene)
# window.add_scene(TestLabelScene())
window.add_scene(MainMenuScene())
window.add_scene(FPSScene())
# Start the event loop # Start the event loop
pyglet.app.run(interval=0) pyglet.app.run(interval=0)

View file

@ -1,2 +1,2 @@
pyglet pyglet==2.0.4
numpy numpy==1.24.2

View file

@ -4,5 +4,5 @@ ne sont pas déjà disponible nativement dans cette librairie, tel que :
- Les scènes (attachable à une fenêtre afin de changer rapidement de menu / d'interface) - Les scènes (attachable à une fenêtre afin de changer rapidement de menu / d'interface)
- Les widgets (attachable à des scènes afin de rajouter des boutons, des textes, ...) - Les widgets (attachable à des scènes afin de rajouter des boutons, des textes, ...)
Ces éléments permettent de plus facilement gérer le redimentionnement de la fenêtre tout en Ces éléments permettent de plus facilement gérer le redimensionnement de la fenêtre tout en
restant suffisament rapide pour ne pas causer de problème de ralentissement. restant suffisament rapide pour ne pas causer de problème de ralentissement.

View file

View file

@ -1,72 +0,0 @@
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()

82
source/gui/scene/Scene.py Normal file
View file

@ -0,0 +1,82 @@
from functools import lru_cache
from typing import TYPE_CHECKING, Callable, Type, Any
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.widget import Widget
class Scene:
"""
A scene that can be attached to a window.
It allows to switch the whole comportment of the window in a simpler way.
It can react to any "on_" event from the window.
"""
def __init__(self, window: "Window", *args, **kwargs):
self.window = window
self._widgets: set["Widget"] = set()
# Widget Managing
def add_widget(self, widget_class: Type["Widget"], *widget_args, **widget_kwargs) -> "Widget":
"""
Add a widget to the scene.
:widget_class: the class of the widget to add.
:widget_args: args for the creation of the widget object.
:widget_kwargs: kwargs for the creation of the widget object.
:return: the new created widget.
"""
widget: "Widget" = widget_class(self, *widget_args, **widget_kwargs)
self._widgets.add(widget)
return widget
def remove_widget(self, widget: "Widget") -> None:
"""
Remove a widget from the scene.
:scene: the widget to remove.
"""
self._widgets.remove(widget)
def clear_widget(self) -> None:
"""
Clear the scene from all the widgets.
"""
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.
try:
func = super().__getattribute__(item)
except AttributeError:
func = lambda *_, **__: "pass" # NOQA E731
def _func(*args, **kwargs) -> None:
func(*args, **kwargs)
for widget in self._widgets:
getattr(widget, item, lambda *_, **__: "pass")(*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 +1 @@
from .MainMenuScene import MainMenuScene from .Scene import Scene

View file

@ -1,33 +0,0 @@
from abc import ABC
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.widget.abc import AbstractWidget
class AbstractScene(ABC):
"""
An abstract scene that can be attached to a window.
Can be used to create a menu, an overlay, ...
"""
def __init__(self, window: "Window"):
self._widgets: list["AbstractWidget"] = [] # the lists of the widgets in the scene
self._window: "Window" = window # the window where the scene is attached
# widget
def add_widget(self, *widgets: "AbstractWidget", priority: int = 0) -> None:
for widget in widgets:
self._widgets.insert(priority, widget)
widget.on_scene_added(self)
def remove_widget(self, *widgets: "AbstractWidget") -> None:
for widget in widgets:
widget.on_scene_removed(self)
self._widgets.remove(widget)
def clear_widget(self) -> None:
self.remove_widget(*self._widgets)

View file

@ -1 +0,0 @@
from .AbstractScene import AbstractScene

View file

@ -1,24 +0,0 @@
from typing import TYPE_CHECKING, Optional
import pyglet.window
from source.gui.scene.abc import AbstractScene
if TYPE_CHECKING:
from source.gui.window import Window
class FPSScene(AbstractScene):
"""
A base scene that can be used as an overlay to display the FPS
"""
def __init__(self):
super().__init__()
self._fps_display: Optional[pyglet.window.FPSDisplay] = None
def on_window_added(self, window: "Window"):
self._fps_display = pyglet.window.FPSDisplay(window)
def on_draw(self, window: "Window"):
self._fps_display.draw()

View file

@ -1 +0,0 @@
from .FPSScene import FPSScene

View file

@ -1,59 +0,0 @@
from typing import TYPE_CHECKING
import pyglet
from source.gui.scene.abc import AbstractScene
from source.gui.widget import WidgetButton
if TYPE_CHECKING:
from source.gui.window import Window
class TestButtonScene(AbstractScene):
"""
A scene used to test the Button widget
"""
def __init__(self):
super().__init__()
self.button_atlas = None
self.background_batch = None
self.label_batch = None
def on_window_added(self, window: "Window") -> None:
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()
for x in range(10):
for y in range(10):
self.add_widget(WidgetButton(
x=x*0.1, y=y*0.1, width=0.1, height=0.1,
normal_texture=normal_region,
hover_texture=hover_region,
click_texture=click_region,
label_text=f"TEST TEST CENTERING {x}.{y}",
label_multiline=True,
label_batch=self.label_batch,
background_batch=self.background_batch,
))
super().on_window_added(window)
def on_draw(self, window: "Window"):
super().on_draw(window)
self.background_batch.draw()
self.label_batch.draw()

View file

@ -1,40 +0,0 @@
from typing import TYPE_CHECKING
import pyglet
from source.gui.scene.abc import AbstractScene
from source.gui.widget import WidgetLabel
if TYPE_CHECKING:
from source.gui.window import Window
class TestLabelScene(AbstractScene):
"""
A scene used to test the Label widget
"""
def __init__(self):
super().__init__()
self.label_batch = None
def on_window_added(self, window: "Window") -> None:
self.label_batch = pyglet.graphics.Batch()
for x in range(10):
for y in range(10):
self.add_widget(WidgetLabel(
x=x*0.1, y=y*0.1, width=0.1, height=0.1,
text=f"{x}.{y}",
label_batch=self.label_batch,
))
super().on_window_added(window)
def on_draw(self, window: "Window"):
super().on_draw(window)
self.label_batch.draw()

View file

@ -1,3 +0,0 @@
from .TestButtonScene import TestButtonScene
from .TestLabelScene import TestLabelScene

View file

@ -0,0 +1,22 @@
from source.gui.widget import Widget
from typing import TYPE_CHECKING
import pyglet
if TYPE_CHECKING:
from source.gui.scene import Scene
class FPSDisplay(Widget):
"""
A widget that display the current FPS of the scene's window
"""
def __init__(self, scene: "Scene", *args, **kwargs):
super().__init__(scene, *args, **kwargs)
self.fps_display = pyglet.window.FPSDisplay(scene.window)
def on_draw(self):
self.fps_display.draw()

22
source/gui/widget/Text.py Normal file
View file

@ -0,0 +1,22 @@
from typing import TYPE_CHECKING
from source.gui.widget import Widget
import pyglet
if TYPE_CHECKING:
from source.gui.scene import Scene
class Text(Widget):
"""
A widget that display a text
"""
def __init__(self, scene: "Scene", *args, **kwargs):
super().__init__(scene, *args, **kwargs)
self.label = pyglet.text.Label(*args, **kwargs)
def on_draw(self):
self.label.draw()

View file

@ -0,0 +1,15 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from source.gui.scene import Scene
class Widget:
"""
A Widget that can be attached to a scene.
It can react to any "on_" event from the scene.
"""
def __init__(self, scene: "Scene", *args, **kwargs):
self.scene = scene

View file

@ -1,168 +0,0 @@
from typing import TYPE_CHECKING, Callable, Optional
import pyglet.image
from source.gui.widget.base import Sprite, Label
from source.gui.widget.abc import AbstractResizableWidget
from source.type import Percentage
from source.utils import in_bbox
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.scene.abc import AbstractScene
class WidgetButton(AbstractResizableWidget):
def __init__(self,
# position
x: int | Percentage,
y: int | Percentage,
width: int | Percentage,
height: int | Percentage,
# background
normal_texture: pyglet.image.AbstractImage,
hover_texture: pyglet.image.AbstractImage = None,
click_texture: pyglet.image.AbstractImage = None,
# label
label_text: str = "",
label_font_name: str = None,
label_font_size: int = None,
label_bold: bool = False,
label_italic: bool = False,
label_stretch: bool = False,
label_color: tuple[int, int, int, int] = (255, 255, 255, 255),
label_align: str = "center",
label_multiline: bool = False,
label_dpi: int = None,
label_rotation: int = 0,
# callback function
on_press: Callable = None,
on_release: Callable = None,
# batch
label_batch: pyglet.graphics.Batch = None,
background_batch: pyglet.graphics.Batch = None,
# group
label_group: pyglet.graphics.Group = None,
background_group: pyglet.graphics.Group = None,
):
super().__init__(x, y, width, height)
self._normal_texture = normal_texture
self._hover_texture = hover_texture
self._click_texture = click_texture
self.on_press = on_press
self.on_release = on_release
self._label: Optional[Label] = None
self._label_kwargs = {
"text": label_text,
"font_name": label_font_name,
"font_size": label_font_size,
"bold": label_bold,
"italic": label_italic,
"stretch": label_stretch,
"color": label_color,
"align": label_align,
"multiline": label_multiline,
"dpi": label_dpi,
"rotation": label_rotation,
"batch": label_batch,
"group": label_group,
}
self._background: Optional[Sprite] = None
self._background_kwargs = {
"batch": background_batch,
"group": background_group,
}
self._hover = False
self._click = False
def on_window_added(self, window: "Window", scene: "AbstractScene"):
super().on_window_added(window, scene)
self._label = Label(
x=self.x + self.width / 2, y=self.y + self.height / 2, width=self.width,
anchor_x="center", anchor_y="center",
**self._label_kwargs
)
self._background = Sprite(
self._normal_texture,
x=self.x, y=self.y, width=self.width, height=self.height,
**self._background_kwargs
)
# button update
@property
def hover(self) -> bool:
return self._hover
@hover.setter
def hover(self, hover: bool) -> None:
self._hover = hover
self.update_sprite()
@property
def click(self) -> bool:
return self._click
@click.setter
def click(self, click: bool) -> None:
self._click = click
self.update_sprite()
@property
def background_texture(self) -> pyglet.image.AbstractImage:
return (
self._click_texture if self._click else
self._hover_texture if self._hover else
self._normal_texture
)
def update_sprite(self):
self._background.image = self.background_texture
def update_size(self):
self._background.update_size(self.x, self.y, self.width, self.height)
self._label.update_size(self.x + self.width // 2, self.y + self.height // 2, self.width)
# event
def on_mouse_motion(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int):
self.hover = in_bbox((x, y), self.bbox)
def on_mouse_press(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int):
if not in_bbox((x, y), self.bbox): return
self.click = True
if self.on_press is not None:
self.on_press(self, window, scene, x, y, button, modifiers)
def on_mouse_release(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int):
old_click = self.click
self.click = False
if not in_bbox((x, y), self.bbox): return
# if this button was the one hovered when the click was pressed
if old_click and self.on_release is not None:
self.on_release(self, window, scene, x, y, button, modifiers)
# TODO: on_resize seem really slow
# TODO: make the percentage dynamic or non dynamic

View file

@ -1,5 +0,0 @@
from source.gui.widget.abc import AbstractResizableWidget
class WidgetImage(AbstractResizableWidget):
...

View file

@ -1,74 +0,0 @@
from typing import TYPE_CHECKING
import pyglet.text
from source.gui.widget.abc import AbstractResizableWidget
from source.type import Percentage
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.scene.abc import AbstractScene
class WidgetInput(AbstractResizableWidget):
def __init__(self,
# position
x: int | Percentage,
y: int | Percentage,
width: int | Percentage,
height: int | Percentage,
# background
texture: pyglet.image.AbstractImage,
# label
label_text: str = "",
label_font_name: str = None,
label_font_size: int = None,
label_bold: bool = False,
label_italic: bool = False,
label_stretch: bool = False,
label_color: tuple[int, int, int, int] = (255, 255, 255, 255),
label_align: str = "center",
label_multiline: bool = False,
label_dpi: int = None,
label_rotation: int = 0,
# batch
label_batch: pyglet.graphics.Batch = None,
background_batch: pyglet.graphics.Batch = None,
# group
label_group: pyglet.graphics.Group = None,
background_group: pyglet.graphics.Group = None,
):
super().__init__(x, y, width, height)
self._label = None
self._label_kwargs = {
"text": label_text,
"font_name": label_font_name,
"font_size": label_font_size,
"bold": label_bold,
"italic": label_italic,
"stretch": label_stretch,
"color": label_color,
"align": label_align,
"multiline": label_multiline,
"dpi": label_dpi,
"rotation": label_rotation,
"batch": label_batch,
"group": label_group,
}
self._background_kwargs = {
"img": texture,
"batch": background_batch,
"group": background_group,
}
def on_window_added(self, window: "Window", scene: "AbstractScene"):
...

View file

@ -1,94 +0,0 @@
from typing import TYPE_CHECKING, Optional
import pyglet.text
from source.gui.widget.base import Sprite, Label
from source.gui.widget.abc import AbstractResizableWidget
from source.type import Percentage
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.scene.abc import AbstractScene
class WidgetLabel(AbstractResizableWidget):
def __init__(self,
# position
x: int | Percentage,
y: int | Percentage,
width: int | Percentage = None,
height: int | Percentage = None,
# label
text: str = "",
font_name: str = None,
font_size: int = None,
bold: bool = False,
italic: bool = False,
stretch: bool = False,
color: tuple[int, int, int, int] = (255, 255, 255, 255),
align: str = "center",
multiline: bool = False,
dpi: int = None,
rotation: int = 0,
# background
texture: pyglet.image.AbstractImage = None,
# batch
label_batch: pyglet.graphics.Batch = None,
background_batch: pyglet.graphics.Batch = None,
# group
label_group: pyglet.graphics.Group = None,
background_group: pyglet.graphics.Group = None
):
super().__init__(x, y, width, height)
self._label: Optional[Label] = None
self._label_kwargs = {
"text": text,
"font_name": font_name,
"font_size": font_size,
"bold": bold,
"italic": italic,
"stretch": stretch,
"color": color,
"align": align,
"multiline": multiline,
"dpi": dpi,
"rotation": rotation,
"batch": label_batch,
"group": label_group,
}
if texture is not None and width is None or height is None:
raise ValueError("You need to set a width and a height to create a Label with a background !")
self._background = None
self._background_kwargs = {
"img": texture,
"batch": background_batch,
"group": background_group,
} if texture is not None else None
def on_window_added(self, window: "Window", scene: "AbstractScene"):
super().on_window_added(window, scene)
self._label = Label(
x=self.x, y=self.y, width=self.width, height=self.height,
**self._label_kwargs
)
if self._background_kwargs is not None:
self._background = Sprite(
x=self.x, y=self.y, width=self.width, height=self.height,
**self._background_kwargs
)
def update_size(self):
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)

View file

@ -1,4 +1,3 @@
from .WidgetLabel import WidgetLabel from .Widget import Widget
from .WidgetInput import WidgetInput from .Text import Text
from .WidgetButton import WidgetButton from .FPSDisplay import FPSDisplay
from .WidgetImage import WidgetImage

View file

@ -1,50 +0,0 @@
from abc import ABC
from source.gui.widget.abc import AbstractWidget
from source.type import BBox
class AbstractBoxWidget(AbstractWidget, ABC):
def __init__(self, x: int, y: int, width: int, height: int):
self._x = x
self._y = y
self._width = width
self._height = height
# getter and setter, allow subclass to react when one of these value is changed
@property
def x(self):
return self._x
@x.setter
def x(self, x: int) -> None:
self._x = x
@property
def y(self):
return self._y
@y.setter
def y(self, y: int) -> None:
self._y = y
@property
def width(self):
return self._width
@width.setter
def width(self, width: int) -> None:
self._width = width
@property
def height(self):
return self._height
@height.setter
def height(self, height: int) -> None:
self._height = height
@property
def bbox(self) -> BBox:
return self.x, self.y, self.x + self.width, self.y + self.height

View file

@ -1,50 +0,0 @@
from abc import ABC
from typing import Optional, TYPE_CHECKING
from source.gui.widget.abc import AbstractBoxWidget
from source.type import Percentage
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.scene.abc import AbstractScene
class AbstractResizableWidget(AbstractBoxWidget, ABC):
def __init__(self, x: int | Percentage, y: int | Percentage, width: int | Percentage, height: int | Percentage):
super().__init__(x, y, width, height)
self._window_width: Optional[int] = None
self._window_height: Optional[int] = None
# getter / setter
def on_window_added(self, window: "Window", scene: "AbstractScene"):
self._window_width = window.width
self._window_height = window.height
@property
def x(self) -> int:
return self._x * self._window_width if isinstance(self._x, Percentage) else self._x
@property
def y(self) -> int:
return self._y * self._window_height if isinstance(self._y, Percentage) else self._y
@property
def width(self) -> int:
return self._width * self._window_width if isinstance(self._width, Percentage) else self._width
@property
def height(self) -> int:
return self._height * self._window_height if isinstance(self._height, Percentage) else self._height
# event
def update_size(self): pass
def on_resize(self, window: "Window", scene: "AbstractScene", width: int, height: int):
self._window_width = width
self._window_height = height
self.update_size()

View file

@ -1,51 +0,0 @@
from abc import ABC
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene
from source.gui.window import Window
class AbstractWidget(ABC):
"""
This class represent a widget that can be attached to a Scene.
It can be used to create a button, a label, ...
"""
# widget event
def on_scene_added(self, scene: "AbstractScene"): pass
def on_scene_removed(self, scene: "AbstractScene"): pass
# scene event
def on_window_added(self, window: "Window", scene: "AbstractScene"): pass
def on_window_removed(self, window: "Window", scene: "AbstractScene"): pass
# global event
def on_draw(self, window: "Window", scene: "AbstractScene"): pass
def on_resize(self, window: "Window", scene: "AbstractScene", width: int, height: int): pass
def on_hide(self, window: "Window", scene: "AbstractScene"): pass
def on_show(self, window: "Window", scene: "AbstractScene"): pass
def on_close(self, window: "Window", scene: "AbstractScene"): pass
def on_expose(self, window: "Window", scene: "AbstractScene"): pass
def on_activate(self, window: "Window", scene: "AbstractScene"): pass
def on_deactivate(self, window: "Window", scene: "AbstractScene"): pass
def on_text(self, window: "Window", scene: "AbstractScene", char: str): pass
def on_move(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
def on_context_lost(self, window: "Window", scene: "AbstractScene"): pass
def on_context_state_lost(self, window: "Window", scene: "AbstractScene"): pass
def on_key_press(self, window: "Window", scene: "AbstractScene", symbol: int, modifiers: int): pass
def on_key_release(self, window: "Window", scene: "AbstractScene", symbol: int, modifiers: int): pass
def on_key_held(self, window: "Window", scene: "AbstractScene", dt: float, symbol: int, modifiers: int): pass
def on_mouse_enter(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
def on_mouse_leave(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
def on_text_motion(self, window: "Window", scene: "AbstractScene", motion: int): pass
def on_text_motion_select(self, window: "Window", scene: "AbstractScene", motion: int): pass
def on_mouse_motion(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int): pass
def on_mouse_press(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_release(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_drag(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): pass
def on_mouse_scroll(self, window: "Window", scene: "AbstractScene", x: int, y: int, scroll_x: float, scroll_y: float): pass

View file

@ -1,3 +0,0 @@
from .AbstractWidget import AbstractWidget
from .AbstractBoxWidget import AbstractBoxWidget
from .AbstractResizableWidget import AbstractResizableWidget

View file

@ -1,15 +0,0 @@
from typing import Optional
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
self.width = width
self.height = height

View file

@ -1,35 +0,0 @@
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)
self._orig_width: int = self.width
self._orig_height: int = self.height
if width is not None: self.width = width
if height is not None: self.height = height
# property
@pyglet.sprite.Sprite.width.setter
def width(self, width: int):
self.scale_x = width / self._orig_width
@pyglet.sprite.Sprite.height.setter
def height(self, height: int):
self.scale_y = height / self._orig_height
# other event
def update_size(self, x: int, y: int, width: int, height: int):
self.x = x
self.y = y
self.width = width
self.height = height

View file

@ -1,2 +0,0 @@
from .Label import Label
from .Sprite import Sprite

View file

@ -1,92 +1,96 @@
from typing import TYPE_CHECKING, Callable, Type from functools import lru_cache
from typing import Type, Callable, TYPE_CHECKING, Any
import pyglet
import pyglet.window
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene from source.gui.scene import Scene
class Window(pyglet.window.Window): # NOQA class Window(pyglet.window.Window): # NOQA
""" """
Similaire à la fenêtre de base de pyglet, mais permet d'ajouter des scènes. A window. Based on the pyglet window object.
Scene can be added to the window
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# scene self._scenes: set["Scene"] = set()
self._scenes: list["AbstractScene"] = []
# a dictionary linking a key pressed to the corresponding event function # Scene Managing
self._on_key_held_events: dict[tuple[int, int], Callable] = {}
# keys event handler def set_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene":
self.keys_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.keys_handler)
# scene system
def set_scene(self, scenes_type: Type["AbstractScene"]) -> "AbstractScene":
""" """
Clear all the previous scene and add a scene to the window Set the scene of the window.
:param scenes_type: the class of the scene to add :scene_class: the class of the scene to add.
:return: the scene :scene_args: args for the creation of the scene object.
:scene_kwargs: kwargs for the creation of the scene object.
:return: the new created scene.
""" """
self.clear()
return self.add_scene(scenes_type)
def add_scene(self, scene_type: Type["AbstractScene"], priority: int = 0) -> "AbstractScene": self.clear_scene()
return self.add_scene(scene_class, *scene_args, **scene_kwargs)
def add_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene":
""" """
Add a scene to the window Add a scene of the window.
:param scene_type: the class of the scene to add :scene_class: the class of the scene to add.
:param priority: the priority of the scene in the display :scene_args: args for the creation of the scene object.
:return: the scene :scene_kwargs: kwargs for the creation of the scene object.
:return: the new created scene.
""" """
scene: "AbstractScene" = scene_type(self)
self._scenes.insert(priority, scene) scene: "Scene" = scene_class(window=self, *scene_args, **scene_kwargs)
self._scenes.add(scene)
return scene return scene
def remove_scene(self, *scenes: "AbstractScene") -> None: def remove_scene(self, scene: "Scene") -> None:
""" """
Remove scenes from the window Remove a scene from the window.
:param scenes: the scenes to remove :scene: the scene to remove.
""" """
for scene in scenes:
self._scenes.remove(scene) self._scenes.remove(scene)
def clear_scene(self) -> None: def clear_scene(self) -> None:
""" """
Remove all the scenes from the window Clear the window from all the scenes.
""" """
self.remove_scene(*self._scenes)
# event self._scenes.clear()
def scene_event_wrapper(self, name: str, func: Callable) -> Callable: # Base Event
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. 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 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. :return: une fonction appelant l'événement original ainsi que ceux des scènes.
""" """
def _func(*args, **kwargs): func = super().__getattribute__(item)
def _func(*args, **kwargs) -> None:
func(*args, **kwargs) func(*args, **kwargs)
for scene in self._scenes: getattr(scene, name, lambda *_, **__: "pass")(*args, **kwargs) for scene in self._scenes:
getattr(scene, item)(*args, **kwargs)
return _func return _func
def __getattribute__(self, item: str): 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. 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é :param item: nom de l'attribut recherché
:return: l'attribut de l'objet correspondant. :return: l'attribut de l'objet correspondant.
""" """
# print(".", end="") # 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)
attribute = super().__getattribute__(item)
# 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