As far as I can see, if an exception occurs in a slot under PyQt, the exception is printed to screen, but not bubbled. This creates a problem in my testing strategy, because if an exception occurs in a slot, I will not see the test fail.
Here is an example:
import sys from PyQt4 import QtGui, QtCore class Test(QtGui.QPushButton): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.setText("hello") self.connect(self, QtCore.SIGNAL("clicked()"), self.buttonClicked) def buttonClicked(self): print "clicked" raise Exception("wow") app=QtGui.QApplication(sys.argv) t=Test() t.show() try: app.exec_() except: print "exiting"
Note how the exception never quits the program.
Is there a way to work around this problem?
Is PyQt thread safe? Remarks# While some parts of the Qt framework are thread safe, much of it is not. The Qt C++ documentation provides a good overview of which classes are reentrant (can be used to instantiate objects in multiple threads).
PyQt5 has a unique signal and slot mechanism to deal with events. Signals and slots are used for communication between objects. A signal is emitted when a particular event occurs. A slot can be any Python callable.
Signals are notifications emitted by widgets when something happens. That something can be any number of things, from pressing a button, to the text of an input box changing, to the text of the window changing. Many signals are initiated by user action, but this is not a rule.
Can create a decorator that wraps PyQt' new signal/slot decorators and provides exception handling for all slots. Can also override QApplication::notify to catch uncaught C++ exceptions.
import sys import traceback import types from functools import wraps from PyQt4 import QtGui, QtCore def MyPyQtSlot(*args): if len(args) == 0 or isinstance(args[0], types.FunctionType): args = [] @QtCore.pyqtSlot(*args) def slotdecorator(func): @wraps(func) def wrapper(*args, **kwargs): try: func(*args) except: print "Uncaught Exception in slot" traceback.print_exc() return wrapper return slotdecorator class Test(QtGui.QPushButton): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.setText("hello") self.clicked.connect(self.buttonClicked) @MyPyQtSlot("bool") def buttonClicked(self, checked): print "clicked" raise Exception("wow") class MyApp(QtGui.QApplication): def notify(self, obj, event): isex = False try: return QtGui.QApplication.notify(self, obj, event) except Exception: isex = True print "Unexpected Error" print traceback.format_exception(*sys.exc_info()) return False finally: if isex: self.quit() app = MyApp(sys.argv) t=Test() t.show() try: app.exec_() except: print "exiting"
You could exit the application with a non-zero return code to indicate that an exception has occurred.
You can catch all exception by installing a global exception hook. I added an example below, but you probably will want to adjust it to your needs.
import sys from PyQt4 import QtGui, QtCore class Test(QtGui.QPushButton): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.setText("hello") self.connect(self, QtCore.SIGNAL("clicked()"), self.buttonClicked) def buttonClicked(self): print "clicked" raise Exception("wow") sys._excepthook = sys.excepthook def exception_hook(exctype, value, traceback): sys._excepthook(exctype, value, traceback) sys.exit(1) sys.excepthook = exception_hook app=QtGui.QApplication(sys.argv) t=Test() t.show() try: app.exec_() except: print "exiting"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With