diff --git a/nw/__init__.py b/nw/__init__.py
index 5ac300e4..c6f815e7 100644
--- a/nw/__init__.py
+++ b/nw/__init__.py
@@ -280,45 +280,10 @@ def main(sysArgs=None):
nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
nwApp.setOrganizationDomain(__domain__)
- # We try to catch critical errors while setting up the main GUI
- # by wrapping the main GUI in a try/except structure. This will
- # not catch all exceptions for other parts of the application.
- # For all other unhandled exceptions, we use a custom exception
- # handler that pops a dialog box with the error message.
+ # Connect the exception handler before making the main GUI
sys.excepthook = exceptionHandler
- try:
- nwGUI = GuiMain()
- sys.exit(nwApp.exec_())
-
- except Exception:
-
- from traceback import print_tb
- from nw.error import formatHtmlErrMsg
-
- exType, exValue, exTrace = sys.exc_info()
-
- logger.critical("%s: %s" % (exType.__name__, str(exValue)))
- print_tb(exTrace)
-
- try:
- del nwApp
-
- errApp = QApplication([])
- errMsg = QErrorMessage()
- errMsg.setWindowTitle("Critical Error")
- errMsg.resize(800, 400)
- errMsg.showMessage((
- "
A critical error has been encountered
"
- "%s"
- "Shutting down ...
"
- ) % formatHtmlErrMsg(exType, exValue, exTrace))
- errApp.exec_()
-
- except Exception as e:
- logger.critical("Could not create error message dialog.")
- logger.critical(str(e))
-
- sys.exit(1)
+ nwGUI = GuiMain()
+ sys.exit(nwApp.exec_())
return
diff --git a/nw/error.py b/nw/error.py
index ec49dd4b..7788508f 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -25,56 +25,113 @@
along with this program. If not, see .
"""
-def formatHtmlErrMsg(exType, exValue, exTrace):
- """Generates a HTML version of an exception.
- """
- try:
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import (
+ qApp, QDialog, QGridLayout, QStyle, QPlainTextEdit, QLabel,
+ QDialogButtonBox
+)
+
+class NWErrorMessage(QDialog):
+
+ def __init__(self, parent):
+ QDialog.__init__(self, parent=parent)
+
+ # Widgets
+ self.msgIcon = QLabel()
+ self.msgIcon.setPixmap(
+ qApp.style().standardIcon(QStyle.SP_MessageBoxCritical).pixmap(64, 64)
+ )
+ self.msgHead = QLabel()
+ self.msgHead.setOpenExternalLinks(True)
+ self.msgHead.setWordWrap(True)
+
+ self.msgBody = QPlainTextEdit()
+ self.msgBody.setReadOnly(True)
+
+ self.btnBox = QDialogButtonBox(QDialogButtonBox.Close)
+ self.btnBox.rejected.connect(self._doClose)
+
+ # Assemble
+ self.mainBox = QGridLayout()
+ self.mainBox.addWidget(self.msgIcon, 0, 0, 2, 1, Qt.AlignTop)
+ self.mainBox.addWidget(self.msgHead, 0, 1, 1, 1, Qt.AlignTop)
+ self.mainBox.addWidget(self.msgBody, 1, 1, 1, 1)
+ self.mainBox.addWidget(self.btnBox, 2, 0, 1, 2)
+ self.mainBox.setSpacing(16)
+
+ self.setLayout(self.mainBox)
+
+ self.setMinimumWidth(500)
+ self.setMinimumHeight(300)
+ self.setSizeGripEnabled(True)
+ self.resize(800, 400)
+
+ return
+
+ def setMessage(self, exType, exValue, exTrace):
+ """Generate a message and append session data, error info and
+ error traceback.
+ """
import sys
from traceback import format_tb
from nw import __issuesurl__, __version__
from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QSysInfo
- fmtTrace = ""
- for trEntry in format_tb(exTrace):
- for trLine in trEntry.split("\n"):
- stripLine = trLine.lstrip(" ")
- nIndent = len(trLine) - len(stripLine)
- fmtTrace += " "*nIndent + stripLine + "
"
+ self.msgHead.setText((
+ "An unhandled error has been encountered.
"
+ "Please report this error by submitting an issue report on "
+ "GitHub, providing a description and including the error "
+ "message and traceback shown below.
"
+ "URL: {issueUrl}
"
+ ).format(
+ issueUrl = __issuesurl__,
+ ))
try:
kernelVersion = QSysInfo.kernelVersion()
except Exception:
kernelVersion = "Unknown"
- theMessage = (
- "Please report this error by submitting an issue report on "
- "GitHub, providing a description and this error message. "
- "URL: <{issueUrl}>.
"
- "Environment
Version: {nwVersion}, OS: {osType} ({osKernel}), "
- "Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}
"
- "Error Type
{exType}: {exMessage}
"
- "Traceback
{exTrace}
"
- ).format(
- nwVersion = __version__,
- osType = sys.platform,
- osKernel = kernelVersion,
- pyVersion = sys.version.split()[0],
- pyHexVer = sys.hexversion,
- qtVers = QT_VERSION_STR,
- pyqtVers = PYQT_VERSION_STR,
- issueUrl = __issuesurl__,
- exType = exType.__name__,
- exMessage = str(exValue),
- exTrace = fmtTrace
- )
+ try:
+ self.msgBody.setPlainText((
+ "Environment:\n"
+ "novelWriter Version: {nwVersion}\n"
+ "Host OS: {osType} ({osKernel})\n"
+ "Python: {pyVersion} ({pyHexVer:#x})\n"
+ "Qt: {qtVers}, PyQt: {pyqtVers}\n"
+ "\n"
+ "{exType}:\n{exMessage}\n"
+ "\n"
+ "Traceback:\n{exTrace}\n"
+ ).format(
+ nwVersion = __version__,
+ osType = sys.platform,
+ osKernel = kernelVersion,
+ pyVersion = sys.version.split()[0],
+ pyHexVer = sys.hexversion,
+ qtVers = QT_VERSION_STR,
+ pyqtVers = PYQT_VERSION_STR,
+ exType = exType.__name__,
+ exMessage = str(exValue),
+ exTrace = "\n".join(format_tb(exTrace)),
+ ))
+ except Exception:
+ self.msgBody.setPlainText("Failed to generate error report ...")
- return theMessage
+ return
- except Exception as e:
- return "Could not generate error message.
%s" % str(e)
+ ##
+ # Slots
+ ##
- return "Could not generate error message."
+ def _doClose(self):
+ """Close the dialog.
+ """
+ self.close()
+ return
+
+# END Class NWErrorMessage
def exceptionHandler(exType, exValue, exTrace):
@@ -83,10 +140,10 @@ def exceptionHandler(exType, exValue, exTrace):
import logging
from traceback import print_tb
from nw import CONFIG
- from PyQt5.QtWidgets import qApp, QErrorMessage
+ from PyQt5.QtWidgets import qApp
logger = logging.getLogger(__name__)
- logger.error("%s: %s" % (exType.__name__, str(exValue)))
+ logger.critical("%s: %s" % (exType.__name__, str(exValue)))
print_tb(exTrace)
if not CONFIG.showGUI:
@@ -103,14 +160,23 @@ def exceptionHandler(exType, exValue, exTrace):
logger.warning("Could not find main GUI window so cannot open error dialog")
return
- errMsg = QErrorMessage(nwGUI)
- errMsg.setWindowTitle("Unhandled Error")
- errMsg.resize(800, 400)
- errMsg.showMessage((
- "An unhandled error has been encountered
%s"
- ) % formatHtmlErrMsg(exType, exValue, exTrace))
+ errMsg = NWErrorMessage(nwGUI)
+ errMsg.setMessage(exType, exValue, exTrace)
+ errMsg.exec_()
+
+ try:
+ # Try a controlled shudown
+ nwGUI.closeProject(isYes=True)
+ nwGUI.closeMain()
+ logger.info("Emergency shutdown successful")
+
+ except Exception as e:
+ logger.critical("Could not close the project before exiting")
+ logger.critical(str(e))
+
+ qApp.exit(1)
except Exception as e:
- logger.error(str(e))
+ logger.critical(str(e))
return