Move make alert to separate module and add a global singleton for pointers

This commit is contained in:
Veronica Berglyd Olsen
2023-08-07 17:01:44 +02:00
parent c3b3120bc1
commit 8959af1f30
6 changed files with 169 additions and 53 deletions
+8 -5
View File
@@ -21,6 +21,7 @@ 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 getopt import getopt
@@ -29,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 from novelwriter.config import Config, Global
## ##
# Version Scheme # Version Scheme
@@ -71,13 +72,13 @@ logger = logging.getLogger(__name__)
# Main Program # Main Program
## ##
# Load the main config as a global object # Create the global singleton instances
CONFIG = Config() CONFIG = Config()
GLOBAL = Global()
def main(sysArgs=None): def main(sysArgs: list | None = None):
"""Parse command line, set up logging, and launch main GUI. """Parse command line, set up logging, and launch main GUI."""
"""
if sysArgs is None: if sysArgs is None:
sysArgs = sys.argv[1:] sysArgs = sys.argv[1:]
@@ -227,6 +228,7 @@ def main(sysArgs=None):
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
if testMode: if testMode:
nwGUI = GuiMain() nwGUI = GuiMain()
GLOBAL.setGUI(nwGUI)
return nwGUI return nwGUI
else: else:
@@ -246,6 +248,7 @@ def main(sysArgs=None):
# Launch main GUI # Launch main GUI
nwGUI = GuiMain() nwGUI = GuiMain()
GLOBAL.setGUI(nwGUI)
nwGUI.postLaunchTasks(cmdOpen) nwGUI.postLaunchTasks(cmdOpen)
sys.exit(nwApp.exec_()) sys.exit(nwApp.exec_())
+78
View File
@@ -0,0 +1,78 @@
"""
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
+65 -3
View File
@@ -1,10 +1,10 @@
""" """
novelWriter Config Class novelWriter Config Class
========================== ==========================
Class holding the user preferences and handling the config file
File History: File History:
Created: 2018-09-22 [0.0.1] Created: 2018-09-22 [0.0.1] Config
Created: 2023-08-07 [2.1b2] NWGlobal
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -22,7 +22,6 @@ 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 from __future__ import annotations
import sys import sys
@@ -30,6 +29,7 @@ import json
import logging import logging
from time import time from time import time
from typing import TYPE_CHECKING
from pathlib import Path from pathlib import Path
from PyQt5.QtGui import QFontDatabase from PyQt5.QtGui import QFontDatabase
@@ -42,6 +42,11 @@ from novelwriter.error import logException, formatException
from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser
from novelwriter.constants import nwFiles, nwUnicode from novelwriter.constants import nwFiles, nwUnicode
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
from novelwriter.gui.theme import GuiTheme
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -793,6 +798,63 @@ class Config:
# END Class Config # END Class Config
class Global:
"""Singleton: Global Pointers
This class holds pointers to the core singletons:
* The GuiMain instance
* The GuiTheme instance
* The NWProject instance
"""
def __init__(self) -> None:
self._gui: GuiMain | None = None
self._theme: GuiTheme | None = None
self._project: NWProject | None = None
return
@property
def gui(self) -> GuiMain:
"""The main gui instance."""
if self._gui is None:
raise Exception("GLOBAL not fully initialised")
return self._gui
@property
def theme(self) -> GuiTheme:
"""The main gui theme instance."""
if self._theme is None:
raise Exception("GLOBAL not fully initialised")
return self._theme
@property
def project(self) -> NWProject:
"""The main project instance."""
if self._project is None:
raise Exception("GLOBAL not fully initialised")
return self._project
def setGUI(self, gui: GuiMain) -> None:
"""Set the GUI instance. Can only be set once."""
if self._gui is None:
self._gui = gui
return
def setTheme(self, theme: GuiTheme) -> None:
"""Set the theme instance. Can only be set once."""
if self._theme is None:
self._theme = theme
return
def setProject(self, project: NWProject) -> None:
"""Set the project instance. Can only be set once."""
if self._project is None:
self._project = project
return
# END Class Global
class RecentProjects: class RecentProjects:
def __init__(self, config): def __init__(self, config):
+7 -2
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Constants novelWriter Constants
======================= =======================
Constants and maps for translating flags and enums to text
File History: File History:
Created: 2019-04-28 [0.0.1] Created: 2019-04-28 [0.0.1]
@@ -24,9 +23,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations from __future__ import annotations
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline from novelwriter.enum import nwAlert, nwBuildFmt, nwItemClass, nwItemLayout, nwOutline
def trConst(text: str) -> str: def trConst(text: str) -> str:
@@ -163,6 +163,11 @@ class nwLabels:
nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"), nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"), nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"),
} }
ALERT_NAME = {
nwAlert.INFO: QT_TRANSLATE_NOOP("Constant", "Information"),
nwAlert.WARN: QT_TRANSLATE_NOOP("Constant", "Warning"),
nwAlert.ERROR: QT_TRANSLATE_NOOP("Constant", "Error"),
}
ITEM_DESCRIPTION = { ITEM_DESCRIPTION = {
"none": QT_TRANSLATE_NOOP("Constant", "None"), "none": QT_TRANSLATE_NOOP("Constant", "None"),
"root": QT_TRANSLATE_NOOP("Constant", "Root Folder"), "root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
-1
View File
@@ -125,7 +125,6 @@ class nwAlert(Enum):
INFO = 0 INFO = 0
WARN = 1 WARN = 1
ERROR = 2 ERROR = 2
BUG = 3
# END Enum nwAlert # END Enum nwAlert
+11 -42
View File
@@ -37,7 +37,8 @@ from PyQt5.QtWidgets import (
QStackedWidget, QVBoxLayout, QWidget QStackedWidget, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, __hexversion__ from novelwriter import CONFIG, GLOBAL, __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
@@ -112,9 +113,13 @@ class GuiMain(QMainWindow):
# Core Classes # Core Classes
# ============ # ============
# Core Classes and Settings # Core Classes
self.mainTheme = GuiTheme() self.mainTheme = GuiTheme()
self.theProject = NWProject(self) self.theProject = NWProject(self)
GLOBAL.setTheme(self.mainTheme)
GLOBAL.setProject(self.theProject)
# Core Settings
self.hasProject = False self.hasProject = False
self.isFocusMode = False self.isFocusMode = False
self.idleRefTime = time() self.idleRefTime = time()
@@ -1102,48 +1107,12 @@ class GuiMain(QMainWindow):
"""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.
""" """
if isinstance(message, list): makeAlert(message, level, exception)
message = list(filter(None, message)) # Strip empty strings
popMsg = "<br>".join(message)
logMsg = " ".join(message)
else:
popMsg = str(message)
logMsg = str(message)
kw = {}
if exception is not None:
kw["exc_info"] = exception
popMsg = f"{popMsg}<br>{type(exception).__name__}: {str(exception)}"
# 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)
elif level == nwAlert.BUG:
logger.error(logMsg, **kw)
# Popup
msgBox = QMessageBox()
if level == nwAlert.INFO:
msgBox.information(self, self.tr("Information"), popMsg)
elif level == nwAlert.WARN:
msgBox.warning(self, self.tr("Warning"), popMsg)
elif level == nwAlert.ERROR:
msgBox.critical(self, self.tr("Error"), popMsg)
elif level == nwAlert.BUG:
popMsg += "<br>%s" % self.tr("This is a bug!")
msgBox.critical(self, self.tr("Internal Error"), popMsg)
return return
def askQuestion(self, title: str, question: str) -> bool: def askQuestion(self, title: str, question: str) -> bool:
"""Ask the user a Yes/No question, and return the answer.""" """Ask the user a Yes/No question, and return the answer."""
msgBox = QMessageBox() return askQuestion(title, question)
msgRes = msgBox.question(self, title, question, QMessageBox.Yes | QMessageBox.No)
return msgRes == 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