Mikołaj Kuranowski
Mikołaj Kuranowski

Reputation: 162

Catching exceptions raised in QApplication

I'm trying to write a PyQt5 application that works in the system tray. The code sometimes raises exceptions, and I need to be able to catch them.

I would expect that when an exception occurs, the application's main event loop would be exited, so catching it should work as follows:

try:
    application.exec()
except:
    do_stuff()

However, when I run the following code, and press the "Raise" button, error caught! is never output to the console, only the traceback:

from PyQt5 import QtWidgets, QtGui, QtCore

class ErrorApp():
    def __init__(self):
        # Init QApplication, QWidet and QMenu
        self.app = QtWidgets.QApplication([])
        self.widget = QtWidgets.QWidget()
        self.menu = QtWidgets.QMenu("menu", self.widget)

        # Add items to menu
        self.menu_action_raise = self.menu.addAction("Raise")
        self.menu_action_raise.triggered.connect(self.raise_error)

        self.menu_action_exit = self.menu.addAction("Exit")
        self.menu_action_exit.triggered.connect(self.app.exit)

        # Create the tray app
        self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon("logo.png"), self.widget)
        self.tray.setContextMenu(self.menu)

        # Show app
        self.tray.show()

    def raise_error(self):
        assert False

e = ErrorApp()

try:
    e.app.exec()

except:
    print("error caught!")

There are two similar questions, but their answers don't do what I need to do:

In question Grab any exception in PyQt, the OP wants to monitor the exceptions, not to exit the event loop.

In question Preventing PyQt to silence exceptions occurring in slots, the accepted answer, involving a decorator, simply doesn't work; adding sys.exit(1) to sys.excepthook just closes the whole program, without printing error caught!

Upvotes: 16

Views: 11575

Answers (1)

eyllanesc
eyllanesc

Reputation: 243897

You must use the exception and, if you want the event loop to end then you must call the quit()(or exit()) method.

import sys
import traceback
from PyQt5 import QtWidgets, QtGui, QtCore


class ErrorApp:
    # ...

    def raise_error(self):
        assert False


def excepthook(exc_type, exc_value, exc_tb):
    tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
    print("error caught!:")
    print("error message:\n", tb)
    QtWidgets.QApplication.quit()
    # or QtWidgets.QApplication.exit(0)


sys.excepthook = excepthook
e = ErrorApp()
ret = e.app.exec_()
print("event loop exited")
sys.exit(ret)

Output:

error caught!:
error message:
 Traceback (most recent call last):
  File "main.py", line 28, in raise_error
    assert False
AssertionError

event loop exited

Upvotes: 30

Related Questions