multithreading - 从 Python 线程更新 PyQt GUI
问题描述
我有一个用 Designer (pyqt5) 制作的 GUI。我的主类中的一个函数需要在一个单独的线程上工作。在操作期间,我还在 QtextEdit LIVE 上捕获了标准输出。到目前为止一切正常。
现在我正在尝试在我的主 GUI 表单上实现 ProgressBar。该栏需要像在 textEdit 上一样显示实时进度。
下面的示例代码在 Linux 上运行,没有任何警告。但是在 Windows 上我得到了错误:
QObject::setParent: Cannot set parent, new parent is in a different thread
我知道这是因为我在线程函数中进行了 ui 元素修改。我进行了研究,但所有答案都指向使用 QThreads(就在我开始了解基本线程时!)。我希望有一种方法来更新我的 GUI,而不必更改下面的当前线程系统。
这是示例代码:
import sys
import threading
import time
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor
from ui_form import Ui_Form
class EmittingStream(QObject):
textWritten = pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class Form(QMainWindow):
finished = pyqtSignal()
def __init__(self, parent=None):
super(Form, self).__init__(parent)
# Install the custom output stream
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.pushButton_run.clicked.connect(self.start_task)
self.finished.connect(self.end_task)
def start_task(self):
self.thread = threading.Thread(target=self.run_test)
self.thread.start()
self.ui.pushButton_run.setEnabled(False)
def end_task(self):
self.ui.pushButton_run.setEnabled(True)
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
cursor = self.ui.textEdit.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.ui.textEdit.setTextCursor(cursor)
self.ui.textEdit.ensureCursorVisible()
def run_test(self):
for i in range(100):
per = i + 1
self.ui.progressBar.setValue(per)
print("%%%s" % per)
time.sleep(0.15) # simulating expensive task
print("Task Completed!")
time.sleep(1.5)
self.ui.progressBar.reset()
self.finished.emit()
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
用户界面:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'form.ui'
#
# Created: Mon Apr 30 13:43:19 2018
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(Form)
self.centralwidget.setObjectName("centralwidget")
self.pushButton_run = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_run.setGeometry(QtCore.QRect(40, 20, 311, 191))
self.pushButton_run.setObjectName("pushButton_run")
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(40, 230, 721, 241))
self.textEdit.setObjectName("textEdit")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(40, 490, 721, 23))
self.progressBar.setObjectName("progressBar")
Form.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(Form)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
self.menubar.setObjectName("menubar")
Form.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(Form)
self.statusbar.setObjectName("statusbar")
Form.setStatusBar(self.statusbar)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "MainWindow"))
self.pushButton_run.setText(_translate("Form", "RUN"))
不知何故,我需要立即通知 gui 线程(从我正在运行的线程)进度条值正在更改(可能需要几分钟才能完成的过程)。
解决方案
定义一个向进度条发送更新的自定义信号:
class Form(QMainWindow):
finished = pyqtSignal()
updateProgress = pyqtSignal(int)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
...
self.updateProgress.connect(self.ui.progressBar.setValue)
def run_test(self):
for i in range(100):
per = i + 1
self.updateProgress.emit(per)
...
推荐阅读
- javascript - 如何使用 react/react native 在单击时删除或隐藏多个组件
- python - 如何在 for 循环 matplotlib 内创建分组条形图
- reactjs - Symfony 400 错误必须提供密钥“电子邮件”
- laravel - 如何使用 Laravel 在同一视图中显示来自两个不同表的记录?
- javascript - 将小数转换为厘米或英寸后有条件地格式化小数
- python - 如何在熊猫数据框中的多列中选择最大值和最小值?
- javascript - 有没有办法使某些字符串或值与 JS 中的其他字符串或值等效?
- javascript - React Native:当您滑回已安装的屏幕时如何触发事件?
- reactjs - 使用 Spring 单点登录
- html - 如何让 Tab 键执行我期望的操作