代码之家  ›  专栏  ›  技术社区  ›  alphanumeric

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

  •  1
  • alphanumeric  · 技术社区  · 8 年前

    代码运行,但打印出错误: 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 回复  |  直到 8 年前
        1
  •  2
  •   Brendan Abel    8 年前

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

    也就是说,如果您想在主线程和第二线程之间来回发送数据,您可能需要查看 工人模式 使用的 Q螺纹 在Qt中。