Samuel
Samuel

Reputation: 479

Exceptions in PyQt event loop and ipython

I have a PyQt program that is displaying some widgets and buttons.

I want the program to run either as a standalone python instance, or inside an ipython environment. In this case, I use the following magic command in Jupyter console (formerly I had to use --gui=qt when launching the ipython qtconsole)

%pylab qt

In order to have a program that works both ways, my main module has the following lines:

APP = QtGui.Qapplication.instance() # retrieves the ipython qt application if any
if APP is None:
    APP = QtGui.QApplication(["foo"]) # create one if standalone execution

if __name__=='__main__':
    APP.exec_() # Launch the event loop here in standalone mode 

Here is my problem: exceptions generated by the event loop are very hard to detect by the user because they pop-out in the background console. I would like to catch any exception occuring in the event loop, and display a warning (for intance in the QMainWindow status bar to make the user aware that an exception occured).

I have tried several strategies, but there seems to be a conspiracy between PyQt's and Ipython's internal machineries to make this impossible:

It is a problem that keeps bothering me since a long time. Does anyone have a solution?

Upvotes: 4

Views: 1597

Answers (1)

Samuel
Samuel

Reputation: 479

Actually, the dev's answer pointed me in the right direction: the problem is that each time an ipython cell is executed, a new sys.excepthook is monkeypatched, once the execution is done, the sys.excepthook is brought back to the previous one (see ipkernel/kernelapp.py).

Because of this, changing sys.excepthook in a normal ipython cell instruction will not change the excepthook that is executed during the qt event loop.

A simple solution is to monkeypatch sys.excepthook inside a qt event:

from PyQt4 import QtCore, QtGui
import sys
from traceback import format_exception

def new_except_hook(etype, evalue, tb):
    QtGui.QMessageBox.information(None, 
                                  str('error'),
                                  ''.join(format_exception(etype, evalue, tb)))

def patch_excepthook():
    sys.excepthook = new_except_hook
TIMER = QtCore.QTimer()
TIMER.setSingleShot(True)
TIMER.timeout.connect(patch_excepthook)
TIMER.start()

A nice thing about this method is that it works for standalone and ipython execution alike.

I guess one could also imagine to monkey patch a different version of new_except_hook depending on what widget is triggering the exception by calling patch_excepthook inside the event_handler of each widget.

Upvotes: 5

Related Questions