Question

I am trying to create a simple program in Pyglet that shows an animation and retrieves some mouse input and saves it to a text file. The code I have shows a significant unstable frame rate even when I am not using the mouse.

I have also looked at similar questions and the answers suggest to use a subclassed window, or to call the function on_draw with schedule_interval. I do not know, however, how to use a subclassed window to display my animation and, when I try to call on_draw with schedule_interval I get the error that on_draw does not receives any argument.

This is part of the code I am using:

fps = pyglet.clock.ClockDisplay()# Show FPS

@mywindow.event
def on_mouse_press(x, y, button, modifiers):
    global timeStart, file, count
    timeNow = time.clock() - timeStart

    if button == mouse.LEFT:
        print('left click press in {}').format(timeNow)
        with open(out_file_name, 'a') as file:
            file.write(str(count) +'\t'+ str(timeNow) +'\t'+ '-1\n')
            #file.write('' + count + timeNow + 'left click press\n')                 
    count += 1

def update_frames(dt):
    global x
    x=x+1

@mywindow.event
def on_draw():
    pyglet.gl.glClearColor(0,0,0,0)
    mywindow.clear()
    glColor4f(1,0,0,1)
    drawSquare(x,y)#this draws an opengl square
    fps.draw()# Show FPS


dt = 1/10.0
pyglet.clock.schedule_interval(update_frames,dt)
pyglet.app.run()

What can I add to the code in order to obtain an stable frame rate?

Was it helpful?

Solution

I'd use something like this instead:

import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time
from os.path import abspath

class GUI(pyglet.window.Window):
    def __init__(self):
        super(GUI, self).__init__(640,340, caption='Test')

        pyglet.gl.glClearColor(1, 1, 1, 1)
        self.alive = True
        self.batches = OrderedDict()
        self.batches['apples'] = pyglet.graphics.Batch()

        self.framerate = 0, time()
        self.count = 0

    def render(self, *args):
        self.clear()

        #glColor4f(1,0,0,1)
        #drawSquare(x,y)

        if time() - self.framerate[1] > 1:
            print('fps:',self.framerate[0])
            self.framerate = 0, time()
        else:
            # Not an optimal way to do it, but it will work.
            self.framerate = self.framerate[0]+1, self.framerate[1]

        self.flip()

    def on_draw(self):
        self.render()

    def on_close(self):
        self.alive = False

    def on_key_press(self, symbol, modkey):
        pass

    def on_key_release(self, symbol, modkey):
        pass

    def on_mouse_release(self, x, y, button, modifiers):
        pass

    def on_mouse_press(self, x, y, button, modifiers):
        self.count += 1
        with open('debug.log', 'w') as fh:
            fh.write(str(count))

    def on_mouse_motion(self, x, y, dx, dy):
        pass

    def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
        pass

    def run(self):
        while self.alive:
            event = self.dispatch_events()
            if event:
                print(event)
            self.render()


if __name__ == '__main__':
    x = GUI()
    pyglet.clock.set_fps_limit(60)
    x.run()

For one, this code actually produces 60 FPS, You get way better control over your application and your main loop.

Secondly, the coding style is perhaps a personal preference but throwing stuff into class objects rather than doing a huge list of functions which you attach to say @window.update etc is more to my liking.. The code looks cleaner.

Give it a go, see if it works.

Note: Key here is event = self.dispatch_events() which must be called for each iteration, it's what replaces app.run().

Tie this together with objects and rendering them

class House(pyglet.sprite.Sprite):
    def __init__(self):
        self.texture = pyglet.image.load(abspath('./image.png'))
        super(House, self).__init__(self.texture)

        self.x = 0
        self.y = 0
        self.rotation = 0

        self.name = 'house'
        self.anchor = 'center'

    def swap_image(self, image):
        self.texture = pyglet.image.load(abspath(image))
        self.image = self.texture

    def rotate(self, deg):
        self.image.anchor_x = self.image.width / 2
        self.image.anchor_y = self.image.height / 2
        self.rotation = self.rotation+deg
        if self.anchor != 'center':
            self.image.anchor_x = 0
            self.image.anchor_y = 0
        return True

    def click(self):
        print('Clicked:',self.name)

    def work(self, *args):
        pass

    def click_check(self, x, y):
        if x > self.x and x < (self.x + self.width):
            if y > self.y and y < (self.y + self.height):
                return self

    def move(self, x, y):
        if self.moveable:
            self.x += x
            self.y += y

    def _draw(self):
        self.draw()

in GUI() you'll do:

class GUI(pyglet.window.Window):
    def __init__(self):
        super(GUI, self).__init__(640,340, caption='Test')
        ...
        self.house = House()

    def render(self, *args):

        self.house.rotate(1)
        self.house._draw()

This should create a "house" (or whatever) and rotate the picture 1 degrees for each render occations, meaning you'll be rotating it 60 degrees a second with a nice flow.

There's more stuff to this than just this, but it's a simplification of what i usually use to squeeze out FPS while still maintaining readable code.. Because graphics get messy quite quickly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top