【问题标题】:PyQt5: QMainWindow freezes when calling a long running functionPyQt5:调用长时间运行的函数时 QMainWindow 冻结
【发布时间】:2021-01-18 10:41:49
【问题描述】:

创建一个 QMainWindow >> 按下开始按钮 >> 连接一个长时间运行的函数和一个 QLabel 作为 arg >> 在运行长函数时更新标签。

我想在 GUI 中更新长时间运行的函数的状态。但是一旦长时间运行的函数启动,整个窗口就会冻结

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())

【问题讨论】:

    标签: python pyqt pyqt5


    【解决方案1】:

    您应该使用 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_()
    

    【讨论】:

    • 谢谢。有效。一个错字,在 update_ui(progress=25) 中应该是百分比而不是进度。
    • @imankalyan 如果那个答案是正确的,could you accept it ? :)
    【解决方案2】:

    您可以手动调用 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?

    【讨论】:

    • 没有。 processEvents 应仅用于可能允许最少事件队列处理或实际上需要“清除”队列的特定情况。对于需要 this 很多时间(10 秒很多)的块,这不是 一个好的选择,最重要的是因为它可能会导致输入交互出现很多问题。跨度>
    • 虽然它正在更新标签,但 GUI 仍然在休眠时冻结。
    猜你喜欢
    • 2012-06-07
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多