Search code examples
pythoncanvastkintertimer

Tkinter GUI stopwatch/timer


How can I make the timer to update in the Canvas?

In create.text(position, **options) the default position can be overridden with anchor=tk.CENTER supposedly, but I get a:

NameError: name 'tk' is not defined

if I try to add the anchor option.

import time
from Tkinter import *

run = True; s=0; m=0; h=0

def Run():
    global run, s, m, h
    while run == True:
        time.sleep(1)
        s+=1
        if s == 59:
            m+=1; s=-1
        elif m == 59:
            h+=1; m=-1

master = Tk()
w = Canvas(master, width=1500, height=800)
w.pack()

x.create_text(
    [750, 400], anchor=tk.CENTER, text="%s:%s:%s" % (s, m, h), font=("Consolas", 400)
    )

mainloop()

Run()

If I put Run() before mainloop() the Canvas doesn't start. After mainloop() and Run() doesn't start.

mainloop(Run()) and Run() starts before Canvas.


Solution

  • This line:

    from Tkinter import *
    

    brings all of the names from the Tkinter library into the global namspace. So, you can just access them directly:

    x.create_text(
        ..., anchor=CENTER, ...
        )
    

    You would only do tk.CENTER had you imported Tkinter like:

    import Tkinter as tk
    

    which I must say is a better approach. It is a bad practice to clutter the global namespace by doing from ... import *. Remember that:

    "Namespaces are one honking great idea -- let's do more of those!"


    Regarding the second part of your question, you cannot use while True: or time.sleep in the same thread as the Tkinter event loop is operating in. Doing either of these will block the Tkinter loop and cause your program to freeze.

    Instead, you can use Tk.after to execute Run in the background:

    from Tkinter import *
    
    run = True
    s = 0
    m = 0
    h = 0
    
    
    def Run():
        global run, s, m, h
    
        # Delete old text
        w.delete('all')
        # Add new text
        w.create_text(
            [750, 400], anchor=CENTER, text="%s:%s:%s" % (s, m, h), font=("Consolas", 400)
            )
    
        s += 1
        if s == 59:
            m += 1
            s -= 1
        elif m == 59:
            h += 1
            m -= 1
    
        # After 1 second, call Run again (start an infinite recursive loop)
        master.after(1000, Run)
    
    
    master = Tk()
    w = Canvas(master, width=1500, height=800)
    w.pack()
    
    master.after(1, Run)  # after 1 millisecond, start Run
    mainloop()
    

    Also, I moved the call to Canvas.create_text inside the Run function so that it is updated with each call of Run. Notice too that you need to call Canvas.delete before you add the new text so that any old text is removed.