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)
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 realize that this question has been asking several times before, though none of them seem to apply to my situation. I have installed PyQt, and am simply trying to open up a window as such:
import sys
from PyQt4 import QtGui as qt
segmentation = qt.QApplication(sys.argv)
main = qt.QWidget()
main.show()
All the other questions I have looked at on here usually were caused by an error with the window going out of scope because of the window's show method being called from within a function, or something similar.
My code uses no functions at all so this cannot be the issue. This should work as it is, no? I am following this tutorial:
https://www.youtube.com/watch?v=JBME1ZyHiP8
and at time 8:58, the instructor has pretty much exactly what I have written, and their window shows up and stays around just fine. Mine displays for a fraction of a second and then closes.
Screen shot of the code block from the video to compare to the code block provided here:
Without seeing all of your code, I'm assuming that you're missing the sys.exit() bit.
For your specific code sys.exit(segmentation.exec_()) would be what you needed.
segmentation = qt.QApplication(sys.argv)
main = qt.QWidget()
main.show()
sys.exit(segmentation.exec_())
A little bit of detail of what's going on here.
segmentation = qt.QApplication(sys.argv) creates the actual application.
main = qt.QWidget() and main.show() creates the Widget and then displays.
When executing the python script, this does exactly what you tell it to:
Create an application
Create a widget
Show the widget
Finished. End of the script.
What the sys.exit() does is cleanly closes the python script. segmentation.exec_() starts the event driven background processing of QT. Once the segementation.exec_() is finished (user closes the application, your software closes the application, or a bug is encountered) it'll return a value which is then passed into the sys.exit() function which in turn terminates the python process.
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.
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.