gpt4 book ai didi

python - 如何在非主线程 QObject 中开始工作

转载 作者:太空宇宙 更新时间:2023-11-04 05:16:24 25 4
gpt4 key购买 nike

根据 Qt 文档和网络上的其他示例,我认为以下使用 QThread.started 信号的程序会在 非主线程。但事实并非如此,而是从 ma​​in 线程调用每个 work 槽:

import time
import sys

from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget


def trap_exc_during_debug(*args):
# when app exits, put breakpoint in next line when run in debugger, and analyse args
pass


sys.excepthook = trap_exc_during_debug


class Checker(QObject):
sig_step = pyqtSignal(int, str)
sig_done = pyqtSignal(int)

def __init__(self, id: int):
super().__init__()
self.__id = id

@pyqtSlot()
def work(self):
thread_name = QThread.currentThread().objectName()
thread_id = int(QThread.currentThreadId())
print('running work #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))

time.sleep(2)
self.sig_step.emit(self.__id, 'step 1')
time.sleep(2)
self.sig_step.emit(self.__id, 'step 2')
time.sleep(2)
self.sig_done.emit(self.__id)


class MyWidget(QWidget):
NUM_THREADS = 3

sig_start = pyqtSignal()

def __init__(self):
super().__init__()

self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(200, 200)

self.push_button = QPushButton()
self.push_button.clicked.connect(self.start_threads)
self.push_button.setText("Start {} threads".format(self.NUM_THREADS))
form_layout.addWidget(self.push_button)

self.log = QTextEdit()
form_layout.addWidget(self.log)
# self.log.setMaximumSize(QSize(200, 80))

self.text_edit = QTextEdit()
form_layout.addWidget(self.text_edit)
# self.text_edit.setMaximumSize(QSize(200, 60))

QThread.currentThread().setObjectName('main')
self.__threads_done = None
self.__threads = None

def start_threads(self):
self.log.append('starting {} threads'.format(self.NUM_THREADS))
self.push_button.setDisabled(True)

self.__threads_done = 0
self.__threads = []
for idx in range(self.NUM_THREADS):
checker = Checker(idx)
thread = QThread()
thread.setObjectName('thread_' + str(idx))
self.__threads.append((thread, checker)) # need to store checker too otherwise will be gc'd
checker.moveToThread(thread)
checker.sig_step.connect(self.on_thread_step)
checker.sig_done.connect(self.on_thread_done)

# self.sig_start.connect(checker.work) # method 1 works: each work() is in non-main thread
thread.started.connect(checker.work) # method 2 doesn't work: each work() is in main thread
thread.start()

self.sig_start.emit() # this is only useful in method 1

@pyqtSlot(int, str)
def on_thread_step(self, thread_id, data):
self.log.append('thread #{}: {}'.format(thread_id, data))
self.text_edit.append('{}: {}'.format(thread_id, data))

@pyqtSlot(int)
def on_thread_done(self, thread_id):
self.log.append('thread #{} done'.format(thread_id))
self.text_edit.append('-- Thread {} DONE'.format(thread_id))
self.__threads_done += 1
if self.__threads_done == self.NUM_THREADS:
self.log.append('No more threads')
self.push_button.setEnabled(True)


if __name__ == "__main__":
app = QApplication([])

form = MyWidget()
form.show()

sys.exit(app.exec_())

如果我改为使用自定义信号,则效果很好。要看到这一点,请注释掉“方法 2”行并取消注释“方法 1”行并重复运行。

在不必创建自定义信号的情况下启动工作人员肯定会更好,有没有办法做到这一点(同时坚持对工作人员调用 moveToThread 的设计)?

注意:QThread.started 信号的文档帮助不大:

This signal is emitted from the associated thread when it starts executing

对我来说,这意味着 started 将在非主线程中发出,这样它所连接的 work 插槽将在非主线程中被调用线程,但显然不是这样。即使我的解释不正确并且信号实际上是在主线程中发出的,连接类型对于这两种方法都是默认的Qt.AutoConnection,到一个插槽QObject 移动到另一个线程,因此 started 信号应该被异步传输(即通过每个检查器的 QThread 事件循环),同样显然不是案件。

最佳答案

我向 jetbrains 发布了支持请求,他们迅速回复:

It was made intentionally for a better debugging. You can uncheck setting Settings | Build, Execution, Deployment | Python Debugger > PyQt compatible and will have workers started as expected.

太棒了!

关于python - 如何在非主线程 QObject 中开始工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41601862/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com