Button isn't displaying the timer - python

I'm making a little game for my 4-year-old nephew. you plug two mice into the computer and try to press your button and I need the start button to turn into a timer. The problem is the button isn't displaying the time. It stops responding until the time is up and then displays "1". (keep in mind this is unfinished)
import tkinter
from tkinter import ttk
import time
score = 0
window = tkinter.Tk()
window.geometry("300x200")
def button1():
score = score - 1
def button2():
score = score + 1
def Start():
t = 30
while t > 0:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
start.config(text=t)
print(timer, end="\r")
time.sleep(1)
t -= 1
start = ttk.Button( window, text = "Start", command = Start)
start.pack(padx= 2, pady = 1)
one = ttk.Button( window, text = "Player 1", command = button1)
one.pack(padx= 2, pady = 1)
two = ttk.Button( window, text = "Player 2", command = button2)
two.pack(padx= 2, pady = 1)
window.mainloop()

Here is an answer on the while loops indegestibility with the tkinter mainloop.
You could also solve this by running your start() function in its own thread:
import threading
def start():
t = 30
while t > 0:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
start_button.config(text=t)
print(timer, end="\r")
time.sleep(1)
t -= 1
def start_in_thread():
thread = threading.Thread(target=start)
thread.daemon = True # forces thread to close when the tkinter mainloop is closed
thread.start()
start_button = ttk.Button(window, text="Start", command=start_in_thread)
Another issue is that you buttonX() functions change the global variable score without gloal keyword:
def button1():
global score
score = score - 1

You just need to remove the while and replace it with a call to after. I also cleaned up your script a little, and solved a future problem you were going to have. Using threading like the other answer states is overkill. You just need to stop blocking the mainloop with the while.
import tkinter as tk
window = tk.Tk()
window.geometry("300x200")
_PLAYTIME = 10
t, score = _PLAYTIME, 0
def tally(i):
global score
score += i
start = tk.Button(window, text="Start")
start.pack(padx= 2, pady = 1)
tk.Button(window, text="Player 1", command=lambda: tally(1)).pack(padx=2, pady=1)
tk.Button(window, text="Player 2", command=lambda: tally(-1)).pack(padx=2, pady=1)
def game():
global t, score
if t > 0:
timer = '{:02d}:{:02d}'.format(*divmod(t, 60))
start.config(text=f'Time: {timer} | Score: {score}')
t -= 1
start.after(1000, game)
else:
start.config(text=f'Score: {score} | Start')
t, score = _PLAYTIME, 0
start.configure(command=game)
window.mainloop()
Here's the exact same game, but made giant and colorful as I assume 4 year olds like things... ;). I also added a 3-2-1-Go! countdown after start is pressed so, the person pressing start doesn't lose an advantage. The red and green rects are your click area. Red subtracts and green adds.
I'm not sure if you are prepared for this but, plugging 2 mice into the same computer is not going to give you 2 pointers. Maybe that's part of your game ~ fighting a 4 year old for mouse control :D. If it isn't, you are going to need TeamPlayer or something similar. Or you could simply bind keyboard keys instead of the mouse.
import tkinter as tk
root = tk.Tk()
root.geometry("800x640")
root.title('CLICKFIGHT')
#seconds to play and player handicaps
_PLAYTIME = 10
_HANDICAP1 = 0
_HANDICAP2 = 0
#time, score, countdown
t, s, cd = _PLAYTIME, 0, 3
#gameboard
board = tk.Canvas(root, highlightthickness=0, width=800, height=640)
board.pack()
#score adjuster
def tally(i):
global s
s += i
#gameboard elements
player1 = board.create_rectangle(0, 0, 400, 600, fill="#ff0000")
player2 = board.create_rectangle(400, 0, 800, 600, fill="#00ff00")
timer = board.create_text(200, 50, font='Helvetica 40 bold', fill='#ffffff')
score = board.create_text(600, 50, font='Helvetica 40 bold', fill='#ffffff')
button = board.create_rectangle(0, 600, 800, 640, fill="#0000ff", tag="button")
start = board.create_text(400, 620, font='Helvetica 20 bold', fill='#ffffff', text="START", tag="button")
cdown = board.create_text(400, 300, font='Helvetica 80 bold', fill='#ffffff')
#game countdown
def countdown():
global cd, p
#so you can't click start again
board.tag_unbind("button", '<1>')
if cd > 0:
board.itemconfig(cdown, text=cd)
cd-=1
root.after(800, countdown)
else:
board.itemconfig(cdown, text='GO!')
root.after(200, game)
cd = 3
#actual game
def game():
global t, s
#game stats
board.itemconfig(timer, text='Time:\t{:02d}:{:02d}'.format(*divmod(t, 60)))
board.itemconfig(score, text=f'Score:\t{s}')
board.itemconfig(start, text='PLAYING...')
if t > 0:
#game looping
t -= 1
root.after(1000, game)
board.itemconfig(cdown, text='')
else:
#reset everything
t, s = _PLAYTIME, 0
board.itemconfig(start, text='START')
board.itemconfig(cdown, text='GAME OVER')
board.tag_bind("button", '<1>', lambda e: countdown())
#mouse events
board.tag_bind(player1 , '<1>', lambda e: tally(-(1+_HANDICAP1)))
board.tag_bind(player2 , '<1>', lambda e: tally(1+_HANDICAP2))
board.tag_bind("button", '<1>', lambda e: countdown())
root.mainloop()

Related

Tkinter Displaying Wrong Thing at Incorrect Time

I am trying to create a clicker game, and all the basic fundamentals are in; there's a button, you can click it, there's an upgrade, you can infinitely buy it while the price goes up 15%. Now, all of this is fine, but I want to implement a "prestige" for the upgrade; if you buy the upgrade 10 times, you will have to pay a price 5x the cost, but after that the upgrade will give you more rewards. I decided to put a counter in, and whenever you buy the upgrade, it goes up. Now, here is my problem. I put an if statement for when the counter reaches 10 (all of the data for that upgrade is stored in its own dictionary), it will change the text and cost. This does not work at all. Instead, it keeps the same text, and after buying it 1 - 3 more times, it will change the reward given, but nothing else.
Here is my code:
from tkinter import *
from threading import Timer
window = Tk()
window.minsize(1200, 500)
window.title("Clicker Game")
# general setup
default_font = ("Calibri", 15, "normal")
clicks = 0
click_power = 1
# upgrade variables
upgrade1_stats = {
"cost": 15,
"can buy": False,
"times bought": 0,
"click power given": 1,
"prestige": False,
}
# functions
def check_clicks():
global clicks, upgrade1_stats
if clicks >= 10:
upgrade1.place(x=200, y=50)
if clicks >= upgrade1_stats["cost"]:
upgrade1_stats["can buy"] = True
else:
upgrade1_stats["can buy"] = False
def get_upgrade1():
global clicks, upgrade1_stats, click_power
if upgrade1_stats["times bought"] == 10 and upgrade1_stats["can buy"]:
upgrade1_stats["cost"] = int(upgrade1_stats["cost"] * 5)
update_stats()
upgrade1_stats["prestige"] = False
upgrade1_stats["click power given"] += 1
upgrade1_stats["times bought"] += 1
if upgrade1_stats["can buy"]:
clicks -= upgrade1_stats["cost"]
click_power += upgrade1_stats["click power given"]
upgrade1_stats["cost"] = int(upgrade1_stats["cost"] * 1.15)
upgrade1_stats["times bought"] += 1
update_stats()
else:
not_enough_clicks()
check_clicks()
if upgrade1_stats["prestige"]:
upgrade1.config(text=f"MAX UPGRADES BOUGHT\nPRESTIGE COST: {upgrade1_stats['cost']}")
else:
upgrade1.config(text=f"+{upgrade1_stats['click power given']} Click Power\nCost: {upgrade1_stats['cost']}")
def not_enough_clicks():
new_label = Label(text="NOT ENOUGH CLICKS", font=("Calibri", 80, "bold"))
new_label.place(x=125, y=180)
t = Timer(1, new_label.destroy)
t.start()
def increase_score(*args):
global clicks
if len(args) > 0:
update_amount = args[0]
else:
update_amount = click_power
clicks += update_amount
update_stats()
check_clicks()
def update_stats(**kwargs):
global clicks, click_power
click_display.config(text=f"{clicks} Clicks")
if click_power == 1:
click_power_display.config(text=f"{click_power} Click per Button Click")
else:
click_power_display.config(text=f"{click_power} Clicks per Button Click")
# title text
title_label = Label(text="Clicker Game", font=("Calibri", 30, "bold"))
title_label.grid(column=1, columnspan=2, row=1)
window.grid_columnconfigure(2, weight=1)
window.grid_rowconfigure(1, weight=1)
# click display
click_display = Label(text=f"{clicks} Clicks", font=default_font)
click_display.grid(column=2, row=2)
window.grid_rowconfigure(2, weight=0, pad=70)
# button to click
clicker = Button(text="Click Me", command=increase_score, height=3, width=10, bg="red")
clicker.grid(column=2, row=3)
window.grid_rowconfigure(3, pad=200)
# click power display
click_power_display = Label(text=f"{click_power} Click per Button Click", font=default_font)
click_power_display.place(x=495, y=150)
# upgrades
upgrade1 = Button(text=f"+1 Click Power\nCost: {upgrade1_stats['cost']}", command=get_upgrade1)
window.mainloop()
The expectation is that once buying the upgrade 10 times, it would change the text to "MAX UPGRADES REACHED. PRESTIGE COST: (prestige cost here)". This is not at all what is happening, and I am very confused why. Any and all help would be greatly appreciated by me.
EDIT
With the help of #Alexander, I did update my code to this:
from tkinter import *
from threading import Timer
window = Tk()
window.minsize(1200, 500)
window.title("Clicker Game")
# general setup
default_font = ("Calibri", 15, "normal")
clicks = 0
click_power = 1
# upgrade variables
upgrade1_stats = {
"cost": 15,
"can buy": False,
"times bought": 0,
"click power given": 1,
"prestige": False,
}
# functions
def check_clicks():
global clicks, upgrade1_stats
if clicks >= 10:
upgrade1.place(x=200, y=50)
if clicks >= upgrade1_stats["cost"]:
upgrade1_stats["can buy"] = True
else:
upgrade1_stats["can buy"] = False
def get_upgrade1():
global clicks, upgrade1_stats, click_power
if upgrade1_stats["times bought"] == 10 and upgrade1_stats["can buy"]:
upgrade1_stats["cost"] = int(upgrade1_stats["cost"] * 5)
update_stats()
upgrade1_stats["prestige"] = True
upgrade1_stats["click power given"] += 1
upgrade1_stats["times bought"] += 1
elif upgrade1_stats["can buy"] and upgrade1_stats["times bought"] != 10:
clicks -= upgrade1_stats["cost"]
click_power += upgrade1_stats["click power given"]
upgrade1_stats["cost"] = int(upgrade1_stats["cost"] * 1.15)
upgrade1_stats["times bought"] += 1
upgrade1_stats["prestige"] = False
update_stats()
else:
not_enough_clicks()
check_clicks()
if upgrade1_stats["prestige"]:
upgrade1.config(text=f"MAX UPGRADES BOUGHT\nPRESTIGE COST: {upgrade1_stats['cost']}")
else:
upgrade1.config(text=f"+{upgrade1_stats['click power given']} Click Power\nCost: {upgrade1_stats['cost']}")
def not_enough_clicks():
new_label = Label(text="NOT ENOUGH CLICKS", font=("Calibri", 80, "bold"))
new_label.place(x=125, y=180)
t = Timer(1, new_label.destroy)
t.start()
def increase_score(*args):
global clicks
if len(args) > 0:
update_amount = args[0]
else:
update_amount = click_power
clicks += update_amount
update_stats()
check_clicks()
def update_stats(**kwargs):
global clicks, click_power
click_display.config(text=f"{clicks} Clicks")
if click_power == 1:
click_power_display.config(text=f"{click_power} Click per Button Click")
else:
click_power_display.config(text=f"{click_power} Clicks per Button Click")
# title text
title_label = Label(text="Clicker Game", font=("Calibri", 30, "bold"))
title_label.grid(column=1, columnspan=2, row=1)
window.grid_columnconfigure(2, weight=1)
window.grid_rowconfigure(1, weight=1)
# click display
click_display = Label(text=f"{clicks} Clicks", font=default_font)
click_display.grid(column=2, row=2)
window.grid_rowconfigure(2, weight=0, pad=70)
# button to click
clicker = Button(text="Click Me", command=increase_score, height=3, width=10, bg="red")
clicker.grid(column=2, row=3)
window.grid_rowconfigure(3, pad=200)
# click power display
click_power_display = Label(text=f"{click_power} Click per Button Click", font=default_font)
click_power_display.place(x=495, y=150)
# upgrades
upgrade1 = Button(text=f"+1 Click Power\nCost: {upgrade1_stats['cost']}", command=get_upgrade1)
window.mainloop()
Now, there is still another problem:
The prestige only shows up after "buying" the upgrade 2 or 3 times after you actually bought it 10 times. I want it to show up after EXACTLY 10 times. Could anybody help me with this?
The reason it takes more than 10 clicks for it to appear is because you do not increment the upgrade1_stats["times bought"] until after you have already checked how many times user has previously clicked the bonus. So it takes exactly 11 times.
The solution would be to increment the times bought before checking if it has been bought 10 times, or alternatively you could start the times bought counter at 1 instead of 0.
upgrade1_stats["times bought"] += 1
I would recommend moving the above line and making a few other alterations to reduce some unnecessary code and improve readability. See inline notes in example below.
from tkinter import *
from threading import Timer
window = Tk()
window.minsize(1200, 500)
window.title("Clicker Game")
# general setup
default_font = ("Calibri", 15, "normal")
clicks = 0
click_power = 1
# upgrade variables
upgrade1_stats = {
"cost": 15,
"times bought": 0,
"click power given": 1
}
def get_upgrade1():
global clicks, click_power
if clicks < upgrade1_stats["cost"]: # if there is not enough clicks
return not_enough_clicks() # exit the function early
upgrade1_stats["times bought"] += 1 # increase times bought before check
if upgrade1_stats["times bought"] == 10:
upgrade1_stats["cost"] *= 5 # simplified multiplication
upgrade1_stats["click power given"] += 1
upgrade1.config(text=f"+{upgrade1_stats['click power given']} Click Power\nCost: {upgrade1_stats['cost']}")
else:
clicks -= upgrade1_stats["cost"]
click_power += upgrade1_stats["click power given"]
upgrade1_stats["cost"] = int(upgrade1_stats["cost"] * 1.15)
upgrade1.config(text=f"MAX UPGRADES BOUGHT\nPRESTIGE COST: {upgrade1_stats['cost']}")
update_stats()
def not_enough_clicks():
new_label = Label(text="NOT ENOUGH CLICKS", font=("Calibri", 80, "bold"))
new_label.place(x=125, y=180)
t = Timer(1, new_label.destroy)
t.start()
def increase_score(*_):
global clicks
clicks += click_power
if clicks >= 10:
upgrade1.place(x=200, y=50)
update_stats()
def update_stats(**_):
verb = "Click" if click_power == 1 else "Clicks"
click_display.config(text=f"{clicks} {verb}")
click_power_display.config(text=f"{click_power} {verb} per Button Click")
# title text
title_label = Label(text="Clicker Game", font=("Calibri", 30, "bold"))
title_label.grid(column=1, columnspan=2, row=1)
window.grid_columnconfigure(2, weight=1)
window.grid_rowconfigure(1, weight=1)
# click display
click_display = Label(text=f"{clicks} Clicks", font=default_font)
click_display.grid(column=2, row=2)
window.grid_rowconfigure(2, weight=0, pad=70)
# button to click
clicker = Button(text="Click Me", command=increase_score, height=3, width=10, bg="red")
clicker.grid(column=2, row=3)
window.grid_rowconfigure(3, pad=200)
# click power display
click_power_display = Label(text=f"{click_power} Click per Button Click", font=default_font)
click_power_display.place(x=495, y=150)
# upgrades
upgrade1 = Button(text=f"+1 Click Power\nCost: {upgrade1_stats['cost']}", command=get_upgrade1)
window.mainloop()

Move window from "second plan/background(minimized)" to the "first plan/active window"

Sorry for my english guys,
I didn't find the correct words to explain this,
I developed a pomodoro clock, and I read the tkinter documentation, but I don't find how can I move the window to front(active window), to the first plan when the timer finish.
for example, if I minimize the app, I need when the time finish, It show on my screen.
could some one help me please? thank you!!
import tkinter as tk
import os
from tkinter.font import NORMAL
# ---------------------------- CONSTANTS ------------------------------- #
PINK = "#e2979c"
RED = "#e7305b"
GREEN = "#9bdeac"
YELLOW = "#f7f5dd"
FONT_NAME = "Courier"
WORK_MIN = 25
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
PATH = os.path.dirname(__file__)
reps = 0
# ---------------------------- TIMER RESET ------------------------------- #
def reset():
global loop, reps
lbl_mark.config(text='')
lbl1.config(text='Timer', fg=GREEN)
canvas.itemconfig(timer, text="00:00")
btn_start.config(state=NORMAL)
myapp.after_cancel(loop)
reps = 0
# ---------------------------- TIMER MECHANISM ------------------------------- #
def start_counter():
global reps
reps += 1
btn_start.config(state=tk.DISABLED)
if reps != 0 and reps % 8 == 0:
lbl_mark.config(text='✔' * int(reps/2))
lbl1.config(text="Long Break", fg=RED)
counter(LONG_BREAK_MIN * 60)
elif reps %2 == 0:
lbl_mark.config(text='✔'* int(reps/2))
lbl1.config(text="Break", fg=PINK)
counter(SHORT_BREAK_MIN * 60)
else:
lbl1.config(text="Work", fg=GREEN)
counter(WORK_MIN * 60)
# ---------------------------- COUNTDOWN MECHANISM ------------------------------- #
def counter(count):
global loop
minutes = int(count / 60)
seconds = count % 60
if seconds < 10:
seconds = f"0{seconds}"
if minutes < 10:
minutes = f"0{minutes}"
canvas.itemconfig(timer, text=f'{minutes}:{seconds}')
if count > 0:
loop = myapp.after(1000, counter, count -1)
else:
start_counter()
# ---------------------------- UI SETUP ------------------------------- #
myapp = tk.Tk()
myapp.title("Pomodoro")
myapp.config(padx=100, pady=50, bg=YELLOW)
#timer label
lbl1 = tk.Label(text="Timer", font=(FONT_NAME, 35, 'bold'),fg=GREEN, bg=YELLOW)
lbl1.grid(column=1, row=0)
#tomato
canvas = tk.Canvas(width=200, height=224, bg=YELLOW, highlightthickness=0)
tomato = tk.PhotoImage(file=os.path.join(PATH,"tomato.png"))
canvas.create_image(100,112, image=tomato)
canvas.grid(column=1, row=1)
#text inside tomato
timer = canvas.create_text(102,130, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
#button start
btn_start = tk.Button(text="Start", command=start_counter)
btn_start.grid(column=0, row=2)
#text mark
lbl_mark = tk.Label(font=(FONT_NAME, 14, 'bold'), fg=GREEN, bg=YELLOW)
lbl_mark.grid(column=1, row=3)
#button reset
btn_reset = tk.Button(text='Reset', command=reset)
btn_reset.grid(column=2, row=2)
myapp.mainloop()

Program lines are executing immediately before returning from function calls in python. Please explain program flow in event driven program

When the start timer is called by the button click, how is it that the lines following the count_down function call are executed immediately before the counter is completed and returned?
Also, if we double click the start button it behaves weirdly.
Please explain the program flow in event-driven programs in python.
from tkinter import *
import math
def start_timer():
work_sec = 15
count_down(work_sec)
timer_label.config(text="Work", fg="red")
print("Debug")
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
window.after(1000, count_down, count-1)
window = Tk()
timer_label = Label(text="Timer", fg="green", font=("Courier", 48, "bold"))
timer_label.pack()
canvas = Canvas(width=300, height=250, bg="black")
timer_text = canvas.create_text(150, 125, text="00:00", fill="white", font=("Arial", 35))
canvas.pack()
start_button = Button(text="start", command=start_timer)
start_button.pack()
window.mainloop()
The point of tk.after is that it doesn't freeze the GUI. In the code count_down updates the count and the canvas and then returns to keep the GUI responsive. In the code below I've added some print statements which show what's being called when in the console. I hope this makes the sequence of calls more obvious. Try clicking the 'start' button more than once a few seconds apart.
import tkinter as tk
from math import floor
start_timer_count = 0
def start_timer():
global start_timer_count
start_timer_count += 1
print( f"start_timer_called for {start_timer_count} th time." )
work_sec = 15
count_down(work_sec, start_timer_count )
print("start_completes")
def count_down(count, start_count ):
print( f"count_down called. count = {count}. Called by start_timer {start_count}." )
count_min = floor(count/60)
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
window.after(1000, count_down, count-1, start_count )
window = tk.Tk()
timer_label = tk.Label(text="Timer", fg="green", font=("Courier", 48, "bold"))
timer_label.pack()
canvas = tk.Canvas(width=300, height=250, bg="black")
timer_text = canvas.create_text(150, 125, text="00:00", fill="white", font=("Arial", 35))
canvas.pack()
start_button = tk.Button(text="start", command=start_timer)
start_button.pack()
window.mainloop()

Is there a way to make a function run after a specified amount of time in Python without the after method?

I am trying to create a simple program that tracks a user's clicks per second in Tkinter, but I have no idea how to make the program wait without freezing the program using the after method. The problem is that I need to log the high score after the time finishes, but using this method, the score logs before the click counter goes up. Here is my code:
from tkinter import *
import time
root = Tk()
root.geometry('600x410')
screen = Canvas(root)
h = 6 #button height
w = 12 #button width
c = 0 #counts amount of times clicked
start_btn = 0 #logs clicks of the start button
high_score = 0 #logs the highest score
time = 0
def count_hs():
high_score = c
def remove_time():
global time
time -= 1
def countdown(n):
for i in range(n):
time = n
root.after(1000, remove_time())
#alternatively i tried this:
#time.sleep(1)
#remove_time()
if time <= 0:
b["text"] = "Test done."
break
def start_test():
global start_btn
b["text"] = "Click to begin."
start_btn += 1
print("start button: " + str(start_btn))
def button_click():
global start_btn
global c
c+=1
print("click counter: " + str(c))
#resets the amount of clicks on the large button when the start button is pressed
if c >= 1 and start_btn >= 1:
print("test1")
c = 1
start_btn = 0
if b["text"] == "Click to begin.":
print("test2")
b["text"] = "Click!"
countdown(6)
count_hs()
print("hs: " +str(high_score))
#primary button
b = Button(root, text=" ", font=("Arial", 40), height = h, width = w, command = lambda: button_click())
b.grid(row=0, column=0)
#start button
start = Button(root, text="Start.", command = lambda: start_test())
start.grid(row=0, column=1)
root.mainloop()
Give it a try
from tkinter import *
root = Tk()
root.geometry('600x410')
screen = Canvas(root)
h = 6 # button height
w = 12 # button width
c = 0 # counts amount of times clicked
start_btn = 0 # logs clicks of the start button
high_score = 0 # logs the highest score
time = 0
def count_hs():
global high_score
if c > high_score:
high_score = c
return high_score
def remove_time():
global time
time -= 1
if time > 0:
root.after(1000, remove_time)
else:
show_score()
def start_test():
global start_btn
global c
global time
b["text"] = "Click to begin."
start_btn += 1
print("start button: " + str(start_btn))
# Reset your timer and counter
time = 6
c = 0
def button_click(*args):
global start_btn
global c
# resets the amount of clicks on the large button when the start button is pressed
if c == 0 and start_btn >= 1:
start_btn = 0
b["text"] = "Click!"
root.after(1000, remove_time)
print("hs: " + str(high_score))
else:
c += 1
print("click counter: " + str(c))
def show_score():
global c
score_label.configure(text=str(c))
high_score_label.configure(text=str(count_hs()))
c = 0
b['text'] = ""
# primary button
b = Button(root, text="", font=("Arial", 40), height=h, width=w, command=button_click)
b.grid(row=0, column=0, rowspan=5)
# start button
start = Button(root, text="Start.", command=lambda: start_test())
start.grid(row=0, column=1)
Label(root, text="Your score").grid(row=1, column=1)
score_label = Label(root, text="")
score_label.grid(row=2, column=1)
Label(root, text="High score").grid(row=3, column=1)
high_score_label = Label(root, text="")
high_score_label.grid(row=4, column=1)
root.mainloop()
Few changes:
In count_hs I assume you would update the highscore only if current score beats it.
You can use remove_time as a timer by making it calling itself until time <= 0, in which case you should end your game.
I've used the start button as a resetter, so that when it is clicked it will reset c and time.
On button_click you can now only bother with updating c (and change text at the beginning).
Finally I've added few labels to show the final results, both current and high scores.
Few suggestions to move on:
Instead of global variables you could create a class for the app, it should make it easier for you to exchange info and avoid subtle errors.
You could improve the layout, especially for the newly added labels.
You could make your original timer a parameter (currently, it is set within start_test).
Instead of importing from tkinter import *, I'd suggest you to do something like import tkinter as tk or from tkinter import ... since it increases readability and reduces the sources of errors.

Python: stop one incidence of recursive function and run another without resuming the first

I am trying to make a game using a countdown timer for 60s.
My issue is that as the countdown timer is recursive, it keeps running until t == 0 when it runs the endgame() function.
if you pass the level, the initial countdown timer will still end after 60s which in turn will end the game. I need a way to run the next level without the initial countdown ending and ending the game.
I have tried using if True statements at the start of the function which only paused the loop until the next instance of the function started.
code:
from tkinter import *
import ctypes
from scrambld_back import *
from tkinter import font
import time
from nltk.corpus import words
user32 = ctypes.windll.user32
screensize = [user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)]
root = Tk()
root.geometry(f'500x500+{screensize[1]-250}+100')
root.title('Scrambld')
root.configure(bg='white')
over = Tk()
over.geometry(f'500x500+{screensize[1]-250}+100')
root.title('Scrambld')
root.configure(bg='white')
over.iconify()
gamefont = font.Font(family='impact', size=30)
levelfont = font.Font(family='impact', size=20)
level = 0
Label(over, text='GAME OVER', font=gamefont)
Label(over, text=f'Level {level}', font=levelfont)
def endgame():
over = Toplevel(root)
over.geometry(f'500x500+{screensize[1]-250}+100')
Label(over, text='GAME OVER', font=gamefont).place(x=250, y=215, anchor='center')
Label(over, text=f'Level {level}', font=levelfont).place(x=250, y=285, anchor='center')
def play(level):
t = 15
gamewords = []
for x in words.words():
if len(x) == level+3:
gamewords.append(x)
word = gamewords[random.randint(0, len(gamewords))]
gameplay = generate(list(word))
Label(root, text=f'Level {level}', font=levelfont, bg='white').place(x=250, y=70, anchor='center')
Label(root, text=gameplay, font=gamefont, bg='white', width=100).place(x=250, y=140, anchor='center')
guess = Entry(root, font=levelfont, bg='white')
guess.place(x=250, y=360, anchor='center')
guess.bind('<Return>', lambda event, word=word: compare(event, word))
def compare(event, word):
if word.upper() == guess.get().upper():
play(level+1)
else:
pass
submit = Button(root, text='SUBMIT', font=levelfont, bg='white', width=21, command=lambda: compare(None, word))
submit.place(x=250, y=420, anchor='center')
timer = StringVar()
Label(root, textvariable=timer, font=levelfont, bg='white', width=8).place(x=250, y=250, anchor='center')
def countdown(t, lev):
print(lev, level)
print(t)
t=int(t)-1
if t < 10:
t=f'0{t}'
timer.set(f'00:{t}')
root.update()
if int(t) < 1 and lev == level:
endgame()
else:
root.after(1000, lambda: countdown(t, lev))
countdown(t, 1)
play(1)

Categories