Tkinter clock crashing with infinite loop? - python

I'm creating a single StringVar called time_display, which contains a string of the fashion 3:14:02, and a single label in the window which should (in theory) reflect updates to said StringVar. However, when I call updateTime to check the time and update it, I crash in an infinite loop. Other adjustments I have made have resulted in no infinite loop but no updating clock either. What am I doing wrong?
from tkinter import *
from tkinter import ttk
import time
root = Tk()
root.title("Clock")
def updateTime(var):
hours = time.localtime().tm_hour
if hours == 0:
hours = 12
minutes = time.localtime().tm_min
if minutes < 10:
minutes = '0' + str(minutes)
else:
minutes = str(minutes)
seconds = time.localtime().tm_sec
if seconds < 10:
seconds = '0' + str(seconds)
else:
seconds = str(seconds)
current_time = str(hours) + ':' + minutes + ':' + seconds
var.set(current_time)
root.after(500, updateTime(var))
time_display = StringVar()
updateTime(time_display)
ttk.Label(root, textvariable=time_display).grid(column=0, row=0)
root.mainloop()

In following line, the code is calling updateTime directly; causing recursive call.
root.after(500, updateTime(var))
# ^ ^
Pass the function and argument without calling it will solve your problem.
root.after(500, updateTime, var)
Alternatively you can use lambda:
root.after(500, lambda: updateTime(var))
BTW, using time.strftime, updateTime can be reduced:
def updateTime(var):
var.set(time.strftime('%H:%M:%S'))
root.after(500, updateTime, var)

Related

stop the clock within the time limit in tkinter python

I want to stop clock within the time limit, here i have given 5 seconds but the clock is not stopping, kindly help.
import tkinter as tk
from tkinter.constants import *
def start():
global hours, minutes, seconds
if hours == 4:
return
seconds -= 1
if seconds == 00:
minutes -= 1
seconds = 60
if minutes == 00 and seconds == 00:
hours = hours+1
clock.config(text=f'{hours:02}:{minutes:02}:{seconds:02}')
root.after(1000, start)
root=tk.Tk()
clock = tk.Label(root, font=("bold",20), anchor=CENTER, text="00:00:00")
clock.place(relx=0.5, rely=0.5, anchor=CENTER)
hours,minutes,seconds = 0,0,5
start()
root.mainloop()
Just add the variable stop and if stop == True then the clock will stop
Solution to your Question
import tkinter as tk
from tkinter.constants import *
def start():
global hours, minutes, seconds, stop
if hours == 4:
return
if stop == False:
seconds -= 1
if seconds == 00:
stop = True
elif seconds == 00 and minutes != 00 and hours != 00:
minutes -= 1
seconds = 60
elif minutes == 00 and seconds == 00:
hours = hours+1
clock.config(text=f'{hours:02}:{minutes:02}:{seconds:02}')
root.after(1000, start)
root=tk.Tk()
clock = tk.Label(root, font=("bold",20), anchor=CENTER, text="00:00:00")
clock.place(relx=0.5, rely=0.5, anchor=CENTER)
hours,minutes,seconds,stop = 0,0,5, False
start()
root.mainloop()
a Complete clock down counter BONUS
import tkinter as tk
from tkinter.constants import *
def start():
global hours, minutes, seconds, stop
if hours == 4:
return
if stop == False:
seconds -= 1
if seconds == 00 and minutes == 00 and hours == 00:
stop = True
elif seconds == -1 and minutes != 00:
minutes -= 1
seconds = 59
elif hours != 00 and minutes == 00 and seconds == -1:
hours -= 1
minutes = 59
seconds = 59
clock.config(text=f'{hours:02}:{minutes:02}:{seconds:02}')
root.after(1000, start)
root=tk.Tk()
clock = tk.Label(root, font=("bold",20), anchor=CENTER, text="00:00:00")
clock.place(relx=0.5, rely=0.5, anchor=CENTER)
hours,minutes,seconds,stop = 0,0,5, False
start()
root.mainloop()
Solution without that many if conditions. I use a timestamp, but I have also some trouble with the timezone I think. Maybe someone can solve my offsets.
My tkinter TIMER app, with bell:
import tkinter as tk
import calendar
from datetime import datetime
import time
import sys
class Countdown(tk.Frame):
def __init__(self,master):
tk.Frame.__init__(self, master)
global target
self.master = master
self.clock = tk.Label(text="00:00:00", fg="Green", font=("bold",40))
self.clock.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
timestamp = calendar.timegm(time.localtime())
print("Timestamp",timestamp)
print("Timestamp_future",timestamp + t_plus)
target = timestamp + t_plus
self.update_clock()
def update_clock(self):
current = calendar.timegm(time.localtime()) -23*3600
countdown = target - current
self.down_time = datetime.fromtimestamp(countdown).strftime("%H:%M:%S")
self.clock.configure(text=self.down_time)
if countdown/3600 != 23:
self.after(1000, self.update_clock)
else:
print('\a', end='', flush=True)
root = tk.Tk()
global t_plus
######### SET YOUR TIME FOR THE COUNTDOWN ############
your_timer_set = '00:00:05' # hours, minutes, seconds
######################################################
countdown = time.strptime(your_timer_set, '%H:%M:%S')
hours = int(countdown.tm_hour)
minutes = int(countdown.tm_min)
seconds = int(countdown.tm_sec)
t_plus = ((hours*3600)+(minutes*60)+seconds)
print("SET_TIME",t_plus)
app=Countdown(root)
root.title("Countdown")
root.geometry("400x400+400+150")
root.mainloop()

Creating a timer with tkinter

I am creating a GUI that needs to have timer starting from 0 and counting up every second until it reaches a limit
This is the code I have right now
limit = 10
score = 0
def update():
global score, limit
time.sleep(1)
score += 1
ScoreL.configure(text=score)
if score < limit:
update()
ScoreL = tkinter.Label(window, text = score)
ScoreL.pack()
update()
window.mainloop
Right now this is increasing the score each second but it does not open the GUI until the score reaches the limit. How can I make it so the GUI opens when the score is 0 but will continue to update each second?
You can use the after method of window to call a function after a specified no of milliseconds.
To use that for a timer you would call after again at the end of the called function.
In the function you would increment score until it reaches the limit, you would then use the after_cancel method to 'stop' the timer.
from tkinter import *
window = Tk()
def update(score, limit):
score +=1
if score < limit:
ScoreL.configure(text=score)
timer = window.after(1000, update, score)
else:
window.after_cancel(timer)
ScoreL.configure(text='Game over')
limit = 10
score = 0
ScoreL = Label(window, text = score)
ScoreL.pack()
timer = window.after(1000, update, score, limit)
window.mainloop()
time.sleep() will block tkinter mainloop from updating, so you can only see the result after the score reaches the limit because at that time tkinter mainloop takes back the control.
Use .after() instead:
import tkinter
window = tkinter.Tk()
limit = 10
score = 0
def update():
global score
score += 1
ScoreL.configure(text=score)
if score < limit:
# schedule next update 1 second later
window.after(1000, update)
ScoreL = tkinter.Label(window, text=score)
ScoreL.pack()
window.after(1000, update) # start the update 1 second later
window.mainloop()

Tkinter lag at the "update" command

I made a timer just to test something out. and for some reason it starts lagging, here is the timer:
from tkinter import *
from time import sleep
main = Tk()
main.title("Timer")
c = Canvas(main,width=1000,height=400)
c.pack()
c.config(bg="black")
hours = -1
while True:
hours += 1
for minutes in range(60):
for seconds in range(60):
c.create_rectangle(0,0,1000,400,fill="black")
c.create_text(500,200,text=(str(hours)+":"+str(minutes)+":"+str(seconds)),font=("",200),fill="white")
c.update()
sleep(1)
Can someone figure out where it happens? I noticed I can run the timer without tkinter and just with print, but I need to use tkinter for other projects too.
You are creating text widgets covered by black rectangles in succession, without ever removing them - in essence, every second, you are piling two more canvas items on top of all the previous ones!
The correct approach is to use the tkinter.mainloop in conjunction with the tkinter.after method, and steer clear from using a while loop, and tkinter.update. You can change the text displayed by a canvas text item using itemconfigure.
The use of time.sleep in a GUI is a recipe to have your GUI stop responding to interactions - don't do that!
Maybe do this instead:
import tkinter as tk
def update_clock():
clock_text = f'{hours}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}'
canvas.itemconfigure(clock, text=clock_text)
main.after(1000, _increment_time)
def _increment_time():
global clock, hours, minutes, seconds
seconds += 1
if seconds == 60:
seconds = 0
minutes += 1
if minutes == 60:
minutes = 0
hours += 1
update_clock()
main = tk.Tk()
main.title('Timer')
canvas = tk.Canvas(main, width=1000, height=400, bg='black')
canvas.pack()
hours, minutes, seconds = 0, 0, 0
clock = canvas.create_text(500, 200, font=('', 200), fill='white')
update_clock()
main.mainloop()

time function always gives me 0.0 output

Im trying to make a CPS counter, and when I reach 100 clicks, its supposed to print "test" and also print the time it took to get to 100 clicks. But it always gives 0.0 as the time output.
import tkinter
import time
counter = tkinter.Tk()
clicks = 0
def addClick():
global clicks
clicks = clicks + 1
lbl.configure(text=clicks)
start = time.time()
if clicks == 100:
print("test")
end = time.time()
print(start - end)
lbl = tkinter.Label(counter, text = clicks)
lbl.pack()
btn = tkinter.Button(counter, text="Click here", command=addClick)
btn.pack()
counter.mainloop()
...
start = time.time()
if clicks == 100:
print("test")
end = time.time()
print(start - end)
You keep restarting start after every click. A possible solution would be to start it only after the first click. This will require start to be a global variable as well.
Also note that you should do end - start, not start - end.
clicks = 0
start = None
...
global clicks
global start
...
if clicks == 1:
# instantiating 'start' only if it was the first click
start = time.time()
elif clicks == 100:
print("test")
end = time.time()
print(end - start)
However, using global variables is quite a code-smell and an anti-pattern, and we already have 2 of them in such a tiny program.
You can try to wrap them in a data-structure such as a dict:
import tkinter
import time
counter = tkinter.Tk()
data = {'clicks': 0, 'start': None}
def addClick():
data['clicks'] += 1
lbl.configure(text=data['clicks'])
if data['clicks'] == 1:
# instantiating 'start' only if it was the first click
data['start'] = time.time()
elif data['clicks'] == 100:
print("test")
end = time.time()
print(end - data['start'])
lbl = tkinter.Label(counter, text=data['clicks'])
lbl.pack()
btn = tkinter.Button(counter, text="Click here", command=addClick)
btn.pack()
counter.mainloop()
Another, real-world fitting solution would be to wrap the entire tkinter app in a class, that can keep track of its own state.

How to change countdown to hour/minutes/seconds

I am new to Tkinter so im just trying to learn as much as possible. I want to try and make an alarm clock but now im stuck on the time format. This is the current code:
from tkinter import *
teer = Tk()
field = Canvas(teer, bd=0, highlightthickness=0, height='190', width='400', bg='#111111')
field.pack()
def start_countdown(count):
coin = 0.5
teer.resizable(False,False)
counter = Label(teer, fg = "#287aff", bg='#232323', font = ("Lato", 35, "bold"), width='15')
counter.place(x=50, y=50)
counter["text"] = count
if count > 0:
teer.after(1000, start_countdown, count -1)
if count < 500:
coin = 0.6
if count < 300:
coin = 0.7
start_countdown(500)
teer.mainloop()
now what i've been trying to do is chop the 500 (seconds) up into minutes / seconds. Or ultimately change it to hours / minutes / seconds if i may choose to insert an int larger than 3600 into the function. I just want the time hardcoded so i thought it wouldn't be such a problem.
What i tried:
-Experimented with different alarms / countdowns that people made (sadly there aren't many out there that count down instead of up and are also in hours/minutes/seconds.
-Experimented with the format (for example) %H:%M:%S
I Just don't seem to get it.
Would appreciate any help or advice about making a GUI-program that counts down.
You can use divmod to calculate the remaining time.
import tkinter as tk
root = tk.Tk()
a = tk.Label(root,text="")
a.pack()
def set_idle_timer(t):
hours, remainder = divmod(t, 3600)
mins, secs = divmod(remainder, 60)
timeformat = "{:02d}:{:02d}:{:02d}".format(hours, mins, secs)
a.config(text=timeformat)
t -=1
root.after(1000,lambda: set_idle_timer(t))
set_idle_timer(3605)
root.mainloop()

Categories