Ska*_*dix 2 python pyqt qthread qt-signals pyqt5
这是一个基于 ekhumoro在这里和这里的回答的后续问题。
我想明白,当一个插槽被正确定义pyqtSlot并分配给QThread(例如 with 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()
Run Code Online (Sandbox Code Playgroud)
但是 .. gui 被阻塞,并且在主线程中调用了插槽。
我错过了什么?必须是小东西(我希望)。
问题是您混淆QThread了Qt 线程,即 Qt 创建的新线程,但不,QThread是处理本机线程的类,只有该run()方法在另一个线程上运行,其他方法存在于线程中在QThread生活中,这是一个QObject,
QObject 存在于哪个线程中?
aQObject所在的线程是父线程,如果它没有父线程,它将是创建它的线程。另一方面, aQObject可以使用 移动到另一个线程moveToThread(),并且它的所有子线程也将移动。只有moveToThread()在QObject没有父级时才能使用,否则会失败。
要使用的方法QThread是创建一个类,该类继承QThread并覆盖 run 方法并调用start()以便它开始运行run(),在该run()方法中将完成繁重的任务,但在您的情况下不可以使用此形式,因为任务不会被连续执行。比繁重任务更好的选择是 a 的一部分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')
Run Code Online (Sandbox Code Playgroud)
另一方面,没有必要指明连接的类型,因为默认情况下它是Qt::AutoConnection,这种类型的连接在运行时决定您是否使用Qt::DirectConnection接收器是否与发射信号的地方位于同一条线上,否则Qt::QueuedConnection使用。正如您所意识到的,Worker 没有父级,因此为了使其具有与类相同的生命周期,它必须是它的一个属性,否则它将是一个被消除的局部变量。另一方面,请注意QThread接收父窗口而不是 MainWindow,因此QThread将存在于 GUI 线程中,但它处理辅助线程。
使用 Worker 概念,更好的是 change_text 信号不再属于 GUI,而是属于更好地解耦对象的工作器。
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')
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1844 次 |
| 最近记录: |