How to stop this after function in python tkinter - python

I am trying to make a Stopwatch program and whenever i run it pressing the button , the function def watch() keeps executing itself and i cant stop it when needed.
is there any way to stop the execution of def watch() function after pressing the button?
Thanking you...
from tkinter import *
root = Tk()
tog = 0
hour = 0
mins = 0
sec = 0
def toggle():
global tog
tog = tog + 1
if tog == 1:
watch()
elif tog == 2:
stop()
tog = 0
def stop():
donothing = 0
def watch():
global sec
global hour
global mins
sec = sec + 1
l1.config(text=sec)
l1.after(1000,watch)
l1 = Label(root)
l1.pack()
Button(root,text="Start",command= lambda: toggle()).pack()
root.mainloop()

You should keep a reference to the after call, and cancel the callback when toggle is False. You can avoid ugly global declarations by using tkinter variables that are objects whose value can be read or set.
import tkinter as tk
def toggle_on_off():
toggle.set(not toggle.get())
if toggle.get():
watch()
def watch():
count_seconds.set(count_seconds.get() + 1)
if toggle.get():
_callback_id.set(root.after(1000, watch))
else:
root.after_cancel(_callback_id.get())
root = tk.Tk()
count_seconds = tk.IntVar(root)
count_seconds.set(0)
toggle = tk.BooleanVar(root)
toggle.set(False)
Button(root,text="Start",command=toggle_on_off).pack()
label = tk.Label(root, textvariable=count_seconds)
label.pack()
_callback_id = tk.StringVar(root)
_callback_id.set(None)
root.mainloop()
[Edit]
The same code with globals is like this:
import tkinter as tk
def toggle_on_off():
global toggle
toggle = not toggle
if toggle:
watch()
def watch():
global count_seconds, _callback_id
count_seconds += 1
label.configure(text=str(count_seconds))
if toggle:
_callback_id = root.after(1000, watch)
else:
root.after_cancel(_callback_id)
root = tk.Tk()
count_seconds = 0
toggle = False
Button(root,text="Start",command=toggle_on_off).pack()
label = tk.Label(root, text=str(count_seconds))
label.pack()
_callback_id = None
root.mainloop()

You can add a global variable , for eaxmple "exit",
and add an if statement before l1.after(1000,watch)
def toggle():
global tog, exit
exit = False
tog = tog + 1
if tog == 1:
watch()
elif tog == 2:
stop()
tog = 0
def watch():
global sec
global hour
global mins
global exit
sec = sec + 1
l1.config(text=sec)
if not exit:
l1.after(1000,watch)
in stop(), you can write
def stop():
global exit
exit = True

Related

name 'coding' is not defined

I wanted to make a game like a clicker, where coding brings money, but I ran into this error. The error seems to be due to the fact that 'coding' is in another function, but I don't understand how I can fix it. Please help me. I will be very grateful to you
from tkinter import *
from time import sleep
from threading import *
from tkinter import ttk
from tkinter import messagebox
button_track = False
button_code = True
wait_code = 30
def check_code():
global button_code
if button_code == True:
money.set(money.get() + 50)
button_code = False
def buy_klava_func():
if money.get() >= 50:
money.set(money.get() - 50)
buy_klava.grid_remove()
coding = Button(root, text='Написать код', command=check_code)
coding.grid(column=0, row=0)
def timer_code():
global button_code
global wait_code
while True:
if button_code == False:
coding['state'] = 'disabled'
sleep(wait_code)
coding['state'] = 'active'
button_code = True
root = Tk()
root.geometry('900x900')
money = IntVar()
money.set(50)
buy_klava = Button(root, text='Купить клавиатуру (50 монет)', command=buy_klava_func)
buy_klava.grid(column=0, row=0)
th_timer_code = Thread(target=timer_code)
th_timer_code.start()
root.mainloop()
Looks like coding is not a global variable. Declare it above all the functions, so that you can use it within all functions.
from tkinter import *
from time import sleep
from threading import *
from tkinter import ttk
from tkinter import messagebox
button_track = False
button_code = True
wait_code = 30
def check_code():
global button_code
if button_code == True:
money.set(money.get() + 50)
button_code = False
def buy_klava_func():
global coding # <=== define coding as global
if money.get() >= 50:
money.set(money.get() - 50)
buy_klava.grid_remove()
coding = Button(root, text='Написать код', command=check_code)
coding.grid(column=0, row=0)
def timer_code():
global button_code
global wait_code
global coding
while True:
if button_code == False:
coding['state'] = 'disabled'
sleep(wait_code)
coding['state'] = 'active'
button_code = True
root = Tk()
root.geometry('900x900')
money = IntVar()
money.set(50)
buy_klava = Button(root, text='Купить клавиатуру (50 монет)', command=buy_klava_func)
buy_klava.grid(column=0, row=0)
th_timer_code = Thread(target=timer_code)
th_timer_code.start()
root.mainloop()
It is because coding is a local variable, so it cannot be accessed inside timer_code().
Since you just replace buy_klava by coding, my suggestion is simply to configure buy_klava instead of removing it and creating coding:
def buy_klava_func():
if money.get() >= 50:
money.set(money.get() - 50)
"""
buy_klava.grid_remove()
coding = Button(root, text='Написать код', command=check_code)
coding.grid(column=0, row=0)
"""
# just configure "buy_klava" instead of removing it and create "coding"
buy_klava.config(text='Написать код', command=check_code)
def timer_code():
global button_code
#global wait_code
while True:
if button_code == False:
buy_klava['state'] = 'disabled' # update buy_klava
sleep(wait_code)
buy_klava['state'] = 'active' # update buy_klava
button_code = True

Python Program Breaks After 1 Run

Thanks so much for Helping, It's fixed now thanks to you guys.
The problem was after I got my cps count the program delayed and froze. It stopped showing the cps.
I can't find out why this happens or how to fix it. The program is a cps counter, it works after 1 try then it breaks and freezes if I use it a second time.
I also have no idea where the code breaks, thank you so much.
Here is the code:
import threading
from tkinter import *
import time
clicks = 0
cps = 0
start = True
wn = Tk()
wn.title("Cps Counter")
wn.geometry("400x300")
def cps5():
global start
global cps
global clicks
global t1
start = False
time.sleep(5)
cps = (clicks / 5)
clicks = 0
CpsButton.config(text=clicks)
CpsAmount.config(text=cps)
start = True
time.sleep(.1
t1 = threading.Thread(target=cps5)
t1.start()
def AddClicks():
global clicks
global start
clicks += 1
CpsButton.config(text=clicks)
if start == True:
cps5()
CpsButton = Button(wn, text=clicks, command=AddClicks, pady=15, padx=15)
CpsAmount = Label(wn, text=cps, pady=15, padx=15)
CpsAmount.pack()
CpsButton.pack()
wn.mainloop()
import threading
from tkinter import *
import time
clicks = 0
cps = 0
start = True
wn = Tk()
wn.title("Cps Counter")
wn.geometry("400x300")
clicks = 0
def cps5():
global start
global cps
global clicks
global t1
start = False
CpsButton.config(text=clicks)
start = True
def AddClicks():
global clicks
global start
clicks += 1
CpsButton.config(text=clicks)
if start == True:
cps5()
CpsButton = Button(wn, text=clicks, command=AddClicks, pady=15, padx=15)
CpsAmount = Label(wn, text=cps, pady=15, padx=15)
CpsAmount.pack()
CpsButton.pack()
def Timer():
global CpsAmount, cps, clicks
for i in range(0, 5):
time.sleep(1)
cps = (clicks / 5)
CpsAmount.config(text=cps)
clicks = 0
Timer()
t1 = threading.Thread(target=cps5)
t1.start()
t2 = threading.Thread(target=Timer)
t2.start()
wn.mainloop()
I created a second thread for the counter, because time.sleep freezes the program and caused trouble.

How to Update Tkinter Label while looking for keyboard input

I am Trying to make a tkinter based Cube Timer Program for which I have done a console based program before I wanted to improve on it by making a GUI for it... I have made some tkinter projects before and I have a decent knowledge of how stuff works the thing is I want the Label in the tkinter window to update in every second while listening for a spacebar key on press I couldn't figure out a way to do it can someone help me?
def on_press(key):
global chk,start,end,timer_label
print('{0} pressed'.format(key))
if key == Key.space:
if chk == True:
end = time.time()
chk = False
messagebox.showinfo('Your Time',"Your Time =>"+str(round(end-start,3)))
def on_release(key):
global chk,start,end,timer_label
if key == Key.space:
if chk == False:
start = time.time()
chk = True
with Listener(on_press=on_press,on_release=on_release) as listener:
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
global timer_label
timer_label = tk.StringVar()
timer_label.set("0.0")
tk.Label(timer_frame,text = timer_label.get()+"s",font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
main.mainloop()
This is what I have tried but didn't work
You need main.after to periodically run a function like updating the label. To listen to keyboard event, you need main.bind. See example below.
Enhancement: use tk.Label(..., textvariable=timer_label) means the label will automatically update when you set timer_label
import tkinter as tk
def key_press(event):
if event.keysym == 'space':
print('pressed')
def key_release(event):
if event.keysym == 'space':
print('released')
def update_label():
# get the time from the string
time = float(timer_label.get()[:-1])
# increment the time and put it back in timer_label
timer_label.set(str(time+1) + 's')
# calling this function again 1000ms later, which will call this again 1000ms later, ...
main.after(1000, update_label)
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
#textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
timer_label = tk.StringVar()
timer_label.set("0.0s")
# using textvariable, you can simply update timer_label and it will be reflected
tk.Label(timer_frame,textvariable = timer_label ,font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
# bind keypress and keyrelease to the functions
main.bind("<KeyPress>", key_press)
main.bind("<KeyRelease>", key_release)
# call update label function for the first time
update_label()
main.mainloop()

How to make timer/program open only after pressing key instead of immediately?

I need to make this clock open only after pressing a key, lets say "t". Now it opens immediately after running it.
import tkinter as tk
def update_timeText():
if (state):
global timer
timer[2] += 1
if (timer[2] >= 100):
timer[2] = 0
timer[1] += 1
if (timer[1] >= 60):
timer[0] += 1
timer[1] = 0
timeString = pattern.format(timer[0], timer[1], timer[2])
timeText.configure(text=timeString)
root.after(10, update_timeText)
def start():
global state
state=True
state = False
root = tk.Tk()
root.wm_title('Simple Kitchen Timer Example')
timer = [0, 0, 0]
pattern = '{0:02d}:{1:02d}:{2:02d}'
timeText = tk.Label(root, text="00:00:00", font=("Helvetica", 50))
timeText.pack()
startButton = tk.Button(root, text='Start', command=start)
startButton.pack()
update_timeText()
root.mainloop()
It is in another program so as I have my graphics window I will press "t" and the clock will open.
Keyboard is a python module that can detect keystrokes. Install it by doing this command.
pip install keyboard
Now you can do this.
while True:
try:
if keyboard.is_pressed('t'):
state = True
elif(state != True):
pass
except:
state = False
break #a key other than t the loop will break
I would recommend you to organize the code little bit, like class structure. One possible implementation would be like that:
import tkinter as tk
TIMER = [0, 0, 0]
PATTERN = '{0:02d}:{1:02d}:{2:02d}'
class Timer:
def __init__(self, master):
#I init some variables
self.master = master
self.state = False
self.startButton = tk.Button(root, text='Start', command=lambda: self.start())
self.startButton.pack()
self.timeText = tk.Label(root, text="00:00:00", font=("Helvetica", 50))
self.timeText.pack()
def start(self):
self.state = True
self.update_timeText()
def update_timeText(self):
if (self.state):
global TIMER
TIMER[2] += 1
if (TIMER[2] >= 100):
TIMER[2] = 0
TIMER[1] += 1
if (TIMER[1] >= 60):
TIMER[0] += 1
TIMER[1] = 0
timeString = PATTERN.format(TIMER[0], TIMER[1], TIMER[2])
self.timeText.configure(text=timeString)
self.master.after(10, self.update_timeText)
if __name__ == '__main__':
root = tk.Tk()
root.geometry("900x600")
root.title("Simple Kitchen Timer Example")
graph_class_object = Timer(master=root)
root.mainloop()
So clock will start when you click to button. If you want to start the clock by pressing "t" in keyboard, you need to bind that key to your function.
You can also add functionality if you want to stop the clock when you click to the button one more time.
EDIT:
if you also want to start to display the clock by clicking the button, you can move the code for initializing the label in to start function.
def start(self):
self.state = True
self.timeText = tk.Label(root, text="00:00:00", font=("Helvetica", 50))
self.timeText.pack()
self.update_timeText()

How to set up multiple frames

This is a simple math game which is currently in progress. The loop starts off in mainGame() which then proceeds to mainMenu(). I am trying to create 2 frames; mframe and gframe in order to .destroy() the frames later on, essentially clearing the previous interface for the next one (similar to changing pages).
error:
Label(gframe, textvariable=self.question_var).pack() #gframe stands
for game frame NameError: name 'gframe' is not defined
from tkinter import *
from random import randint
root = Tk()
mframe = Frame(root).pack()
gframe = Frame(root).pack()
frame.pack()
start = True
class mainMenu:
def __init__(self):
gframe.destroy() #gets rid of previous interface
title = Label(mframe, text = "main menu").pack() #mfame stands for menu frame
class mainGame:
def __init__(self):
if start == False:
mframe.destroy() #gets rid of previous interface
#question
self.question_var = StringVar()
Label(gframe, textvariable=self.question_var).pack() #gframe stands for game frame
#answer
self.user_answer_var = StringVar()
entry = Entry(gframe, textvariable=self.user_answer_var)
entry.pack()
submit = Button(gframe, text = "submit", command = self.check_answer).pack()
#response output
self.response_var = StringVar()
self.count = 0
self.score = 0
Label(gframe, textvariable=self.response_var).pack()
#starts loop
self.ask_question()
root.mainloop()
def ask_question(self):
if self.count == 1:
self.endGame()
num1 = randint(1, 10)
num2 = randint(1, 10)
self.question_var.set("What is "+str(num1)+" + " +str(num2)+"?")
self.true_answer = num1 + num2
#print(self.true_answer) #testing purposes
def check_answer(self):
self.count += 1
user_answer = self.user_answer_var.get()
#print(user_answer) #testing purposes
if int(user_answer) == self.true_answer:
text = "Good job"
self.score += 1
else:
text = "Oh no"
self.response_var.set(text)
#clear answer for next loop
self.user_answer_var.set("")
self.ask_question()
def endGame(self):
print("endGame")
mainMenu()
mainGame()
As said in the comments above, the pack() method returns None. What you need to do is first create the two frames and assign them to variables, then pack them later. This way, the variables still point to the frame instances and not None.
You should change;
root = Tk()
mframe = Frame(root).pack()
gframe = Frame(root).pack()
frame.pack()
start = True
to;
root = Tk()
mframe = Frame(root)
gframe = Frame(root)
mframe.pack()
gframe.pack()
start = True

Categories

Resources