Stopping program with button Tkinter Python 3 - python

I have a program that works with an user interface : I start it using a button and I would like to have the possibility to stop it during the process using the user interface:
The problem is : When I start the program, I cannot click on any buttons because when I drag the mouse in the tkinter window I have the "looping" / "waiting" cursor and it doesn't allow me to click on any buttons inside the interface.
I tried to start the infinit loop function in a new thread, but I don't know if it is really a good way to solve my problem... Any of you have any solutions ?
I try to simulate my problem with a simple code :
from tkinter import *
import loop
window = Tk()
window.title("test")
start = Button(window, text="start", bg ="green", command=loop.start)
stop = Button(window, text="stop", bg ="green", command=loop.stop)
thread = Button(window, text="threads", bg ="green", command=loop.how_many_threads)
start.grid(column = 1, row = 1)
stop.grid(column = 2, row = 1)
thread.grid(column = 3 , row = 1)
window.mainloop()
and few functions :
import threading
continu = True
def infinit(x):
cpt = 0
while(x):
cpt += 1
#print(cpt)
print("loop is over !")
def start():
threadSimulation = threading.Thread(target= infinit(continu)).start()
def stop():
self.continu = False
def how_many_threads():
for t in threading.enumerate():
print("thread names : ",t.getName())

Here's how to fix your implementation. The biggest error is that you have to provide arguments to the thread in the args part, not like a normal function call.
import tkinter as tk
import threading
import time
def infinit(x):
cpt = 0
while(continu):
cpt += 1
print(cpt)
time.sleep(1)
print("loop is over !")
def start():
threadSimulation = threading.Thread(target= infinit, args=(continu,))
threadSimulation.daemon = True
threadSimulation.start()
def stop():
global continu
continu = False
def how_many_threads():
for t in threading.enumerate():
print("thread names : ",t.getName())
continu = True
window = tk.Tk()
window.title("test")
start = tk.Button(window, text="start", bg ="green", command=start)
stop = tk.Button(window, text="stop", bg ="green", command=stop)
thread = tk.Button(window, text="threads", bg ="green", command=how_many_threads)
start.grid(column = 1, row = 1)
stop.grid(column = 2, row = 1)
thread.grid(column = 3 , row = 1)
window.mainloop()

If you want to close the program, you can use "window.quit" in the command section of your button like this:
stop = Button(window, text="stop", bg ="green", command=window.quit)

Related

after_cancel doesn't work in python tkinter

I made a simple countdown in my trivia game and every time that I go to the next slide it doesn't cancel the previous countdown timer and overlaps with the previous one.
I searched about that on the web and found the after_cancel function that cancels the timer after I go to the next slide and then I recreate it. But it still overlaps even after I added this function.
I think that I didn't give the after_cancel the correct arguments.
from tkinter import *
window = Tk()
window.attributes('-fullscreen',True)
WaitState = StringVar()
count = 10
button_label = StringVar()
def clear():
WaitState.set(1)
for widgets in window.winfo_children():
widgets.destroy()
def clear_time():
clear()
time_up_label = Label(
window,
text="Time is up",
bg = "#6378ff",
fg="#000000",
font=("Arial", 100,"bold"))
time_up_label.place(relx = 0.5,rely = 0.5,anchor = 'center')
continue_but = Button(
window,
text="Continue",
font=("Knewave",25,"bold"),
bg="#942222",
width=11,
height=5,
command=clear)
continue_but.place(relx = 1.0,rely = 1.0,anchor =SE)
continue_but.wait_variable(WaitState)
def button_countdown(i, label):
if i > 0:
i -= 1
label.set(i)
window.after_id = window.after(1000, lambda: button_countdown(i, label))
else:
window.after_cancel(window.after_id)
clear_time()
for index in range(5):
continue_but = Button(
window,
text="Continue",
font=("Knewave",25,"bold"),
bg="#942222",
width=11,
height=5,
command=clear)
continue_but.place(relx = 1.0,rely = 1.0,anchor =SE)
button_label.set(count)
timer_label = Label(
window,
textvariable=button_label,
bg="#6378ff",
fg="#ff0000",
font=("Arial",46,"bold"))
timer_label.pack()
button_countdown(count, button_label)
continue_but.wait_variable(WaitState)
window.mainloop()

use a while true loop while the code below is still running (python)

Im making this game called:
IDLE PEN ,(MAKE PENS)
and every 1 second i get a bonus pen
how i get a bonus pen is doing this
Import time
While true
make a pen()
time.sleep(1)
but i have some code under the while true loop.
the code under the while true loop is like buttons to
upgrade the pens or make a pen
So how do i make the code under the while true loop work?
This is my game im happy for anyone to copy it
its not ready yet
import functools
import tkinter
import tkinter.messagebox
import time
from random import seed
from random import randint
# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
window = tkinter.Tk()
window.title('Idle Pen')
def print_pen(number: int):
return f"Pens: {number}"
class pencount:
def __init__(self):
self.pencount = 0
self.text = tkinter.Text(height=1, width=30)
self.text.insert("1.0", print_pen(0))
self.text['state'] = 'disabled'
self.text.pack()
def changepencount(self, count):
if self.pencount + count < 0:
return
self.pencount = self.pencount + count
self.text['state'] = 'normal'
self.text.delete("1.0", "end")
self.text.insert("1.0", print_pen(self.pencount))
self.text['state'] = 'disabled'
self.text.pack()
pen = pencount()
changepenup = functools.partial(pen.changepencount, 1)
B = tkinter.Button(window, text="Make Pen", command=changepenup)
changependown = functools.partial(pen.changepencount, -100)
A = tkinter.Button(window, text='Penmaker', command=changependown)
Q = tkinter.Button(window, text="Quit", command=window.destroy)
U = tkinter.Button
# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
B.pack()
A.pack()
Q.pack()
window.mainloop()
You could use threading to run your loop in separated thread and then main thread may run tkitner
OR you can use tkinter function after() (instead of while True) to run function with delay and this function should use again after() to run itself.
import tkinter as tk
# --- functions ---
def update():
global value
value += 1
text = f'make penny: {value}'
print(text)
label['text'] = text
# run again after 1s (1000ms)
root.after(1000, update)
# --- main ---
value = 0
root = tk.Tk()
label = tk.Label(root, text="make penny: 0")
label.pack()
button = tk.Button(root, text="Exit", command=root.destroy)
button.pack()
# run first time after 1s (1000ms)
root.after(1000, update)
root.mainloop()

How to change tkinter label while another process is running?

I have a large code where a button press is supposed to run a code that will take roughly 15 seconds to complete. Within this time I want to display a label that says "Processing, please wait" or something of that sort. However in python, the whole GUI created using tkinter will freeze and unfreeze once the procedure is over. How do I get around to doing this? I created a smaller code so that I can explain easier.
from tkinter import *
from threading import Thread
import os
import sys
import time
master = Tk()
master.geometry("500x500")
master.resizable(False,False)
def tryout():
sign2.config(text = "AAA")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "BBB")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "CCC")
def close_window():
master.destroy()
sys.exit()
sign1 = Label(master, text = "VNA GUI").grid(pady=10, padx=10)
sign2 = Label(master, text = "Choose option to continue")
sign2.grid(pady=10, padx=10, ipadx=50)
Button(master, text='Exit', command=close_window).grid(pady=10, padx=20)
butTest = Button(master, text='test', command=tryout)
butTest.grid(pady=10, padx=20)
master.mainloop( )
So in this code I expect to see 'AAA' on the label first, followed by 'BBB' at the middle of the count from 0 to 4, and then 'CCC' at the end of the final count from 0 to 4. What happens here is the GUI freezes at the beginning, the count carries on and I just see 'CCC'. How do I get around this?
There are only a few changes necessary to do that with threading.
First create a function start_tryout:
def start_tryout():
Thread(target=tryout, daemon=True).start() # deamon=True is important so that you can close the program correctly
Then create the button with the new command:
butTest = Button(master, text='test', command=start_tryout)
Then it should no longer freeze the gui and you should be able to see the label change.
You can try threading. I've made changes below to the code and tested it here, and it worked.
from tkinter import *
from threading import Thread
import os
import sys
import time
import threading # NEW
master = Tk()
master.geometry("500x500")
master.resizable(False,False)
def tryout():
sign2.config(text = "AAA")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "BBB")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "CCC")
def close_window():
master.destroy()
sys.exit()
def thread(): # NEW
threading.Thread(target=tryout).start() # NEW
sign1 = Label(master, text = "VNA GUI").grid(pady=10, padx=10)
sign2 = Label(master, text = "Choose option to continue")
sign2.grid(pady=10, padx=10, ipadx=50)
Button(master, text='Exit', command=close_window).grid(pady=10, padx=20)
butTest = Button(master, text='test', command=thread) # Changed
butTest.grid(pady=10, padx=20)
master.mainloop( )

Interrupting Loops in a tkinter GUI via a button.

I'm coding a timer app, which has encountered a lot of difficulties, but one of the first ones has been in making a loop that can be broken by the press of a button. I've looked into it, and my research shows I should use threading, but I couldn't figure out how it would work.
What I decided to try, then, was to make an exception when I invoked a keyboard interrupt, and then make a button that calls that same interrupt. However, my current code refuses to interrupt when I ctrl-c.
My sample code looks like this
from Tkinter import *
from sys import exit
class Timer:
def __init__(self, master):
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command= quit)
buttonquit.grid(row = 1, column = 2)
global timertext
timertext = DoubleVar()
timertext.set(0)
display = Label(master, textvariable = timertext)
display.grid(row = 0, column = 0)
timertext.set(timertext)
def timerlogic(self):
pass
def pause(self):
pass
def start(self):
global timertext
try:
while True:
#do things
except KeyboardInterrupt:
print "Interrupted"
def lap(self):
pass
root = Tk()
app = Timer(root)
root.mainloop()
root.destroy()
Basically, I don't think my code as it stands is viable, but I don't know how to edit it to make a loop I can interrupt as needed.
You set some variable to True or False. Also, a while loop interrupts the Tkinter loop so Tkinter will do nothing until the while loop exits. Use Tkinter's after function instead.
from Tkinter import *
##from sys import exit
class Timer:
def __init__(self, master):
self.master=master
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command=self.quitit)
buttonquit.grid(row = 1, column = 2)
self.timertext = DoubleVar()
self.timertext.set(0)
display = Label(master, textvariable = self.timertext)
display.grid(row = 0, column = 0)
## timertext.set(timertext) ## Huh!!
self.timeit=False
def increment_timer(self):
ctr=int(self.timertext.get())
self.timertext.set(ctr+1)
if self.timeit:
self.master.after(500, self.increment_timer)
def start(self):
self.timeit=True
self.increment_timer()
def quitit(self):
self.timeit=False
root = Tk()
app = Timer(root)
root.mainloop()

tkinter halt or stop a function (def) from root window

If a function is called from the root window and the function reaches no solution or the user wants to stop the function, can this be done from the root window and if so how? The following code produces two buttons - Start starts "while" using start() but start() cannot be halted by Quit. Using root.update_idletasks() in start() produces no effect.
#!/usr/bin/env python
from Tkinter import *
def start():
while True:
print "Stop me if you can from Quit"
root.update_idletasks()
root = Tk()
root.title('Example')
button1 = Button(root,text = 'Start', command = start)
button1.grid(row = 0, column = 0)
button2 = Button(root,text = 'Quit', command = root.destroy)
button2.grid(row = 1, column = 0)
root.mainloop()
REPLACE root.update_idletasks() WITH root.update() and start() kills the root window from button2.
#!/usr/bin/env python
from Tkinter import *
def start():
while True:
print "Stop me if you can from Quit"
root.update()
root = Tk()
root.title('Example')
button1 = Button(root,text = 'Start', command = start)
button1.grid(row = 0, column = 0)
button2 = Button(root,text = 'Quit', command = root.destroy)
button2.grid(row = 1, column = 0)
root.mainloop()

Categories

Resources