From 35ed4a1c8d4fe4b87e9ca19b39c9002752197031 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Mon, 17 Aug 2020 20:32:52 +0200
Subject: [PATCH] Created a new error handler and dialog class
---
nw/error.py | 153 +++++++++++++++++++++++++++++++++++++---------------
1 file changed, 109 insertions(+), 44 deletions(-)
diff --git a/nw/error.py b/nw/error.py
index ec49dd4b..869ffdaf 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)
+
+ self.mainBox = QGridLayout()
+
+ 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)
+
+ 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.setIcon(QMessageBox.Critical)
+ 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,22 @@ 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.critical("Emergency shutdown successful")
+ except Exception as e:
+ logger.error("Could not close the project before exiting.")
+ logger.error(str(e))
+
+ qApp.exit(1)
except Exception as e:
- logger.error(str(e))
+ logger.critical(str(e))
return