Rewrite the makeAlert and askQuestion functions to use recommended interface

This commit is contained in:
Veronica Berglyd Olsen
2023-08-07 20:19:51 +02:00
parent ec010bf18e
commit 90f9fa4451
9 changed files with 123 additions and 182 deletions
+4 -4
View File
@@ -30,7 +30,7 @@ import logging
from PyQt5.QtWidgets import QApplication, QErrorMessage from PyQt5.QtWidgets import QApplication, QErrorMessage
from novelwriter.error import exceptionHandler, logException from novelwriter.error import exceptionHandler, logException
from novelwriter.config import Config, Global from novelwriter.config import Config, NWApp
## ##
# Version Scheme # Version Scheme
@@ -74,7 +74,7 @@ logger = logging.getLogger(__name__)
# Create the global singleton instances # Create the global singleton instances
CONFIG = Config() CONFIG = Config()
GLOBAL = Global() APP = NWApp()
def main(sysArgs: list | None = None): def main(sysArgs: list | None = None):
@@ -228,7 +228,7 @@ def main(sysArgs: list | None = None):
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
if testMode: if testMode:
nwGUI = GuiMain() nwGUI = GuiMain()
GLOBAL.setGUI(nwGUI) APP.setGUI(nwGUI)
return nwGUI return nwGUI
else: else:
@@ -248,7 +248,7 @@ def main(sysArgs: list | None = None):
# Launch main GUI # Launch main GUI
nwGUI = GuiMain() nwGUI = GuiMain()
GLOBAL.setGUI(nwGUI) APP.setGUI(nwGUI)
nwGUI.postLaunchTasks(cmdOpen) nwGUI.postLaunchTasks(cmdOpen)
sys.exit(nwApp.exec_()) sys.exit(nwApp.exec_())
-78
View File
@@ -1,78 +0,0 @@
"""
novelWriter Alert Functions
=============================
File History:
Created: 2023-08-07 [2.1b2]
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from PyQt5.QtWidgets import QMessageBox
from novelwriter import GLOBAL
from novelwriter.enum import nwAlert
from novelwriter.constants import nwLabels
logger = logging.getLogger(__name__)
def makeAlert(message: list[str] | str, level: nwAlert, exc: Exception | None = None) -> None:
"""Alert both the user and the logger at the same time. The
message can be either a string or a list of strings.
"""
if isinstance(message, list):
message = list(filter(None, message)) # Strip empty strings
popMsg = "<br>".join(message)
logMsg = " ".join(message)
else:
popMsg = str(message)
logMsg = str(message)
kw = {}
if exc is not None:
kw["exc_info"] = exc
popMsg = f"{popMsg}<br>{type(exc).__name__}: {str(exc)}"
# Write to Log
if level == nwAlert.INFO:
logger.info(logMsg, **kw)
elif level == nwAlert.WARN:
logger.warning(logMsg, **kw)
elif level == nwAlert.ERROR:
logger.error(logMsg, **kw)
# Popup
msgBox = QMessageBox()
if level == nwAlert.INFO:
msgBox.information(GLOBAL.gui, nwLabels.ALERT_NAME[level], popMsg)
elif level == nwAlert.WARN:
msgBox.warning(GLOBAL.gui, nwLabels.ALERT_NAME[level], popMsg)
elif level == nwAlert.ERROR:
msgBox.critical(GLOBAL.gui, nwLabels.ALERT_NAME[level], popMsg)
return
def askQuestion(title: str, question: str) -> bool:
"""Ask the user a Yes/No question, and return the answer."""
msgBox = QMessageBox()
msgRes = msgBox.question(GLOBAL.gui, title, question, QMessageBox.Yes | QMessageBox.No)
return msgRes == QMessageBox.Yes
@@ -17,8 +17,8 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
[Map] [Map]
add = typ_plus.svg add = typ_plus.svg
alert_error = typ_info-large-full.svg alert_error = typ_delete-full.svg
alert_info = typ_delete-full.svg alert_info = typ_info-large-full.svg
alert_question = typ_lightbulb-full.svg alert_question = typ_lightbulb-full.svg
alert_warn = typ_warning-full.svg alert_warn = typ_warning-full.svg
backward = typ_chevron-left.svg backward = typ_chevron-left.svg
@@ -17,8 +17,8 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
[Map] [Map]
add = typ_plus.svg add = typ_plus.svg
alert_error = typ_info-large-full.svg alert_error = typ_delete-full.svg
alert_info = typ_delete-full.svg alert_info = typ_info-large-full.svg
alert_question = typ_lightbulb-full.svg alert_question = typ_lightbulb-full.svg
alert_warn = typ_warning-full.svg alert_warn = typ_warning-full.svg
backward = typ_chevron-left.svg backward = typ_chevron-left.svg
+2 -2
View File
@@ -798,7 +798,7 @@ class Config:
# END Class Config # END Class Config
class Global: class NWApp:
"""Singleton: Global Pointers """Singleton: Global Pointers
This class holds pointers to the core singletons: This class holds pointers to the core singletons:
@@ -852,7 +852,7 @@ class Global:
self._project = project self._project = project
return return
# END Class Global # END Class NWApp
class RecentProjects: class RecentProjects:
+1
View File
@@ -167,6 +167,7 @@ class nwLabels:
nwAlert.INFO: QT_TRANSLATE_NOOP("Constant", "Information"), nwAlert.INFO: QT_TRANSLATE_NOOP("Constant", "Information"),
nwAlert.WARN: QT_TRANSLATE_NOOP("Constant", "Warning"), nwAlert.WARN: QT_TRANSLATE_NOOP("Constant", "Warning"),
nwAlert.ERROR: QT_TRANSLATE_NOOP("Constant", "Error"), nwAlert.ERROR: QT_TRANSLATE_NOOP("Constant", "Error"),
nwAlert.ASK: QT_TRANSLATE_NOOP("Constant", "Question"),
} }
ITEM_DESCRIPTION = { ITEM_DESCRIPTION = {
"none": QT_TRANSLATE_NOOP("Constant", "None"), "none": QT_TRANSLATE_NOOP("Constant", "None"),
+1
View File
@@ -125,6 +125,7 @@ class nwAlert(Enum):
INFO = 0 INFO = 0
WARN = 1 WARN = 1
ERROR = 2 ERROR = 2
ASK = 3
# END Enum nwAlert # END Enum nwAlert
+21 -17
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Exception Handling novelWriter Exception Handling
================================ ================================
Error handling function and error dialog
File History: File History:
Created: 2020-08-02 [0.10.2] Created: 2020-08-02 [0.10.2]
@@ -22,18 +21,24 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import random import random
import logging import logging
from typing import TYPE_CHECKING
from PyQt5.QtGui import QFont, QFontDatabase from PyQt5.QtGui import QFont, QFontDatabase
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
qApp, QDialog, QGridLayout, QStyle, QPlainTextEdit, QLabel, QWidget, qApp, QDialog, QGridLayout, QStyle, QPlainTextEdit, QLabel,
QDialogButtonBox QDialogButtonBox
) )
if TYPE_CHECKING: # pragma: no cover
from types import TracebackType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -41,15 +46,15 @@ logger = logging.getLogger(__name__)
# Utility Functions # Utility Functions
# =============================================================================================== # # =============================================================================================== #
def logException(): def logException() -> None:
"""Log the content of an exception message. """Log the content of an exception message."""
"""
exType, exValue, _ = sys.exc_info() exType, exValue, _ = sys.exc_info()
if exType is not None: if exType is not None:
logger.error("%s: %s", exType.__name__, str(exValue)) logger.error("%s: %s", exType.__name__, str(exValue))
return
def formatException(exc): def formatException(exc) -> str:
"""Format an exception as a string the same way the default """Format an exception as a string the same way the default
exception handler does. exception handler does.
""" """
@@ -62,7 +67,7 @@ def formatException(exc):
class NWErrorMessage(QDialog): class NWErrorMessage(QDialog):
def __init__(self, parent): def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self.setObjectName("NWErrorMessage") self.setObjectName("NWErrorMessage")
@@ -113,7 +118,7 @@ class NWErrorMessage(QDialog):
return return
def setMessage(self, exType, exValue, exTrace): def setMessage(self, exType: type, exValue: BaseException, exTrace: TracebackType) -> None:
"""Generate a message and append session data, error info and """Generate a message and append session data, error info and
error traceback. error traceback.
""" """
@@ -142,7 +147,7 @@ class NWErrorMessage(QDialog):
enchantVersion = "Unknown" enchantVersion = "Unknown"
try: try:
exTrace = "\n".join(format_tb(exTrace)) txtTrace = "\n".join(format_tb(exTrace))
self.msgBody.setPlainText(( self.msgBody.setPlainText((
"Environment:\n" "Environment:\n"
f"novelWriter Version: {__version__}\n" f"novelWriter Version: {__version__}\n"
@@ -151,7 +156,7 @@ class NWErrorMessage(QDialog):
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n" f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
f"enchant: {enchantVersion}\n\n" f"enchant: {enchantVersion}\n\n"
f"{exType.__name__}:\n{str(exValue)}\n\n" f"{exType.__name__}:\n{str(exValue)}\n\n"
f"Traceback:\n{exTrace}\n" f"Traceback:\n{txtTrace}\n"
)) ))
except Exception: except Exception:
self.msgBody.setPlainText("Failed to generate error report ...") self.msgBody.setPlainText("Failed to generate error report ...")
@@ -162,18 +167,17 @@ class NWErrorMessage(QDialog):
# Slots # Slots
## ##
def _doClose(self): @pyqtSlot()
"""Close the dialog. def _doClose(self) -> None:
""" """Close the dialog."""
self.close() self.close()
return return
# END Class NWErrorMessage # END Class NWErrorMessage
def exceptionHandler(exType, exValue, exTrace): def exceptionHandler(exType: type, exValue: BaseException, exTrace: TracebackType) -> None:
"""Function to catch unhandled global exceptions. """Function to catch unhandled global exceptions."""
"""
from traceback import print_tb from traceback import print_tb
from PyQt5.QtWidgets import qApp from PyQt5.QtWidgets import qApp
+90 -77
View File
@@ -31,14 +31,13 @@ from pathlib import Path
from datetime import datetime from datetime import datetime
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence, QPixmap
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter, qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter,
QStackedWidget, QVBoxLayout, QWidget QStackedWidget, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, GLOBAL, __hexversion__ from novelwriter import CONFIG, APP, __hexversion__
from novelwriter.alert import askQuestion, makeAlert
from novelwriter.gui.theme import GuiTheme from novelwriter.gui.theme import GuiTheme
from novelwriter.gui.sidebar import GuiSideBar from novelwriter.gui.sidebar import GuiSideBar
from novelwriter.gui.outline import GuiOutlineView from novelwriter.gui.outline import GuiOutlineView
@@ -67,7 +66,7 @@ from novelwriter.enum import (
nwDocAction, nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView nwDocAction, nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView
) )
from novelwriter.common import getGuiItem, hexToInt from novelwriter.common import getGuiItem, hexToInt
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles, nwLabels, trConst
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -116,8 +115,8 @@ class GuiMain(QMainWindow):
# Core Classes # Core Classes
self.mainTheme = GuiTheme() self.mainTheme = GuiTheme()
self.theProject = NWProject(self) self.theProject = NWProject(self)
GLOBAL.setTheme(self.mainTheme) APP.setTheme(self.mainTheme)
GLOBAL.setProject(self.theProject) APP.setProject(self.theProject)
# Core Settings # Core Settings
self.hasProject = False self.hasProject = False
@@ -318,6 +317,16 @@ class GuiMain(QMainWindow):
# Handle Windows Mode # Handle Windows Mode
self.showNormal() self.showNormal()
# Cache Icons
aPx = CONFIG.pxInt(48)
self.alertPix: dict[nwAlert, QPixmap] = {
nwAlert.INFO: self.mainTheme.getPixmap("alert_info", (aPx, aPx)),
nwAlert.WARN: self.mainTheme.getPixmap("alert_warn", (aPx, aPx)),
nwAlert.ERROR: self.mainTheme.getPixmap("alert_error", (aPx, aPx)),
nwAlert.ASK: self.mainTheme.getPixmap("alert_question", (aPx, aPx)),
}
logger.debug("Ready: GUI") logger.debug("Ready: GUI")
if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG: if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG:
@@ -325,7 +334,7 @@ class GuiMain(QMainWindow):
"You are running an untested development version of novelWriter. " "You are running an untested development version of novelWriter. "
"Please be careful when working on a live project " "Please be careful when working on a live project "
"and make sure you take regular backups." "and make sure you take regular backups."
), nwAlert.WARN) ), level=nwAlert.WARN)
logger.info("novelWriter is ready ...") logger.info("novelWriter is ready ...")
self.setStatus(self.tr("novelWriter is ready ...")) self.setStatus(self.tr("novelWriter is ready ..."))
@@ -385,7 +394,7 @@ class GuiMain(QMainWindow):
if not self.closeProject(): if not self.closeProject():
self.makeAlert(self.tr( self.makeAlert(self.tr(
"Cannot create a new project when another project is open." "Cannot create a new project when another project is open."
), nwAlert.ERROR) ), level=nwAlert.ERROR)
return False return False
if projData is None: if projData is None:
@@ -403,7 +412,7 @@ class GuiMain(QMainWindow):
self.makeAlert(self.tr( self.makeAlert(self.tr(
"A project already exists in that location. " "A project already exists in that location. "
"Please choose another folder." "Please choose another folder."
), nwAlert.ERROR) ), level=nwAlert.ERROR)
return False return False
logger.info("Creating new project") logger.info("Creating new project")
@@ -425,13 +434,10 @@ class GuiMain(QMainWindow):
return True return True
if not isYes: if not isYes:
msgYes = self.askQuestion( msgYes = self.askQuestion("%s<br>%s" % (
self.tr("Close Project"), self.tr("Close the current project?"),
"%s<br>%s" % ( self.tr("Changes are saved automatically.")
self.tr("Close the current project?"), ))
self.tr("Changes are saved automatically.")
)
)
if not msgYes: if not msgYes:
return False return False
@@ -443,10 +449,7 @@ class GuiMain(QMainWindow):
if self.theProject.data.doBackup and CONFIG.backupOnClose: if self.theProject.data.doBackup and CONFIG.backupOnClose:
doBackup = True doBackup = True
if CONFIG.askBeforeBackup: if CONFIG.askBeforeBackup:
msgYes = self.askQuestion( msgYes = self.askQuestion(self.tr("Backup the current project?"))
self.tr("Backup Project"),
self.tr("Backup the current project?")
)
if not msgYes: if not msgYes:
doBackup = False doBackup = False
@@ -485,19 +488,28 @@ class GuiMain(QMainWindow):
# Try to open the project # Try to open the project
if not self.theProject.openProject(projFile): if not self.theProject.openProject(projFile):
# The project open failed. # The project open failed.
lockStatus = self.theProject.getLockStatus() lockStatus = self.theProject.getLockStatus()
if lockStatus is None: if lockStatus is None:
# The project is not locked, so failed for some other # The project is not locked, so failed for some other
# reason handled by the project class. # reason handled by the project class.
return False return False
lockText = self.tr(
"The project is already open by another instance of "
"novelWriter, and is therefore locked. Override lock "
"and continue anyway?"
)
lockInfo = self.tr(
"Note: If the program or the computer previously "
"crashed, the lock can safely be overridden. However, "
"overriding it is not recommended if the project is "
"open in another instance of novelWriter. Doing so "
"may corrupt the project."
)
try: try:
lockDetails = ( lockDetails = self.tr(
"<br>%s" % self.tr( "The project was locked by the computer "
"The project was locked by the computer " "'{0}' ({1} {2}), last active on {3}."
"'{0}' ({1} {2}), last active on {3}."
)
).format( ).format(
lockStatus[0], lockStatus[1], lockStatus[2], lockStatus[0], lockStatus[1], lockStatus[2],
datetime.fromtimestamp(int(lockStatus[3])).strftime("%x %X") datetime.fromtimestamp(int(lockStatus[3])).strftime("%x %X")
@@ -505,27 +517,7 @@ class GuiMain(QMainWindow):
except Exception: except Exception:
lockDetails = "" lockDetails = ""
msgBox = QMessageBox() if self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN):
msgRes = msgBox.warning(
self, self.tr("Project Locked"),
"%s<br><br>%s<br>%s" % (
self.tr(
"The project is already open by another instance of "
"novelWriter, and is therefore locked. Override lock "
"and continue anyway?"
),
self.tr(
"Note: If the program or the computer previously "
"crashed, the lock can safely be overridden. However, "
"overriding it is not recommended if the project is "
"open in another instance of novelWriter. Doing so "
"may corrupt the project."
),
lockDetails
),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
)
if msgRes == QMessageBox.Yes:
if not self.theProject.openProject(projFile, overrideLock=True): if not self.theProject.openProject(projFile, overrideLock=True):
return False return False
else: else:
@@ -565,9 +557,7 @@ class GuiMain(QMainWindow):
# Check if we need to rebuild the index # Check if we need to rebuild the index
if self.theProject.index.indexBroken: if self.theProject.index.indexBroken:
self.makeAlert(self.tr( self.makeAlert(self.tr("The project index is outdated or broken. Rebuilding index."))
"The project index is outdated or broken. Rebuilding index."
), nwAlert.INFO)
self.rebuildIndex() self.rebuildIndex()
# Make sure the changed status is set to false on things opened # Make sure the changed status is set to false on things opened
@@ -756,23 +746,20 @@ class GuiMain(QMainWindow):
except Exception as exc: except Exception as exc:
self.makeAlert(self.tr( self.makeAlert(self.tr(
"Could not read file. The file must be an existing text file." "Could not read file. The file must be an existing text file."
), nwAlert.ERROR, exception=exc) ), level=nwAlert.ERROR, exception=exc)
return False return False
if self.docEditor.docHandle() is None: if self.docEditor.docHandle() is None:
self.makeAlert(self.tr( self.makeAlert(self.tr(
"Please open a document to import the text file into." "Please open a document to import the text file into."
), nwAlert.ERROR) ), level=nwAlert.ERROR)
return False return False
if not self.docEditor.isEmpty(): if not self.docEditor.isEmpty():
msgYes = self.askQuestion( msgYes = self.askQuestion(self.tr(
self.tr("Import Document"), "Importing the file will overwrite the current content of "
self.tr( "the document. Do you want to proceed?"
"Importing the file will overwrite the current content of " ))
"the document. Do you want to proceed?"
)
)
if not msgYes: if not msgYes:
return False return False
@@ -871,9 +858,7 @@ class GuiMain(QMainWindow):
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
if not beQuiet: if not beQuiet:
self.makeAlert(self.tr( self.makeAlert(self.tr("The project index has been successfully rebuilt."))
"The project index has been successfully rebuilt."
), nwAlert.INFO)
return True return True
@@ -921,7 +906,7 @@ class GuiMain(QMainWindow):
if dlgConf.needsRestart: if dlgConf.needsRestart:
self.makeAlert(self.tr( self.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted." "Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO) ))
if dlgConf.refreshTree: if dlgConf.refreshTree:
self.projView.populateTree() self.projView.populateTree()
@@ -1102,17 +1087,48 @@ class GuiMain(QMainWindow):
return return
def makeAlert(self, message: list[str] | str, level: nwAlert = nwAlert.INFO, def makeAlert(self, text: str, info: str = "", details: str = "",
exception: Exception | None = None) -> None: level: nwAlert = nwAlert.INFO, exception: Exception | None = None) -> None:
"""Alert both the user and the logger at the same time. The """Alert both the user and the logger at the same time. The
message can be either a string or a list of strings. message can be either a string or a list of strings.
""" """
makeAlert(message, level, exception) logText = " ".join(filter(None, [text, info, details]))
if level == nwAlert.INFO:
logger.info(logText, stacklevel=2)
elif level == nwAlert.WARN:
logger.warning(logText, stacklevel=2)
elif level == nwAlert.ERROR:
logger.error(logText, stacklevel=2, exc_info=exception)
if exception is not None:
excText = f"{type(exception).__name__}: {str(exception)}"
info = f"{info}<br>{excText}" if info else excText
msgBox = QMessageBox(self)
msgBox.setWindowTitle(trConst(nwLabels.ALERT_NAME[level]))
msgBox.setText(text)
msgBox.setInformativeText(info)
msgBox.setDetailedText(details)
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msgBox.setIconPixmap(self.alertPix[level])
msgBox.adjustSize()
msgBox.exec_()
return return
def askQuestion(self, title: str, question: str) -> bool: def askQuestion(self, text: str, info: str = "", details: str = "",
level: nwAlert = nwAlert.ASK) -> bool:
"""Ask the user a Yes/No question, and return the answer.""" """Ask the user a Yes/No question, and return the answer."""
return askQuestion(title, question) msgBox = QMessageBox(self)
msgBox.setWindowTitle(trConst(nwLabels.ALERT_NAME[level]))
msgBox.setText(text)
msgBox.setInformativeText(info)
msgBox.setDetailedText(details)
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msgBox.setIconPixmap(self.alertPix[level])
msgBox.adjustSize()
msgBox.exec_()
return msgBox.result() == QMessageBox.Yes
def reportConfErr(self) -> bool: def reportConfErr(self) -> bool:
"""Checks if the Config module has any errors to report, and let """Checks if the Config module has any errors to report, and let
@@ -1120,7 +1136,7 @@ class GuiMain(QMainWindow):
errors since it is initialised before the GUI itself. errors since it is initialised before the GUI itself.
""" """
if CONFIG.hasError: if CONFIG.hasError:
self.makeAlert(CONFIG.errorText(), nwAlert.ERROR) self.makeAlert(CONFIG.errorText(), level=nwAlert.ERROR)
return True return True
return False return False
@@ -1131,13 +1147,10 @@ class GuiMain(QMainWindow):
def closeMain(self) -> bool: def closeMain(self) -> bool:
"""Save everything, and close novelWriter.""" """Save everything, and close novelWriter."""
if self.hasProject: if self.hasProject:
msgYes = self.askQuestion( msgYes = self.askQuestion("%s<br>%s" % (
self.tr("Exit"), self.tr("Do you want to exit novelWriter?"),
"%s<br>%s" % ( self.tr("Changes are saved automatically.")
self.tr("Do you want to exit novelWriter?"), ))
self.tr("Changes are saved automatically.")
)
)
if not msgYes: if not msgYes:
return False return False
@@ -1388,7 +1401,7 @@ class GuiMain(QMainWindow):
"from the Tools menu, or by pressing {1}." "from the Tools menu, or by pressing {1}."
).format( ).format(
tag, "F9" tag, "F9"
), nwAlert.ERROR) ), level=nwAlert.ERROR)
return None, None return None, None
return tHandle, sTitle return tHandle, sTitle