在GUI程序中,主线程也叫GUI线程,它是唯一被允许执行GUI相关操作的线程。对于一些耗时多的任务,如果放在主线程中,就可能会造成阻塞从而出现界面无响应的问题。先上代码:

class AppMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(AppMainWindow, self).__init__(parent)
        self.setupUi(self)

    snp_path = '' 
    data = []  

    def loadSNP(self):
        if self.snp_path:
            self.labelFileStat.setText("Loading...")
            self.labelFileStat.repaint()
            self.data = file_process(self.snp_path)  # 此任务耗时可达数十秒
            if self.data:
                self.labelFileStat.setText(str(len(self.data))+" lines of data loaded.")
            else:
                self.labelFileStat.setText("FAILED!!")

解决方案找到了两种:

  1. 使用多线程:一个线程处理应用程序用户界面的线程,另一个线程执行耗时任务()
  2. 调用QApplication.processEvents()

方案1暂且按下不表,后面用到再研究,挖个坑先。
最近写的这个程序本身很简单,所以采用了方案2,在file_process()中周期性地调用QApplication.processEvents(),这个函数告诉Qt来处理任何没有被处理的事件,防止造成阻塞。Qt官方文档里也写了:

You can call this function occasionally when your program is busy doing a long operation (e.g. copying a file).

需要注意的是,processEvents()默认处理所有Qt事件,这样在耗时任务执行期间存在因用户操作而产生非预期事件的风险,例如文件处理任务执行过程中用户点击关闭窗口。QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)可以避免这种情况发生。
此外,processEvents()还支持传入maxtime参数,用于指定处理事件的最大时间,单位ms,Qt文档原话:

Processes pending events for the calling thread for maxtime milliseconds or until there are no more events to process, whichever is shorter.

参考链接:
PyQt v5.15.0 Reference Guide

标签: Python, PyQt, 编程

添加新评论