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 12, 2018 in Python by bug_seeker
• 15,310 points
430 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 12, 2018 by Priyaj
• 56,160 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,500 points
106 views
+2 votes
11 answers

How to print array/ list without brackets in python?

name=["a","b","c"] print (', '.join(name)) It will simply take all ...READ MORE

answered Apr 18, 2018 in Python by aayushi
• 750 points
16,555 views
0 votes
1 answer

Raw_input method is not working in python3. How to use it?

raw_input is not supported anymore in python3. ...READ MORE

answered May 4, 2018 in Python by aayushi
• 750 points
99 views
+3 votes
2 answers

how to print array integer without [] bracket in python like result = 1,2,3,4,5

Hey @abhijmr.143, you can print array integers ...READ MORE

answered Aug 4, 2018 in Python by Omkar
• 67,140 points

edited Aug 8, 2018 by Omkar 400 views
+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 6 in Python by Neha
• 330 points

edited Jul 8 by Kalgi 134 views
0 votes
1 answer
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
• 56,160 points
41 views