【问题标题】:PyQt5 app exits on error where PyQt4 app would notPyQt5 应用程序因错误退出而 PyQt4 应用程序不会
【发布时间】:2016-10-27 11:08:30
【问题描述】:

我已经使用 PyQt4 开发一个科学应用程序已经有几个星期了,并决定切换到 PyQt5。除了要解决的几件事之外,还有一件事让我感到困惑,我不确定它是否是预期的行为。

使用 PyQt4 时: 如果我遇到 python 错误(AttributeError、FileNotFoundError 或其他),错误消息将打印到 python 控制台,但我可以继续使用 PyQt4 gui 应用程序

使用 PyQt5 时,当我遇到 python 错误时,整个应用程序都会关闭我。这是一个设置,还是这是预期的行为?如果出现错误,这可能像以前一样是灾难性的,我可以保存我获得的数据,但现在应用程序将在没有警告的情况下关闭。

这是一个演示该行为的示例。此脚本打开一个带有一个按钮的小部件,该按钮可激活文件对话框。如果选择了有效文件,代码会将文件指针对象打印到命令行。如果因为用户点击取消而没有选择文件,则不会处理这种情况,python 会尝试打开路径为 '' 的文件。在此 PyQt4 和 PyQt5 版本都抛出相同的 python 错误:

FileNotFoundError: [Errno 2] No such file or directory: ''

但是,PyQt4 版本将保持小部件打开,用户可以继续,而 PyQt5 版本关闭,退出代码为 1。

这里是示例代码,由“python script.py”执行

import sys
# from PyQt4 import QtGui as qt
# from PyQt4.QtCore import PYQT_VERSION_STR
from PyQt5 import QtWidgets as qt
from PyQt5.QtCore import PYQT_VERSION_STR

def open_a_file():
    fname = qt.QFileDialog.getOpenFileName()
    if PYQT_VERSION_STR[0] == '4':
        f = open(fname, 'r')
        print(f)
    else:
        f = open(fname[0], 'r')
        print(f)
    f.close()

if __name__ == '__main__':
    app = qt.QApplication(sys.argv)

    w = qt.QWidget()
    w.resize(250, 150)
    w.move(300, 300)
    w.setWindowTitle('PyQt 4 v 5')
    btn = qt.QPushButton("Open a file", w)
    btn.clicked.connect(open_a_file)
    w.show()

    sys.exit(app.exec_())

我可以使用 PyQt5,但它不会像 PyQt4 版本那样崩溃吗?

这里是我目前的系统信息系统信息:
Windows 7 64 位
蟒蛇,Python 3.5
PyQt4 --> 来自 conda 来源
PyQt5 --> 使用:

conda install --channel https://conda.anaconda.org/m-labs qt5
conda install --channel https://conda.anaconda.org/m-labs pyqt5

PyQt4 和 PyQt5 并排安装

【问题讨论】:

  • 您有未捕获的异常:它们应该始终中止程序。如果你想抓住他们,你必须这样做。 PyQt4 的行为是错误的。当您运行任何 Python 程序并且有未捕获的异常时,它将中止。 PyQt 并不是要改变如此根本的东西。
  • 我认为“错误”可能取决于您的观点。作为一个编写 python 来帮助研究而不是部署企业应用程序的人,我认为 PyQt4 的行为更符合我的目标,即把事情做好和快速,但不会有丢失数据的风险,也不会过于严格遵守编码约定和范式时,这种遵守需要的时间比我多。
  • 带走PyQt4,Python 默认会因异常而中止。所以这就是你应该期待的。如果您想改变这一点,就看您自己了:捕获异常很容易。您必须明确说明您想要什么; PyQt4 改变它是错误的,因为它让你认为(错误地!)做出这样的改变是它的工作。它不是。这是你的工作。你认为这不是你的工作的唯一原因是 PyQt4 的设计有一个愚蠢的错误。这个问题的存在就是那个错误的后果,并突出了它的错误程度。 /颤抖

标签: python qt pyqt pyqt4 pyqt5


【解决方案1】:

可以通过调用此代码来强制执行旧行为,这是我经过更多搜索后发现的。 我不确定我是否理解为什么这是需要弃用的不良行为,但这确实有效。

我认为这不应该是默认行为,正确捕获异常是编程的正确方法,但考虑到我编程的特定目的和时间限制,我发现作为可选模式,因为我仍然可以看到打印到控制台的 python 异常跟踪,并且不会因为未捕获的异常而丢失任何未保存的数据。

import sys

def my_excepthook(type, value, tback):
    # log the exception here

    # then call the default handler
    sys.__excepthook__(type, value, tback)

sys.excepthook = my_excepthook

【讨论】:

  • 由于上面提到的@kuba-ober 的原因 - 默默地隐藏错误是一个坏主意,这是 Python(以及几乎任何其他编程语言都有例外)默认情况下所做的。 (免责声明:我最初提出了更改)
  • @TheCompiler 虽然您是对的,但在初始开发阶段可能会有一些情况,您希望在实现其他功能时不处理一些异常(稍后再返回),而不会使程序彻底崩溃,但只是记录它们(到标准错误,到文件,到对话框,...)。虽然猴子修补sys.excepthook 似乎“有效”,但我非常喜欢通过重新实现/覆盖QApplication 实例中的方法来拦截未处理异常的方法。 (reimplementing notify 不能防止崩溃)
  • 异常记录后GUI是否仍然崩溃?还是继续运行?如果 GUI 因未处理的异常而崩溃,我会很好,我只想要像任何其他 python 脚本一样的回溯。我不喜欢为每个方法都设置 try 块来记录和重新引发异常。
  • 如果在初始启动时发生异常,GUI 永远不会完全初始化并且系统退出。如果在您开始与之交互后在事件循环中发生异常,则 GUI 不会崩溃。您当然可以调用 exit() 并使其在异常处理程序中崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多