线程:在GUI线程外使用pixmaps是不安全的

时间:2011-12-27 21:01:18

标签: python multithreading qt pyqt qpixmap

我正在构建一个音乐播放器,用SqueezePlay检查状态,SqueezePlay是一个SqueezeBox控制器应用程序。简而言之,我通过使用线程来检查Squeezeplay的状态,持续5秒。如果歌曲标题改变了,我让它更新标签(Qlabel,专辑封面(QPixmap)等等。但是,当我要求它通过线程更新它时,我得到在外面使用pixmaps是不安全的GUI线程

如何进行线程处理但仍设置QPixmap?

示例代码:

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

非常感谢!

更新 我尝试了下面的Emit,但它不起作用,有人可以看看我做错了吗?

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)

4 个答案:

答案 0 :(得分:6)

所有图形Qt操作都应该在主线程中进行。其他线程实际上不允许调用Qt图形操作(包括可能是pixmaps)。

他们可以emit Qt signals到主线程。或者简单地(在Linux上)写入管道,并让主thread等待该管道上的输入。

当然,您必须定义所需的信号(以及插槽)。在C ++代码中,您需要使用signals:(或slots:)标记它们,并且您的C ++代码应由moc处理。我不知道什么是Python对应物(也许python反射能力可能就足够了,我真的不知道)。然后,您必须使用排队的连接将信号连接到插槽。我不知道如何在Python中做到这一点。

答案 1 :(得分:4)

回答有关如何在python中发出信号的问题:

与C ++不同,当发出用户定义的 PyQt信号(而不是Qt信号)时,应省略签名。

因此,要发出信号,请执行以下操作:

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

要连接信号,请执行以下操作:

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

只是要明确:

为了使其正常工作,非gui线程必须发出信号,然后由主gui线程中的相应小部件接收该信号。在非gui线程中创建QImage应该没问题,但绝不会尝试在主线程之外调用任何与gui相关的方法。

<强> NB

我在这里使用过旧式的信号语法,因为这就是您正在使用的内容。但是,您可能希望查看PyQt的new-style signal and slot support,因为它更加灵活和pythonic。

答案 2 :(得分:1)

您可能需要将所有绘图作业发送到主线程。

答案 3 :(得分:0)

我已经尝试过了,请告诉我是否响了铃,我已经做了类似的事情(但是我离蟒蛇很远一段时间了,所以我可能也犯了错误,如果是的话,抱歉。)

class MyThread(QThread, ui):
    def __init__(self, ui):
        super(MyThread, self).__init__(self)
        self.ui = ui

    def run(self):
       coverArt = self.ui.getArtwork()
       coverPixMap = QtGui.QPixmap()
       coverPixmap.convertFromImage(QtGui.QIcon(coverArt))
       icon = QtGui.QImage(coverPixMap)
       self.ui.item.setIcon(icon)  // set icon
       self.ui.singerLabel.setText("Singer")  // update label 

# your gui class
class YourInterface(QtGui.QWidget):
    def __init__(self):
       QtGui.QWidget.__init__(self)
        myThread = MyThread(self)
        self.myButton.clicked.connect(myThread.run)
        # all other stuff
        #
        #