I am using PyGTK to build a GUI application. I want to update the textview widget from another thread but the widget is not getting updated everytime i try an update. What should i do to get a reliable GUI updating?
GTK+ is not thread-safe, so you should not simply call GUI update methods from other threads. glib.idle_add (or gobject.idle_add in older PyGTK versions) can be used for this purpose.
Instead of writing:
label.set_text("foo")
you would write:
glib.idle_add(label.set_text, "foo")
which causes the function call to be queued in GTK+.
If you need to run multiple statements, it's often easier to wrap them in a function:
def idle():
label1.set_text("foo")
label2.set_text("bar")
glib.idle_add(idle)
Make sure that the function passed to idle_add does not return True; otherwise it will be queued again.
Edit: As Daniel pointed out, you need to call gtk.gdk.threads_init() anywhere in your program first.
As stated in the previous answers, GTK is not "thread safe," but it is "thread-aware" - see this page on Threads: https://developer.gnome.org/gdk2/stable/gdk2-Threads.html
In order to modify GTK widgets from another thread you have to use GTK's locking. Call gtk.threads_init() immediately after importing the gtk module, and then you can update like so:
gtk.threads_enter()
# make changes...
gtk.threads_leave()
Note that the above will not work on Windows (see the link above). On Windows you must use gobject.idle_add() as explained above, though don't forget to put gobject.threads_init() directly after importing gobject in your code! The idle_add() function will execute the update itself in the main thread (the thread running gtk.main()).
the same may be achieved using gobject.idle_add method whose syntax is same as above,you have to import the module gobject
What Johannes said is correct, however since GTK is a wrapper for the glib and gobject things, you would actually want to use gtk.idle_add(). No need for the unnecessary imports.
Related
I've been fighting for three hours now to get this process multithreaded, so that I can display a progress box. I finally got it working, insomuch as the process completes as expected, and all the functions call, including the ones to update the progress indicator on the window.
However, the window never actually displays. This is a PyGObject interface designed in Glade. I am not having fun.
def runCompile(obj):
compileWindow = builder.get_object("compilingWindow")
compileWindow.show_all()
pool = ThreadPool(processes=1)
async_result = pool.apply_async(compileStrings, ())
output = async_result.get()
#output = compileStrings() #THIS IS OLD
compileWindow.hide()
return output
As I mentioned, everything works well, except for the fact that the window doesn't appear. Even if I eliminate the compileWindow.hide() command, the window never shows until the process is done. In fact, the whole stupid program freezes until the process is done.
I'm at the end of my rope. Help?
(By the way, the "recommended" processes of using generators doesn't work, as I HAVE to have a return from the "long process".)
I'm not a pyGobject expert and i don't really understand your code. I think that you should post more. Why are you calling the builder in a function? you can call it at the init of the GUI?
Anyways.. It seems that you are having the common multithread problems..
are you using at the startup GObject.threads_init() and Gdk.threads_init() ?
Then, if you want to show a window from a thread you need to use Gdk.threads_enter() and Gdk.threads_leave().
here is an useful doc
I changed the overall flow of my project, so that may affect it. However, it is imperative that Gtk be given a chance to go through its own main loop, by way of...
if Gtk.events_pending():
Gtk.main_iteration()
In this instance, I only want to call it once, to ensure the program doesn't hang.
(The entire program source code can be found on SourceForge. The function in question is on line 372 as of this posting, in function compileModel().
I have a piece of code that displays a Gui which has a QTextEdit Field. I would like to print to this field in real time similar to how the print function outputs to the console.
I have tried using multiple instances of the append function. Ex:
self.textEdit.append(_translate("MainWindow", ">>> Text", None))
The problem is that no matter where they are in the code, they seem to only show after the program is executed. My goal is to have them show in line like the print function does on the console.
I feel like this is an easy answer, but I have had no luck searching.. I am fairly new to Python and any help or guidance will be appreciated.
Thanks in advance!
Indeed, as mata mentioned the freezing comes from doing all your work in the same (main) thread, which also handles UI updates. One way to solve your responsiveness issue is indeed to frequently use QApplication.processEvents() in your blocking code. That will give the impression of a responsive GUI to the user if frequent enough.
However, using threads in Python (whether native or QThread) will not always work. That is because of the existence of the Global Interpreter Lock (GIL, the wiki has a good short intro). In short, Python does not allow more than one thread to execute code at the same time.
If your background task is light, or is based on IO, you can get around this as most IO-heavy modules for Python release the GIL while doing their job. However, if you are performing heavy computations in Python, the GIL will be locked by your processes, and as such your UI will still be unresponsive.
Consider the following example, built using PySide:
import sys, random
from threading import Thread
from time import sleep
from urllib import urlopen
from PySide import QtCore, QtGui
class Window(QtGui.QMainWindow):
update_signal = QtCore.Signal(int)
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.progress_bar = QtGui.QProgressBar(self)
self.progress_bar.setRange(0, 10)
self.setCentralWidget(self.progress_bar)
self.update_signal[int].connect(self.progress_bar.setValue)
self.show()
self.t = Thread(target=self.worker)
self.t.start()
def worker(self):
while self.progress_bar.value() < 10:
self.update_signal.emit(self.progress_bar.value()+1)
print "Starting Sleep"
sleep(5)
print "End of Sleep"
if __name__ == '__main__':
qapp = QtGui.QApplication(sys.argv)
win = Window()
sys.exit(qapp.exec_())
Then, try replacing the worker function to:
def worker(self):
while self.progress_bar.value() < 10:
self.update_signal.emit(self.progress_bar.value()+1)
v = 0
print "Starting Add"
for i in xrange(5000000):
v = v+random.uniform(0, 100)
print "End of Add"
The first case maintains a responsive UI, as the call to sleep() releases the GIL. But the second example does not, as the computationally-intense algorithm keeps the lock.
One solution could be using the multiprocessing package.
From the docs:
multiprocessing is a package that supports spawning processes using an
API similar to the threading module. The multiprocessing package
offers both local and remote concurrency, effectively side-stepping
the Global Interpreter Lock by using subprocesses instead of threads.
Due to this, the multiprocessing module allows the programmer to fully
leverage multiple processors on a given machine.
A simple, illustrative example of using python multipocessing.
Also, if you have further interest, this blog post about multi-processing techniques might be of interest.
This means that you're probably doing all your work in the GUI thread. This is a common mistake, and it means that the GUI will freeze and not respond while there is something else going on.
You can add a call to QApplication.processEvents() to allow for the GUI to update after you change the text in your QTextEdit, but that will only partially solve the problem, the GUI will nevertheless freeze up between those calls.
The solution is simple: do the work in a separate thread. You should read Threading Basics from the Qt documentation, that should get you started.
I found myself in the situation where I needed to close a window which I had subclassed from Gtk.Window. I was connecting the window.destroy event to close the window initially to Gtk.main_quit for testing, but when I loaded the module into my main program, I couldn't do it that way, of course, without closing the main program. When I wrote a function, I stupidly called the window.destroy() method which, stupid of me, resulted in a max recursion error. So, I decided to connect a 'delete-event' and then call the window.destroy() which still recursed because (as I learned) a destroy event calls delete-event. So, I connected the destroy event to a function and tried to call:
window.emit('delete-event')
which worked, but resulted in an error wanting another parameter. Researching, I learned it needed to be something like this from gtk2:
window.emit("delete-event", gtk.gdk.Event(gtk.gdk.DELETE))
I can't find Gdk under Gtk like I can find gdk under gtk. I don't like this. I imported Gdk and Gdk.Event is found, but no equivalent to the gtk.gdk.DELETE variable. I did a:
l = dir(Gdk)
for i in l:
if 'DELETE' in i:
print(i)
and came up with no DELETE variable defined in Gdk.
How can we access Gdk from Gtk? If not, how do we translate these gtk.gdk... things when they don't exist under Gdk?
Ultimately, I just did:
del window
to do what I needed to do, but I still would like to know how to do the emit signal above in Gtk3 for learning purposes.
Thanks,
Narnie
There is no gtk.gdk in GTK 3, all of that functionality, or its equivalent, must be available using from gi.repository import Gdk. The previous gtk.gdk hierarchy was actually for convenience, but didn't make any sense because GDK is a completely independent package and is not part of GTK. The new organization makes more sense because the hierarchy and variable names are exactly the same as in the C, Javascript, etc. APIs.
In this case, gtk.gdk.DELETE is available as Gdk.EventType.DELETE, so you should be able to do
window.emit('delete-event', Gdk.Event(Gdk.EventType.DELETE))
However, you are not supposed to emit event signals yourself, so it would be better to do:
window.event(Gdk.Event(Gdk.EventType.DELETE))
Or even better, don't connect to the event at all in your module. Then you can use window.destroy() without a recursion error. Connect to the destroy signal in your main program, then you can simply call Gtk.main_quit.
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.
Is there a crash reporting framework that can be used for pure Python Tkinter applications? Ideally, it should work cross-platform.
Practically speaking, this is more of 'exception reporting' since the Python interpreter itself hardly crashes.
Here's a sample crash reporter:
Rather than polluting your code with try..except everywhere, you should just implement your own except hook by setting sys.excepthook. Here is an example:
import sys
import traceback
def install_excepthook():
def my_excepthook(exctype, value, tb):
s = ''.join(traceback.format_exception(exctype, value, tb))
dialog = ErrorReportDialog(None, s)
dialog.exec_()
sys.excepthook = my_excepthook
Call install_exception() when your application starts.
ErrorReportDialog is a Qt dialog I've made. traceback.format_exception() will format argument passed to the except hook in the same way it does in Python's interpreter.
EDIT: I forgot to mention a little gotcha with that. It doesn't work with threads (well, at least it didn't last time I checked). For code running in another thread, you will need to wrap it in a try..except block.
Stick try excepts everywhere your application can crash (I/O, networking etc.). Whenever an except is called, call a function that will kill the old window, spawn a new tkinter notification window, or a custom one with your error message.
Do a root.after to the new window and send your error report (urllib).
Put a restart button if you wish.
There is no crash reporting framework - as tkinter is not that type of GUI. It's pretty much a wrapper for simple command line apps.
Go pyqt/gtk or wxpython if you want the features seen in the screen-shot above. But I'm pretty sure that where ever you go, you'll have to write your own reporter.