Rewrite the makeAlert and askQuestion functions to use recommended interface
This commit is contained in:
+90
-77
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user