0

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?

Super User
  • 147
  • 1
  • 2
  • 7
  • 2
    Whenever you say that something "works" or "doesn't work", please make sure to be more specific. Something "not working" might mean that it errors out, doesn't error but also doesn't do what you expect it to, etc. What exactly doesn't "work" in the first section of code? – Mark Hildreth Apr 30 '13 at 00:43
  • @MarkHildreth Thanks for the advice! I mean, in the first code, update doesn't print anything and c looks like if not ticked, but I don't know why... – Super User Apr 30 '13 at 00:54

1 Answers1

1

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.

Mark Hildreth
  • 42,023
  • 11
  • 120
  • 109
  • I want subclass Clock to provide some extra functionality to the Pyglet Clock class. For that reason and the clearly in the code I choose do the call to set_default in the constructor. In this context, do you think the call to set_default is better inside or outside the constructor? – Super User Apr 30 '13 at 03:21
  • Thank you for the explanation. The code I write in the question is just an example (not the production code). Maybe I have to give you more information about the code of my project so that you can understand my point. I think the constructor is the best solution because my Clock object is a Singleton and the constructor method leaves the clock into a valid state (without this line in the constructor the clock don't tick!). – Super User May 01 '13 at 02:53
  • @SuperUser: My goal in this answer was to provide to you the difference between the code that worked and the code that didn't. Hopefully, you have SOMETHING that works now and you understand why. If it's different than what I recommend in my answer, that's fine. If you have something that works, and want others opinions on if it is a good way to do it, I would recommend posting on the [Code Review StackExchange site](http://codereview.stackexchange.com/). – Mark Hildreth May 02 '13 at 16:26
  • Thanks for all your help! I just wanted say in my previous comment why I resolved the problem by this way. – Super User May 03 '13 at 04:07