Explanation of need for Multi Threading GUI programming - python

I'm looking for a good explanation of the need to use multi-threading in graphical applications. In the examples below Python is used but the question is not python specific it applies to maybe the general design of graphical programming in any language.
Lets take a simple example. Let's assume there is an application that does some sort of time consuming operation on a collection of files, and that it outputs it's progress to the console. Lets assume that this operation takes 2 seconds per file and that there are 10 files to process called 1.txt, 2.txt,3.txt, ... 10.txt. Then an example implementation could look like the following:
console
import time
def process(file):
print 'processing {0}...'.format(file)
time.sleep(2.0) #simulate slow operation
files = ['{0}.txt'.format(i) for i in range(1, 11)]
map(process, files)
The console example is of course single threaded and does the job just fine. Now if we wanted to add a graphical progress bar a single threaded implementation might look like the following:
single-threaded gui
import time, gtk, gobject
def process(file):
print 'processing {0}...'.format(file)
time.sleep(2.0)
class MainWindow(gtk.Window):
def __init__(self):
super(MainWindow, self).__init__()
self.progress = gtk.ProgressBar()
self.progress.set_fraction(0)
self.add(self.progress)
self.connect("destroy", gtk.main_quit)
self.show_all()
files = ['{0}.txt'.format(i) for i in range(1, 11)]
gobject.timeout_add(100, self.submit, files, 0)
def submit(self, files, i):
process(files[i])
self.progress.set_fraction((i + 1.0)/len(files))
if i + 1 < len(files):
gobject.idle_add(self.submit, files, i + 1)
win = MainWindow()
gtk.main()
This seems to work fine, but as you run the application if you try and interact with the application like try and resize the window for example it will get stuck and only respond every two seconds when it gets freed up to process pending gui events. The final example is a multi-threaded implementation and stays responsive throughout execution.
multi-threaded gui
import time, gtk, gobject, threading
def process(file):
print 'processing {0}...'.format(file)
time.sleep(2.0)
class MainWindow(gtk.Window):
def __init__(self):
super(MainWindow, self).__init__()
self.progress = gtk.ProgressBar()
self.progress.set_fraction(0)
self.add(self.progress)
self.connect("destroy", gtk.main_quit)
self.show_all()
files = ['{0}.txt'.format(i) for i in range(1, 11)]
threading.Thread(target=self.submit, args=(files,)).start()
def submit(self, files):
for i, file in enumerate(files):
process(file)
gobject.idle_add(self.progress.set_fraction, (i + 1.0)/len(files))
if not self.get_visible():
return
gtk.gdk.threads_init()
win = MainWindow()
gtk.main()
It seems perfectly clear and logical to me that if you have a long-running blocking operation in your code and you want a responsive gui that you have to use a multi-threaded solution. There is no other way around it. Is this the case? I have tried to explain this many times to fellow developers but many don't understand or don't agree. Can someone provide an explanation of this concept, a link to an article on it, or correct me if my understanding is incorrect.

Your understanding is correct. If an application isn't muli-threaded the application waits for every operation to finish. When your application is multi-threaded you use one thread to handle GUI actions and another to process the files.
I don't have an article or a reference to something similar. Maybe it helps if you think of threads as people, each has it's own job and each can only do one thing at a time.

The main reason is, that GUI toolkit process all events (mouse movement, button click, keyboard input, system events, etc) in the mainloop, which is not part of the code you write for you GUI application.
This main loop call all the event handlers and other functions you provide. Now, if one of those functions take too long (e.g. > 100ms), then there will be a very noticeable effect on UI responsiveness, as the main loop will not be able to process more events.
This topic should be discussed in details in the "advanced concepts" section of any book on GUI programming, regardless of programming language of toolkit used.

Multithreading is one way of approaching this, but not necessarily the only (or best) way. As a python example, greenlets provide a way of running concurrent processes within the same thread, avoiding the problems with locking associated with multithreading. I would certainly consider greenlets to be a preferred solution in most cases simply because of the relative ease of coding them.

Related

CPython extension using omp freezes Qt UI

I am working on a scientific algorithm (image processing), which is written in C++, and uses lots of parallelization, handled by OpenMP. I need it to be callable from Python, so I created a CPython package, which handles the wrapping of the algorithm.
Now I need some UI, as user interaction is essential for initializing some stuff. My problem is that the UI freezes when I run the algorithm. I start the algorithm in a separate thread, so this shouldn't be a problem (I even proved it by replacing the function call with time.sleep, and it works fine, not causing any freeze). For testing I reduced the UI to two buttons: one for starting the algorithm, and another just to print some random string to console (to check UI interactions).
I also experienced something really weird. If I started moving the mouse, then pressed the button to start the computation, and after that kept moving the mouse continuously, the UI did not freeze, so hovering over the buttons gave them the usual blueish Windows-style tint. But if I stopped moving my mouse for a several seconds over the application window, clicked a button, or swapped to another window, the UI froze again. It's even more strange that the UI stayed active if I rested my mouse outside of the application window.Here's my code (unfortunately I cannot share the algorithm for several reasons, but I hope I manage to get some help even like this):
if __name__ == "__main__":
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import time
from CustomAlgorithm import Estimator # my custom Python package implemented in C++
class Worker(QObject):
finished = pyqtSignal()
def run(self):
estimator = Estimator()
estimator.calculate()
# if the above two lines are commented, and the next line is uncommented,
# everything's fine
# time.sleep(5)
print("done")
app = QApplication([])
thread = QThread()
window = QWidget()
layout = QVBoxLayout()
# button to start the calculation
btn = QPushButton("start")
layout.addWidget(btn)
btn.clicked.connect(thread.start)
# button to print some text to console
btn2 = QPushButton("other button")
layout.addWidget(btn2)
btn2.clicked.connect(lambda: print("other button clicked"))
window.setLayout(layout)
# handling events
worker = Worker(app)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
window.show()
app.exec_()
I tried multiple variants of using threads, like threading.Thread, multiprocessing.Process, PyQt5.QtCore.QThread (as seen above), even napari's worker implementation, but the result was the same. I even tried removing omp from the code, just in case it interferes somehow with python threads, but it didn't help.
As for the reason I use python, is that the final goal is to make my implementation available in napari.
Any help would be highly appreciated!
Because of Python's "Global Interpreter Lock", only one thread can run Python code at a time. However, other threads can do I/O at the same time.
If you want to allow other threads to run (just like I/O does) you can surround your code with these macros:
Py_BEGIN_ALLOW_THREADS
// computation goes here
Py_END_ALLOW_THREADS
Other Python threads will be allowed to run while the computation is happening. You can't access anything from Python between these two lines - so get that data in order before Py_BEGIN_ALLOW_THREADS.
Reference

Run Kivy Application method in background

I have built a desktop kivy application with some methods that perform changes in my app as well as other calculations that take some time (around 30 seconds). When I call these methods, the app freezes during their execution, which is not desirable. Is there a way to prevent the app from freezing and instead display some kind of popup with an animated gif, so that the user knows the app is processing something and did not crash/stop responding?
I have tried to use multiprocessing to run these methods in the background while an informative Popup appears, but these processes do not share memory and data structures with the main app process. So, even though the method run successfully and the popup was shown, there were no changes in the app...
Anyway, this is the multiprocessing code:
def run_in_background(self, func, *args):
def run_func(m, a):
# Execute the function with args
m(*a)
def check_process(proc, dt):
if not proc.is_alive():
Clock.unschedule(check_func)
self.dismiss_popup()
# Create background process and start it
p = multiprocessing.Process(target=run_func, args=(func, args))
p.start()
# Create waiting dialog
content = CrunchData()
# Show waiting popup
self.show_popup(title="Some title", content=content, size=(400, 300))
# Create schedulled check for process state
check_func = partial(check_process, p)
Clock.schedule_interval(check_func, .1)
Any help would be much appreciated.
You've got a good start to your problem, but polling is_alive is not what you want to do. Since you are only doing one background task and showing a dialog, you could use either threading OR multiprocessing for this. Their API is very similar.
To avoid polling, you can use a processing pool (easier to deal with) and pass a callback to pool.apply_async.
This callback will not be run on the main thread, though, so if you need to run code on the main thread you will need to schedule it in your callback.
An alternative is just to use a separate thread instead of process, and use Kivy's clock to schedule a function call at the end of the thread function. See kivy threading docs.
This SO answer is also an option for transferring data from a thread to the main thread: https://stackoverflow.com/a/22033066. It's from a Kivy dev.

How can I print to QTextEdit to mimic printing to console? (Python 3.3)

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.

Which GUI frameworks are best for a multi-threaded Python program?

I am writing a multi-threaded Python program with a GUI, with several modules that "touch" the GUI by changing text and background colors. I am currently using PyGTK and am finding that the GUI sometimes crashes "silently" (no error messages; the program just terminates), and sometimes encounters segmentation faults.
This site notes that GTK is not completely thread-safe, and that PyGTK multi-threaded programming is tricky. Are there better Python GUI frameworks for multi-threaded programs that are less likely to produce problems?
Ohh, I definitely recommend PyQt4. At first, I didn't get all this SIGNAL and EMIT nonsense, but now that I've made a program with it, the QThread module is amazingly useful.
As for stability, I have never had a crash, ever. Even while I was debugging half-functional code, QT didn't have any problems. It just threw an error to the console window whenever I clicked a button with an invalid signal slot.
GTK, on the other hand, just 'sploded once in a while with no errors whatsoever. Just your extremely descriptive and friendly Segmentation Fault. That was one of the reasons I find PyQt a joy to work with. When you get an error, you actually know what's wrong.
I'm pretty sure it's personal preference after that, but one more plus is native-looking GUIs on Mac, Linux, and Windows. GTK+ on Windows (don't get me wrong. I use Ubuntu) just has this X-org feel to it, which disturbs me.
Good luck!
Just to make PyQt a bit more attractive, here's an excerpt from my book binding application (it's a bit messy):
class Binder(QtCore.QThread):
'''
Class for binding the actual book
'''
def __init__(self, parent = None):
super(Binder, self).__init__(parent)
def initialize(self, pages, options, outfile):
self.pages = pages
self.options = options
self.outFile = outfile
self.book = organizer.Book()
self.enc = Encoder(self.options)
self.ocr = ocr.OCR(self.options)
self.connect(self.enc, QtCore.SIGNAL('updateProgress(int, int)'), self.updateProgress)
def updateProgress(self, percent, item):
self.emit(QtCore.SIGNAL('updateProgress(int, QString)'), int(percent), 'Binding the book...')
self.emit(QtCore.SIGNAL('updateBackground(int, QColor)'), int(item), QtGui.QColor(170, 255, 170, 120))
if int(percent) == 100:
time.sleep(0.5)
self.emit(QtCore.SIGNAL('finishedBinding'))
def run(self):
self.die = False
for page in self.pages:
self.add_file(page, 'page')
if not self.die:
self.analyze()
if not self.die:
self.book.get_dpi()
if self.options['ocr'] and not self.die:
self.get_ocr()
if not self.die:
self.enc.initialize(self.book, self.outFile)
self.enc.start()
If you are updating the GUI from a thread, you might want to use gobject.idle_add() so that the GUI update function is called later in the loop, most GUI frameworks (like Qt) require you to add a callback that will be called later when the mainloop is idle. GTK also supports calling the GUI functions from threads by using the gtk.gdk.lock context manager or calling gtk.gdk.threads_enter and gtk.gdk.threads_leave around your GUI calls.
So you either do:
gobject.idle_add(lambda: window.whatever(arg1, arg2))
Or you do:
with gtk.gdk.lock:
window.whatever(arg1, arg2)

Using external GUI libraries to make user interfaces in Autodesk Maya

I develop tools in Autodesk Maya. Many of the tools I build have simple windowed GUIs for the animators and modellers to use. These GUIs often contain what you'd normally expect to see in any basic window; labels, lists, menus, buttons, textfields, etc. However, there are limitations to the complexity of the UIs you can build with the available tools, specifically in the types of available widgets.
I'm interested in using some of the more advanced wxPython widgets such as the ListView (grid), Tree, etc. This would involve using a complete wxFrame (window) to display the whole UI, which would essentially mean that window would no longer be tied to Maya. Not a deal breaker, but it means when Maya is minimized, the window won't follow suit.
I've tried something like this before with tkinter as a test, but found that it needed a MainLoop to run in its own thread. This is logical, but in my case, it conflicts with Maya's own thread, essentially making Maya hang until the window is closed. This is due to the fact that Maya runs all scripts, be they MEL or Python, in a single thread that the main Maya GUI shares. This is to prevent one script from, say, deleting an object while another script is trying to do work on the same object.
wxPython has this same "mainloop" methodolgy. I'm wondering if there's any way around it so that it can work within Maya?
I'm not sure if this is germane, but some googling turns up that PyQt is pretty popular inside of Maya. You could try the technique here or here (explained here with source code) of creating a new threadloop via Maya and executing inside of that. It seems Maya has a module included that sets up a new thread object, with a QApplication inside it:
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = QtGui.QApplication(sys.argv)
pumpedThread = threading.Thread(target = pumpQt, args = ())
pumpedThread.start()
and then sets up a function to process the Qt events:
def pumpQt():
global app
def processor():
app.processEvents()
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
You can probably do something similar with wxPython as well. (utils.executeDeferred is a Maya function.) Be sure to check out how to create a non-blocking GUI on the wxPython wiki. Instead of processEvents(), you'll want to set up an event loop and check for "Pending" events inside the (hopefully renamed?) pumpQt function above. (The wxPython source has a Python implementation of MainLoop.) Likely this should be done through the app.Yield() function, but I'm not sure.
def pumpWx():
global app
def processor():
app.Yield(True)
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = wx.App(False)
pumpedThread = threading.Thread(target = pumpWx, args = ())
pumpedThread.start()
The wxPython docs indicate SafeYield() is preferred. Again, this seems like it could be a first step, but I'm not sure it will work and not just crash horribly. (There's some discussion about what you want to do on the wxPython mailing list but it's from a few minor versions of wx ago.) There is also some indication in various forums that this technique causes problems with keyboard input. You might also try doing:
def processor():
while app.Pending(): app.Dispatch()
to deal with the current list of events.
Good luck!
I don't know if there is a way around a mainloop for the gui, since it is needed to handle all event chains and redraw queues.
But there are several means of inter-process communication, like pipes or semaphores. Maybe it is an option to split your Maya extension into the actual plugin, being tight into maya, and a separate application for the gui. These two could use such means to communicate and exchange model information between plugin and gui.
I'm not sure, however, if I can really recommend this approach because it very much complicates the application.
You could have a look at IPython, an interactive Python shell, whose dev team has put some effort into integrating it with wxPython. They have some way of interrupting the event loop and hooking into it to do their own stuff.
The best way to go is creating a QWidget with what you need, and using it from within a MPxCommand thru the C++ API. That way you also have the chance to inject complete custom editors into Maya via scriptedPanels.
But if you're bound to Python, pyQt is the way to go.

Categories