Session 5 of 6

Keeping Time

Update the timestamp label and progress bar as the song plays.

🎯 By the end of this lesson you will be able to…

Part 1 β€” The tick: a heartbeat for your app

Once a song is playing you need the screen to keep updating β€” the timestamp counting up, the progress bar filling in. But your code has already finished running from top to bottom. Who does the updating?

The pypod module contains a hidden timer. Every 500 milliseconds (half a second) while a song is playing it fires a tick. If you have registered an on_tick callback, the timer calls your function automatically and passes in how many seconds have elapsed so far.

⏱️

Think of the tick like a clock ticking in the background. Your function is the clock hand β€” every tick, it moves forward and updates what is shown on screen.

Part 2 β€” Registering an on_tick callback

on_tick works exactly like on_song_selected from Lesson 4 β€” register a function and it will be called automatically. The only difference is that your function receives one argument: the number of seconds elapsed.

from pypod import on_tick

@on_tick
def update_display(elapsed):
    print(elapsed)   # 0, 0, 1, 1, 2, 2, 3 … (twice per second)

Part 3 β€” Formatting the time as m:ss

The elapsed value is just a plain number of seconds β€” e.g. 79. The timestamp label on screen expects a string like "1:19". Here is how to convert it:

def format_time(seconds):
    m = seconds // 60    # integer division β€” how many whole minutes?
    s = seconds  % 60    # remainder β€” how many seconds left over?
    return "{:d}:{:02d}".format(m, s)

print(format_time(79))    # 1:19
print(format_time(4))     # 0:04
print(format_time(183))   # 3:03
πŸ’‘ What does :02d mean?

Inside a format string, {:02d} means: print an integer (d), at least 2 digits wide (2), padding with zeroes if needed (0). So 4 becomes "04" and 33 stays "33". That is how you get the proper 0:04 rather than 0:4.

Part 4 β€” Updating the timestamp label

show_timestamp() takes a number of seconds and updates the label on screen. Call it inside your tick handler:

from pypod import on_tick, show_timestamp

@on_tick
def update_display(elapsed):
    show_timestamp(elapsed)

Part 5 β€” Updating the progress bar

show_progress(elapsed, total) fills the progress bar proportionally. total is the total duration of the track in seconds. Because we do not always know the exact length, using a safe upper bound of 400 (about 6.5 minutes) works well enough:

from pypod import on_tick, show_timestamp, show_progress

@on_tick
def update_display(elapsed):
    show_timestamp(elapsed)
    show_progress(elapsed, 400)

The progress bar will advance steadily and simply stop growing once the song ends.

Part 6 β€” The full main.py so far

main.py
from pypod import (
    Track, add_track,
    play, next_track,
    on_song_selected, on_track_ended, on_tick,
    show_timestamp, show_progress,
    list_music_files, music_path, lyrics_path,
    start,
)

# --- functions from previous lessons ---
def parse_filename(filename): ...
def load_lyrics(path): ...

# --- build the track list ---
for filename in list_music_files():
    title, artist = parse_filename(filename)
    track = Track(title=title, artist=artist,
                  file_path=music_path(filename),
                  lyrics=load_lyrics(lyrics_path(title)))
    add_track(track)

# --- callbacks ---
current_track = None

@on_song_selected
def handle_selection(track):
    global current_track
    current_track = track
    play(track)

@on_track_ended
def handle_end():
    next_track()

@on_tick
def update_display(elapsed):
    show_timestamp(elapsed)
    show_progress(elapsed, 400)

start()
  1. Add the on_tick import and the update_display function to your main.py.
  2. Play a song β€” the timestamp and progress bar should now update every half-second.
  3. Pause and resume β€” does the timestamp pause and resume correctly?
πŸ† Challenge