Tkinter window not closing until python program ends - python

I got a behavior that I don't understand in my test program;
I'm using tkinter to display a window that show some radio box, and sent back the value to the python program that will continue to execute.
But I've noticed that after the Tkwindow is asked to close with a destroy() command :
IF the following of my code is asking an input() the window is closing as requested,
BUT if the following of my code continue to execute without any user input, the window will not close until all the code ends.
the code after the maintop is basically some selenium based code that fills an online webpage.
But as I wrote earlier, if I ask an input() (after the maintop here), the window does close, but if I don't ak an input and let the code with selenium fills a webpage, the window will not close until the webpage fills (and it can take some times as it has multiple pages calls.)
def Close():
window.destroy()
window.quit()
def selection():
global bsiInput
bsiInput=str(value.get())
window = Tk()
window.geometry("350x350")
value = StringVar()
label = Label(window, text="Choose : ")
bouton1 = Radiobutton(window, text=" A ", variable=value, value='a',command=selection)
bouton2 = Radiobutton(window, text=" B " , variable=value, value='b',command=selection)
boutonOk = Button(window, text="Valider", command=Close)
label.pack()
bouton1.pack()
bouton2.pack()
boutonOk.pack()
window.mainloop()
Edit: I try both remarks suggested:
So, as I tried what you both suggest, I defines all my code in a MainProgram function.To be clear I just put a print("closed") after the mainloop(), and in my MainProgram() I start it with a print("prog starts"), from there:
def Close():
window.destroy()
window.after(0,MainProgram)
//outputs "closed" and end program as if MainProgram is never called
def Close():
window.destroy()
...
window.mainloop()
window.after(0,MainProgram)
//outputs "closed" and end programs as if MainProgram is never called
and
window.mainloop()
MainProgram()
//outputs "closed""prog starts" but don't close the window until the end of the program

Related

Open window on button click, close current window?

I have been searching for a solution for this issue, but I can't seem to find a viable answer.
I have the following code to open another tkinter script on button click:
# Program to Open on Button Click
def tkinter1():
ret = os.system('python C:\filepath\new_script.py"')
if ret:
# record updated, reload data
treeview_preview()
b1 = Button(master, text="New Window", command=tkinter1)
My problem I am facing is that I want the current window to close on the button click and only keep the new window open.
I know it is possible. I have this instance with many different windows and I seem to be stuck.
The unfortunate thing is that I have an entire different script for different parts of the beta software and the only way I could successfully run all of them is to access them as stated above.
I tried using the if ret: exit() command at the end with the same result. I have windows opening over and over again.
It seems simple, but I haven't been programming tkinter script for too long.(I still have a lot to learn)
All help is appreciated.
You can use master.withdraw(). I
made your code runnable with a few lines. Ok lets say your fixed main code is this:
import tkinter as tk
from tkinter import *
from seccond import *
from multiprocessing import Process
import os
def main():
master = tk.Tk()
# Program to Open on Button Click
def tkinter1():
master.withdraw()
master.destroy()
ret = Process(target=treeview_preview())
ret.start()
#if ret:
# # record updated, reload data
# treeview_preview()
#commented out bc i don't know what it is
b1 = Button(master, text="New Window", command=tkinter1)
b1.pack()
master.mainloop()
if __name__ == '__main__':
main()
the secondary code name seccond.py in the same folder as the main code is called as an external function but also as a separate process.
import tkinter as tk
def treeview_preview():
root = tk.Tk()
S = tk.Scrollbar(root)
T = tk.Text(root, height=4, width=50)
S.pack(side=tk.RIGHT, fill=tk.Y)
T.pack(side=tk.LEFT, fill=tk.Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set)
quote = """this is the seccond window."""
T.insert(tk.END, quote)
tk.mainloop()
The code will remove the window but not the terminal box as it will use it for the second script.

Python: tkinter.TclError: can't invoke "label" command: application has been destroyed

When i exit the tk window i get this long library error, and I have no idea how to fix it. the GUI itself and error message are below the code.
def golf_records()->None:
"""
This function prints the information
of golfer names and the score they have
"""
with open("golf.txt", "r") as myfile:
myfile_content = myfile.read()
GUI_print(myfile_content)
def GUI_print(data)->None:
"""
The function takes in a string found in golf.txt file
and prints it out through tkinter GUI
"""
my_label = Label(root,text = str(data),font=("ariel",15)).place(x=250,y=120)
root = Tk()
root.geometry("600x600")
#Lables
header = Label(root,text = "Current Golf Records",font=("ariel",15,"bold")).place(x=150,y=20)
header = Label(root,text = "-----------------------------",font=("ariel",15)).place(x=150,y=50)
header = Label(root,text = "Press enter to list players data: ",font=("ariel",15)).place(x=150,y=80)
#Buttons
enter = Button(root, text="Enter", activebackground = "green", command=golf_records).place(x=440,y=80)
root.mainloop()
if __name__ == "__main__":
golf_records()
Basically imagine like your code is executed till the mainloop() and it pauses there till you break out of the loop, ie, by exiting the application. Then the code after the mainloop() gets executed, ie, the if statements which is true, hence running the function golf_record() which calls the GUI_print() which activates the label.
How to solve this? Im not sure what your trying to do with this code here, but if you can move that root.mainloop() to the end of the if statement, itll execute the function while the code is initially executed. Do let me know if you have more doubts. Or the better way would be to get rid of the function call golf_records() inside the if because you have a button that calls the function anyway.

Tkinter not waiting for user input inside functions

I am trying to make a program that when conditions are met, goes back to the beginning and waits. But instead of waiting for the user to push a button, it continues through the code.
I am using python 3.7.4 and Windows 10.
I assume this problem occurs because tkinter doesn't wait for user input in this situation, and instead continues through the code.
My code:
from tkinter import *
from tkinter.ttk import *
def start():
print("Start")
# Removes all widgets without destroying root
for widget in root.winfo_children():
widget.destroy()
button_1 = Button(root, text="Begin", command=begin).pack()
button_2 = Button(root, text="Do something else", command=something).pack()
# I want the program to wait here for the user to click a button
def begin():
print("\nDoing stuff")
if True:
start()
print("This should not be printed")
def something():
pass
root = Tk()
root.geometry("300x300")
btn1 = Button(root, text = "Start", command = start)
btn1.pack()
root.mainloop()
This outputs:
Start
Doing stuff
Start
This should not be printed
I want this to output:
Start
Doing stuff
Start
And then wait for the user to select a button.
If you want a function to wait for a user action, you need to explicitly tell it to wait.
Tkinter has three functions for that. One is wait_window, which will wait for a window to be destroyed. One is wait_visibility, which will wait for the visibility of a window to change. The third one is wait_variable, which waits for a specific tkinter variable to be set.
While tkinter is waiting, it's able to service other events.
In your case, the solution might look something like this:
var = BooleanVar(value=False)
def do_something():
something()
var.set(True)
button_2 = Button(root, text="Do something else", command=do_something).pack()
print("waiting...")
root.wait_variable(var)
print("done waiting.")
When you modify your code to include the above snippet, you'll notice that "waiting..." will be printed on stdout, and then nothing else will be printed until you click on the "Do something else" button and something returns, allowing the variable to be modified.

Tkinter Button Function Control by MessageBox

The code below shows part of my program and the issue im facing.
def checkAnswer():
mainAnswer = answer01.get()
if len(mainAnswer) == 0:
messagebox.showwarning(message='Please answer the question!')
return
if int(mainAnswer) != answer:
messagebox.showwarning(message='Incorrect! The correct answer is: ' + str(answer))
else:
nxtquest.config(state=NORMAL)
messagebox.showinfo(message='Correct! :)')question01 = Label(easy)
question01.grid(row=2, column=0)
answer01 = Entry(easy)
answer01.grid(row=3, column=2)
answer01.bind('<Return>', func=lambda e:checkAnswer())
start = Button(easy, text = "Start!", command=ask, bg='green', fg='white')
start.grid(row=3, column=3)
nxtquest = Button(easy, text='Next Question', command=ask)
nxtquest.grid(row=5, column=2)
checkbut = Button(easy, text='Check', command=checkAnswer)
checkbut.grid(row=4, column=2)
#check button and answer01 enabled after start pressed
launch = 1
if launch == 1:
answer01.config(state=DISABLED)
checkbut.config(state=DISABLED)
nxtquest.config(state=DISABLED)
The issue which im struggling here is that whenever i run the program everything is okay. When the window is displayed checkbut, nxtquest and label answer01 are greyed out (disabled).
The start button enables only checkbut and answer01 and then is destroyed. (So far so good)
So nxtquest will enable once the input is correct as seen in the
else:
nxtquest.config(state=NORMAL)
But when I reach another question the nxtquest button is already enabled, this is the problem!
How could I make it so the button will enable itself only after the warning message box is displayed?
Could I ask for some help with this and possibly suggestions if you see any rookie mistakes ?
Whilst I don't know of any way you could do this with a messagebox widget (although I'm sure there's an event you could use as the trigger) you can most certainly do this by substituting the messagebox with a Toplevel widget and using .protocol("WM_DELETE_WINDOW", callback()) on the widget.
This would mean that whenever the Toplevel widget was "closed" we would actually be overwriting the action taken when the event was raised and would manually handle the closing of the widget as well as whatever else we wanted it to do.
This would look something like the below:
from tkinter import *
root = Tk()
button = Button(root, text="Ok", state="disabled")
button.pack()
top = Toplevel(root)
def close():
top.destroy()
button.configure(state="active")
top.protocol("WM_DELETE_WINDOW", close)
root.mainloop()
If you close the Toplevel widget you will see that the button is now active instead. This would equally work if we added a Button to the Toplevel widget which called the function close().

Break ongoing loop started by a tkinter button

I want to build a tkinter interface that can start doing some repetitive work when a "start" button is pressed, and break the ongoing loop when the "stop" button is pressed. However I noticed that the tkinter does not process any further job untill the ongoing loop is finished, (e.g. does not respond to the "stop" button click, does not update the textbox or label text as shown in the attached code). It seems to be related to the single thread nature of tkinter from what I read at stackoverflow. Can someone help with some specific code? I am new to tkinter/Python, I could not figure out a real solution although I read many general discussions on board.
import time
import Tkinter as Tk
def _start():
for outer in range(5):
if active_stat:
time.sleep(1) # some code in the real app
else:
break
for inner in range(5):
if active_stat:
#counterstr.set("%02d-%02d" % (outer,inner)) #does not update till the end of loop
textbox.insert(Tk.END, "%02d-%02d\n" % (outer,inner)) #does not show till the end of loop
print "%02d-%02d" % (outer,inner)
time.sleep(1) #some code in the real app
else:
break
def _stop():
active_stat=False
active_stat=True
root = Tk.Tk()
#counterstr=Tk.StringVar()
#Tk.Label(root, textvariable=counterstr).pack(side=Tk.TOP)
textbox=Tk.Text(root)
textbox.pack(side=Tk.TOP)
Tk.Button(root, text='Start', command=_start).pack(side=Tk.LEFT)
Tk.Button(root, text='Stop', command=_stop).pack(side=Tk.LEFT)
Tk.Button(root, text='Quit', command=root.quit).pack(side=Tk.LEFT)
root.mainloop()
What I would do is, is that I would make your active_stat a tkinter variable, and then call its get method when ever you want to check if it:
EDIT: also, add root.update() to your loop. That should fix your problem. The reason for the tkinter variables is because of variable scope problems, which I initially though where your problem. (note: this code is for python 3...)
import time
import tkinter as Tk
def _start():
for outer in range(5):
if active_stat.get():
time.sleep(1) # some code in the real app
else:
active_stat.set(True)
break
for inner in range(5):
if active_stat.get():
#counterstr.set("%02d-%02d" % (outer,inner)) #does not update till the end of loop
textbox.insert(Tk.END, "%02d-%02d\n" % (outer,inner)) #does not show till the end of loop
print ("{}-{}".format(outer,inner))
time.sleep(1) #some code in the real app
else:
active_stat.set(True)
break
root.update()
def _stop():
active_stat.set(False)
root = Tk.Tk()
active_stat = Tk.BooleanVar(root)
active_stat.set(True)
#counterstr=Tk.StringVar()
#Tk.Label(root, textvariable=counterstr).pack(side=Tk.TOP)
textbox=Tk.Text(root)
textbox.pack(side=Tk.TOP)
Tk.Button(root, text='Start', command=_start).pack(side=Tk.LEFT)
Tk.Button(root, text='Stop', command=_stop).pack(side=Tk.LEFT)
Tk.Button(root, text='Quit', command=root.quit).pack(side=Tk.LEFT)
root.mainloop()

Categories

Resources