added more widgets

This commit is contained in:
Faraphel 2023-02-09 18:26:03 +01:00
parent 920914ad8c
commit 3cb8a94c13
16 changed files with 162 additions and 228 deletions

View file

@ -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)

View file

@ -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()

View file

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

View file

@ -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)

View file

@ -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
"""

View file

@ -1 +1 @@
from .FPSScene import FPSAbstractScene
from .FPSScene import FPSScene

View file

@ -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,

View file

@ -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}",

View file

@ -12,7 +12,7 @@ if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene
class Button(AbstractResizableWidget):
class WidgetButton(AbstractResizableWidget):
def __init__(self,
# position

View file

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

View file

@ -10,7 +10,7 @@ if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene
class Input(AbstractResizableWidget):
class WidgetInput(AbstractResizableWidget):
def __init__(self,
# position

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)