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
|
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_())
|
||||||
|
|||||||
@@ -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
|
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 2018–2023, Veronica Berglyd Olsen
|
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
|
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):
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user