How can I schedule updates (f/e, to update a clock) in tkinter?

Question:

I’m writing a program with Python’s tkinter library.

My major problem is that I don’t know how to create a timer or a clock like hh:mm:ss.

I need it to update itself (that’s what I don’t know how to do); when I use time.sleep() in a loop the whole GUI freezes.

Asked By: Diego Castro

||

Answers:

Tkinter root windows have a method called after which can be used to schedule a function to be called after a given period of time. If that function itself calls after you’ve set up an automatically recurring event.

Here is a working example:

# for python 3.x use 'tkinter' rather than 'Tkinter'
import Tkinter as tk
import time

class App():
    def __init__(self):
        self.root = tk.Tk()
        self.label = tk.Label(text="")
        self.label.pack()
        self.update_clock()
        self.root.mainloop()

    def update_clock(self):
        now = time.strftime("%H:%M:%S")
        self.label.configure(text=now)
        self.root.after(1000, self.update_clock)

app=App()

Bear in mind that after doesn’t guarantee the function will run exactly on time. It only schedules the job to be run after a given amount of time. It the app is busy there may be a delay before it is called since Tkinter is single-threaded. The delay is typically measured in microseconds.

Answered By: Bryan Oakley

Python3 clock example using the frame.after() rather than the top level application. Also shows updating the label with a StringVar()

#!/usr/bin/env python3

# Display UTC.
# started with https://docs.python.org/3.4/library/tkinter.html#module-tkinter

import tkinter as tk
import time

def current_iso8601():
    """Get current date and time in ISO8601"""
    # https://en.wikipedia.org/wiki/ISO_8601
    # https://xkcd.com/1179/
    return time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())

class Application(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

    def createWidgets(self):
        self.now = tk.StringVar()
        self.time = tk.Label(self, font=('Helvetica', 24))
        self.time.pack(side="top")
        self.time["textvariable"] = self.now

        self.QUIT = tk.Button(self, text="QUIT", fg="red",
                                            command=root.destroy)
        self.QUIT.pack(side="bottom")

        # initial time display
        self.onUpdate()

    def onUpdate(self):
        # update displayed time
        self.now.set(current_iso8601())
        # schedule timer to call myself after 1 second
        self.after(1000, self.onUpdate)

root = tk.Tk()
app = Application(master=root)
root.mainloop()
Answered By: David Poole

I just created a simple timer using the MVP pattern (however it may be
overkill for that simple project). It has quit, start/pause and a stop button. Time is displayed in HH:MM:SS format. Time counting is implemented using a thread that is running several times a second and the difference between the time the timer has started and the current time.

Source code on github

Answered By: user2778712
from tkinter import *
import time
tk=Tk()
def clock():
    t=time.strftime('%I:%M:%S',time.localtime())
    if t!='':
        label1.config(text=t,font='times 25')
    tk.after(100,clock)
label1=Label(tk,justify='center')
label1.pack()
clock()
tk.mainloop()
Answered By: Ravikiran D

I have a simple answer to this problem. I created a thread to update the time. In the thread i run a while loop which gets the time and update it. Check the below code and do not forget to mark it as right answer.

from tkinter import *
from tkinter import *
import _thread
import time


def update():
    while True:
      t=time.strftime('%I:%M:%S',time.localtime())
      time_label['text'] = t



win = Tk()
win.geometry('200x200')

time_label = Label(win, text='0:0:0', font=('',15))
time_label.pack()


_thread.start_new_thread(update,())

win.mainloop()
Answered By: Hardeep Singh

You should call .after_idle(callback) before the mainloop and .after(ms, callback) at the end of the callback function.

Example:

import tkinter as tk
import time


def refresh_clock():
    clock_label.config(
        text=time.strftime("%H:%M:%S", time.localtime())
    )
    root.after(1000, refresh_clock)  # <--


root = tk.Tk()

clock_label = tk.Label(root, font="Times 25", justify="center")
clock_label.pack()

root.after_idle(refresh_clock)  # <--
root.mainloop()
Answered By: Demian Wolf
from tkinter import *

from tkinter import messagebox

root = Tk()

root.geometry("400x400")

root.resizable(0, 0)

root.title("Timer")

seconds = 21

def timer():

    global seconds
    if seconds > 0:
        seconds = seconds - 1
        mins = seconds // 60
        m = str(mins)

        if mins < 10:
            m = '0' + str(mins)
        se = seconds - (mins * 60)
        s = str(se)

        if se < 10:
            s = '0' + str(se)
        time.set(m + ':' + s)
        timer_display.config(textvariable=time)
        # call this function again in 1,000 milliseconds
        root.after(1000, timer)

    elif seconds == 0:
        messagebox.showinfo('Message', 'Time is completed')
        root.quit()


frames = Frame(root, width=500, height=500)

frames.pack()

time = StringVar()

timer_display = Label(root, font=('Trebuchet MS', 30, 'bold'))

timer_display.place(x=145, y=100)

timer()  # start the timer

root.mainloop()
Answered By: user13083014

You can emulate time.sleep with tksleep and call the function after a given amount of time. This may adds readability to your code, but has its limitations:

def tick():
    while True:
        clock.configure(text=time.strftime("%H:%M:%S"))
        tksleep(0.25) #sleep for 0.25 seconds
    

root = tk.Tk()
clock = tk.Label(root,text='5')
clock.pack(fill=tk.BOTH,expand=True)
tick()
root.mainloop()
Answered By: Thingamabobs
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.