完成GUI渲染后,在程序启动时执行长时间运行的代码

时间:2017-10-13 22:59:13

标签: python pyqt

我有一个可以从命令行启动的应用程序,并带有可选的文件名作为参数。如果存在,则应在启动时加载此文件。由于文件处理需要一些时间,fileOpen()会阻止程序并显示加载指示符。

在正常操作期间,这没关系。但是,当我尝试在启动时执行相同操作时(如下所示),窗口的轮廓出现在show()之后,但其内容在app.exec_()之前不会呈现。

我的问题:我该如何处理这种情况?

  • 我无法将fileOpen()放在app.exec_()之前,因为GUI尚未完全呈现。我无法告知用户仍在处理加载。
  • 我不能放? fileOpen()之后的app.exec_()因为它不会被执行,直到程序结束。

示例代码:

def main(args):
    app = QtGui.QApplication()
    mainwindow = MainWindow()
    mainwindow.show()
    if args.filename:
        mainwindow.fileOpen(args.filename)
    ret_val = app.exec_()
    sys.exit(ret_val)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename', help='(optional) file to load at startup')
    args = parser.parse_args()
    main(args)

2 个答案:

答案 0 :(得分:0)

我发现单次定时器可以解决这个问题,但我只是在Linux上用Openbox窗口管理器测试过,所以我不能保证它能在所有平台上运行。您可能需要调整超时的持续时间,以使其在您的系统上运行。

这是一个适合我的简单演示:

import sys
from PyQt5 import QtCore, QtWidgets

class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.edit = QtWidgets.QTextEdit(self)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.edit)

    def fileOpen(self, path):
        QtWidgets.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)
        QtCore.QThread.sleep(3)
        self.edit.setText(open(path).read())
        QtWidgets.qApp.restoreOverrideCursor()

def main():
    app = QtWidgets.QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.setGeometry(600, 100, 300, 200)
    mainwindow.show()
    QtCore.QTimer.singleShot(50, lambda: mainwindow.fileOpen(__file__))
    sys.exit(app.exec_())

if __name__ == '__main__':

    main()

答案 1 :(得分:0)

注意:此解决方案不适用于Linux(X11)(请参阅ekhumoro的评论和回答)

感谢所有答案。虽然他们每个人都有一些缺点(将在下面讨论),但他们给我带来了正确的解决方案:

qApp.processEvents() 之后致电mainwindow.show()

def main(args):
    app = QtGui.QApplication()
    mainwindow = MainWindow()
    mainwindow.show()
    qApp.processEvents()
    if args.filename:
        mainwindow.fileOpen(args.filename)
    ret_val = app.exec_()
    sys.exit(ret_val)

原因:这正是我们想要做的事情:

  • 我们希望处理与主窗口绘图相关的事件。
  • 然后执行我们的自定义代码。
  • 然后继续正常的事件循环。

讨论替代建议:

为什么我不打电话给qApp.processEvents() 中的fileOpen():这对所有fileOpen()来电都有效。在长时间运行的文件打开调用期间处理其他事件可能会导致意外行为,如果应用程序的设计没有考虑到这一点,例如:你可以在第一个fileOpen()运行时发出第二个fileOpen()

为什么我不使用计时器来执行SecondActivity :我想在GUI完全加载之后但在任何用户输入之前执行代码。计时器只是批准正确的执行顺序。此外,正确的延迟可能会因CPU,系统使用情况和其他因素而异,因此这种解决方案不是很强大。