I’ve been working with PyQT lately and got stuck on a seemingly simple problem: updating the UI from another thread. Having never used PyQT before it wasn’t obvious what the solution was and any Stack Overflow results I found gave incomplete code samples. I’m hoping this post helps give pointers for anyone searching for the same things I did.
This particular example is very contrived but it’s the only solution I could find for updating an image with QPixmap objects in a multithreaded interface, overcoming the “QPixmap: It is not safe to use pixmaps outside the GUI thread” error message. I think part of my problem was that I wasn’t using QThreads in my threaded code and I wasn’t willing to refactor a large codebase just to improve PyQT integration.
First another thread calls someFunctionCalledFromAnotherThread, which uses PyQT’s signal mechanism to pass events across threads. This function creates a LoadImageThread with the filename and desired size as arguments, connects it to a signal to call the showImage function, then starts the thread.
def someFunctionCalledFromAnotherThread(self):
thread = LoadImageThread(file="test.png", w=512, h=512)
self.connect(thread, QtCore.SIGNAL("showImage(QString, int, int)"), self.showImage)
thread.start()
def showImage(self, filename, w, h):
pixmap = QtGui.QPixmap(filename).scaled(w, h)
self.image.setPixmap(pixmap)
self.image.repaint()
LoadImageThread then does nothing other than emit a response to the showImage signal we connected above, passing the thread arguments back. This means showImage will be executed on the GUI thread, avoiding those nasty QPixmap errors. Note the __del__ function below; that prevents the thread from being garbage collected while running.
class LoadImageThread(QtCore.QThread):
def __init__(self, file w, h):
QtCore.QThread.__init__(self)
self.file = file
self.w = w
self.h = h
def __del__(self):
self.wait()
def run(self):
self.emit(QtCore.SIGNAL('showImage(QString, int, int)'), self.file, self.w, self.h)
There we have it – a stupid and contrived solution to a stupid problem.