0

i am trying to create a timer in python and put it on to a GUI application using tkinter. Right now i am have got an algorithm written out to slowly countdown from the input given by the user. Although i have done this, the GUI application freezes till it get to 0 then display the finished time. If anyone could let me know on why it isn't display the change in time every second, it would be greatly appreciated. Below is my code

import tkinter as tk
import tkinter.font as tkFont
import time


def navbar(self,page):
    page = page
    timerBtn = tk.Button(self.master, text = 'Timer', command = lambda: timers(self,page))
    timerBtn.place(x=0,y=550,width = 333,height = 50)
    
    ClockBtn = tk.Button(self.master, text = 'Clock', command = lambda: clocks(self,page))
    ClockBtn.place(x=333,y=550, width = 333, height = 50)
    
    stopWatchBtn = tk.Button(self.master, text = 'Stop Watch', command = lambda: stopWatchs(self,page))
    stopWatchBtn.place(x=666,y=550,width = 333, height = 50)
    
    
def timers(self,page):
    if page != 'Timer':
        self.master.destroy()
        root = tk.Tk()
        app = timer(root)
    
def clocks(self,page):
    if page != 'Clock':
        self.master.destroy()
        root = tk.Tk()
        app = clock(root)
    
def stopWatchs(self,page):
    if page != 'StopWatch':
        self.master.destroy()
        root = tk.Tk()
        app = stopWatch(root)
    

class timer():
    def __init__(self, master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")
        
        self.place()
        
    def place(self):
        self.hoursLabel = tk.Label(self.master, text = 'Hours')
        self.hoursLabel.place(x=220,y=200)
        
        self.hoursEntry = tk.Entry(self.master)
        self.hoursEntry.place(x=200,y= 200,width = 20)
        
        self.hoursUpArrow = tk.Button(self.master, text = "\N{UPWARDS BLACK ARROW}",command = lambda: self.change('hours', 'Up'))
        self.hoursUpArrow.place(x=201, y= 170)
        
        self.hoursDownArrow = tk.Button(self.master, text = "\N{DOWNWARDS BLACK ARROW}",command = lambda: self.change('hours', 'Down'))
        self.hoursDownArrow.place(x=201, y= 225)
        
        self.minsLabel = tk.Label(self.master, text = 'Minutes')
        self.minsLabel.place(x=520,y=200)
        
        self.minsEntry = tk.Entry(self.master)
        self.minsEntry.place(x=500,y= 200, width = 20)
        
        self.minsUpArrow = tk.Button(self.master, text = "\N{UPWARDS BLACK ARROW}",command = lambda: self.change('mins', 'Up'))
        self.minsUpArrow.place(x=501, y= 170)
        
        self.minsDownArrow = tk.Button(self.master, text = "\N{DOWNWARDS BLACK ARROW}",command = lambda: self.change('mins', 'Down'))
        self.minsDownArrow.place(x=501, y= 225)
        
        self.secondsLabel = tk.Label(self.master, text = 'Seconds')
        self.secondsLabel.place(x=820,y=200)
        
        self.secondsEntry = tk.Entry(self.master)
        self.secondsEntry.place(x=800,y= 200, width = 20)
        
        self.secondsUpArrow = tk.Button(self.master, text = "\N{UPWARDS BLACK ARROW}",command = lambda: self.change('sec', 'Up'))
        self.secondsUpArrow.place(x=801, y= 170)
        
        self.secondsDownArrow = tk.Button(self.master, text = "\N{DOWNWARDS BLACK ARROW}",command = lambda: self.change('sec', 'Down'))
        self.secondsDownArrow.place(x=801, y= 225)

        self.cancelButton = tk.Button(self.master, text = 'Cancel', command = self.cancel)
        self.cancelButton.place(x=150,y=400)
        
        self.startButton = tk.Button(self.master, text= 'Start', command = self.start)
        self.startButton.place(x=750,y=400)

        navbar(self,'Timer')        
      
    def change(self,time, change):
        changeable = 0
        if time == 'hours':
            changeable = self.hoursEntry.get()
        elif time == 'mins':
            changeable = self.minsEntry.get()
        elif time == 'sec':
            changeable = self.secondsEntry.get()
         
        if changeable == '':
            changeable = 0
        else:
            changeable = int(changeable)
        
        if time == 'hours':
            if change == 'Up':
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.hoursEntry.delete(0,tk.END)
            self.hoursEntry.insert(0,str(changeable))
        elif time == 'mins':
            if change == 'Up' and changeable < 60:
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.minsEntry.delete(0,tk.END)
            self.minsEntry.insert(0,str(changeable))
        elif time == 'sec':
            if change == 'Up' and changeable < 60:
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.secondsEntry.delete(0,tk.END)
            self.secondsEntry.insert(0,str(changeable))
    def cancel(self):
        self.place()
        self.TimeLabel.place_forget()
    
    def start(self):
        self.hours = self.hoursEntry.get()
        self.mins = self.minsEntry.get()
        self.seconds = self.secondsEntry.get()
        
        if self.hours == '':
            self.hours = '00'
        if self.mins == '':
            self.mins = '00'
        if self.seconds == '':
            self.seconds = '00'
            
        self.hours = int(self.hours)
        self.mins = int(self.mins)
        self.seconds = int(self.seconds)
        
        
        self.hoursLabel.place_forget()
        self.hoursEntry.place_forget()
        self.hoursUpArrow.place_forget()
        self.hoursDownArrow.place_forget()
        self.minsLabel.place_forget()
        self.minsEntry.place_forget()
        self.minsUpArrow.place_forget()
        self.minsDownArrow.place_forget()
        self.secondsLabel.place_forget()
        self.secondsEntry.place_forget()
        self.secondsUpArrow.place_forget()
        self.secondsDownArrow.place_forget()
        
        fontStyle = tkFont.Font(family="Lucida Grande", size=50)
        Time = str(self.hours) + ':' + str(self.mins) + ':' + str(self.seconds)
        
        self.TimeLabel = tk.Label(self.master, text = Time, font = fontStyle)
        self.TimeLabel.place(x=380, y= 200)
        self.on = True
        self.timer(Time)
    def timer(self,Time):
        time.sleep(1)
        self.seconds -= 1
        if self.seconds <= 0:
            if self.mins > 0:
                self.seconds = 60
                self.mins -= 1
            else:
                if self.hours >= 1:
                    self.mins = 59
                    self.seconds = 60
                    self.hours -= 1
                else:
                    self.on = False
                  
        Time = str(self.hours) + ':' + str(self.mins) + ':' + str(self.seconds)
        self.TimeLabel.config(text = Time)
        
        if self.on == True:
            self.timer(Time)
            
class clock():
    def __init__(self,master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")



        navbar(self,'Clock')     
    
class stopWatch():
    def __init__(self,master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")



        navbar(self,'StopWatch')     


root = tk.Tk()
app = timer(root)
root.mainloop()
1
  • When your class timer's timer() method is called by its start() method a recursive loop is created because the method calls itself as long self.on == True. This tight loop interferes with tkinter's own mainloop() which is why the GUI freezes. There are numerous questions here regarding the correct way to implement timers in tkinter — the answer often involves using the universal widget method after() to schedule periodic calls to an update function. Commented Jul 13, 2021 at 0:26

1 Answer 1

1

Here's how to get the Timer class portion of your GUI application to work without freezing-up using that after() widget method I mentioned in a comment. Note: I also reformatted a lot of your code to make it more readable and changed the spelling of your class names to conform to the PEP 8 - Style Guide for Python Code guidelines and (reasonably important) naming conventions. I strongly suggest you read and start following them yourself.

The code changes to make it work were mainly in the Timer class' start() method which no now calls a new method I added named countdown() which replaces the one you had called timer(). I also changed the cancel() method to make it work.

import tkinter as tk
import tkinter.font as tkFont
import time


def navbar(self, page):
    timerBtn = tk.Button(self.master, text='Timer',
                         command=lambda: timers(self, page))
    timerBtn.place(x=0, y=550, width=333, height=50)

    ClockBtn = tk.Button(self.master, text='Clock',
                         command=lambda: clocks(self, page))
    ClockBtn.place(x=333, y=550, width=333, height=50)

    stopWatchBtn = tk.Button(self.master, text='Stop Watch',
                             command=lambda: stopWatchs(self, page))
    stopWatchBtn.place(x=666, y=550, width=333, height=50)

def timers(self, page):
    if page != 'Timer':
        self.master.destroy()
        root = tk.Tk()
        app = Timer(root)

def clocks(self, page):
    if page != 'Clock':
        self.master.destroy()
        root = tk.Tk()
        app = Clock(root)

def stopWatchs(self, page):
    if page != 'StopWatch':
        self.master.destroy()
        root = tk.Tk()
        app = StopWatch(root)


class Timer:
    def __init__(self, master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")
        self.place()

    def place(self):
        self.hoursLabel = tk.Label(self.master, text='Hours')
        self.hoursLabel.place(x=220,y=200)

        self.hoursEntry = tk.Entry(self.master)
        self.hoursEntry.place(x=200, y=200, width=20)

        self.hoursUpArrow = tk.Button(self.master, text="\N{UPWARDS BLACK ARROW}",
                                      command=lambda: self.change('hours', 'Up'))
        self.hoursUpArrow.place(x=201, y= 170)

        self.hoursDownArrow = tk.Button(self.master, text="\N{DOWNWARDS BLACK ARROW}",
                                      command=lambda: self.change('hours', 'Down'))
        self.hoursDownArrow.place(x=201, y= 225)

        self.minsLabel = tk.Label(self.master, text='Minutes')
        self.minsLabel.place(x=520,y=200)

        self.minsEntry = tk.Entry(self.master)
        self.minsEntry.place(x=500,y= 200, width=20)

        self.minsUpArrow = tk.Button(self.master, text="\N{UPWARDS BLACK ARROW}",
                                     command=lambda: self.change('mins', 'Up'))
        self.minsUpArrow.place(x=501, y=170)

        self.minsDownArrow = tk.Button(self.master, text="\N{DOWNWARDS BLACK ARROW}",
                                       command=lambda: self.change('mins', 'Down'))
        self.minsDownArrow.place(x=501, y= 225)

        self.secondsLabel = tk.Label(self.master, text='Seconds')
        self.secondsLabel.place(x=820,y=200)

        self.secondsEntry = tk.Entry(self.master)
        self.secondsEntry.place(x=800,y= 200, width=20)

        self.secondsUpArrow = tk.Button(self.master, text="\N{UPWARDS BLACK ARROW}",
                                        command=lambda: self.change('sec', 'Up'))
        self.secondsUpArrow.place(x=801, y= 170)

        self.secondsDownArrow = tk.Button(self.master, text="\N{DOWNWARDS BLACK ARROW}",
                                          command=lambda: self.change('sec', 'Down'))
        self.secondsDownArrow.place(x=801, y=225)

        self.cancelButton = tk.Button(self.master, text='Cancel', command=self.cancel)
        self.cancelButton.place(x=150,y=400)

        self.startButton = tk.Button(self.master, text= 'Start', command=self.start)
        self.startButton.place(x=750,y=400)
        navbar(self, 'Timer')

    def change(self,time, change):
        changeable = 0
        if time == 'hours':
            changeable = self.hoursEntry.get()
        elif time == 'mins':
            changeable = self.minsEntry.get()
        elif time == 'sec':
            changeable = self.secondsEntry.get()

        if changeable == '':
            changeable = 0
        else:
            changeable = int(changeable)

        if time == 'hours':
            if change == 'Up':
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.hoursEntry.delete(0,tk.END)
            self.hoursEntry.insert(0,str(changeable))
        elif time == 'mins':
            if change == 'Up' and changeable < 60:
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.minsEntry.delete(0,tk.END)
            self.minsEntry.insert(0,str(changeable))
        elif time == 'sec':
            if change == 'Up' and changeable < 60:
                changeable += 1
            else:
                if changeable > 0:
                    changeable -= 1
            self.secondsEntry.delete(0,tk.END)
            self.secondsEntry.insert(0,str(changeable))

    def cancel(self):
        self.on = False
        self.TimeLabel.place_forget()
        self.place()

    def start(self):
        self.hours = self.hoursEntry.get()
        self.mins = self.minsEntry.get()
        self.seconds = self.secondsEntry.get()

        if self.hours == '':
            self.hours = '00'
        if self.mins == '':
            self.mins = '00'
        if self.seconds == '':
            self.seconds = '00'

        self.hours = int(self.hours)
        self.mins = int(self.mins)
        self.seconds = int(self.seconds)

        self.hoursLabel.place_forget()
        self.hoursEntry.place_forget()
        self.hoursUpArrow.place_forget()
        self.hoursDownArrow.place_forget()
        self.minsLabel.place_forget()
        self.minsEntry.place_forget()
        self.minsUpArrow.place_forget()
        self.minsDownArrow.place_forget()
        self.secondsLabel.place_forget()
        self.secondsEntry.place_forget()
        self.secondsUpArrow.place_forget()
        self.secondsDownArrow.place_forget()

        fontStyle = tkFont.Font(family="Lucida Grande", size=50)
        time_string = f'{self.hours:2d}:{self.mins:02d}:{self.seconds:02d}'

        self.TimeLabel = tk.Label(self.master, text=time_string, font=fontStyle)
        self.TimeLabel.place(x=380, y=200)
        self.on = True
        self.master.after(1000, self.countdown)  # Start countdown in 1 second.

    def countdown(self):
        self.seconds -= 1
        if self.seconds <= 0:
            if self.mins > 0:
                self.seconds = 60
                self.mins -= 1
            else:
                if self.hours >= 1:
                    self.mins = 59
                    self.seconds = 60
                    self.hours -= 1
                else:
                    self.on = False

        time_string = f'{self.hours:2d}:{self.mins:02d}:{self.seconds:02d}'
        self.TimeLabel.config(text=time_string)

        if self.on:  # Not finished?
            self.master.after(1000, self.countdown)  # Call again in 1 second.


class Clock:
    def __init__(self,master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")
        navbar(self, 'Clock')


class StopWatch:
    def __init__(self,master):
        self.master = master
        self.master.title("Clock Clone")
        self.master.geometry("999x600")
        navbar(self,'StopWatch')


root = tk.Tk()
app = Timer(root)
root.mainloop()

Here's a screenshot of it running:

screenshot of running app

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.