implemented a Scene system for pyglet to simplify the creation of multiple different interface
This commit is contained in:
parent
445ade8c33
commit
69cf45bd72
8 changed files with 251 additions and 31 deletions
0
gui/__init__.py
Normal file
0
gui/__init__.py
Normal file
23
gui/scene/FPSCounterScene.py
Normal file
23
gui/scene/FPSCounterScene.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from typing import Optional
|
||||
|
||||
import pyglet
|
||||
|
||||
from gui.scene import Scene
|
||||
from gui.window import Window
|
||||
|
||||
|
||||
class FPSCounterScene(Scene):
|
||||
"""
|
||||
This scene represent a simple FPS Counter.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.fps_display: Optional[pyglet.window.FPSDisplay] = None
|
||||
|
||||
def on_window_added(self, window: Window):
|
||||
# the fps display need to be defined here because it is the moment where the window is first accessible
|
||||
self.fps_display = pyglet.window.FPSDisplay(window=window)
|
||||
|
||||
def on_draw(self, window: Window) -> None:
|
||||
self.fps_display.draw()
|
||||
|
38
gui/scene/HelloWorldScene.py
Normal file
38
gui/scene/HelloWorldScene.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import pyglet
|
||||
|
||||
from gui.scene import Scene
|
||||
from gui.window import Window
|
||||
|
||||
|
||||
class HelloWorldScene(Scene):
|
||||
"""
|
||||
This scene is a simple Hello World.
|
||||
|
||||
You can type anything with the keyboard or use backspace to remove characters.
|
||||
The text is centered on the screen.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._backspace_hold_frame: int = 0
|
||||
|
||||
self.label = pyglet.text.Label(
|
||||
"Hello World !",
|
||||
anchor_x="center",
|
||||
anchor_y="center"
|
||||
)
|
||||
|
||||
def on_draw(self, window: Window) -> None:
|
||||
if window.keys[pyglet.window.key.BACKSPACE]:
|
||||
if self._backspace_hold_frame % 5 == 0: self.label.text = self.label.text[:-1]
|
||||
self._backspace_hold_frame += 1
|
||||
else:
|
||||
self._backspace_hold_frame = 0
|
||||
|
||||
self.label.draw()
|
||||
|
||||
def on_resize(self, window: Window, width: int, height: int) -> None:
|
||||
self.label.x = width // 2
|
||||
self.label.y = height // 2
|
||||
|
||||
def on_text(self, window: Window, char: str):
|
||||
self.label.text += char
|
43
gui/scene/Scene.py
Normal file
43
gui/scene/Scene.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import pyglet.event
|
||||
|
||||
from gui.window import Window
|
||||
|
||||
|
||||
class Scene(pyglet.event.EventDispatcher):
|
||||
"""
|
||||
This class represent a scene that can be applied to a pyglet window.
|
||||
|
||||
The scene can represent anything like the main menu, the game, the
|
||||
options' menu, the multiplayer menu, ...
|
||||
"""
|
||||
|
||||
def on_window_added(self, window: Window): pass # when the Scene is added to a window
|
||||
def on_window_removed(self, window: Window): pass # when the Scene is removed from a window
|
||||
|
||||
def on_draw(self, window: Window): pass
|
||||
def on_resize(self, window: Window, width: int, height: int): pass
|
||||
def on_hide(self, window: Window): pass
|
||||
def on_show(self, window: Window): pass
|
||||
def on_close(self, window: Window): pass
|
||||
def on_expose(self, window: Window): pass
|
||||
def on_activate(self, window: Window): pass
|
||||
def on_deactivate(self, window: Window): pass
|
||||
def on_text(self, window: Window, char: str): pass
|
||||
def on_move(self, window: Window, x: int, y: int): pass
|
||||
def on_context_lost(self, window: Window): pass
|
||||
def on_context_state_lost(self, window: Window): pass
|
||||
def on_key_press(self, window: Window, symbol: int, modifiers: int): pass
|
||||
def on_key_release(self, window: Window, symbol: int, modifiers: int): pass
|
||||
def on_mouse_enter(self, window: Window, x: int, y: int): pass
|
||||
def on_mouse_leave(self, window: Window, x: int, y: int): pass
|
||||
def on_text_motion(self, window: Window, motion: int): pass
|
||||
def on_text_motion_select(self, window: Window, motion: int): pass
|
||||
def on_mouse_motion(self, window: Window, x: int, y: int, dx: int, dy: int): pass
|
||||
def on_mouse_press(self, window: Window, x: int, y: int, button: int, modifiers: int): pass
|
||||
def on_mouse_release(self, window: Window, x: int, y: int, button: int, modifiers: int): pass
|
||||
def on_mouse_drag(self, window: Window, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): pass
|
||||
def on_mouse_scroll(self, window: Window, x: int, y: int, scroll_x: float, scroll_y: float): pass
|
||||
|
||||
|
||||
|
||||
|
3
gui/scene/__init__.py
Normal file
3
gui/scene/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .Scene import Scene
|
||||
from .HelloWorldScene import HelloWorldScene
|
||||
from .FPSCounterScene import FPSCounterScene
|
137
gui/window/Window.py
Normal file
137
gui/window/Window.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
from typing import Optional
|
||||
|
||||
import pyglet.window
|
||||
|
||||
from gui.scene import Scene
|
||||
|
||||
|
||||
class Window(pyglet.window.Window): # NOQA - pycharm think pyglet window is abstract
|
||||
"""
|
||||
This class represent a Window based on the pyglet Window object.
|
||||
|
||||
Allow to use a "Scene" system to create very different interface like
|
||||
a main menu, options menu, ... that can overlay each other without
|
||||
putting everything in the window code.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, scenes: Optional[Scene] = None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._scenes: list[Scene] = [] if scenes is None else scenes
|
||||
|
||||
# add a keys handler to the window
|
||||
self.keys = pyglet.window.key.KeyStateHandler()
|
||||
self.push_handlers(self.keys)
|
||||
|
||||
# scene methods
|
||||
|
||||
def set_scene(self, scene: Scene) -> None:
|
||||
"""
|
||||
Set the scene of the window
|
||||
:param scene: the scene to set
|
||||
"""
|
||||
self._scenes = [scene]
|
||||
scene.on_window_added(self)
|
||||
|
||||
def clear_scene(self) -> None:
|
||||
"""
|
||||
Clear all the scenes of the window
|
||||
"""
|
||||
for scene in self._scenes: scene.on_window_removed(self)
|
||||
self._scenes.clear()
|
||||
|
||||
def add_scene(self, scene: Scene, priority: int = 0) -> None:
|
||||
"""
|
||||
Add a scene to the window
|
||||
:param scene: the scene to add
|
||||
:param priority: the priority level of the scene. The higher, the more the scene will be drawn on top
|
||||
"""
|
||||
self._scenes.insert(priority, scene)
|
||||
scene.on_window_added(self)
|
||||
|
||||
def remove_scene(self, scene: Scene) -> None:
|
||||
"""
|
||||
Remove a scene from the window
|
||||
:param scene: the scene to remove
|
||||
"""
|
||||
scene.on_window_removed(self)
|
||||
self._scenes.remove(scene)
|
||||
|
||||
# window event methods
|
||||
|
||||
# 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, ...
|
||||
|
||||
def on_draw(self): # NOQA
|
||||
self.clear() # clear the window to reset it
|
||||
for scene in self._scenes: scene.on_draw(self)
|
||||
|
||||
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 on_hide(self):
|
||||
for scene in self._scenes: scene.on_hide(self)
|
||||
|
||||
def on_show(self):
|
||||
for scene in self._scenes: scene.on_show(self)
|
||||
|
||||
def on_close(self):
|
||||
super().close() # this function is already defined and used
|
||||
for scene in self._scenes: scene.on_close(self)
|
||||
|
||||
def on_expose(self):
|
||||
for scene in self._scenes: scene.on_expose(self)
|
||||
|
||||
def on_activate(self):
|
||||
for scene in self._scenes: scene.on_activate(self)
|
||||
|
||||
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
|
||||
for scene in self._scenes: scene.on_key_press(self, symbol, modifiers)
|
||||
|
||||
def on_key_release(self, symbol: int, modifiers: int):
|
||||
for scene in self._scenes: scene.on_key_release(self, 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)
|
1
gui/window/__init__.py
Normal file
1
gui/window/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .Window import Window
|
37
main.pyw
37
main.pyw
|
@ -1,39 +1,14 @@
|
|||
import pyglet
|
||||
|
||||
from gui.window import Window
|
||||
from gui.scene import HelloWorldScene, FPSCounterScene
|
||||
|
||||
# Créer une fenêtre
|
||||
window = pyglet.window.Window(resizable=True)
|
||||
|
||||
# Créer un texte "Hello World !"
|
||||
label = pyglet.text.Label(
|
||||
"Hello World !",
|
||||
anchor_x="center",
|
||||
anchor_y="center"
|
||||
)
|
||||
|
||||
|
||||
# Lorsque la fenêtre change de taille, change la position du texte pour le centrer
|
||||
@window.event
|
||||
def on_resize(width: int, height: int):
|
||||
label.x = width // 2
|
||||
label.y = height // 2
|
||||
|
||||
|
||||
# Lorsqu'une touche est enfoncée
|
||||
@window.event
|
||||
def on_key_press(symbol, modifiers):
|
||||
print(
|
||||
pyglet.window.key.symbol_string(symbol),
|
||||
pyglet.window.key.modifiers_string(modifiers)
|
||||
)
|
||||
|
||||
|
||||
# À chaque frame, rafraichi la fenêtre et dessine le texte
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
label.draw()
|
||||
window = Window(resizable=True, visible=False)
|
||||
|
||||
window.add_scene(HelloWorldScene())
|
||||
window.add_scene(FPSCounterScene())
|
||||
|
||||
# Lance la fenêtre
|
||||
window.set_visible(True)
|
||||
pyglet.app.run()
|
||||
|
|
Loading…
Reference in a new issue