The following code hangs without doing anything in Python 3.2.2 in Linux:
import tkinter
from multiprocessing import Process
def f():
root = tkinter.Tk()
label = tkinter.Label(root)
label.pack()
root.mainloop()
p = Process(target=f)
p.start()
The only information I have found about this problem is issue 5527, in which it is noted that the problem is with tkinter being imported before the process is forked, and that it can be fixed by importing tkinter inside the function f, and that the problem occurs in Linux but not Solaris.
What exactly is causing this problem, and is it a bug? Is there any workaround other than to import tkinter locally everywhere I need it (which isn't very Pythonic)? Do any other modules have similar issues with multiprocessing?
My suspicion is that the problem has to do with the connection to the X server (usually a socket). If this is created before the process is fork()-ed, the child process inherits this connection. But if it tries to use it, the X server gets confused.
After a cursory look at at Tkinter.py, it looks like maybe calling the NoDefaultRoot function before starting the process might be useful. It all depends on when the connection to the X server is made.
Otherwise importing Tkinter after the fork seems the way to go.
As of September 2013, there are some additional comments on the bug report that give more insight into what the actual problem is.
http://bugs.python.org/issue5527#msg194848
http://bugs.python.org/issue5527#msg195480
Based on the above, I'm guessing something like the following is happening: Tkinter is not thread safe, so (for whatever reason) Tkinter wants to know which thread is the main thread. Tkinter assumes that the main thread when the Tkinter module is loaded will also be the main thread for program execution. When you fork or multiprocess after loading Tkinter, this assumption is broken. (For example, after a fork, the remembered main thread is in the parent, not the child.)
If you can get a thread started, just do root.mainloop() after it and it should work fine.
Related
I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.
The problem is that, while the main function is doing its stuff, the window freezes.
I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time.
Anyway I don't want to change my code for such a stupid application.
My question is: is it possible there's no easy way to update my Tkinter window every second? I just want to apply the KISS rule!
I'll give you a pseudo code example below that I tried but didn't work:
class Gui:
[...]#costructor and other stuff
def refresh(self):
self.root.update()
self.root.after(1000,self.refresh)
def start(self):
self.refresh()
doingALotOfStuff()
#outside
GUI = Gui(Tk())
GUI.mainloop()
It simply will execute refresh only once, and I cannot understand why.
Thanks a lot for your help.
Tkinter is in a mainloop. Which basically means it's constantly refreshing the window, waiting for buttons to be clicked, words to be typed, running callbacks, etc. When you run some code on the same thread that mainloop is on, then nothing else is going to perform on the mainloop until that section of code is done. A very simple workaround is spawning a long running process onto a separate thread. This will still be able to communicate with Tkinter and update it's GUI (for the most part).
Here's a simple example that doesn't drastically modify your psuedo code:
import threading
class Gui:
[...]#costructor and other stuff
def refresh(self):
self.root.update()
self.root.after(1000,self.refresh)
def start(self):
self.refresh()
threading.Thread(target=doingALotOfStuff).start()
#outside
GUI = Gui(Tk())
GUI.mainloop()
This answer goes into some detail on mainloop and how it blocks your code.
Here's another approach that goes over starting the GUI on it's own thread and then running different code after.
'Not responding' problem can be avoided using Multithreading in python using the thread module.
If you've defined any function, say combine() due to which the Tkinter window is freezing, then make another function to start combine() in background as shown below:
import threading
def combine():
...
def start_combine_in_bg():
threading.Thread(target=combine).start()
Now instead of calling combine(), you have to call start_combine_in_bg()
I did this and now my window is not freezing so I shared it here. Remember you can run only one thread at a time.
Have a look for reference: stackoverflow - avoid freezing of tkinter window using one line multithreading code
Here's how I managed to work around this issue (maybe it's quite dirty I don't know):
Whenever I update my tkinter window through the doingALotOfStuff() function, I call myTk.update().
It avoids freezing for me.
I have recently started using multithreading to be able to, for example, supply input during a for loop. This was successfull but I wanted to try more.
When I tried threading in combination with Tkinter things went wrong.
So for my example I have a very simple piece code (Python 2.7.11). I realize it beats the point of threading but it shows my problem:
import Tkinter
import threading
def func():
root=Tkinter.Tk() #Open Tkinter window
Tkinter.mainloop()
root.quit() #Extra quit, although it doesn't seem to help
thread1=threading.Thread(target=func)
thread1.start()
thread1.join() #Wait for thread to end (for me to close the window)
del thread1 #Extra deletes (although they don't seem to help)
del func
Running this code once works fine. The Tkinter window opens, I close it manually and the rest works fine. However if I run it again the interpreter freezes. If a second thread, or a simple loose print statement, is added it doesn't even start those. The entire enterpreter freezes and I can only re-run this code by restarting the Kernel.
So do I have to quit the thread/Tkinter in a special way to prevent this from happening?
Additional information:
My CPU usage goes way up. When running it the 2nd time task manager shows up to 30% CPU usage. So I guess there is an infinite loop going on somewhere.
I have read that Tkinter does not take threading well, but I have seen instances where this workes. What I have here is a very simple thing so it must be something I am doing wrong.
Hopefully someone can help me, because I couldn't find an answer in whatever way I tried to Google it.
Thanks in advance.
I need to run some simple function in multi-threading with a Tkinter GUI, so I've tried mtTkinter.
Everything works fine except for a particular: even if I just start the GUI and then I close it without touching nothing some thread keeps running.
In other words; I have this code:
from Tkinter import *
root = Tk()
#simple GUI code with buttons, labels, text and scrollbars widget
...
...
root.mainloop()
If I run this code the GUI appears and when I close it this python script ends successfully.
Now if I replace Tkinter with mtTkinter
from mtTkinter import *
root = Tk()
#simple GUI code with buttons, labels, text and scrollbars widget
...
...
root.mainloop()
the GUI appears once again, but if I close it there is still some thread from mtTkinter that keeps running!
Any help would be apprecied, thank you in advance and sorry for my bad english!
I ran into a similar problem for my application (https://github.com/joecole889/spam-filter). After some investigation, I realized that when I close my application Tkinter (or possibly Matplotlib) uses a threading._DummyThread instance to delete one of the widgets. I have a Matplotlib graph in a Tkinter canvas widget in my application. In any case, it looks like an “image delete” event is added to the event queue and mtTkinter blocks waiting for a response on the responseQueue that never comes.
I was able to fix the problem by allowing events from instances of threading._DummyThread to run without going through the queue infrastructure of mtTkinter. That is, I changed:
if threading.currentThread() == self._tk._creationThread:
to
if (threading.currentThread() == self._tk._creationThread) or \
isinstance(threading.currentThread(), threading._DummyThread) :
Things seem to be working for me now...hope this helps!
I've "resolved" not using it. mTkinter seems to be a bit buggy.
This is an old topic, but I don't see where it was even closed. I have a python application using 4 threads using the 'theading' module and MtTkinter.
I was having similar problems with MtTkinter. The application worked but would not close. I have searched and tried quite a few solutions, none worked. For my application, using queues would have been a chore.
Here is what I did. Its not elegant, but it worked. Its pretty ruthless.
cleanup():`
pidx = os.getpid()
cmd1 = "kill" + " " + str(pidx)
if __name__ == "__main__":
os.system(cmd1)
I have a Python script which uses Tkinter for the GUI. My little script should create a Toplevel widget every X seconds. When I run my code, the first Toplevel widget is created successfully, but when it tries to create a second one the program crashes.
What I am doing is using the after method to call the function startCounting every 5 seconds alongside root's mainloop. Every time this function is called, I append a Toplevel widget object into a list and start a new thread which hopefully will be running the new mainloop.
I would be very grateful if someone could figure this problem out. By the way, this is just a little script that I am currently using to solve my problem, which is preventing me from going on with my real school project.
The code:
import threading,thread
from Tkinter import *
def startCounting():
global root
global topLevelList
global classInstance
topLevelList.append (Toplevel())
topLevelList[len(topLevelList)-1].title("Child")
classInstance.append(mainLoopThread(topLevelList[len(topLevelList)-1]))
root.after(5000,startCounting)
class mainLoopThread(threading.Thread):
def __init__(self,toplevelW):
self.toplevelW = toplevelW
threading.Thread.__init__(self)
self.start()
def run(self):
self.toplevelW.mainloop()
global classInstance
classInstance = []
global topLevelList
topLevelList = []
global root
root = Tk()
root.title("Main")
startCounting()
root.mainloop()
Tkinter is designed to run from the main thread, only. See the docs:
Just run all UI code in the main
thread, and let the writers write to a
Queue object; e.g.
...and a substantial example follows, showing secondary threads writing requests to a queue, and the main loop being exclusively responsible for all direct interactions with Tk.
Many objects and subsystems don't like receiving requests from multiple various threads, and in the case of GUI toolkit it's not rare to need specfically to use the main thread only.
The right Python architecture for this issue is always to devote a thread (the main one, if one must) to serving the finicky object or subsystem; every other thread requiring interaction with said subsystem or object must them obtain it by queueing requests to the dedicated thread (and possibly waiting on a "return queue" for results, if results are required as a consequence of some request). This is also a very sound Python architecture for general-purpose threading (and I expound on it at length in "Python in a Nutshell", but that's another subject;-).
Tkinter has issues dealing with input from multiple threads, I use mtTkinter instead, you won't need to change any code and everything will work fine. Just import mtTkinter instead of Tkinter.
You can get it here:
http://tkinter.unpythonic.net/wiki/mtTkinter
Is there a reason you want (or think you need) one event loop per toplevel window? A single event loop is able to handle dozens (if not hundreds or thousands) of toplevel windows. And, as has been pointed out in another answer, you can't run this event loop in a separate thread.
So, to fix your code you need to only use a single event loop, and have that run in the main thread.
I want to spawn another process to display an error message asynchronously while the rest of the application continues.
I'm using the multiprocessing module in Python 2.6 to create the process and I'm trying to display the window with TKinter.
This code worked okay on Windows, but running it on Linux the TKinter window does not appear if I call 'showerror("MyApp Error", "Something bad happened.")'. It does appear if I run it in the same process by calling showerrorprocess directly. Given this, it seems TKinter is working properly. I can print to the console and do other things from processes spawned by multiprocessing, so it seems to be working too.
They just don't seem to work together. Do I need to do something special to allow spawned subprocesses to create windows?
from multiprocessing import Process
from Tkinter import Tk, Text, END, BOTH, DISABLED
import sys
import traceback
def showerrorprocess(title,text):
"""Pop up a window with the given title and text. The
text will be selectable (so you can copy it to the
clipboard) but not editable. Returns when the
window is closed."""
root = Tk()
root.title(title)
text_box = Text(root,width=80,height=15)
text_box.pack(fill=BOTH)
text_box.insert(END,text)
text_box.config(state=DISABLED)
def quit():
root.destroy()
root.quit()
root.protocol("WM_DELETE_WINDOW", quit)
root.mainloop()
def showerror(title,text):
"""Pop up a window with the given title and text. The
text will be selectable (so you can copy it to the
clipboard) but not editable. Runs asynchronously in
a new child process."""
process = Process(target=showerrorprocess,args=(title,text))
process.start()
Edit
The issue seems to be that TKinter was imported by the parent process, and "inherited" into the child process, but somehow its state is inextricably linked to the parent process and it cannot work in the child. So long as you make sure not to import TKinter before you spawn the child process, it will work because then it is the child process that is importing it for the first time.
This discussion could be helpful.
Here's some sample problems I found:
While the multiprocessing module follows threading closely, it's definitely not an exact match. One example: since parameters to a
process must be pickleable, I had to go through a lot of code
changes to avoid passing Tkinter objects since these aren't
pickleable. This doesn't occur with the threading module.
process.terminate() doesn't really work after the first attempt. The second or third attempt simply hangs the interpreter, probably
because data structures are corrupted (mentioned in the API, but this
is little consolation).
Maybe calling the shell command xhost + before calling your program from that same shell will work?
I am guessing your problem lies with the X-server.