简体   繁体   中英

How to subclass Clock in Pyglet?

I want to subclass Clock class of pyglet.clock module, but I have some troubles when I use schedule_interval:

The following code doesn't print anything and the object c looks like if not ticked at all:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyglet

class Clock(pyglet.clock.Clock):
    def __init__(self):
        super(Clock, self).__init__()

class Test(object):
    def update(self, dt):
        print dt

w = pyglet.window.Window()
@w.event
def on_draw():
    w.clear()

t = Test()

c = pyglet.clock.Clock()
c.schedule_interval(t.update, 1/60.0)

pyglet.app.run()

But the next works fine.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyglet

class Clock(pyglet.clock.Clock):
    def __init__(self):
        super(Clock, self).__init__()

        pyglet.clock.set_default(self)

class Test(object):
    def update(self, dt):
        print dt

w = pyglet.window.Window()
@w.event
def on_draw():
    w.clear()

t = Test()

c = Clock()
c.schedule_interval(t.update, 1/60.0)

pyglet.app.run()

The only difference is the pyglet.clock.set_default(self) sentence in the constructor method of Clock.

I think this is not clear or, at least, is not the best way of subclassing pyglet.clock.Clock to have your own derived Clock class.

The questions:

There is some way to set the default clock automatically with Pyglet?

There is a solution more elegant or pythonic?

Is possible do this without the pyglet.clock.set_default(self) line?

I haven't used pyglet, but here's my guess:

You're almost there. The reason your first implementation probably didn't work is because of these lines:

c = pyglet.clock.Clock()
c.schedule_interval(t.update, 1/60.0)

Here, you are creating a new Clock instance and are scheduling a callback on it. However, at no point are you actually associating that instance of a clock with pyglet. So, when you run...

pyglet.app.run()

...you never actually told pyglet about the new clock instance c . Instead, pyglet will use an instance that it made itself. Check out this source code for pyglet from the clock module ...:

# Default clock.
_default = Clock()

def set_default(default):
    '''Set the default clock to use for all module-level functions.

    By default an instance of `Clock` is used.

    :Parameters:
        `default` : `Clock`
            The default clock to use.
    '''
    global _default
    _default = default

def get_default():
    '''Return the `Clock` instance that is used by all module-level
    clock functions.

    :rtype: `Clock`
    :return: The default clock.

When pyglet starts up, it creates its own instance of the clock (called _default ). If you want to use your own, you need to use set_default() to replace it. Therefore, to fix your first piece of code, you probably would need to do one of the following:

c = pyglet.clock.get_default()
c.schedule_interval(t.update, 1/60.0)

...or...

c = pyglet.clock.Clock()
pyglet.clock.set_default(c)
c.schedule_interval(t.update, 1/60.0)

The second example above is pointless: pyglet already gives you an instance of Clock, so you'd really just duplicating something that pyglet has already done for you. Either way though, you end up scheduling the callback on the clock that pyglet is using .

So, it should now make sense that, yes, you do need to call set_default() . This is how you tell pyglet to use your object rather than the one it makes by default. Now, you could conceivably put this set_default() call where you currently have it (in the constructor). However, it probably makes more sense to do this...

class Clock(pyglet.clock.Clock):
    def __init__(self):
        super(Clock, self).__init__()

...

c = Clock()
pyglet.clock.set_default(c)

Edit :

In response to the question of why you would do this outside the constructor:

First, as a general rule of thumb, a constructor should only be used to construct the object. By adding set_default, you are not only constructing the object, you are also changing the state of some other entity (the pyglet.clock module). This can cause confusion, as I will show below. Assume I wrote code that looked like this...

c = MyClock()
c2 = UnpausedClock()

In this example, I have previously implemented two different clock types: NewClock and UnpausedClock . UnpausedClock will only consider time to be passing when the game is unpaused. If I put set_default() in the constructor of these two new classes, then UnpausedClock would become the new default clock (which I don't want). By NOT putting set_default() in the constructor, and instead doing the following:

c = MyClock()
c2 = UnpausedClock()
pyglet.clock.set_default(c)

My code is more explicit, and less confusing.

Of course, the code will work either way, but I feel that having the set_default OUTSIDE the constructor gives you more flexibility to use the class as you need to later on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM