Move make alert to separate module and add a global singleton for pointers
This commit is contained in:
@@ -21,6 +21,7 @@ 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 sys
|
||||
import getopt
|
||||
@@ -29,7 +30,7 @@ import logging
|
||||
from PyQt5.QtWidgets import QApplication, QErrorMessage
|
||||
|
||||
from novelwriter.error import exceptionHandler, logException
|
||||
from novelwriter.config import Config
|
||||
from novelwriter.config import Config, Global
|
||||
|
||||
##
|
||||
# Version Scheme
|
||||
@@ -71,13 +72,13 @@ logger = logging.getLogger(__name__)
|
||||
# Main Program
|
||||
##
|
||||
|
||||
# Load the main config as a global object
|
||||
# Create the global singleton instances
|
||||
CONFIG = Config()
|
||||
GLOBAL = Global()
|
||||
|
||||
|
||||
def main(sysArgs=None):
|
||||
"""Parse command line, set up logging, and launch main GUI.
|
||||
"""
|
||||
def main(sysArgs: list | None = None):
|
||||
"""Parse command line, set up logging, and launch main GUI."""
|
||||
if sysArgs is None:
|
||||
sysArgs = sys.argv[1:]
|
||||
|
||||
@@ -227,6 +228,7 @@ def main(sysArgs=None):
|
||||
from novelwriter.guimain import GuiMain
|
||||
if testMode:
|
||||
nwGUI = GuiMain()
|
||||
GLOBAL.setGUI(nwGUI)
|
||||
return nwGUI
|
||||
|
||||
else:
|
||||
@@ -246,6 +248,7 @@ def main(sysArgs=None):
|
||||
|
||||
# Launch main GUI
|
||||
nwGUI = GuiMain()
|
||||
GLOBAL.setGUI(nwGUI)
|
||||
nwGUI.postLaunchTasks(cmdOpen)
|
||||
|
||||
sys.exit(nwApp.exec_())
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
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
|
||||
+65
-3
@@ -1,10 +1,10 @@
|
||||
"""
|
||||
novelWriter – Config Class
|
||||
==========================
|
||||
Class holding the user preferences and handling the config file
|
||||
|
||||
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
|
||||
Copyright 2018–2023, 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
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
@@ -30,6 +29,7 @@ import json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtGui import QFontDatabase
|
||||
@@ -42,6 +42,11 @@ from novelwriter.error import logException, formatException
|
||||
from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser
|
||||
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__)
|
||||
|
||||
|
||||
@@ -793,6 +798,63 @@ 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:
|
||||
|
||||
def __init__(self, config):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
novelWriter – Constants
|
||||
=======================
|
||||
Constants and maps for translating flags and enums to text
|
||||
|
||||
File History:
|
||||
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 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:
|
||||
@@ -163,6 +163,11 @@ class nwLabels:
|
||||
nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
|
||||
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 = {
|
||||
"none": QT_TRANSLATE_NOOP("Constant", "None"),
|
||||
"root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
|
||||
|
||||
@@ -125,7 +125,6 @@ class nwAlert(Enum):
|
||||
INFO = 0
|
||||
WARN = 1
|
||||
ERROR = 2
|
||||
BUG = 3
|
||||
|
||||
# END Enum nwAlert
|
||||
|
||||
|
||||
+11
-42
@@ -37,7 +37,8 @@ from PyQt5.QtWidgets import (
|
||||
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.sidebar import GuiSideBar
|
||||
from novelwriter.gui.outline import GuiOutlineView
|
||||
@@ -112,9 +113,13 @@ class GuiMain(QMainWindow):
|
||||
# Core Classes
|
||||
# ============
|
||||
|
||||
# Core Classes and Settings
|
||||
self.mainTheme = GuiTheme()
|
||||
self.theProject = NWProject(self)
|
||||
# Core Classes
|
||||
self.mainTheme = GuiTheme()
|
||||
self.theProject = NWProject(self)
|
||||
GLOBAL.setTheme(self.mainTheme)
|
||||
GLOBAL.setProject(self.theProject)
|
||||
|
||||
# Core Settings
|
||||
self.hasProject = False
|
||||
self.isFocusMode = False
|
||||
self.idleRefTime = time()
|
||||
@@ -1102,48 +1107,12 @@ class GuiMain(QMainWindow):
|
||||
"""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 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)
|
||||
|
||||
makeAlert(message, level, exception)
|
||||
return
|
||||
|
||||
def askQuestion(self, title: str, question: str) -> bool:
|
||||
"""Ask the user a Yes/No question, and return the answer."""
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(self, title, question, QMessageBox.Yes | QMessageBox.No)
|
||||
return msgRes == QMessageBox.Yes
|
||||
return askQuestion(title, question)
|
||||
|
||||
def reportConfErr(self) -> bool:
|
||||
"""Checks if the Config module has any errors to report, and let
|
||||
|
||||
Reference in New Issue
Block a user