通过单击按钮调用 class 方法的不同方式
different ways of calling class method with button clicks
我正在探索如何在 PyQt 应用程序中调用 class 方法。作为测试,我创建了一个小部件来初始化一个 worker,将它放在一个单独的线程上,然后启动该线程。我还创建了两个按钮:
- 左边的按钮直接连接worker的
run
功能
- 右边的按钮连接到调用 worker
run
函数的 widget 方法
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
from time import sleep
class Worker(QObject):
worker_finished = pyqtSignal()
def __init__(self):
super().__init__()
@pyqtSlot()
def run(self):
print("Sleeping")
for ii in range(5):
print(ii)
sleep(1)
print("Finished sleeping")
class MainApp(QWidget):
def __init__(self):
super().__init__()
self._threads = []
#Create the worker
self.worker = Worker()
layout = QHBoxLayout(self)
#Move the worker on the thread and start
self.put_on_thread_and_start(self.worker)
#This button calls worker.run() directly
button = QPushButton("Call \"\"run\"\" method directly")
button.clicked.connect(self.worker.run)
layout.addWidget(button)
#This button calls a widget method, which calls worker.run()
button = QPushButton("Call \"\"run\"\" method through this widget class method")
button.clicked.connect(self.make_the_worker_run)
layout.addWidget(button)
def put_on_thread_and_start(self, worker_class):
myThread = QThread()
self._threads.append(myThread)
worker_class.moveToThread(myThread)
print("Starting thread...")
myThread.start()
def make_the_worker_run(self):
self.worker.run()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainApp()
window.show()
sys.exit(app.exec_())
当我单击左键时,worker 按预期在后台执行。但是,当我单击右键时,小部件会冻结,直到工作人员完成 运行。这两种方法有什么区别?
当一个信号被发射到一个函数(槽)并且该信号被发射时,Qt 检测信号发射和接收对象是否在同一个线程中。如果它们是 not,槽将在接收方的线程中执行,并且控制会立即返回到发送方。这就是允许在 Qt 上使用线程而不阻塞“主 Qt 线程”(负责保持 UI 响应)的原因。
需要理解的一个非常重要的事情是 Qt 能够检测到哪个线程发出了信号以及 slot 在哪个线程上,然后它决定是否可以直接使用 slot是否执行。
在第一种情况下,按钮(将发出信号)和 self.worker.run
在不同的线程上,Qt 知道当它尝试调用 run
时;结果是该函数将在另一个线程中执行。
在第二种情况下,Qt 只知道 make_the_worker_run
,从它的角度来看,它与按钮在同一个线程中:Qt 对您在该函数中实际执行的操作一无所知。 run
在一个对象的方法中,这个方法已经被移动到另一个线程中,这并不意味着什么,结果是该函数将在主线程中执行,因此阻塞。
在有关 Threads and QObjects 的 Qt 文档中阅读有关此主题的更多信息。
对于线程、QThread、信号等的工作方式存在错误的概念。
当 QObject 移动到 QThread 时,它只告诉 Qt 事件循环,如果它调用该 QObject 的槽,则它必须在管理 QThread 的线程中执行(如果 Qt::QueuedConnection 或 Qt::AutoConnection 标志在连接中使用)。 Qt 事件循环如何调用函数?那么,为此使用信号、定时器、QMetaObject::invokedMethod、QEvents 等
上面解释了为什么第一种方法有效:通过使用点击信号调用“运行”,它通知事件循环它应该调用该方法,并且使用前面的规则它将调用线程管理 QThread。
在第二种方法中,您直接在主线程中调用它,这会阻止主线程的事件循环冻结 GUI。
我正在探索如何在 PyQt 应用程序中调用 class 方法。作为测试,我创建了一个小部件来初始化一个 worker,将它放在一个单独的线程上,然后启动该线程。我还创建了两个按钮:
- 左边的按钮直接连接worker的
run
功能 - 右边的按钮连接到调用 worker
run
函数的 widget 方法
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
from time import sleep
class Worker(QObject):
worker_finished = pyqtSignal()
def __init__(self):
super().__init__()
@pyqtSlot()
def run(self):
print("Sleeping")
for ii in range(5):
print(ii)
sleep(1)
print("Finished sleeping")
class MainApp(QWidget):
def __init__(self):
super().__init__()
self._threads = []
#Create the worker
self.worker = Worker()
layout = QHBoxLayout(self)
#Move the worker on the thread and start
self.put_on_thread_and_start(self.worker)
#This button calls worker.run() directly
button = QPushButton("Call \"\"run\"\" method directly")
button.clicked.connect(self.worker.run)
layout.addWidget(button)
#This button calls a widget method, which calls worker.run()
button = QPushButton("Call \"\"run\"\" method through this widget class method")
button.clicked.connect(self.make_the_worker_run)
layout.addWidget(button)
def put_on_thread_and_start(self, worker_class):
myThread = QThread()
self._threads.append(myThread)
worker_class.moveToThread(myThread)
print("Starting thread...")
myThread.start()
def make_the_worker_run(self):
self.worker.run()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainApp()
window.show()
sys.exit(app.exec_())
当我单击左键时,worker 按预期在后台执行。但是,当我单击右键时,小部件会冻结,直到工作人员完成 运行。这两种方法有什么区别?
当一个信号被发射到一个函数(槽)并且该信号被发射时,Qt 检测信号发射和接收对象是否在同一个线程中。如果它们是 not,槽将在接收方的线程中执行,并且控制会立即返回到发送方。这就是允许在 Qt 上使用线程而不阻塞“主 Qt 线程”(负责保持 UI 响应)的原因。
需要理解的一个非常重要的事情是 Qt 能够检测到哪个线程发出了信号以及 slot 在哪个线程上,然后它决定是否可以直接使用 slot是否执行。
在第一种情况下,按钮(将发出信号)和 self.worker.run
在不同的线程上,Qt 知道当它尝试调用 run
时;结果是该函数将在另一个线程中执行。
在第二种情况下,Qt 只知道 make_the_worker_run
,从它的角度来看,它与按钮在同一个线程中:Qt 对您在该函数中实际执行的操作一无所知。 run
在一个对象的方法中,这个方法已经被移动到另一个线程中,这并不意味着什么,结果是该函数将在主线程中执行,因此阻塞。
在有关 Threads and QObjects 的 Qt 文档中阅读有关此主题的更多信息。
对于线程、QThread、信号等的工作方式存在错误的概念。
当 QObject 移动到 QThread 时,它只告诉 Qt 事件循环,如果它调用该 QObject 的槽,则它必须在管理 QThread 的线程中执行(如果 Qt::QueuedConnection 或 Qt::AutoConnection 标志在连接中使用)。 Qt 事件循环如何调用函数?那么,为此使用信号、定时器、QMetaObject::invokedMethod、QEvents 等
上面解释了为什么第一种方法有效:通过使用点击信号调用“运行”,它通知事件循环它应该调用该方法,并且使用前面的规则它将调用线程管理 QThread。
在第二种方法中,您直接在主线程中调用它,这会阻止主线程的事件循环冻结 GUI。