[Tutor] Countdown Clock Programming Question

Alan Gauld alan.gauld at btinternet.com
Tue Nov 24 13:19:22 EST 2015


On 24/11/15 14:01, Evan Sommer wrote:

> goal is for the clock to change from green to yellow at 2 minutes, and
> yellow to red at 1 minute. 


> def count_down():
>     # start with 4 minutes --> 240 seconds
>     for t in range(240, 120, -1):
>         # format as 2 digit integers, fills with zero to the left
>         # divmod() gives minutes, seconds
>         sf = "{:01d}:{:02d}".format(*divmod(t, 60))
>         #print(sf)  # test
>         time_str.set(sf)
>         root.update()
>         # delay one second
>         time.sleep(1)# create root/main window

Its not recommended to use sleep in a GUI program, the Tkinter
library provides the after() method to do this instead. In
your case you'd call it with a timeout of 1000ms

Also because sleep() and after() are rather imprecise in their
timings it will be more accurate to use the time module to get
the actual start time and then on each update fetch that again
and compute the difference. (Although I doubt it will make
much difference over 4 minutes!)

eg

target_time = time.time() + 240   # now plus 4 min
and then
display_period = target_time - time.time()  # accurate seconds left

> root = tk.Tk()

> time_str = tk.StringVar()
> # create the time display label, give it a large font
> # label auto-adjusts to the font
> label_font = ('helvetica', 100)
> tk.Label(root, textvariable=time_str, font=label_font, bg='green',
>          fg='white', relief='raised', bd=3).pack(fill='x', padx=5, pady=5)

It would be better to create the label once and then
just change its colours. But for that you need to
store a reference to it:

timeLabel = tk.Label(root, textvariable=time_str,
                     font=label_font,  bg='green',
                     fg='white', relief='raised',
                     bd=3).pack(fill='x', padx=5, pady=5)

I'd also miss out the Stringvar and just update the text
attribute directly. Stringvar is great when you want two
way interaction with a field but for a label it doesn't really
help, IMHO.


>  # start with 2 minutes --> 119 seconds
> for t in range(240, 120, -1):
>         # format as 2 digit integers, fills with zero to the left
>         # divmod() gives minutes, seconds
>         sf = "{:01d}:{:02d}".format(*divmod(t, 60))
>         #print(sf)  # test
>         time_str.set(sf)
>         root.update()
>         # delay one second
>         time.sleep(1)

> # create the time display label, give it a large font
> # label auto-adjusts to the font
> label_font = ('helvetica', 100)

You already defined label_font no need to do it again.

> tk.Label(root, textvariable=time_str, font=label_font, bg='yellow',
>          fg='white', relief='raised', bd=3).pack(fill='x', padx=5, pady=5)
>  # start with 1 minutes --> 59 seconds

Doesn't this create a second label on your display?
Or do you delete the old one somewhere that I haven't seen?

> for t in range(120,60, -1):
>         # format as 2 digit integers, fills with zero to the left
>         # divmod() gives minutes, seconds
>         sf = "{:01d}:{:02d}".format(*divmod(t, 60))
>         #print(sf)  # test
>         time_str.set(sf)
>         root.update()
>         # delay one second
>         time.sleep(1)
> # create the time display label, give it a large font
> # label auto-adjusts to the font
> label_font = ('helvetica', 100)
> tk.Label(root, textvariable=time_str, font=label_font, bg='red',
>          fg='white', relief='raised', bd=3).pack(fill='x', padx=5, pady=5)
>  # start with 4 minutes --> 240 seconds

See previous comments re duplicating code.

> for t in range(60,-1, -1):
>         # format as 2 digit integers, fills with zero to the left
>         # divmod() gives minutes, seconds
>         sf = "{:01d}:{:02d}".format(*divmod(t, 60))
>         #print(sf)  # test
>         time_str.set(sf)
>         root.update()
>         # delay one second
>         time.sleep(1)
> # start the GUI event loop
> root.mainloop()
> 


Personally I'd do it in a pair of functions that look
loosely like

def initialise():
    set up GUI
    create label with startuing font/size/color
    target_time = time.time() + 240
    label[text] =  "4:00"
    after(1000, update_display)

def update_display()
    display_period = target_time - time.time()
    min,sec = divmod(display_period,60)
    if min < 2: set color to yellow
    elif: min < 1: set color to red
    else: set color to green
    set label text to min/sec
    after(1000, update_display)

No loops required and no repeated code.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos




More information about the Tutor mailing list