PyQT5:如何使用相同的工作定义在单独的 QThread 中运行任意 Callable?
如何定义一个工作线程以便我可以在单独的 QThread 中运行任意 Callable ?
我尝试使用 lambda 表达式在运行时传递参数,但这不起作用(工作线程仍然在主线程中运行),请参阅下面的代码,方法 run_in_separate_thread
import sys
from time import sleep
from typing import Callable
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget, QPlainTextEdit,
)
from PyQt5.QtCore import QObject, QThread, pyqtSignal
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.resize(300, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.text_edit = QPlainTextEdit()
self.bt = QPushButton("Run in separate thread", self)
layout = QVBoxLayout()
layout.addWidget(self.text_edit)
layout.addWidget(self.bt)
self.centralWidget.setLayout(layout)
self.bt.clicked.connect(lambda: self.run_in_separate_thread(my_long_function))
def report_progress(self, text: str) -> None:
""" Report progress of function in separate thread """
self.text_edit.appendPlainText(str(text))
def run_in_separate_thread(self, function: Callable) -> None:
""" Wrapper to run a function in a separate thread """
self.thread = QThread(objectName="workerThread")
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.report_progress)
self.thread.started.connect(lambda: self.worker.run(function))
self.thread.start()
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self, function: Callable):
function(self.progress)
self.finished.emit()
def my_long_function(signal):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
signal.emit("hello")
app = QApplication(sys.argv)
QThread.currentThread().setObjectName('main')
print(f"Main thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
win = Window()
win.show()
sys.exit(app.exec())
:给我以下输出,并且 QTextEdit 中没有显示“实时”输出。
Main thread: ('main', 139754959259456)
mylongfunction thread: ('main', 139754959259456)
是因为 lambda 表达式在主线程中“存活”吗? 如果我在工作 run()
方法中硬编码 my_long_function
并删除所有 lambda 表达式,它会按预期工作,请参见下面的代码:
import sys
from time import sleep
from typing import Callable
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget, QPlainTextEdit,
)
from PyQt5.QtCore import QObject, QThread, pyqtSignal
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.resize(300, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.text_edit = QPlainTextEdit()
self.bt = QPushButton("Run in separate thread", self)
layout = QVBoxLayout()
layout.addWidget(self.text_edit)
layout.addWidget(self.bt)
self.centralWidget.setLayout(layout)
self.bt.clicked.connect(self.run_in_separate_thread)
def report_progress(self, text: str) -> None:
""" Report progress of function in separate thread """
self.text_edit.appendPlainText(str(text))
def run_in_separate_thread(self) -> None:
""" Wrapper to run a function in a separate thread """
self.thread = QThread(objectName="workerThread")
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.report_progress)
self.thread.started.connect(self.worker.run)
self.thread.start()
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self):
self.my_long_function()
self.finished.emit()
def my_long_function(self):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
self.progress.emit("hello")
def my_long_function(signal):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
signal.emit("hello")
app = QApplication(sys.argv)
QThread.currentThread().setObjectName('main')
print(f"Main thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
win = Window()
win.show()
sys.exit(app.exec())
这会给出以下所需的输出:
Main thread: ('main', 139687209740096)
mylongfunction thread: ('workerThread', 139686815192832)
How can I define a worker so that I can run an arbitrary Callable
in a separate QThread?
I tried using lambda
expression to pass arguments at run-time, but this does not work (worker still runs in the main thread), see code below, method run_in_separate_thread
:
import sys
from time import sleep
from typing import Callable
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget, QPlainTextEdit,
)
from PyQt5.QtCore import QObject, QThread, pyqtSignal
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.resize(300, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.text_edit = QPlainTextEdit()
self.bt = QPushButton("Run in separate thread", self)
layout = QVBoxLayout()
layout.addWidget(self.text_edit)
layout.addWidget(self.bt)
self.centralWidget.setLayout(layout)
self.bt.clicked.connect(lambda: self.run_in_separate_thread(my_long_function))
def report_progress(self, text: str) -> None:
""" Report progress of function in separate thread """
self.text_edit.appendPlainText(str(text))
def run_in_separate_thread(self, function: Callable) -> None:
""" Wrapper to run a function in a separate thread """
self.thread = QThread(objectName="workerThread")
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.report_progress)
self.thread.started.connect(lambda: self.worker.run(function))
self.thread.start()
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self, function: Callable):
function(self.progress)
self.finished.emit()
def my_long_function(signal):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
signal.emit("hello")
app = QApplication(sys.argv)
QThread.currentThread().setObjectName('main')
print(f"Main thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
win = Window()
win.show()
sys.exit(app.exec())
This gives me the following output, and no "live" output is shown in the QTextEdit.
Main thread: ('main', 139754959259456)
mylongfunction thread: ('main', 139754959259456)
Is it because lambda expression "live" in the Main thread?
If I hard code my_long_function
in the worker run()
method and remove all lambdas expressions, it works as intended, see code below:
import sys
from time import sleep
from typing import Callable
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget, QPlainTextEdit,
)
from PyQt5.QtCore import QObject, QThread, pyqtSignal
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.resize(300, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.text_edit = QPlainTextEdit()
self.bt = QPushButton("Run in separate thread", self)
layout = QVBoxLayout()
layout.addWidget(self.text_edit)
layout.addWidget(self.bt)
self.centralWidget.setLayout(layout)
self.bt.clicked.connect(self.run_in_separate_thread)
def report_progress(self, text: str) -> None:
""" Report progress of function in separate thread """
self.text_edit.appendPlainText(str(text))
def run_in_separate_thread(self) -> None:
""" Wrapper to run a function in a separate thread """
self.thread = QThread(objectName="workerThread")
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.report_progress)
self.thread.started.connect(self.worker.run)
self.thread.start()
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self):
self.my_long_function()
self.finished.emit()
def my_long_function(self):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
self.progress.emit("hello")
def my_long_function(signal):
print(f"mylongfunction thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
for _ in range(5):
sleep(1)
signal.emit("hello")
app = QApplication(sys.argv)
QThread.currentThread().setObjectName('main')
print(f"Main thread: {QThread.currentThread().objectName(), int(QThread.currentThreadId())}")
win = Window()
win.show()
sys.exit(app.exec())
This gives following desired output:
Main thread: ('main', 139687209740096)
mylongfunction thread: ('workerThread', 139686815192832)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的,这里有一个解决方案,通过将 Callable 定义为工作线程的实例变量:
这样,工作线程就可以在单独的线程中正确运行。
完整代码:
OK here is a solution, by defining the Callable as an instance variable of the worker:
With this the worker is properly running in a separate thread.
full code: