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.