5
\$\begingroup\$

This is my brief summary of Pyglet: it's a powerful, efficient, high-tech cage. But once you're in there, once you call pyglet.app.run(), it's all over - you can't go back. Thus, there is this huge initialization process that you must split up into as many separate pieces as you can or you'll get lost.

I tried to split it into scenes by writing a scene manager. It works, but looks horrible and I'm worried about its efficiency.

""" Pyglet scene manager. """
import pyglet
class Scene_Manager(object):
 """ Runs and switches between different scenes. """
 def on_step(self, dt):
 """ Logic function executed every frame. """
 if not self.running:
 self.window.close()
 pyglet.app.exit()
 else:
 self.scenes[self.current].on_step(self, dt)
 def __init__(self, start, scenes, x = 640, y = 360, title = "Untitled", fps = 30):
 """ Initialize and run. """
 self.running = True
 self.current = start
 self.scenes = scenes
 self.window = pyglet.window.Window(x, y, title)
 pyglet.clock.schedule_interval( self.on_step, 1.0 / fps)
 @self.window.event
 def on_activate():
 self.scenes[self.current].on_activate(self)
 @self.window.event
 def on_close():
 self.scenes[self.current].on_close(self)
 @self.window.event
 def on_context_lost():
 self.scenes[self.current].on_context_lost(self)
 @self.window.event
 def on_context_state_lost():
 self.scenes[self.current].on_context_state_lost(self)
 @self.window.event
 def on_deactivate():
 self.scenes[self.current].on_deactivate(self)
 @self.window.event
 def on_draw():
 self.scenes[self.current].on_draw(self)
 @self.window.event
 def on_expose():
 self.scenes[self.current].on_expose(self)
 @self.window.event
 def on_hide():
 self.scenes[self.current].on_hide(self)
 @self.window.event
 def on_key_press(symbol, modifiers):
 self.scenes[self.current].on_key_press(self, symbol, modifiers)
 @self.window.event
 def on_key_release(symbol, modifiers):
 self.scenes[self.current].on_key_release(self, symbol, modifiers)
 @self.window.event
 def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
 self.scenes[self.current].on_mouse_drag(self, x, y, dx, dy, buttons, modifiers)
 @self.window.event
 def on_mouse_enter(x, y):
 self.scenes[self.current].on_mouse_enter(self, x, y)
 @self.window.event
 def on_mouse_leave(x, y):
 self.scenes[self.current].on_mouse_leave(self, x, y)
 @self.window.event
 def on_mouse_motion(x, y, dx, dy):
 self.scenes[self.current].on_mouse_motion(self, x, y, dx, dy)
 @self.window.event
 def on_mouse_press(x, y, button, modifiers):
 self.scenes[self.current].on_mouse_press(self, x, y, button, modifiers)
 @self.window.event
 def on_mouse_release(x, y, button, modifiers):
 self.scenes[self.current].on_mouse_release(self, x, y, button, modifiers)
 @self.window.event
 def on_mouse_scroll(x, y, scroll_x, scroll_y):
 self.scenes[self.current].on_mouse_scroll(self, x, y, scroll_x, scroll_y)
 @self.window.event
 def on_move(x, y):
 self.scenes[self.current].on_move(self, x, y)
 @self.window.event
 def on_resize(width, height):
 self.scenes[self.current].on_resize(self, width, height)
 @self.window.event
 def on_show():
 self.scenes[self.current].on_show(self)
 @self.window.event
 def on_text(text):
 self.scenes[self.current].on_text(self, text)
 @self.window.event
 def on_text_motion(motion):
 self.scenes[self.current].on_text_motion(self, motion)
 @self.window.event
 def on_text_motion_select(motion):
 self.scenes[self.current].on_text_motion_select(self, motion)
 pyglet.app.run()
class Scene(object):
 """ Scene template. """
 def on_step(self, app, dt):
 pass
 def on_activate(self, app):
 pass 
 def on_close(self, app):
 pass 
 def on_context_lost(self, app):
 pass 
 def on_context_state_lost(self, app):
 pass 
 def on_deactivate(self, app):
 pass 
 def on_draw(self, app):
 pass 
 def on_expose(self, app):
 pass 
 def on_hide(self, app):
 pass 
 def on_key_press(self, app, symbol, modifiers):
 pass 
 def on_key_release(self, app, symbol, modifiers):
 pass 
 def on_mouse_drag(self, app, x, y, dx, dy, buttons, modifiers):
 pass 
 def on_mouse_enter(self, app, x, y):
 pass 
 def on_mouse_leave(self, app, x, y):
 pass 
 def on_mouse_motion(self, app, x, y, dx, dy):
 pass 
 def on_mouse_press(self, app, x, y, button, modifiers):
 pass 
 def on_mouse_release(self, app, x, y, button, modifiers):
 pass 
 def on_mouse_scroll(self, app, x, y, scroll_x, scroll_y):
 pass 
 def on_move(self, app, x, y):
 pass 
 def on_resize(self, app, width, height):
 pass 
 def on_show(self, app):
 pass 
 def on_text(self, app, text):
 pass 
 def on_text_motion(self, app, motion):
 pass 
 def on_text_motion_select(self, app, motion):
 pass

You use it like this:

import pyglet
import helper
class Menu_Scene(helper.Scene):
 """ Menu scene in progress. """
 def __init__(self):
 super().__init__()
 self.bg = pyglet.image.load("menu_bg.jpg")
 self.snake = pyglet.sprite.Sprite ( pyglet.image.load("snake.png") )
 self.snake.position = 320, 0
 def on_draw(self, manager):
 super().on_draw(manager)
 manager.window.clear()
 self.bg.blit(0, 0)
 self.snake.draw()
helper.Scene_Manager("menu", {"menu" : Menu_Scene() } )

Before I'm going even further, splitting scenes into layers like this;

def on_activate(self, app):
 for layer in self.layers:
 layer.on_activate(app)

Please, tell me if I'm doing something really wrong. I've never written 200 lines of code so easily and quickly before.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Apr 25, 2015 at 13:03
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Changes

It looks good, but have you tested the fps?

The on_draw method of Scene_Manager is not limited by the fps parameter, only the on_step is.

At Scene_Manager, at the end of __init__method, for our testing, add:

self.fps_display = pyglet.window.FPSDisplay(self.window) # for our test

And then edit the on_draw window event to show it:

@self.window.event
def on_draw():
 self.scenes[self.current].on_draw(self)
 self.fps_display.draw() # for our test

If you test it now, the fps value goes wild, up to the hundreds.

Pyglet has a built-in function to limit fps. To use it, in your Scene_Manager's __init__, add:

pyglet.clock.set_fps_limit(fps)

Note that, despite the pyglet.clock also having a function to show fps(clock.ClockDisplay()), it doesn't work, and we should actually use the window.FPSDisplay as we did.

Also, your class names don't follow pep8 style guide. Unless you have a specific reason not to use it, they should be: SceneManager instead of Scene_Manager; MenuScene instead of Menu_Scene.

As a last suggestion, as we've already prepared the code to show the fps_display for our testing, it should be a good idea to take one more argument on SceneManager.__init__, such as show_fps = False to allow anyone to easily use it in case of need.

Testable, updated code

You code relies on some images that we don't have access to. The code below reflects the changes discussed above, adds a few minor changes on docstrings to follow pep257, and can be tested, which usually raises more attention/answers to a question (the sprite and bg lines that relied on images not provided were commented):

"""Pyglet scene manager."""
import pyglet
class SceneManager(object):
 """Runs and switches between different scenes."""
 def on_step(self, dt):
 """Logic function executed every frame."""
 if not self.running:
 self.window.close()
 pyglet.app.exit()
 else:
 self.scenes[self.current].on_step(self, dt)
 def __init__(self, start, scenes, x=640, y=360, title="Untitled",
 fps=30, show_fps=False):
 """Initialize and run."""
 self.running = True
 self.current = start
 self.scenes = scenes
 self.window = pyglet.window.Window(x, y, title)
 pyglet.clock.schedule_interval(self.on_step, 1.0 / fps)
 self.show_fps = show_fps 
 pyglet.clock.set_fps_limit(fps)
 self.fps_display = pyglet.window.FPSDisplay(self.window)
 @self.window.event
 def on_activate():
 self.scenes[self.current].on_activate(self)
 @self.window.event
 def on_close():
 self.scenes[self.current].on_close(self)
 @self.window.event
 def on_context_lost():
 self.scenes[self.current].on_context_lost(self)
 @self.window.event
 def on_context_state_lost():
 self.scenes[self.current].on_context_state_lost(self)
 @self.window.event
 def on_deactivate():
 self.scenes[self.current].on_deactivate(self)
 @self.window.event
 def on_draw():
 self.scenes[self.current].on_draw(self)
 if self.show_fps:
 self.fps_display.draw()
 @self.window.event
 def on_expose():
 self.scenes[self.current].on_expose(self)
 @self.window.event
 def on_hide():
 self.scenes[self.current].on_hide(self)
 @self.window.event
 def on_key_press(symbol, modifiers):
 self.scenes[self.current].on_key_press(self, symbol, modifiers)
 @self.window.event
 def on_key_release(symbol, modifiers):
 self.scenes[self.current].on_key_release(self, symbol, modifiers)
 @self.window.event
 def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
 self.scenes[self.current].on_mouse_drag(
 self, x, y, dx, dy, buttons, modifiers)
 @self.window.event
 def on_mouse_enter(x, y):
 self.scenes[self.current].on_mouse_enter(self, x, y)
 @self.window.event
 def on_mouse_leave(x, y):
 self.scenes[self.current].on_mouse_leave(self, x, y)
 @self.window.event
 def on_mouse_motion(x, y, dx, dy):
 self.scenes[self.current].on_mouse_motion(self, x, y, dx, dy)
 @self.window.event
 def on_mouse_press(x, y, button, modifiers):
 self.scenes[self.current].on_mouse_press(
 self, x, y, button, modifiers)
 @self.window.event
 def on_mouse_release(x, y, button, modifiers):
 self.scenes[self.current].on_mouse_release(
 self, x, y, button, modifiers)
 @self.window.event
 def on_mouse_scroll(x, y, scroll_x, scroll_y):
 self.scenes[self.current].on_mouse_scroll(
 self, x, y, scroll_x, scroll_y)
 @self.window.event
 def on_move(x, y):
 self.scenes[self.current].on_move(self, x, y)
 @self.window.event
 def on_resize(width, height):
 self.scenes[self.current].on_resize(self, width, height)
 @self.window.event
 def on_show():
 self.scenes[self.current].on_show(self)
 @self.window.event
 def on_text(text):
 self.scenes[self.current].on_text(self, text)
 @self.window.event
 def on_text_motion(motion):
 self.scenes[self.current].on_text_motion(self, motion)
 @self.window.event
 def on_text_motion_select(motion):
 self.scenes[self.current].on_text_motion_select(self, motion)
 pyglet.app.run()
class Scene(object):
 """Scene template."""
 def on_step(self, app, dt):
 pass
 def on_activate(self, app):
 pass
 def on_close(self, app):
 pass
 def on_context_lost(self, app):
 pass
 def on_context_state_lost(self, app):
 pass
 def on_deactivate(self, app):
 pass
 def on_draw(self, app):
 pass
 def on_expose(self, app):
 pass
 def on_hide(self, app):
 pass
 def on_key_press(self, app, symbol, modifiers):
 pass
 def on_key_release(self, app, symbol, modifiers):
 pass
 def on_mouse_drag(self, app, x, y, dx, dy, buttons, modifiers):
 pass
 def on_mouse_enter(self, app, x, y):
 pass
 def on_mouse_leave(self, app, x, y):
 pass
 def on_mouse_motion(self, app, x, y, dx, dy):
 pass
 def on_mouse_press(self, app, x, y, button, modifiers):
 pass
 def on_mouse_release(self, app, x, y, button, modifiers):
 pass
 def on_mouse_scroll(self, app, x, y, scroll_x, scroll_y):
 pass
 def on_move(self, app, x, y):
 pass
 def on_resize(self, app, width, height):
 pass
 def on_show(self, app):
 pass
 def on_text(self, app, text):
 pass
 def on_text_motion(self, app, motion):
 pass
 def on_text_motion_select(self, app, motion):
 pass
class MenuScene(Scene):
 """Menu scene in progress."""
 def __init__(self):
 super().__init__()
 # Commenting the lines that depend on images.
 # We don't have access to them.
 # self.bg = pyglet.image.load("menu_bg.jpg")
 # self.snake = pyglet.sprite.Sprite ( pyglet.image.load("snake.png") )
 # self.snake.position = 320, 0
 def on_draw(self, manager):
 super().on_draw(manager)
 manager.window.clear()
 # self.bg.blit(0, 0)
 # self.snake.draw()
if __name__ == '__main__':
 SceneManager("menu", {"menu": MenuScene()}, show_fps=True)
answered Feb 3, 2016 at 16:14
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.