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)
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()
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?
So I have two python threads running from inside a class. I have checked using
threading.active_count()
and it says both threads are running. The first thread includes a tkinter window which works fine. The second thread I am using as an event manager for the first window, which also works okay by itself. However, when I run the second thread alongside the first thread, the first thread does not work, ie. the window does not appear. This is even if the first thread is executed first. When I remove the infinite loop from the second thread, the first thread works again, can anyone explain this to me? Here is the class:
class Quiz(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def show(self, question):
self.question = quiz[question][0]
self.correct = quiz[question][1]
self.incorrectA = quiz[question][2]
self.incorrectB = quiz[question][3]
self.ref = quiz[question][4]
questionDisplay.config(text=self.question)
correctButton = "answer" + str(self.ref[0])
eval(correctButton).config(text=self.correct, command=lambda : check(True))
incorrect1 = "answer" + str(self.ref[1])
eval(incorrect1).config(text=self.incorrectA, command= lambda : check(False))
incorrect2 = "answer" + str(self.ref[2])
eval(incorrect2).config(text=self.incorrectB, command= lambda : check(False))
return self.correct
def run(self):
print("thread started")
print(threading.active_count())
while True:
print(questionQueue.qsize())
if questionQueue.qsize() >= 1:
pass
else:
pass
print("looped")
Thanks
From the code as currently shown it is not obvious where the problem lies. But keep the following in mind;
Tk is event-driven like basically all GUI toolkits. So for the GUI to work you need to run Tk's mainloop. The only pieces of your code that it runs in the main loop are the various callbacks attached to things like buttons, menus and timers.
Like most GUI toolkits Tk isn't thread-safe because of the overhead that would require. To keep it working properly, you should only call Tk functions and methods from one thread.
Python's threads are operating system threads. This means they are subject to operating system scheduling. And the OS sometimes gives more time to threads that are busy. So if a thread that is spinning in a busy-loop is pre-empted (as is done regularly), chances are that it ends up being run again instead of the GUI thread.
I am Python 3 noob creating a checkers game, I have a bunch of functions... and my game's loop is as follows:
while EndGame==0:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
PrintBoardGui() that I run at the beggining of each turn and creates a Tkinter window and draws the board on a new Canvas in a Tkinter Frame.
After that, I have to close the window in order for the program to continue.
I know this is a sub-optimal solution.
I have looked around trying to understand Tkinter's loops and read a bit about the after() function but i really don't know how I could implement it in my code.
Ultimatly I would like my Tkinter window to stay open (mabye disabled or something) while I input stuff in the console to move the pieces. Can you help me?
At first, how do you want to interact with your game ?
With text in the console ?
With buttons ?
With keyboard/mouse event ?
The "after" method is used to refresh your screen after an amount of time.
It will call the method that you pass through the parameters.
You shouldn't have to put an infinite loop in it. But you will have to check with a simple condition the game ending, to display another screen.
If you have to use the console entry, it can be a bit hard for a beginner to manage the GUI update and the console.
I am not sure what ur question was but, If you would want to run the code forever (until you stop it). you would have to use the while loop like this:
while "thing" == True:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
thing = False
at the end you change thing to False otherwise it would be infinite and it would bug.
I'm experimenting with Tkinter and the threads mechanism. Can anyone explain why this raises the exception:
<class '_tkinter.TclError'> out of stack space (infinite loop?)
and how can I solve this? Below is the code. BTW, I know some people suggest to use the threading module instead of thread, but for now I'd like to use the thread module which is simpler just to introduce myself to the mechanism.
from Tkinter import *
import thread
import time
def main_thread(master):
try:
frame = Frame(master)
frame.pack(side='bottom')
scrollbar = Scrollbar(master)
scrollbar.pack(side='right',fill='y')
t = "Title"
title = StringVar()
title.set(t)
ttl = Label(master, textvariable=title, font=("Helvetica", 18))
ttl.pack(side='top')
cont = Text(master, font=("Helvetica",14), yscrollcommand=scrollbar.set)
cont.pack(side='bottom')
button = Button(frame,text="Exit", command=root.destroy)
button.pack(side='bottom')
n = 0
while 1:
n += 1
cont.insert('end', str(n)+"\n")
time.sleep(1)
except Exception as e:
print type(e), e
if __name__ == '__main__':
root = Tk()
root.title("My counting application")
thread.start_new_thread(main_thread, (root,)) # FIXME: out of stack space (infinite loop?)
root.mainloop()
Thank you,
Luca
EDIT
I solved substituting
n = 0
while 1:
n += 1
cont.insert('end', str(n)+"\n")
time.sleep(1)
with
n = 0
def do_every_second(n):
cont.insert("end", str(n) + "\n")
n += 1
master.after(1000, do_every_second, n)
do_every_second(n)
and calling
main_thread(root)
instead of
thread.start_new_thread(main_thread, (root,))
You have a couple of fatal flaws in your code. For one, you simply can't write code that touches tkinter widgets from more than one thread. You are creating the root window in the main thread, so you can only ever directly access widgets from the main thread. Tkinter is not thread safe.
The second problem is that you have an infinite loop that is constantly appending to the text widget. It has no choice but to eventually run out of memory.
To solve your problem you should:
not have an infinite loop that forever appends to the text widget
not access any widgets from more than a single thread
If you want to run a function once a second, there are better ways to do that than with threads. In short:
def do_every_second():
cont.insert("end", str(n) + "\n")
root.after(1000, do_every_second)
This will cause do_every_second to do whatever it does, then arranges for itself to be called again one second in the future.
The error is correct - there's an infinite loop on one of the tkinter elements. The thread module has nothing to do with it. The specific line causing the error:
cont.insert('end', str(n)+"\n")
Since cont is a tkinter element, you can not run it in your infinite while loop. To do what you want, you would need to print to the console instead. This can be demonstrated if you replace the offending line with a simple:
print(str(n)+"\n")
Edit: If you truly want to achieve the same effect you originally intended, this post deals with a potential alternative.
Edit2: I would assume the exception is a design choice by the tkinter library (although I'm no expert). This would make sense since tkinter already uses an infinite loop for its event loop. I would imagine an infinite loop would prevent tkinter from ever drawing the changes to the screen and instead the libraries authors chose to throw an exception instead. A print should work since there's nothing new to draw, plus it's in it's own thread allowing tkinter's event loop to continue.