PyQt5:调用长 运行 函数时 QMainWindow 冻结
PyQt5: QMainWindow freezes when calling a long running function
创建一个 QMainWindow >> 按下开始按钮 >> 将长运行 函数与 QLabel 作为参数连接 >> 在 运行 长函数时更新标签。
我想在 GUI 中更新 long-运行 函数的状态。但是一旦 long-运行 函数启动,整个 window 就会冻结
import sys
import time
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
def runLongTask(label):
label.setText('<1> sleeping for 10s ...')
time.sleep(10)
label.setText('<2> sleeping for 10s ...')
time.sleep(10)
label.setText('End')
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.setWindowTitle("GUI freeze FIX")
self.resize(350, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.label = QLabel()
self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.label.setText('No Update')
countBtn = QPushButton("Start")
countBtn.clicked.connect(lambda: runLongTask(self.label))
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(countBtn)
self.centralWidget.setLayout(layout)
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
您可以手动调用 processEvents:
def runLongTask(label):
label.setText('<1> sleeping for 10s ...')
QApplication.processEvents()
time.sleep(10)
label.setText('<2> sleeping for 10s ...')
QApplication.processEvents()
time.sleep(10)
label.setText('End')
label.update()
有关更多信息,您应该搜索“qt Keeping the GUI Responsive”,
或此处的 QThread/QtConcurrent 答案:How to make Qt work when main thread is busy?
您应该使用 PyQt5 的线程在不同的线程中启动您的长函数。这样,主 UI 线程本身就不会忙,甚至可以从其他线程接收信号并因此更新 UI.
This article很好的介绍了QThread的用法
这是单击按钮时执行的长任务的示例。长任务使用虚拟 time.sleep(x)
使其变长,但请注意 update_ui
函数是如何传递的,就像回调一样,以更新 UI.
import sys
import time
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QProgressBar,
QPushButton,
QVBoxLayout,
QWidget,
)
def long_running_function(update_ui):
# Doing something
time.sleep(1)
update_ui(percent=25)
# Doing something else
time.sleep(1)
update_ui(percent=50)
# Another long thing
time.sleep(1)
update_ui(percent=75)
# Almost done
time.sleep(1)
update_ui(percent=100)
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
def run(self):
# Here we pass the update_progress (uncalled!)
# function to the long_running_function:
long_running_function(self.update_progress)
self.finished.emit()
def update_progress(self, percent):
self.progress.emit(percent)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
layout = QVBoxLayout()
self.progress = QProgressBar()
self.button = QPushButton("Start")
layout.addWidget(self.progress)
layout.addWidget(self.button)
self.button.clicked.connect(self.execute)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
def execute(self):
self.update_progress(0)
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
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.update_progress)
self.thread.start()
self.button.setEnabled(False)
def update_progress(self, progress):
self.progress.setValue(progress)
self.button.setEnabled(progress == 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
app.exec_()
创建一个 QMainWindow >> 按下开始按钮 >> 将长运行 函数与 QLabel 作为参数连接 >> 在 运行 长函数时更新标签。
我想在 GUI 中更新 long-运行 函数的状态。但是一旦 long-运行 函数启动,整个 window 就会冻结
import sys
import time
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
def runLongTask(label):
label.setText('<1> sleeping for 10s ...')
time.sleep(10)
label.setText('<2> sleeping for 10s ...')
time.sleep(10)
label.setText('End')
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.setWindowTitle("GUI freeze FIX")
self.resize(350, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.label = QLabel()
self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.label.setText('No Update')
countBtn = QPushButton("Start")
countBtn.clicked.connect(lambda: runLongTask(self.label))
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(countBtn)
self.centralWidget.setLayout(layout)
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
您可以手动调用 processEvents:
def runLongTask(label):
label.setText('<1> sleeping for 10s ...')
QApplication.processEvents()
time.sleep(10)
label.setText('<2> sleeping for 10s ...')
QApplication.processEvents()
time.sleep(10)
label.setText('End')
label.update()
有关更多信息,您应该搜索“qt Keeping the GUI Responsive”, 或此处的 QThread/QtConcurrent 答案:How to make Qt work when main thread is busy?
您应该使用 PyQt5 的线程在不同的线程中启动您的长函数。这样,主 UI 线程本身就不会忙,甚至可以从其他线程接收信号并因此更新 UI.
This article很好的介绍了QThread的用法
这是单击按钮时执行的长任务的示例。长任务使用虚拟 time.sleep(x)
使其变长,但请注意 update_ui
函数是如何传递的,就像回调一样,以更新 UI.
import sys
import time
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QProgressBar,
QPushButton,
QVBoxLayout,
QWidget,
)
def long_running_function(update_ui):
# Doing something
time.sleep(1)
update_ui(percent=25)
# Doing something else
time.sleep(1)
update_ui(percent=50)
# Another long thing
time.sleep(1)
update_ui(percent=75)
# Almost done
time.sleep(1)
update_ui(percent=100)
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
def run(self):
# Here we pass the update_progress (uncalled!)
# function to the long_running_function:
long_running_function(self.update_progress)
self.finished.emit()
def update_progress(self, percent):
self.progress.emit(percent)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
layout = QVBoxLayout()
self.progress = QProgressBar()
self.button = QPushButton("Start")
layout.addWidget(self.progress)
layout.addWidget(self.button)
self.button.clicked.connect(self.execute)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
def execute(self):
self.update_progress(0)
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
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.update_progress)
self.thread.start()
self.button.setEnabled(False)
def update_progress(self, progress):
self.progress.setValue(progress)
self.button.setEnabled(progress == 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
app.exec_()