Creating a timer with tkinter - python

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()

Related

How can I use a single button to pause and unpause timer in tkinter? (without using pygame)

I'm working on a simple timer that counts down 30 mins for me to study and 5 mins for a break. So far, the start_timer function and count_down function work well, I just cannot figure out how to write the pause function. I did some research for a few days. Most articles are using pygame or to bind with different keys. I am wondering what function I should use for one tkinter button to pause/unpause my timer if something comes up and I want to pause the timer till I'm back.
Thank you #TimRoberts, I can pause the timer now. However, I don't know how to unpause the timer to let it continue counting down.
from tkinter import *
import math
WORK_MIN = 30
BREAK_MIN = 5
reps = 0
paused = False
# --------------------------- TIMER ---------------------------- #
def start_timer():
global reps
reps += 1
work_sec = WORK_MIN * 60
break_sec = BREAK_MIN * 60
if reps % 2 == 1:
title_label.config(text="Study")
count_down(work_sec)
else:
title_label.config(text="Break")
count_down(break_sec)
window.attributes('-topmost', 0)
# ------------------------ COUNTDOWN--------------------------- #
def count_down(count):
global paused
count_min = math.floor(count / 60)
count_sec = count % 60
if count_min < 10:
count_min = f"0{count_min}"
if count_sec < 10:
count_sec = f"0{count_sec}"
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}" )
if count > 0:
if not paused:
count -= 1
window.after(1000, count_down, count-1)
else:
start_timer()
# ---------------------------- PAUSE ------------------------------- #
def pause_function():
global paused
paused = not paused
# ---------------------------- UI ------------------------------- #
window = Tk()
title_label = Label(text="Timer")
title_label.grid(column=1, row=0)
check_marks = Label(text="")
check_marks.grid(column=1, row=4)
canvas = Canvas(width=200, height=224, bg="lightblue")
timer_text = canvas.create_text(100, 128, text="00:00", fill="white", font=("Courier", 45, "bold"))
canvas.grid(column=1, row=1)
start_button = Button(text="Start", command=start_timer)
start_button.grid(column=0, row=2)
pause_button = Button(text="Pause", command=pause_function)
pause_button.grid(column=2, row=2)
window.mainloop()
You need to do the "after" call even if you're paused, otherwise you'll never notice when you unpause. Also, since you're decrementing count once, you don't need to do it again:
def count_down(count):
count_min = count // 60
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min:02d}:{count_sec:02d}" )
if count:
if not paused:
count -= 1
window.after(1000, count_down, count)
else:
start_timer()
If you want to be tricky, you could use:
if count:
count -= not paused
since True is 1 and False is 0.

level and coins increases as the button is pressed

We just started Tkinter and as our professor in programming asked us if we could try to make a game using Tkinter as our final project in 1st year college, specifically 4 pics 1 word, I need assistance as to how can I increase the level to +1 and the coins to +10 whenever the next button is pressed? any insights are very much appreciated. The code and sample output is given below for reference
from distutils.cmd import Command
from tkinter import *
from turtle import heading, width
from PIL import Image, ImageTk
root = Tk()
root.title("4 pics 1 word")
root.geometry("500x650")
root.maxsize(500,650)
root.iconbitmap ("4picslogo.ico")
#Variable
levelno = 1
coins = 100
counter = 0
picNum = 0
#def
def nextlevel():
global levelno
levelno = levelno + 1
if levelno==50:
levelno=1
global coins
coins = coins + 10
def changeImage():
global picNum
picNum+=1
if picNum==50:
picNum=0
pics.config(file=picfiles[picNum]+".png")
nextPic.config(text="PASS "+str(picNum+1))
#Frames
frame_1 = Frame(root, width=500, height=150)
frame_1.pack (side=TOP)
frame_2 = Frame(root,width=500, height=300)
frame_2.pack ()
frame_3 = Frame(root, width=500, height=200)
frame_3.pack ()
#FrameOne (Level and coins bar)
Blue_bar = Label (frame_1, width=71,height=5,bg="#4472c4")
Blue_bar.grid()
levelcounter = Label (frame_1, text="Level: "+str(levelno), font=("Helvetica",25,"bold"),fg="white",bg="#4472c4")
levelcounter.grid(row=0,column=0,sticky=W)
coinimg = PhotoImage(file="coins.png")
coins_pic = Canvas(frame_1, width=55, height=55, bg="#4472c4")
coins_pic.create_image(30,30, image=coinimg)
coins_pic.grid(row=0,column=0,padx=(320,0))
coin_counter = Label (frame_1, text=""+str(coins), font=("Helvetica",25,"bold"),fg="white",bg="#4472c4")
coin_counter.grid(row=0,column=0,sticky=E,padx=(0,1))
#FrameTwo (Pictures to guess)
################################################################################################################################
f = open("picList.txt","r")
x = f.readlines()
picfiles = list()
for p in x:
fn = p.strip().split(';')
picfiles.append(fn[1])
pics = PhotoImage(file=picfiles[0]+".png")
pic_viewer = Label(frame_2,image=pics)
pic_viewer.grid(row=0,column=0,pady=(40,0))
################################################################################################################################
#FrameThree (Buttons)
button_picture_pass = PhotoImage(file='pass.png')
nextPic = Button(frame_3,image=button_picture_pass,text=""+str(levelno+1),command=lambda:[changeImage(), nextlevel()])
nextPic.grid(padx=(400,0),pady=(135,0))
quitbutton = Button(root,text="Save and Quit", command = root.quit)
quitbutton.pack(anchor=E,padx=(0,22))
root.mainloop()
Updating levelno and coins will not update levelcounter and coin_counter automatically. You need to update them inside nextlevel():
def nextlevel():
global levelno, coins
levelno += 1
if levelno == 50:
levelno = 1
coins += 10
# update counter labels
levelcounter['text'] = f'Level: {levelno}'
coin_counter['text'] = coins
I'm not familiar with this code but do have some experience in writing code in general.
it seems the thing you want is already in this function
def nextlevel():
global levelno
levelno = levelno + 1
if levelno==50:
levelno=1
global coins
coins = coins + 10
and here you define the Next button:
button_picture_pass = PhotoImage(file='pass.png')
and here you trigger the function to increase the level and money when pressed:
nextPic = Button(frame_3,image=button_picture_pass,text=""+str(levelno+1),command=lambda:[changeImage(), nextlevel()])
so I'm not sure what the question is :D

Python Tkinter Timer

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

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.

Categories