threading: It is not safe to use pixmaps outside the GUI thread


Question

I'm building a music player, that checks the status with SqueezePlay, which is a SqueezeBox controller app. To cut a long story short, I'm checking the status of Squeezeplay ever 5 seconds by using threading. If the song title changes, I let it update the labels (Qlabel, album artwork (QPixmap), etc. However, when I ask it to update it via threading, I'm getting It is not safe to use pixmaps outside the GUI thread .

How can I do threading but still set the QPixmap?

Sample code:

#self.sq.getArtwork() returns variable with the image
coverArt = self.sq.getArtwork()
coverPixMap = QtGui.QPixmap()
coverPixMap.loadFromData(coverArt)
self.albumArt.setPixmap(coverPixMap)

Many thanks!

Update: I tried the following with Emit, but it doesn't work, can someone take a look what I'm doing wrong?

def setNewArtwork(self, image):
    coverPixMap = QtGui.QPixmap()
    coverPixMap.convertFromImage(image)
    icon = QtGui.QIcon(coverPixMap)
    item.setIcon(icon)

def getNewArtwork(self):
    coverArt = self.sq.getArtwork()
    icon = QtGui.QImage(coverArt)
    self.emit(QtCore.SIGNAL('setNewArtwork(QImage)'), icon)
1
4
10/27/2016 4:56:27 PM

Accepted Answer

All graphical Qt operations should happen in the main thread. Other threads are not really allowed to call Qt graphical operations (including probably pixmaps).

They could emit Qt signals to the main thread. Or simply (on Linux) write into a pipe, and have the main thread wait for input on that pipe.

Of course, you have to define the signals (and also the slots) you want. In C++ code, you need to mark them with signals: (or slots:) and your C++ code should be processed by the moc. I don't know what is the Python counterpart (perhaps python reflection abilities might be enough, I really don't know). You then have to connect signals to slots, with a queued connection. I have no idea how to do that in Python.

6
1/13/2017 12:21:47 AM

To answer the question regarding how to emit the signal in python:

Unlike C++, when emitting a user-defined PyQt signal (as opposed to a Qt one), the signature should be omitted.

So, to emit the signal, do something like this:

thread.emit(QtCore.SIGNAL('newArtworkAvailable'), icon)

And to connect to the signal, do something like this:

widget.connect(thread, QtCore.SIGNAL('newArtworkAvailable'),
               widget.setNewArtwork)

And just to be clear:

For this to work, the non-gui thread must emit the signal, which is then received by the appropriate widget in the main gui thread. Creating a QImage in the non-gui thread should be okay, but never attempt to call any gui-related methods outside of the main thread.

NB:

I have used old-style signal syntax here because that is what you appear to be using. However, you might want to look at PyQt's new-style signal and slot support as it is much more flexible and pythonic.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon