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
+90 -77
View File
@@ -31,14 +31,13 @@ from pathlib import Path
from datetime import datetime
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 (
qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter,
QStackedWidget, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, GLOBAL, __hexversion__
from novelwriter.alert import askQuestion, makeAlert
from novelwriter import CONFIG, APP, __hexversion__
from novelwriter.gui.theme import GuiTheme
from novelwriter.gui.sidebar import GuiSideBar
from novelwriter.gui.outline import GuiOutlineView
@@ -67,7 +66,7 @@ from novelwriter.enum import (
nwDocAction, nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView
)
from novelwriter.common import getGuiItem, hexToInt
from novelwriter.constants import nwFiles
from novelwriter.constants import nwFiles, nwLabels, trConst
logger = logging.getLogger(__name__)
@@ -116,8 +115,8 @@ class GuiMain(QMainWindow):
# Core Classes
self.mainTheme = GuiTheme()
self.theProject = NWProject(self)
GLOBAL.setTheme(self.mainTheme)
GLOBAL.setProject(self.theProject)
APP.setTheme(self.mainTheme)
APP.setProject(self.theProject)
# Core Settings
self.hasProject = False
@@ -318,6 +317,16 @@ class GuiMain(QMainWindow):
# Handle Windows Mode
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")
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. "
"Please be careful when working on a live project "
"and make sure you take regular backups."
), nwAlert.WARN)
), level=nwAlert.WARN)
logger.info("novelWriter is ready ...")
self.setStatus(self.tr("novelWriter is ready ..."))
@@ -385,7 +394,7 @@ class GuiMain(QMainWindow):
if not self.closeProject():
self.makeAlert(self.tr(
"Cannot create a new project when another project is open."
), nwAlert.ERROR)
), level=nwAlert.ERROR)
return False
if projData is None:
@@ -403,7 +412,7 @@ class GuiMain(QMainWindow):
self.makeAlert(self.tr(
"A project already exists in that location. "
"Please choose another folder."
), nwAlert.ERROR)
), level=nwAlert.ERROR)
return False
logger.info("Creating new project")
@@ -425,13 +434,10 @@ class GuiMain(QMainWindow):
return True
if not isYes:
msgYes = self.askQuestion(
self.tr("Close Project"),
"%s<br>%s" % (
self.tr("Close the current project?"),
self.tr("Changes are saved automatically.")
)
)
msgYes = self.askQuestion("%s<br>%s" % (
self.tr("Close the current project?"),
self.tr("Changes are saved automatically.")
))
if not msgYes:
return False
@@ -443,10 +449,7 @@ class GuiMain(QMainWindow):
if self.theProject.data.doBackup and CONFIG.backupOnClose:
doBackup = True
if CONFIG.askBeforeBackup:
msgYes = self.askQuestion(
self.tr("Backup Project"),
self.tr("Backup the current project?")
)
msgYes = self.askQuestion(self.tr("Backup the current project?"))
if not msgYes:
doBackup = False
@@ -485,19 +488,28 @@ class GuiMain(QMainWindow):
# Try to open the project
if not self.theProject.openProject(projFile):
# The project open failed.
lockStatus = self.theProject.getLockStatus()
if lockStatus is None:
# The project is not locked, so failed for some other
# reason handled by the project class.
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:
lockDetails = (
"<br>%s" % self.tr(
"The project was locked by the computer "
"'{0}' ({1} {2}), last active on {3}."
)
lockDetails = self.tr(
"The project was locked by the computer "
"'{0}' ({1} {2}), last active on {3}."
).format(
lockStatus[0], lockStatus[1], lockStatus[2],
datetime.fromtimestamp(int(lockStatus[3])).strftime("%x %X")
@@ -505,27 +517,7 @@ class GuiMain(QMainWindow):
except Exception:
lockDetails = ""
msgBox = QMessageBox()
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 self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN):
if not self.theProject.openProject(projFile, overrideLock=True):
return False
else:
@@ -565,9 +557,7 @@ class GuiMain(QMainWindow):
# Check if we need to rebuild the index
if self.theProject.index.indexBroken:
self.makeAlert(self.tr(
"The project index is outdated or broken. Rebuilding index."
), nwAlert.INFO)
self.makeAlert(self.tr("The project index is outdated or broken. Rebuilding index."))
self.rebuildIndex()
# Make sure the changed status is set to false on things opened
@@ -756,23 +746,20 @@ class GuiMain(QMainWindow):
except Exception as exc:
self.makeAlert(self.tr(
"Could not read file. The file must be an existing text file."
), nwAlert.ERROR, exception=exc)
), level=nwAlert.ERROR, exception=exc)
return False
if self.docEditor.docHandle() is None:
self.makeAlert(self.tr(
"Please open a document to import the text file into."
), nwAlert.ERROR)
), level=nwAlert.ERROR)
return False
if not self.docEditor.isEmpty():
msgYes = self.askQuestion(
self.tr("Import Document"),
self.tr(
"Importing the file will overwrite the current content of "
"the document. Do you want to proceed?"
)
)
msgYes = self.askQuestion(self.tr(
"Importing the file will overwrite the current content of "
"the document. Do you want to proceed?"
))
if not msgYes:
return False
@@ -871,9 +858,7 @@ class GuiMain(QMainWindow):
qApp.restoreOverrideCursor()
if not beQuiet:
self.makeAlert(self.tr(
"The project index has been successfully rebuilt."
), nwAlert.INFO)
self.makeAlert(self.tr("The project index has been successfully rebuilt."))
return True
@@ -921,7 +906,7 @@ class GuiMain(QMainWindow):
if dlgConf.needsRestart:
self.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO)
))
if dlgConf.refreshTree:
self.projView.populateTree()
@@ -1102,17 +1087,48 @@ class GuiMain(QMainWindow):
return
def makeAlert(self, message: list[str] | str, level: nwAlert = nwAlert.INFO,
exception: Exception | None = None) -> None:
def makeAlert(self, text: str, info: str = "", details: str = "",
level: nwAlert = nwAlert.INFO, exception: 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.
"""
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
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."""
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:
"""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.
"""
if CONFIG.hasError:
self.makeAlert(CONFIG.errorText(), nwAlert.ERROR)
self.makeAlert(CONFIG.errorText(), level=nwAlert.ERROR)
return True
return False
@@ -1131,13 +1147,10 @@ class GuiMain(QMainWindow):
def closeMain(self) -> bool:
"""Save everything, and close novelWriter."""
if self.hasProject:
msgYes = self.askQuestion(
self.tr("Exit"),
"%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
)
)
msgYes = self.askQuestion("%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
))
if not msgYes:
return False
@@ -1388,7 +1401,7 @@ class GuiMain(QMainWindow):
"from the Tools menu, or by pressing {1}."
).format(
tag, "F9"
), nwAlert.ERROR)
), level=nwAlert.ERROR)
return None, None
return tHandle, sTitle