这是基于ekhumoro的答案here和here的后续问题。
我想了解的是,当使用pyqtSlot
正确定义插槽并将其分配给QThread
(例如,使用moveToThread()
)时,它将在此QThread中执行,而不是在调用时执行一。此外,还需要与Qt.QueuedConnection
或Qt.AutoConnection
建立连接。
我编写了代码对此进行了测试。我的目标是实现类似以下的简单操作:
Gui带有一个按钮,开始一些耗时的工作,然后返回结果并显示回GUI。
from PyQt5.Qt import *
class MainWindow(QMainWindow):
change_text = pyqtSignal(str)
def __init__(self):
super().__init__()
self.button = QPushButton('Push me!', self)
self.setCentralWidget(self.button)
print('main running in:', QThread.currentThread())
thread = Thread(change_text, self)
thread.start()
self.button.clicked.connect( thread.do_something_slow, Qt.QueuedConnection)
self.change_text.connect(self.display_changes, Qt.QueuedConnection)
@pyqtSlot(str)
def display_changes( self, text ):
self.button.setText(text)
class Thread(QThread):
def __init__(self, signal_to_emit, parent):
super().__init__(parent)
self.signal_to_emit = signal_to_emit
#self.moveToThread(self) #doesn't help
@pyqtSlot()
def do_something_slow( self ):
print('Slot doing stuff in:', QThread.currentThread())
import time
time.sleep(5)
self.signal_to_emit.emit('I did something')
if __name__ == '__main__':
app = QApplication([])
main = MainWindow()
main.show()
app.exec()
但是.. gui被阻塞,并且在主线程中调用了插槽。
我想念什么?一定要小(我希望)。
答案 0 :(得分:2)
问题是您对QThread
是 Qt线程(即Qt创建的新线程)感到困惑,但没有,QThread
是一个处理本机线程,只有run()
方法正在另一个线程上运行,其他方法位于QThread
所在的线程中,该线程为QObject
,< / p>
QObject位于哪个线程中?
QObject
所在的线程是父线程,如果没有父线程,它将是创建该线程的线程。另一方面,QObject
可以使用moveToThread()
移动到另一个线程,并且其所有子代也将移动。如果moveToThread()
没有父母,则只能使用QObject
,否则将失败。
使用QThread
的方法是创建一个类,该类继承自QThread
,并覆盖run方法并调用start()
,以便它在运行中开始运行run()
。 run()
方法可以完成繁重的任务,但是在您的情况下则不能使用此表单,因为该任务将不会连续执行。比繁重的任务更好的选择是QObject
的一部分,并将QObject
移到另一个线程。
class MainWindow(QMainWindow):
change_text = pyqtSignal(str)
def __init__(self):
super().__init__()
self.button = QPushButton('Push me!', self)
self.setCentralWidget(self.button)
print('main running in:', QThread.currentThread())
# A Worker without a parent is created
# so that it can be moved to another thread.
self.worker = Worker(self.change_text)
thread = QThread(self)
self.worker.moveToThread(thread)
thread.start()
# All methods of self.worker are now executed in another thread.
self.button.clicked.connect(self.worker.do_something_slow)
self.change_text.connect(self.display_changes)
@pyqtSlot(str)
def display_changes( self, text ):
self.button.setText(text)
class Worker(QObject):
def __init__(self, signal_to_emit, parent=None):
super().__init__(parent)
self.signal_to_emit = signal_to_emit
@pyqtSlot()
def do_something_slow( self ):
print('Slot doing stuff in:', QThread.currentThread())
import time
time.sleep(5)
self.signal_to_emit.emit('I did something')
另一方面,不必指明连接类型,因为默认情况下为Qt::AutoConnection
,如果接收机使用相同的连接类型,则在运行时决定是否使用Qt::DirectConnection
信号发射的地方,否则使用Qt::QueuedConnection
。如您所知,Worker没有父级,因此对于它来说,其生命周期等于类,它必须是它的属性,因为否则它将是被消除的局部变量。另一方面,请注意QThread
接收的父对象比MainWindow多,因此QThread
将驻留在GUI线程中,但它处理辅助线程。
使用Worker概念,最好change_text信号不再属于GUI,而应由Worker更好地将对象解耦。
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button = QPushButton('Push me!', self)
self.setCentralWidget(self.button)
print('main running in:', QThread.currentThread())
self.worker = Worker()
thread = QThread(self)
self.worker.moveToThread(thread)
thread.start()
self.button.clicked.connect(self.worker.do_something_slow)
self.worker.change_text.connect(self.display_changes)
@pyqtSlot(str)
def display_changes( self, text ):
self.button.setText(text)
class Worker(QObject):
change_text = pyqtSignal(str)
@pyqtSlot()
def do_something_slow( self ):
print('Slot doing stuff in:', QThread.currentThread())
import time
time.sleep(5)
self.change_text.emit('I did something')