Python Tkinter Timer - python

I am trying to make a clock in Tkinter and I am trying to create a timer however, whenever I try to start it the label just remains at the number chosen.
For the clock, I have an update variable that is always refreshing so I am trying to incorporate the timer into that. This is the update code:
def Update():
now = datetime.now()
time = now.strftime("%H:%M:%S")
Time.config(text = f"{time}")
if TimerOn == True:
root.after(1000, Update)
root.after(1000, TimerUpdate)
else:
root.after(1000, Update)
The "TimerOn" Variable is set true here:
def Start():
Timer.config(font = ("Times New Roman", 50))
Timer.place(x = "160", y = "250")
TimerOn = True

Related

Tkinter very odd behaviour when using the "root.after(1000)" function

in tkinter I am trying to create a stopwatch/timer and I have the basic code done however something odd happens and I have no idea how to explain it so just see this video.
https://imgur.com/a/H38faKM
(and if just keeps on going)
Here is the code that handles the timer:
def TimerUpdate():
global TimerVar
TimerVar = TimerVar - 1
Timer.config(text = f"{TimerVar}")
root.after(1000, TimerUpdate)
def Update():
global TimerOn
now = datetime.now()
time = now.strftime("%H:%M:%S")
Time.config(text = f"{time}")
if TimerOn == True:
root.after(1000, TimerUpdate)
else:
root.after(1000, Update)
And here is the code that mentions the "TimerOn" variable
def Start():
global TimerOn
Timer.config(font = ("Times New Roman", 50))
Timer.place(x = "160", y = "250")
TimerOn = True
I am obviously trying to make it go down each second but I have no idea whats happening here.
After TimerOn = True the update function checks if TimerOn == True and since it's True it keeps calling the TimerUpdate function every time. Try making TimerOn = False after calling TimerUpdate:
if TimerOn == True:
root.after(1000, TimerUpdate)
TimerOn = False
else:
root.after(1000, Update)

After() method in tkinter for timer

I want to start a timer when the user clicks a button for the first time in my number click game. I tried to use the after method for this, but when I click a button, the timer stays at 0. The rest of the code works fine without any error messages.
Here's the code:
import tkinter as tk
from random import randint
# create window
window = tk.Tk()
window.title('Clicker')
# create list for random numbers
new_list = []
# define time count:
def time_event():
global current_time, after_id
if clock_started:
current_time += 1
clock["text"] = str(current_time)
after_id = clock.after(1000, time_event)
# define click event
def click_event(event):
global clock_started
new_button = event.widget
clicked_val = int(new_button["text"])
clock_started = True
if not clock_started:
clock_started = True
clock.after(1000, time_event)
if clicked_val == new_list[0]:
del new_list[0]
new_button["state"] = tk.DISABLED
if len(new_list) == 0:
clock.started = False
clock.after_cancel(after_id)
# create buttons
for i in range(25):
new_num = randint(1, 999)
while i in new_list:
new_num = randint(1, 999)
new_list.append(new_num)
new_list.sort()
new_button = tk.Button(window, text=new_num)
new_button.grid(column=i // 5, row=i % 5)
new_button.bind("<Button-1>", click_event)
# create clock
current_time = 0
clock = tk.Label(window, text=str(current_time))
clock.grid(column=2, row=6)
clock_started = False
# run game
window.mainloop()
In your code, clock_started has been initialized to True which implies that this condition if not clock_started: will not be satisfied to begin with and hence the timer doesn't work without giving an error. Your final click_event(event) should look like this:
def click_event(event):
global clock_started
new_button = event.widget
clicked_val = int(new_button["text"])
clock_started = False
if not clock_started:
clock_started = True
clock.after(1000, time_event)
if clicked_val == new_list[0]:
del new_list[0]
new_button["state"] = tk.DISABLED
if len(new_list) == 0:
clock.started = False
clock.after_cancel(after_id)

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 stop timer at 20 minutes

I am making a timer that starts when the user hits "space" and stops on "p", showing the ending time. I can I stop it at a maximum time of 20 minutes? Is there something like
if time_passed==20:
break
My code:
from turtle import*
from datetime import datetime
...
def start():
undo()
global break1, timerint, startime
break1 = 0
startime = datetime.now()
while True:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
if break1 == 1:
break
def stop():
global break1, timerint, startime
timerint=datetime.now()-startime
write(timerint,font=("Arial",50))
break1 = 1
# Turtle placement code removed
onkeypress(start,"space")
onkeypress(stop,"p")
listen()
No, but you can always check elapsed time with the time.time() method.
import time
start = time.time()
while ...
....
now = time.time()
if now - start > 20 * 60:
break
That's the low-tech version. If you want more sophisticated things, such as a separate timer process, try a full browser search for "Python timer process".
Also, you might consider using Boolean values:
global timer_running, timerint, startime
timer_running = True
startime = datetime.now()
while timer_running:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
def stop():
global timer_running, timerint, startime
timerint = datetime.now()-startime
write(timerint, font=("Arial", 50))
timer_running = False
I recommend getting rid of the while loop and instead build upon turtle's ontimer() events:
from turtle import Turtle, Screen
from datetime import datetime
FONT = ("Arial", 50)
def start():
global timer_running, start_time
if timer_running:
return
start_time = datetime.now()
timer_running = True
screen.ontimer(lambda time=start_time: automatic_stop(time), 20 * 60 * 1000)
screen.ontimer(update, 100)
def update():
if not timer_running:
return
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
screen.ontimer(update, 100)
def manual_stop():
global timer_running
if not timer_running:
return
timer_running = False
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
def automatic_stop(time):
global timer_running
if timer_running and start_time == time: # make sure *this* timer is still valid
timer_running = False
marker.undo()
marker.write("Expired!", align='center', font=FONT)
screen = Screen()
marker = Turtle(visible=False)
marker.penup()
marker.write("Hit 'space' to start timer; 'p' to stop", align='center', font=FONT)
start_time = None
timer_running = False
screen.onkeypress(start, "space")
screen.onkeypress(manual_stop, "p")
screen.listen()
screen.mainloop()
We pass automatic_stop() a copy of start_time so that when it wakes up in the distant future it can check if it is still a valid end event or not based on the current start_time. (If you work at the Tk level instead of turtle, you might be able to cancel the timer when no longer needed.)

Tkinter events outside of the mainloop?

The program I am writing has a tkinter window that is constantly being fed with data manually rather than being part of a mainloop. It also needs to track mouse location. I havn't found a workaround for tracking the mouse outside of mainloop yet, but if you have one please do tell.
from Tkinter import *
import random
import time
def getCoords(event):
xm, ym = event.x, event.y
str1 = "mouse at x=%d y=%d" % (xm, ym)
print str1
class iciclePhysics(object):
def __init__(self, fallrange, speed=5):
self.speed = speed
self.xpos = random.choice(range(0,fallrange))
self.ypos = 0
def draw(self,canvas):
try:
self.id = canvas.create_polygon(self.xpos-10, self.ypos, self.xpos+10, self.ypos, self.xpos, self.ypos+25, fill = 'lightblue')
except:
pass
def fall(self,canvas):
self.ypos+=self.speed
canvas.move(self.id, 0, self.ypos)
root = Tk()
mainFrame = Frame(root, bg= 'yellow', width=300, height=200)
mainFrame.pack()
mainCanvas = Canvas(mainFrame, bg = 'black', height = 500, width = 500, cursor = 'circle')
mainCanvas.bind("<Motion>", getCoords)
mainCanvas.pack()
root.resizable(0, 0)
difficulty = 1500
#root.mainloop()
currentIcicles = [iciclePhysics(difficulty)]
root.update()
currentIcicles[0].draw(mainCanvas)
root.update_idletasks()
time.sleep(0.1)
currentIcicles[0].fall(mainCanvas)
root.update_idletasks()
tracker = 0
sleeptime = 0.04
while True:
tracker+=1
time.sleep(sleeptime)
if tracker % 3 == 0 and difficulty > 500:
difficulty -= 1
elif difficulty <= 500:
sleeptime-=.00002
currentIcicles.append(iciclePhysics(difficulty))
currentIcicles[len(currentIcicles)-1].draw(mainCanvas)
for i in range(len(currentIcicles)):
currentIcicles[i].fall(mainCanvas)
root.update_idletasks()
for i in currentIcicles:
if i.ypos >= 90:
currentIcicles.remove(i)
root.update_idletasks()
There is no way. Mouse movement is presented to the GUI as a series of events. In order to process events, the event loop must be running.
Also, you should pretty much never do a sleep inside a GUI application. All that does is freeze the GUI during the sleep.
Another hint: you only need to create an icicle once; to make it fall you can use the move method of the canvas.
If you are having problems understanding event based programming, the solution isn't to avoid the event loop, the solution is to learn how event loops work. You pretty much can't create a GUI without it.

Categories