Search code examples
pythonexceptionpyqt5

Can you catch a PyQt5 "fail fast exception" in Python?


Usually, if you call a non-existing method on a PyQt5 object, like calling .show() on a boolean variable as in the click handler below, which is an example that will always trigger an exception:

import sys, os
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton

class MainWindow(QMainWindow):
  def __init__(self):
    super().__init__()
    print("Hello from MainWindow")
    self.setWindowTitle("My App")
    button = QPushButton("Press me!")
    button.clicked.connect(self.button_clicked)
    self.setCentralWidget(button)
  def button_clicked(self, s):
    print("click", s)
    s.show()

def main(argv=None):
  app = QApplication(sys.argv)
  window = MainWindow()
  window.show()
  try:
    app.exec()
  except:
    print("Exception")

if __name__ == '__main__':
    main()

So when I click the button in this example, I get:

gui with exception

---------------------------
My App: python3.exe - Fail Fast Exception
---------------------------
A fail fast exception occurred. Exception handlers will not be invoked and the process will be terminated immediately. 
---------------------------
OK   
---------------------------

Ok - but if I wrap the app.exec() in try ... catch block, - obviously, - it does not catch this exception (probably because its from a C++ PyQt5 object)?

So, my question is: can I somehow catch/handle this kind of an exception in python (but hopefully somewhere "down the chain", as in trying to catch any such application exception with a single statement, as I've tried to do with try ... catch around app.exec())?


Solution

  • Try it:

    import sys
    from PyQt5.QtWidgets import QApplication, QMainWindow, \
        QPushButton, QMessageBox
        
    
    class MainWindow(QMainWindow):
      def __init__(self):
        super().__init__()
        print("\nHello from MainWindow")
        self.setWindowTitle("My App")
        
        button = QPushButton("Press me!")
        button.clicked.connect(self.button_clicked)
        self.setCentralWidget(button)
        
      def button_clicked(self, checked):
        print(f"\nbutton checked: {checked}\n")
        checked.show()
    
    # +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    import traceback
    def except_hook(exc_type, exc_value, exc_tb):
        tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
        msg = QMessageBox.warning(
            None, 
            'Attention! ERROR!', 
            f'{tb}' 
        )        
    # +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
      
        sys.excepthook = except_hook                              # +++ <----
          
        window = MainWindow()
        window.show()
        exit(app.exec())
    

    enter image description here