8

I have been developing a scientific application using PyQt4 for a couple of weeks, and decided to switch over to PyQt5. Aside from a few things to iron out one thing is puzzling me, and I'm not sure if its intended behavior or not.

When Using PyQt4: if I had a python error (AttributeError, FileNotFoundError or whatever) the error message would print out to the python console, but I could continue using the PyQt4 gui application

When Using PyQt5, when I have a python error, the entire app closes on me. Is this a setting, or is this intended behavior? This is potentially disastrous as before if there was a bug, I could save the data I had acquired, but now the application will just close without warning.

Here is an example that demonstrates the behavior. This script opens a widget with a button that activates a file dialog. If a valid file is selected, the code will print the filepointer object to the command line. If no file is selected because the user hits cancel, then that case is not handled and python tries to open a file with path ''. In this both PyQt4 and PyQt5 versions throw the same python error:

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

However, the PyQt4 version will leave the widget open and the user can continue, whereas the PyQt5 version closes, with exit code of 1.

Here is the example code, executed by: "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_())

Can I use PyQt5, but have it not crash the way that the PyQt4 version does?

Here is my current system information system information:
Windows 7 64-bit
Anaconda, Python 3.5
PyQt4 --> from conda sources
PyQt5 --> using:

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

both PyQt4 and PyQt5 are installed side by side

Vince W.
  • 3,561
  • 3
  • 31
  • 59
  • You have uncaught exceptions: they should always abort the program. If you want to catch them, it's on you to do so. The behavior from PyQt4 was wrong. When you run any Python program and there's an uncaught exception, it will abort. PyQt isn't meant to change something so fundamental. – Kuba hasn't forgotten Monica Jun 24 '16 at 19:50
  • I think 'wrong' probably depends on your perspective. As someone who codes python to aid in Research, rather than deploying enterprise applications, I think that the PyQt4 behavior is rather more amenable to my goals, namely doing things well and quickly, but not at risk to losing data, and not with overly strict adherence to coding conventions and paradigms when such adherence takes more time than I have. – Vince W. Jun 24 '16 at 20:30
  • Take `PyQt4` away and Python by default will abort on exceptions. So that's what you should expect. If you want to change that, it's on you: catching exceptions is easy. You must be explicit about what you want here; PyQt4 changing it was wrong precisely because it made you think (wrongly!) that it's its job to make such a change. It's not. It's your job. The only reason you think it's not your job is because of a silly mistake that PyQt4's design had. The existence of this very question is the fallout from that mistake, and highlights how wrong it was. /shiver – Kuba hasn't forgotten Monica Jun 24 '16 at 20:34

1 Answers1

9

The old behavior can be forced by calling this code, which I found after more searching. I'm not sure I understand why this is bad behavior that needed to be deprecated, but this does work.

I submit that this should not be the default behavior, and that properly catching exceptions is the correct way to program, but given the specific purpose of my programming, and my time constraints, I find it useful to have access to as an optional mode, as I can still see the python exception traces printed to the console, and won't lose any unsaved data because of an uncaught exception.

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
Vince W.
  • 3,561
  • 3
  • 31
  • 59
  • For the reasons @kuba-ober mentioned above - silently hiding errors is a bad idea, and it's what Python (and pretty much any other programming language with exceptions) does by default. (disclaimer: I originally proposed the change) – The Compiler Jun 25 '16 at 08:02
  • @TheCompiler while you're right, there may be occasions in the initial development phase where you want to leave some exceptions unhandled while implementing other functionality (and come back to them later) without outright crashing the program, but just logging them (to stderr, to a file, to a dialog, ...). While monkey patching `sys.excepthook` seems to "work", I'd very much prefer a way to intercept unhandled exceptions by reimplementing/overriding a method in an instance of `QApplication`. ([reimplementing `notify`](http://stackoverflow.com/a/19015654/794539) doesn't prevent the crash) – blubberdiblub Jan 03 '17 at 12:12
  • Does the GUI still crash after the exception is logged? Or does it continue running? I'd be fine with the GUI crashing on an unhandled exception, I just want a traceback like any other python script. I don't like the thought of having try blocks for every method everywhere just to log and re-raise exceptions. – flutefreak7 Mar 16 '18 at 05:29
  • If the exception happens in the initial launch, the GUI never initializes fully and the system exits. If the exception happens in the event loop after you start interacting with it the GUI does not crash. You could of course call exit() and make it crash in the exception handler. – Vince W. Mar 16 '18 at 11:18