如何从QThread调用widget的方法

时间:2016-05-11 17:21:43

标签: python multithreading qt pyqt qthread

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

可能是什么原因?

enter image description here

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_()

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

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_()

1 个答案:

答案 0 :(得分:2)

你需要做两件事。您需要将发射器对象移动到第二个线程,并且需要将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除外)将在主线程中执行。

话虽这么说,如果你想在主线程和第二线程之间来回发送数据,你可能想要查看在Qt中使用QThread Worker Pattern