diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 7008ba64d2..dbf931b37e 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -5,14 +5,15 @@ import webbrowser import faulthandler import tempfile import os -import urllib -from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QCoreApplication -from PyQt5.QtGui import QPixmap -from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QVBoxLayout, QLabel, QTextEdit +from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication +from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox +from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply from UM.Logger import Logger +from UM.View.GL.OpenGL import OpenGL from UM.i18n import i18nCatalog + catalog = i18nCatalog("cura") MYPY = False @@ -23,6 +24,7 @@ else: from cura.CuraVersion import CuraDebugMode except ImportError: CuraDebugMode = False # [CodeStyle: Reflecting imported value] +CuraDebugMode = True ## TODO Remove when done. Just for debug purposes # List of exceptions that should be considered "fatal" and abort the program. # These are primarily some exception types that we simply cannot really recover from @@ -35,83 +37,145 @@ fatal_exception_types = [ SystemError, ] -def show(exception_type, value, tb): - Logger.log("c", "An uncaught exception has occurred!") - for line in traceback.format_exception(exception_type, value, tb): - for part in line.rstrip("\n").split("\n"): - Logger.log("c", part) +class CrashHandler: - if not CuraDebugMode and exception_type not in fatal_exception_types: - return + def __init__(self, exception_type, value, tb): - application = QCoreApplication.instance() - if not application: - sys.exit(1) + self.exception_type = exception_type + self.value = value + self.traceback = tb - dialog = QDialog() - dialog.setMinimumWidth(640) - dialog.setMinimumHeight(640) - dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report")) + Logger.log("c", "An uncaught exception has occurred!") + for line in traceback.format_exception(exception_type, value, tb): + for part in line.rstrip("\n").split("\n"): + Logger.log("c", part) - layout = QVBoxLayout(dialog) + if not CuraDebugMode and exception_type not in fatal_exception_types: + return - #label = QLabel(dialog) - #pixmap = QPixmap() - #try: - # data = urllib.request.urlopen("http://www.randomkittengenerator.com/cats/rotator.php").read() - # pixmap.loadFromData(data) - #except: - # try: - # from UM.Resources import Resources - # path = Resources.getPath(Resources.Images, "kitten.jpg") - # pixmap.load(path) - # except: - # pass - #pixmap = pixmap.scaled(150, 150) - #label.setPixmap(pixmap) - #label.setAlignment(Qt.AlignCenter) - #layout.addWidget(label) + application = QCoreApplication.instance() + if not application: + sys.exit(1) - label = QLabel(dialog) - layout.addWidget(label) + self._createDialog() - #label.setScaledContents(True) - label.setText(catalog.i18nc("@label", """

A fatal exception has occurred that we could not recover from!

-

Please use the information below to post a bug report at http://github.com/Ultimaker/Cura/issues

- """)) + ## Creates a modal dialog. + def _createDialog(self): - textarea = QTextEdit(dialog) - layout.addWidget(textarea) + self.dialog = QDialog() + self.dialog.setMinimumWidth(640) + self.dialog.setMinimumHeight(640) + self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report")) - try: - from UM.Application import Application - version = Application.getInstance().getVersion() - except: - version = "Unknown" + layout = QVBoxLayout(self.dialog) - trace = "".join(traceback.format_exception(exception_type, value, tb)) + layout.addWidget(self._messageWidget()) + layout.addWidget(self._informationWidget()) + layout.addWidget(self._exceptionInfoWidget()) + layout.addWidget(self._logInfoWidget()) + layout.addWidget(self._userDescriptionWidget()) + layout.addWidget(self._buttonsWidget()) - crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}" - crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace) + def _messageWidget(self): + label = QLabel() + label.setText(catalog.i18nc("@label", """

A fatal exception has occurred that we could not recover from!

+

Please use the button below to post a bug report automatically to our servers

+ """)) - tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True) - os.close(tmp_file_fd) - with open(tmp_file_path, "w") as f: - faulthandler.dump_traceback(f, all_threads=True) - with open(tmp_file_path, "r") as f: - data = f.read() + return label - msg = "-------------------------\n" - msg += data - crash_info += "\n\n" + msg + def _informationWidget(self): + group = QGroupBox() + group.setTitle("System information") + layout = QVBoxLayout() + label = QLabel() - textarea.setText(crash_info) + try: + from UM.Application import Application + version = Application.getInstance().getVersion() + except: + version = "Unknown" - buttons = QDialogButtonBox(QDialogButtonBox.Close, dialog) - layout.addWidget(buttons) - buttons.addButton(catalog.i18nc("@action:button", "Open Web Page"), QDialogButtonBox.HelpRole) - buttons.rejected.connect(dialog.close) - buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues")) + crash_info = "Version: {0}
Platform: {1}
Qt: {2}
PyQt: {3}
OpenGL: {4}" + crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, self._getOpenGLInfo()) + label.setText(crash_info) - dialog.exec_() - sys.exit(1) + layout.addWidget(label) + group.setLayout(layout) + + return group + + def _exceptionInfoWidget(self): + group = QGroupBox() + group.setTitle("Exception traceback") + layout = QVBoxLayout() + + text_area = QTextEdit() + trace = "".join(traceback.format_exception(self.exception_type, self.value, self.traceback)) + text_area.setText(trace) + + layout.addWidget(text_area) + group.setLayout(layout) + + return group + + def _logInfoWidget(self): + group = QGroupBox() + group.setTitle("Logs") + layout = QVBoxLayout() + + text_area = QTextEdit() + tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True) + os.close(tmp_file_fd) + with open(tmp_file_path, "w") as f: + faulthandler.dump_traceback(f, all_threads=True) + with open(tmp_file_path, "r") as f: + data = f.read() + + text_area.setText(data) + + layout.addWidget(text_area) + group.setLayout(layout) + + return group + + + def _userDescriptionWidget(self): + group = QGroupBox() + group.setTitle("User description") + layout = QVBoxLayout() + + text_area = QTextEdit() + + layout.addWidget(text_area) + group.setLayout(layout) + + return group + + def _buttonsWidget(self): + buttons = QDialogButtonBox() + buttons.addButton(QDialogButtonBox.Close) + buttons.addButton(catalog.i18nc("@action:button", "Send to developers"), QDialogButtonBox.AcceptRole) + buttons.rejected.connect(self.dialog.close) + buttons.accepted.connect(self._sendCrashReport) + + return buttons + + def _getOpenGLInfo(self): + info = "" + info = info.format(OpenGL.getInstance().getGPUVersion(), OpenGL.getInstance().getGPUVendorName(), OpenGL.getInstance().getGPUType()) + return info + + def _sendCrashReport(self): + print("Hello") + # _manager = QNetworkAccessManager() + # api_url = QUrl("url") + # put_request = QNetworkRequest(api_url) + # put_request.setHeader(QNetworkRequest.ContentTypeHeader, "text/plain") + # _manager.put(put_request, crash_info.encode()) + # + # sys.exit(1) + + def show(self): + self.dialog.exec_() + sys.exit(1) \ No newline at end of file diff --git a/cura_app.py b/cura_app.py index 6869fd2111..d725bc1200 100755 --- a/cura_app.py +++ b/cura_app.py @@ -41,8 +41,9 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0. def exceptHook(hook_type, value, traceback): - import cura.CrashHandler - cura.CrashHandler.show(hook_type, value, traceback) + from cura.CrashHandler import CrashHandler + _crash_handler = CrashHandler(hook_type, value, traceback) + _crash_handler.show() sys.excepthook = exceptHook