Reputation: 479
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
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