Rewrite the makeAlert and askQuestion functions to use recommended interface
This commit is contained in:
@@ -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_())
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
"""
|
|
||||||
novelWriter – Alert Functions
|
|
||||||
=============================
|
|
||||||
|
|
||||||
File History:
|
|
||||||
Created: 2023-08-07 [2.1b2]
|
|
||||||
|
|
||||||
This file is a part of novelWriter
|
|
||||||
Copyright 2018–2023, 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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user