CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyglet

Cross-platform windowing and multimedia library for Python with OpenGL graphics, event handling, and audio/video playback

Overview
Eval results
Files

app-clock.mddocs/

Application and Event Loop

Application lifecycle management, event loop control, and timing/scheduling system.

When to Use This Module

  • Running the main game/application loop
  • Scheduling periodic updates (game logic, AI, spawning)
  • Implementing frame-rate independent movement
  • Creating timers and delayed actions

Quick Reference

# Start event loop
pyglet.app.run()  # Blocks until exit
pyglet.app.exit()  # Stop the loop

# Schedule functions
pyglet.clock.schedule(update_func)  # Every frame
pyglet.clock.schedule_interval(update_func, 1/60)  # Fixed rate
pyglet.clock.schedule_once(delayed_func, 3.0)  # One-time delay
pyglet.clock.unschedule(update_func)  # Remove scheduled

# Manual timing
dt = pyglet.clock.tick()  # Returns delta time
fps = pyglet.clock.get_fps()  # Current FPS estimate

Application Control

def pyglet.app.run(interval=1/60):
    """Start event loop. Blocks until pyglet.app.exit() or all windows closed."""

def pyglet.app.exit():
    """Exit the event loop."""

# Global objects
pyglet.app.event_loop: EventLoop
pyglet.app.windows: weakref.WeakSet  # All open windows

Scheduling Functions

Function Signatures

All scheduled functions must accept dt parameter:

def update(dt):  # dt = delta time in seconds
    player.x += velocity * dt

Schedule Types

# Every frame (variable timing)
pyglet.clock.schedule(func)
# Use for: Animation, visual effects, input polling

# Fixed interval (consistent timing)
pyglet.clock.schedule_interval(func, interval_seconds)
# Use for: Physics, game logic, spawning

# Approximate interval (drift allowed)
pyglet.clock.schedule_interval_soft(func, interval_seconds)
# Use for: Autosave, garbage collection, non-critical tasks

# One-time delay
pyglet.clock.schedule_once(func, delay_seconds)
# Use for: Delayed actions, cooldowns, timers

# Remove scheduled function
pyglet.clock.unschedule(func)

With Arguments

def spawn_enemy(dt, enemy_type, x, y):
    enemies.append(Enemy(enemy_type, x, y))

pyglet.clock.schedule_once(spawn_enemy, 2.0,
                           enemy_type='zombie', x=100, y=200)

Essential Patterns

Standard Game Loop

import pyglet

window = pyglet.window.Window()
batch = pyglet.graphics.Batch()

# Setup
player = Player(batch)
enemies = []

# Update function
def update(dt):
    """Game logic - called 60 times per second"""
    player.update(dt)
    for enemy in enemies:
        enemy.update(dt)
    check_collisions()

# Render function
@window.event
def on_draw():
    """Rendering - called when needed"""
    window.clear()
    batch.draw()

# Schedule
pyglet.clock.schedule_interval(update, 1/60)

# Run
pyglet.app.run()

Frame-Rate Independent Movement

class Player:
    def __init__(self):
        self.x = 0
        self.velocity_x = 100  # pixels per second (not per frame!)

    def update(self, dt):
        # CORRECT: velocity * dt
        self.x += self.velocity_x * dt

        # WRONG: velocity without dt (frame-rate dependent!)
        # self.x += self.velocity_x

Multiple Update Rates

# Fast physics update
def update_physics(dt):
    physics.step(dt)

# Slower AI update
def update_ai(dt):
    for enemy in enemies:
        enemy.think()

# Very slow autosave
def autosave(dt):
    save_game()

pyglet.clock.schedule_interval(update_physics, 1/120)  # 120 Hz
pyglet.clock.schedule_interval(update_ai, 1/10)  # 10 Hz
pyglet.clock.schedule_interval(autosave, 60.0)  # Every minute

Timer Class

class Timer:
    """Reusable countdown timer"""
    def __init__(self, duration, callback):
        self.duration = duration
        self.callback = callback
        self.elapsed = 0
        self.active = False

    def start(self):
        self.elapsed = 0
        self.active = True
        pyglet.clock.schedule(self.update)

    def update(self, dt):
        if not self.active:
            return
        self.elapsed += dt
        if self.elapsed >= self.duration:
            self.stop()
            self.callback()

    def stop(self):
        self.active = False
        pyglet.clock.unschedule(self.update)

# Usage
timer = Timer(3.0, lambda: print("Time's up!"))
timer.start()

Pause/Resume System

class GameScheduler:
    def __init__(self):
        self.funcs = []  # [(func, interval), ...]
        self.paused = False

    def schedule(self, func, interval=None):
        self.funcs.append((func, interval))
        if not self.paused:
            self._schedule_func(func, interval)

    def _schedule_func(self, func, interval):
        if interval is None:
            pyglet.clock.schedule(func)
        else:
            pyglet.clock.schedule_interval(func, interval)

    def pause(self):
        if not self.paused:
            for func, _ in self.funcs:
                pyglet.clock.unschedule(func)
            self.paused = True

    def resume(self):
        if self.paused:
            for func, interval in self.funcs:
                self._schedule_func(func, interval)
            self.paused = False

Clock Class

class pyglet.clock.Clock:
    time: float  # Current time

    @staticmethod
    def sleep(microseconds: float)

    def tick(poll=False) -> float  # Update and return dt
    def get_frequency() -> float  # Clock update frequency

FPS Limiting

# Method 1: Use vsync (recommended)
window = pyglet.window.Window(vsync=True)  # Syncs to monitor

# Method 2: Custom interval
pyglet.app.run(interval=1/60)  # Cap at 60 FPS

Critical Reminders

1. Always Accept dt Parameter

# CORRECT
def update(dt):
    player.x += velocity * dt

# WRONG - will crash
def update():
    player.x += velocity

2. Always Multiply Movement by dt

# CORRECT: Frame-rate independent
x += velocity * dt

# WRONG: Frame-rate dependent
x += velocity

3. Always Unschedule When Done

# CORRECT
def cleanup():
    pyglet.clock.unschedule(update_func)

# WRONG: Memory leak
# (never unscheduling)

4. Don't Schedule in on_draw

# WRONG: Scheduled multiple times
@window.event
def on_draw():
    pyglet.clock.schedule_once(foo, 1.0)  # BAD!
    window.clear()
    batch.draw()

# CORRECT: Schedule outside
pyglet.clock.schedule_once(foo, 1.0)

5. Use Correct Schedule Type

# Physics/game logic: Use schedule_interval (consistent timing)
pyglet.clock.schedule_interval(update_physics, 1/60)

# Animation/visual: Use schedule (smooth, every frame)
pyglet.clock.schedule(update_animation)

# Non-critical periodic: Use schedule_interval_soft (CPU efficient)
pyglet.clock.schedule_interval_soft(cleanup, 10.0)

Debug/Monitoring

# Get current FPS
fps = pyglet.clock.get_fps()
print(f"FPS: {fps:.1f}")

# Get clock frequency
freq = pyglet.clock.get_frequency()

# Access clock directly
clock = pyglet.clock.get_default()
current_time = clock.time()

Performance Tips

  1. Use schedule_interval for physics - More consistent than schedule()
  2. Limit scheduled functions - Each has overhead
  3. Use schedule_interval_soft for rare tasks - Reduces CPU usage
  4. Unschedule unused functions - Prevents unnecessary calls
  5. Cache clock object - If checking time frequently

Common Mistakes

MistakeProblemSolution
Forget dt parameterCrashAlways: def update(dt):
Don't multiply by dtFrame-rate dependentAlways: x += velocity * dt
Never unscheduleMemory leakCall unschedule() when done
Schedule in on_drawMultiple schedulesSchedule outside on_draw
Wrong schedule typeInconsistent behaviorschedule_interval for physics

Custom Event Loop (Advanced)

window = pyglet.window.Window()
running = True

while running:
    window.dispatch_events()  # Process events
    dt = pyglet.clock.tick()  # Update clock

    # Call scheduled functions
    pyglet.clock.get_default().call_scheduled_functions(dt)

    # Custom logic here
    # ...

    # Render
    window.dispatch_event('on_draw')
    window.flip()

Next Steps: See windowing.md for window events, sprites-shapes.md for rendering.

Install with Tessl CLI

npx tessl i tessl/pypi-pyglet@2.1.1

docs

3d-models.md

app-clock.md

audio-video.md

graphics-rendering.md

gui.md

images-textures.md

index.md

input-devices.md

math.md

opengl.md

resource-management.md

sprites-shapes.md

text-rendering.md

windowing.md

tile.json