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.

\$\endgroup\$
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)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.