How to invoke method on GUI thread but without have that method in QMainWindow class Pyqt

0 votes

I have made the simply logging of crashes (unhandled exceptions): At start you simple call CrashEngine.register("sw", "1.1.7") from main thread.

import sys
import time
import os
import traceback
from PyQt5.QtWidgets import *

class CrashEngine:
    @staticmethod
    def register(name, version):
        CrashEngine.name = name
        CrashEngine.version = version
        sys.excepthook = CrashEngine.__logCrash

    @staticmethod
    def __logCrash(exc_type, exc_value, exc_traceback):
        crash = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
        with open("crash.log", "w") as f:
            f.write(time.ctime() + "\n")
            f.write("Software name: " + CrashEngine.name + "\n")
            f.write("Software version: " + CrashEngine.version + "\n")
            f.write("\n")
            f.write(crash)

        CrashEngine.__showDialog()

    @staticmethod
    def __showDialog():
        message = ("Fatal error occurred and application will be terminated.\n\n"
                   "Crash log was created at:\n" +
                    os.getcwd() + "\crash.log.\n\n"
                   "Please send log to ***@***.com")
        msg = QMessageBox(QMessageBox.Critical, "Application Crashed", message)
        msg.exec()
        quit(1)

Everything worked excellent until i have meet multithreaded app where is sys.excepthook raised sometimes from different thread than main thread. As we know Calling GUI from different threads will result in unexpected behavior and crash in most of time.

The only thing i know is create slot in QMainWindow and create Signal in CrashEngine and connect them. But this is what I don't want to because CrashEngine is used in so many scripts, programs, etc and I don't want to add same piece of code (showing MsgBox) in all of them.

UPDATE: I reworked code according to @three_pineapples suggestion but via PyQt framework instead of pure Python.

@staticmethod
def __showDialog():
    path = sys.executable
    arg = os.path.dirname(os.path.abspath(__file__)) + "\\show_crash.py"
    QProcess.startDetached(path, [arg])
    sys.exit(1)

and show_crash.py contains:

import sys
import os
from PyQt5.QtWidgets import *


class ErrorWindow(QMessageBox):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Application Crashed")
        message = ("Fatal error occurred and application was terminated.\n\n"
                   "Crash log was created at:\n" +
                   os.getcwd() + "\crash.log.\n\n"
                   "Please send log to ***@***.com")
        self.setText(message)
        self.setIcon(QMessageBox.Critical)
        self.show()


def main():
    app = QApplication(sys.argv)
    ex = ErrorWindow()
    sys.exit(app.exec_())


main()

Sep 24, 2018 in Python by bug_seeker
• 15,510 points
3,105 views

1 answer to this question.

0 votes

It is possible to handle this by posting using the signal/slot approach as you suggest (but would like to avoid) or by posting a custom event back to the Qt event loop via QApplication.instance().postEvent() both of these methods are fundamentally flawed because they rely on the Qt event loop functioning correctly (which may not be the case if part of your application has entered an error state).

The only reliable way to show an exception in a graphical message box is to have your exception handler launch a new process that creates the message box. It's reasonably easy to write a standalone application that shows sys.argv[1] in the message box, and to launch that using subprocess.Popen from your excepthook handler.

My colleagues and I have done this for a large Qt project, and it works very well. We've also gone to the effort of launching the message box using Tkinter (which ships by default with almost every Python install) rather than Qt, just in case the exception is caused by a missing or broken installation of Qt. It's open-sourced here.

answered Sep 24, 2018 by Priyaj
• 58,020 points

Related Questions In Python

0 votes
1 answer

Question on PyQt: How to connect a signal to a slot to start a background operation in Python

It shouldn't matter whether the connection is ...READ MORE

answered Nov 27, 2018 in Python by Nymeria
• 3,560 points
1,727 views
0 votes
1 answer

How to invoke a python static method inside class via string method name?

Use __import__ function to import the module by giving ...READ MORE

answered Dec 3, 2020 in Python by Gitika
• 65,770 points
1,229 views
+1 vote
12 answers
0 votes
2 answers
+1 vote
2 answers

how can i count the items in a list?

Syntax :            list. count(value) Code: colors = ['red', 'green', ...READ MORE

answered Jul 7, 2019 in Python by Neha
• 330 points

edited Jul 8, 2019 by Kalgi 4,434 views
0 votes
1 answer
0 votes
1 answer

How to invoke method on GUI thread but without have that method in QMainWindow class (Pyqt)

It is possible to handle this by ...READ MORE

answered Sep 12, 2018 in Python by Priyaj
• 58,020 points
2,633 views
0 votes
1 answer

“stub” __objclass__ in a Python class how to implement it?

You want to avoid interfering with this ...READ MORE

answered Sep 27, 2018 in Python by Priyaj
• 58,020 points
2,349 views
webinar REGISTER FOR FREE WEBINAR X
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP