如何从 QThread 调用小部件的方法

How to call widget's method from QThread

代码运行但打印出错误:QObject::setParent: Cannot set parent, new parent is in a different thread.

可能是什么原因?

import Queue, threading
from PyQt4 import QtGui, QtCore
app = QtGui.QApplication([])    

class MessageBox(QtGui.QMessageBox):
    def __init__(self, parent=None):
        QtGui.QMessageBox.__init__(self, parent)     

    def showMessage(self):
        self.setText('Completed')
        self.show()

class Thread(QtCore.QThread):
    def __init__(self, queue, parent=None):
        QtCore.QThread.__init__(self, parent)     
        self.queue=queue        

    def run(self):    
        while True:
            number=self.queue.get()
            result = self.process(number)
            messagebox.showMessage()
            self.queue.task_done()

    def process(self, number):
        timer = QtCore.QTimer()
        for i in range(number):
            print 'processing: %s'%i
            QtCore.QThread.sleep(1)        
        return True    

messagebox = MessageBox()

queue = Queue.Queue()
thread = Thread(queue)
thread.start()

lock=threading.Lock()
lock.acquire()
queue.put(3)
lock.release()    
app.exec_()

在下面发布的示例中,我们使用信号和槽机制(而不是直接从线程调用它)到达小部件的方法。代码执行按预期工作。即使在我 "know" 解决方案时,我也想知道它为什么会发生。

class Emitter(QtCore.QObject):
    signal = QtCore.pyqtSignal()

class MessageBox(QtGui.QMessageBox):
    def __init__(self, parent=None):
        QtGui.QMessageBox.__init__(self, parent)     

    def showMessage(self):
        self.setText('Completed')
        self.show()

class Thread(QtCore.QThread):
    def __init__(self, queue, parent=None):
        QtCore.QThread.__init__(self, parent)     
        self.queue=queue        

    def run(self):
        emitter = Emitter()
        emitter.signal.connect(messagebox.showMessage)  

        while True:
            number=self.queue.get()
            result = self.process(number)
            emitter.signal.emit()
            self.queue.task_done()

    def process(self, number):
        timer = QtCore.QTimer()
        for i in range(number):
            print 'processing: %s'%i
            QtCore.QThread.sleep(1)        
        return True

messagebox = MessageBox()

queue = Queue.Queue()
thread = Thread(queue)
thread.start()

lock=threading.Lock()
lock.acquire()
queue.put(3)
lock.release()

app.exec_()

你需要做两件事。需要将发射器对象移动到第二个线程,需要声明showMessage为槽

emitter = Emitter()
emitter.moveToThread(self)
@QtCore.pyqtSlot()
def showMessage(self):
    ...

但是,最好在主线程中创建发射器并连接信号和槽,然后将其移至第二个线程

emitter = Emitter()
emitter.signal.connect(messagebox.showMessage)
emitter.moveToThread(thread)

另外,QThreads继承自QObject,所以你不是绝对需要发射器对象,你可以把信号直接放在QThread上。请注意 QThread 实际上存在于主线程中,并且您拥有的任何插槽(run 除外)都将在主线程中执行。

话虽如此,如果您想在主线程和第二线程之间来回发送数据,您可能需要查看使用 [=16= 的 Worker Pattern ] 在 Qt 中。