Search code examples
pythonpython-3.xtkinterraspberry-pi

Countdown clock not updating in a multi-frame tkinter app


I'm in the progress of writing a python app to show a countdown clock in one window, and a video in another (haven't started integrating the video part yet).

However, the countdown clock I have written doesn't update, and only shows the initial countdown time when the app was launched. I need it to update around every 500ms.

This is the area of interest in my code PageOne & show_clock. PageOne is the display frame which should update to show a live clock countdown.

EDIT: Code has been updated since original post. Now I don't get the clock label updating at all. I'm experiencing the following errors:

Line 73 : Statement seems to have no effect
It looks like the statement doesnt have (or at least seems to) any effect.
Line 108 : Name 'show_time' is not defined
class PageOne(tk.Frame):

def __init__(self, parent, controller):
    
    tk.Frame.__init__(self, parent)
    self.controller = controller
    fnt = font.Font(family='Quicksand Medium', size=30)
    
    self.txt = tk.StringVar()
    self.show_time
    
    lbl = tk.Label(self, textvariable=self.txt, font=fnt, foreground="black", background="#FFFFDD")
    lbl.pack()
    button = tk.Button(self, text="Go to the start page",
                       command=lambda: controller.show_frame("StartPage"))
    button.pack()

def show_time(self):
    endTime = datetime.datetime(2029, 7, 20, 16, 0, 0)
    # Get the time remaining until the event
    remainder = endTime - datetime.datetime.now()
    # remove the microseconds part
    remainder = remainder - datetime.timedelta(microseconds=remainder.microseconds)

    yrs = remainder.days // 365
    yrsTxt = ' years, '
    if yrs == 1:
        yrsTxt = ' year, '

    days = remainder.days % 365
    daysTxt = ' days,'
    if days == 1:
    daysTxt = ' day,'

    timeTxt = str(remainder).split(',')[1]

    # Show the time left
    global myClock
    myClock = str(yrs) + yrsTxt + str(days) + daysTxt + timeTxt
    
    # Show the time left
    self.txt.set(f"{yrs} {yrsTxt}, {days} {daysTxt}, {timeTxt}")
    
    self.after(1000, show_time)

Solution

  • You need to use instance variable instead of local variable if the variable will be accessed across functions. Also you need to prefix self. when calling class methods.

    Below is the modified code:

    class PageOne(tk.Frame):
    
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
    
            self.controller = controller
            fnt = font.Font(family='Quicksand Medium', size=30)
    
            self.txt = tk.StringVar() # use instance variable instead
            lbl = tk.Label(self, textvariable=self.txt, font=fnt,
                           foreground="black", background="#FFFFDD")
            lbl.pack()
    
            button = tk.Button(self, text="Go to the start page",
                               command=lambda: controller.show_frame("StartPage"))
            button.pack()
    
            self.update_clock() # start updating clock
    
    
        def update_clock(self):
            self.show_time()
            self.after(1000, self.update_clock)
    
    
        def show_time(self): # added self argument
            endTime = datetime.datetime(2029, 7, 20, 16, 0, 0)
            # Get the time remaining until the event
            remainder = endTime - datetime.datetime.now()
            # remove the microseconds part
            remainder -= datetime.timedelta(microseconds=remainder.microseconds)
    
            yrs, days = divmod(remainder.days, 365)
            yrsTxt = "years" if yrs > 1 else "year"
            daysTxt = "days" if days > 1 else "day"
            timeTxt = str(remainder).split(',')[1]
    
            # Show the time left
            self.txt.set(f"{yrs} {yrsTxt}, {days} {daysTxt}, {timeTxt}")
    

    Note that you can combine update_clock() and show_time() into one function for your case.