0

This seems to be a very recurring question but I cannot get the solutions to work consistently.

I have tried to implement this and it does work but not consistently.

Here is an example code that reproduces the error ( click on the break button to cause a crash )
This code breaks due to not saving the thread reference ( it does not persist outside of the scope of the break_thread() method ). Therefore it does not reach the exception raise RuntimeError("broken") as Qt crashes before

import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QPushButton
from PyQt5.QtCore import QThread


def except_hook(cls, exception, error_traceback):
    sys.__excepthook__(cls, exception, error_traceback)


class MadeToFail(QThread):
    def __init__(self):
        super(MadeToFail, self).__init__()

    def run(self):
        time.sleep(1)
        print("finished sleep, about to crash")
        raise RuntimeError("broken")


class Window(QtWidgets.QMainWindow):
    def break_thread(self):
        print("I have been clicked")
        try:
            thread = MadeToFail()
            thread.start()
        except Exception as e:
            print("caught in class")
            print(e)

    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle('PyQt5 App')
        self.setGeometry(100, 100, 280, 80)
        self.move(60, 15)
        break_button = QPushButton(parent=self)
        break_button.setText("break")
        break_button.clicked.connect(self.break_thread)
        self.show()

try:
    sys.excepthook = except_hook
    app = QApplication(sys.argv)
    window = Window()
    app.exec_()
except Exception as e:
    print("caught in program")
print("this should print if the exception is caught")

The issue seems to be that Qt crashes internally but in a way that causes the application to end abruptly also circumventing my try / except on Exception. This prevents me form running any kind of code after the crash.

Qt also displays it's own error message ( QThread: Destroyed while thread is still running in this occurrence ) but nothing more and the sys.excepthook = except_hook does not seem to be able to give me the full error.

In this case I only have one thread and finding the cause is easy but it with a bigger code it won't be as simple to diagnose.

Here are my questions :

  • How can I get a error messages the way Python usually gives them in a reliable manner ?
  • How can I catch the Qt errors ? Or at the very least how can I be able to execute some code after Qt has crashed ?

1 Answers1

2

The execution of a thread is not synchronous but the start () method indicates that when the eventloop will start the execution of the thread, that is why Qt does not use exceptions (in addition to saving memory) since they would only make sense in synchronous tasks . Qt instead to indicate that something has failed uses 2 methods: If the function is synchronous then within what it returns is a status value, and if it is asynchronous then it uses a signal.

import sys
import time

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from PyQt5.QtCore import pyqtSignal, QThread


def some_function():
    time.sleep(1)
    print("finished sleep, about to crash")
    raise RuntimeError("broken")


class MadeToFail(QThread):
    error_ocurred = pyqtSignal(Exception, name="errorOcurred")

    def run(self):
        try:
            some_function()
        except Exception as e:
            self.error_ocurred.emit(e)


class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("PyQt5 App")
        self.setGeometry(100, 100, 280, 80)
        self.move(60, 15)
        break_button = QPushButton(parent=self)
        break_button.setText("break")
        break_button.clicked.connect(self.break_thread)

    def break_thread(self):
        test_thread = MadeToFail(self)
        test_thread.error_ocurred.connect(self.handle_error_ocurred)
        test_thread.start()

    def handle_error_ocurred(self, exception):
        print(exception)


def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()


main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241