Make crash dialog available before Application starts

CURA-4895
This commit is contained in:
Lipu Fei 2018-01-30 13:40:47 +01:00 committed by Diego Prado Gesto
parent 8e86c49c81
commit 1d946085d3
3 changed files with 48 additions and 13 deletions

View File

@ -1,7 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import sys
import platform import platform
import traceback import traceback
import faulthandler import faulthandler
@ -14,7 +13,7 @@ import ssl
import urllib.request import urllib.request
import urllib.error import urllib.error
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox
from UM.Application import Application from UM.Application import Application
@ -49,10 +48,11 @@ fatal_exception_types = [
class CrashHandler: class CrashHandler:
crash_url = "https://stats.ultimaker.com/api/cura" crash_url = "https://stats.ultimaker.com/api/cura"
def __init__(self, exception_type, value, tb): def __init__(self, exception_type, value, tb, has_started = True):
self.exception_type = exception_type self.exception_type = exception_type
self.value = value self.value = value
self.traceback = tb self.traceback = tb
self.has_started = has_started
self.dialog = None # Don't create a QDialog before there is a QApplication self.dialog = None # Don't create a QDialog before there is a QApplication
# While we create the GUI, the information will be stored for sending afterwards # While we create the GUI, the information will be stored for sending afterwards
@ -67,10 +67,6 @@ class CrashHandler:
if not CuraDebugMode and exception_type not in fatal_exception_types: if not CuraDebugMode and exception_type not in fatal_exception_types:
return return
application = QCoreApplication.instance()
if not application:
sys.exit(1)
self.dialog = QDialog() self.dialog = QDialog()
self._createDialog() self._createDialog()
@ -79,6 +75,7 @@ class CrashHandler:
self.dialog.setMinimumWidth(640) self.dialog.setMinimumWidth(640)
self.dialog.setMinimumHeight(640) self.dialog.setMinimumHeight(640)
self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report")) self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
self.dialog.finished.connect(self._close)
layout = QVBoxLayout(self.dialog) layout = QVBoxLayout(self.dialog)
@ -89,6 +86,9 @@ class CrashHandler:
layout.addWidget(self._userDescriptionWidget()) layout.addWidget(self._userDescriptionWidget())
layout.addWidget(self._buttonsWidget()) layout.addWidget(self._buttonsWidget())
def _close(self):
os._exit(1)
def _messageWidget(self): def _messageWidget(self):
label = QLabel() label = QLabel()
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal error has occurred. Please send us this Crash Report to fix the problem</p></b> label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal error has occurred. Please send us this Crash Report to fix the problem</p></b>

View File

@ -1,5 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket from PyQt5.QtNetwork import QLocalSocket
@ -113,6 +112,8 @@ class CuraApplication(QtApplication):
# changes of the settings. # changes of the settings.
SettingVersion = 4 SettingVersion = 4
Created = False
class ResourceTypes: class ResourceTypes:
QmlFiles = Resources.UserType + 1 QmlFiles = Resources.UserType + 1
Firmware = Resources.UserType + 2 Firmware = Resources.UserType + 2
@ -256,7 +257,7 @@ class CuraApplication(QtApplication):
self._center_after_select = False self._center_after_select = False
self._camera_animation = None self._camera_animation = None
self._cura_actions = None self._cura_actions = None
self._started = False self.started = False
self._message_box_callback = None self._message_box_callback = None
self._message_box_callback_arguments = [] self._message_box_callback_arguments = []
@ -409,6 +410,8 @@ class CuraApplication(QtApplication):
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
CuraApplication.Created = True
def _onEngineCreated(self): def _onEngineCreated(self):
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
@ -503,7 +506,7 @@ class CuraApplication(QtApplication):
# #
# Note that the AutoSave plugin also calls this method. # Note that the AutoSave plugin also calls this method.
def saveSettings(self): def saveSettings(self):
if not self._started: # Do not do saving during application start if not self.started: # Do not do saving during application start
return return
ContainerRegistry.getInstance().saveDirtyContainers() ContainerRegistry.getInstance().saveDirtyContainers()
@ -732,7 +735,7 @@ class CuraApplication(QtApplication):
for file_name in self._open_file_queue: #Open all the files that were queued up while plug-ins were loading. for file_name in self._open_file_queue: #Open all the files that were queued up while plug-ins were loading.
self._openFile(file_name) self._openFile(file_name)
self._started = True self.started = True
self.exec_() self.exec_()

View File

@ -71,8 +71,40 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
def exceptHook(hook_type, value, traceback): def exceptHook(hook_type, value, traceback):
from cura.CrashHandler import CrashHandler from cura.CrashHandler import CrashHandler
_crash_handler = CrashHandler(hook_type, value, traceback) from cura.CuraApplication import CuraApplication
_crash_handler.show() has_started = False
if CuraApplication.Created:
has_started = CuraApplication.getInstance().started
#
# When the exception hook is triggered, the QApplication may not have been initialized yet. In this case, we don't
# have an QApplication to handle the event loop, which is required by the Crash Dialog.
# The flag "CuraApplication.Created" is set to True when CuraApplication finishes its constructor call.
#
# Before the "started" flag is set to True, the Qt event loop has not started yet. The event loop is a blocking
# call to the QApplication.exec_(). In this case, we need to:
# 1. Remove all scheduled events so no more unnecessary events will be processed, such as loading the main dialog,
# loading the machine, etc.
# 2. Start the Qt event loop with exec_() and show the Crash Dialog.
#
# If the application has finished its initialization and was running fine, and then something causes a crash,
# we run the old routine to show the Crash Dialog.
#
from PyQt5.Qt import QApplication
if CuraApplication.Created:
_crash_handler = CrashHandler(hook_type, value, traceback, has_started)
if not has_started:
CuraApplication.getInstance().removePostedEvents(None)
_crash_handler.show()
sys.exit(CuraApplication.getInstance().exec_())
else:
_crash_handler.show()
sys.exit(1)
else:
application = QApplication(sys.argv)
_crash_handler = CrashHandler(hook_type, value, traceback, has_started)
_crash_handler.dialog.show()
sys.exit(application.exec_())
if not known_args["debug"]: if not known_args["debug"]:
sys.excepthook = exceptHook sys.excepthook = exceptHook