I'm having a "deadlock-like" problem, where a thread needs to wait for the main thread to close before closing, but of course that one won't close until the other one closes.
My code is analogous to this:
import threading
from tkinter import *
def questions_funct():
while True:
if input("Do you want to close?:")=="YES":
root.destroy()
break
root = Tk()
questions_thread = threading.Thread(target=questions_funct)
questions_thread.start()
root.mainloop()
From what I've tested, nothing after root.destroy executes, but stuff after root.mainloop() does. So, it's clear root.destroy() is executed successfully which is further backed up by the window closing. Of course, the console is still open, a clear indicator that something is still running.
Might be important to add that all the testing was done with print("Test") stuff, so I'm definitely not sure if what I found out is true.
I've been banging my head against the wall in search of answers. Any help?
Related
To understand my question kindly follow the paragraphs written below:
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
consider the code:
from tkinter import *
window = Tk()
print("lol")
print("Hello World")
window.mainloop()
the output didn't print "Hello World" or "lol" infinite number of times, so the mainloop() doesn't loop the code of the current module.
Now consider this code:
from tkinter import *
print("lol")
window = Tk()
print("Hello World")
while True:
window.update()
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
Now the first question arises what does the window.update() function do to update the values in the GUI, does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
The second question is :
I read this article
"Mainloop in Python Tkinter is an infinite loop of the application window which runs forever so that we can see the still screen.
The application window is like a frame that keeps on destroying every microsecond but the main loop keeps on creating a new updated window.
This process of destroying old window screens and creating a new one happens so fast that human eyes don’t even realize it.
Since the process runs infinite time that is why we are able to see the application in front of us and when we close the window then the loop terminates or exits."
Now if this is true then to recreate an updated window the root.mainloop() must read the entire root GUI code again and again entirely or is there another explanation to it.
I have been trying to understand this for the past 6hrs and I have visited every site and I cannot find the solution for the life of me.
Regards,
Rashik
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
No.
Via this function, it calls this C code which has the embedded Tcl interpreter process one event, or wait for Tkinter_busywaitinterval before trying to process another event
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
window.update() calls TCL update, which is described to
[...] bring the application “up to date” by entering the event loop repeatedly until all pending events (including idle callbacks) have been processed.
Your infinite loop doesn't have a sleep, so it's spinning your CPU as hard as possible to do practically nothing.
[...] Does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
It certainly doesn't re-read your code. It processes any pending widget updates, which may have happened by running e.g. window.text("...") in e.g. a click callback or an .after() timeout, etc.
I read this article [...]
That article seems wrong and/or at least over-simplifies things.
This simple example clock should clarify how things work:
import time
import tkinter as tk
root = tk.Tk()
text = tk.Label(root)
text.pack()
def tick():
text["text"] = time.ctime() # update `text` widget's content
root.after(1000, tick) # schedule for this function to be called after 1 second
if __name__ == '__main__':
tick() # call the `tick` function once before entering main loop
root.mainloop()
This is NOT a duplicate of Python tkinter mainloop not quitting on closing the window
I have an app that builds on tkinter. I observed at sometimes after I close the window using the X button, the code will not execute past the mainloop() line. This happens completely randomly about 10% of chance. Rest of the time, it works like a charm. I would like to ask if there are any way to force it. As I said, the code blocks on the mainloop() line, thus calling sys.exit() after it does not help.
I am using Python 3.9.8.
This is not 100% reproducible, but here is something that might trigger the problem:
from tkinter import *
root = Tk()
Label(root, 'hi').pack()
mainloop()
print('exited')
My first thought is to use root.mainloop() instead of tkinter.mainloop(). This makes a difference if you are using several windows.
That said, I did see this a long time ago on some old OSes. Never figured out the reason, so I just wrote my own quit function, like this:
import tkinter as tk
def _quit():
root.quit()
root.destroy()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", _quit)
tk.Label(root, 'hi').pack()
root.mainloop()
print('exited')
I am a beginner in Python.
I use to code with Tkinter. I want to improve my programs with the extension "threading".
However, in some of my programs, if i leave the Tkinter window, the program is still running.
I would like to know how stop it. I guess it's the thread which is still running. I tried different ways to close the thread, but the only issue i found is to create an error (and it is not a nice solution according to me) :).
I would like to know if an instruction like the following one exist to stop the thread.
Thread(target=FinTemps).stop()
(I know that 'stop' is not working)
and if it is possible to stop the Thread with a loop 'while', I tried this and i don't understand why it doesn't work :
import tkinter as tk
from threading import Thread as Th
from time import *
global end
end=False
begining=time()
def screen():
global end
while end==False:
chrono=time()-beginning
if chrono<5:
sentence.set("Hey !")
elif chrono<8:
sentence.set("What's up ?")
window=tk.Tk()
window.geometry("{}x{}".format(fenêtre.winfo_screenwidth(), window.winfo_screenheight()))
window.title("Test Tkinter & Thread")
sentence=tk.StringVar()
sentence.set("none")
GreatTitle=tk.Label(window, textvariable=sentence, width=500)
Th(target=screen).start()
GreatTitle.pack(expand=1)
window.mainloop()
end=True
Thank you ;)
(And sorry if my english is not wonderful (I'm french) or if my code or explainations weren't very understandable or without following traditional presentation).
Have a good day :)
What you are doing is not safe. tkinter is not thread-safe, and running any tkinter API cross-thread risks corruption of various kinds.
Bright side is, you don't need threads here. In real code, you wouldn't use a thread at all, you'd just schedule the changes to occur on tkinter's event loop with the after method, simplifying your code to just:
import tkinter as tk
window=tk.Tk()
window.geometry("{}x{}".format(window.winfo_screenwidth(), window.winfo_screenheight()))
window.title("Test Tkinter & Thread")
sentence=tk.StringVar()
sentence.set("none")
GreatTitle=tk.Label(window, textvariable=sentence, width=500)
GreatTitle.pack(expand=1)
window.after(0, sentence.set, "Hey !") # Happens almost immediately, as in thread case
window.after(5000, sentence.set, "What's up ?") # Delays five seconds, as in thread case
window.mainloop()
This is also far more efficient than what you had; as written, your thread would spin forever, constantly looping without pause for as long as it was allowed to do so, keeping a core constantly running at 100%; the GUI itself would run slower because it couldn't do much of its work while the other thread held the GIL. Using .after, no meaningful resources are consumed at any time aside from the moment the event fires and the sentence is updated.
It may be that your while loop checks end too frequent, it may cause race condition when you try to set end to True after the root window is destroyed.
You can try adding a short sleep inside the while loop to reduce the chance of race condition:
while end == False:
...
sleep(0.1)
I'm really lost...I open a window with two buttons, and when you click on the button called "REGISTER SOME KEY PRESSES" it runs the script called registerSomeKeyPresses.py, BUUUUT once finished I want to close that execution but keep the first window displaying...it's being impossible for me....
Please, i would reaaaally appreciate any help...
Thanks!
#!/usr/bin/env python
from Tkinter import *
import threading
v0 = Tk()
def finishApplication(): v0.destroy()
def registerSomeKeyPresses():
t = threading.Thread(target=execfile("registerSomeKeyPresses.py"))
t.start()
def waitAndRun(f): v0.after(200, f)
b1=Button(v0,text="TERMINAR APLICACION",command=lambda: finishApplication()).pack()
button_keyPresses=Button(v0,text="REGISTER SOME KEY PRESSES",command=lambda: waitAndRun(registerSomeKeyPresses())).pack()
v0.mainloop()
================ registerSomeKeyPresses.py ===========================
Do several things and last command:
io.quit()
When you destroy the instance of Tk, your programm will (and should) exit. If you want to create and destroy windows, create and destroy an instance of Toplevel while keeping the main window active. If you don't want to see the main window you can hide it.
Also, tkinter and threads don't mix very well. You cannot call any methods on any widgets from another thread. I've heard other people say you can call event_generate from another thread, but I think that's the only tkinter function you can call from another thread.
Edit 1
A second try as a response to your comment:
from Tkinter import *
from subprocess import call
import sys
t = Tk()
def click():
t.iconify()
try:
call([sys.executable, 'script.py'])
finally:
t.deiconify() # if it should close do t.quit() and t.destroy()
b = Button(t, command= click)
b.pack()
t.mainloop()
Old Version
What does that do?
================ registerSomeKeyPresses.py ===========================
v0.quit()
v0.destroy()
io.mainloop()
An other error is:
threading.Thread(target=execfile, args = ("registerSomeKeyPresses.py",))
if you really neeed a thread.
Do never mix tkinter mainloop things with threads. Threads can use event_generate - thats safe.
Using Tkinter with Python on Linux, I'm trying to make Ctrl+C stop execution by using the KeyboardInterrupt Exception, but when I press it nothing happens for a while. Eventually it "takes" and exits. Example program:
import sys
from Tkinter import *
try:
root = Tk()
root.mainloop()
except:
print("you pressed control c")
sys.exit(0)
How can the program react quicker?
That is a little problematic because, in a general way, after you invoke the mainloop method you are relying on Tcl to handle events. Since your application is doing nothing, there is no reason for Tcl to react to anything, although it will eventually handle other events (as you noticed, this may take some time). One way to circumvent this is to make Tcl/Tk do something, scheduling artificial events as in:
from Tkinter import Tk
def check():
root.after(50, check) # 50 stands for 50 ms.
root = Tk()
root.after(50, check)
root.mainloop()
According to Guido van Rossum, this is because you are stuck in the Tcl/Tk main loop, while the signal handlers are only handled by the Python interpreter.
You can work around the problem, by binding Ctrl-c to a callback function:
import sys
import Tkinter as tk
def quit(event):
print "you pressed control c"
root.quit()
root = tk.Tk()
root.bind('<Control-c>', quit)
root.mainloop()