diff --git a/novelwriter/common.py b/novelwriter/common.py index a325e604..8ccf0bf0 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -121,8 +121,7 @@ def checkUuid(value: Any, default: str) -> str: def checkPath(value: Any, default: Path) -> Path: - """Check if a value is a valid path. Non-empty strings are accepted. - """ + """Check if a value is a valid path.""" if isinstance(value, Path): return value elif isinstance(value, str): @@ -289,8 +288,7 @@ def transferCase(source: str, target: str) -> str: def fuzzyTime(seconds: int) -> str: - """Converts a time difference in seconds into a fuzzy time string. - """ + """Convert a time difference in seconds into a fuzzy time string.""" if seconds < 0: return QCoreApplication.translate( "Common", "in the future" @@ -350,8 +348,7 @@ def fuzzyTime(seconds: int) -> str: def numberToRoman(value: int, toLower: bool = False) -> str: - """Convert an integer to a Roman number. - """ + """Convert an integer to a Roman number.""" if not isinstance(value, int): return "NAN" if value < 1 or value > 4999: @@ -424,12 +421,14 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str: return "".join(buffer) -def xmlIndent(tree: ET.Element | ET.ElementTree): +def xmlIndent(tree: ET.Element | ET.ElementTree) -> None: """A modified version of the XML indent function in the standard library. It behaves more closely to how the one from lxml does. """ if isinstance(tree, ET.ElementTree): tree = tree.getroot() + if not isinstance(tree, ET.Element): + return indentations = ["\n"] diff --git a/novelwriter/config.py b/novelwriter/config.py index 6bb94e52..cafa3e36 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -28,18 +28,23 @@ import json import logging from time import time +from typing import TYPE_CHECKING from pathlib import Path from PyQt5.QtGui import QFontDatabase from PyQt5.QtCore import ( - QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths, - QSysInfo, QLocale, QLibraryInfo, QTranslator + PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo, + QLocale, QStandardPaths, QSysInfo, QTranslator ) +from PyQt5.QtWidgets import QApplication -from novelwriter.error import logException, formatException -from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser +from novelwriter.error import formatException, logException +from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp from novelwriter.constants import nwFiles, nwUnicode +if TYPE_CHECKING: # pragma: no cover + from novelwriter.gui.theme import GuiTheme + logger = logging.getLogger(__name__) @@ -48,7 +53,7 @@ class Config: LANG_NW = 1 LANG_PROJ = 2 - def __init__(self): + def __init__(self) -> None: # Initialisation # ============== @@ -64,6 +69,7 @@ class Config: self._confPath = confRoot.absolute() / self.appHandle # The user config location self._dataPath = dataRoot.absolute() / self.appHandle # The user data location self._homePath = Path.home().absolute() # The user's home directory + self._backPath = self._homePath / "Backups" self._appPath = Path(__file__).parent.absolute() self._appRoot = self._appPath.parent @@ -90,7 +96,8 @@ class Config: # User Settings # ============= - self._recentProj = RecentProjects(self) + self._themeObj = None + self._recentObj = RecentProjects(self) # General GUI Settings self.guiLocale = self._qLocale.name() @@ -102,7 +109,6 @@ class Config: self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.lastNotes = "0x0" # The latest release notes that have been shown - self._lastPath = self._homePath # The user's last used path # Size Settings self._mainWinSize = [1200, 650] # Last size of the main GUI window @@ -116,7 +122,6 @@ class Config: self.autoSaveProj = 60 # Interval for auto-saving project, in seconds self.autoSaveDoc = 30 # Interval for auto-saving document, in seconds self.emphLabels = True # Add emphasis to H1 and H2 item labels - self._backupPath = None # Backup path to use, can be none self.backupOnClose = False # Flag for running automatic backups self.askBeforeBackup = True # Flag for asking before running automatic backup @@ -169,6 +174,10 @@ class Config: self.fmtPadAfter = "" self.fmtPadThin = False + # User Paths + self._lastPath = self._homePath # The user's last used path + self._backupPath = self._backPath # Backup path to use, can be none + # Spell Checking Settings self.spellLanguage = "en" @@ -228,53 +237,59 @@ class Config: ## @property - def hasError(self): + def hasError(self) -> bool: return self._hasError @property - def recentProjects(self): - return self._recentProj + def recentProjects(self) -> RecentProjects: + return self._recentObj @property - def mainWinSize(self): + def theme(self) -> GuiTheme: + if self._themeObj is None: + raise Exception("Cannot access GUI theme before it is initialised") + return self._themeObj + + @property + def mainWinSize(self) -> list[int]: return [int(x*self.guiScale) for x in self._mainWinSize] @property - def preferencesWinSize(self): + def preferencesWinSize(self) -> list[int]: return [int(x*self.guiScale) for x in self._prefsWinSize] @property - def projLoadColWidths(self): + def projLoadColWidths(self) -> list[int]: return [int(x*self.guiScale) for x in self._projLoadCols] @property - def mainPanePos(self): + def mainPanePos(self) -> list[int]: return [int(x*self.guiScale) for x in self._mainPanePos] @property - def viewPanePos(self): + def viewPanePos(self) -> list[int]: return [int(x*self.guiScale) for x in self._viewPanePos] @property - def outlinePanePos(self): + def outlinePanePos(self) -> list[int]: return [int(x*self.guiScale) for x in self._outlnPanePos] ## # Getters ## - def getTextWidth(self, focusMode=False): + def getTextWidth(self, focusMode: bool = False) -> int: """Get the text with for the correct editor mode.""" if focusMode: return self.pxInt(max(self.focusWidth, 200)) else: return self.pxInt(max(self.textWidth, 200)) - def getTextMargin(self): + def getTextMargin(self) -> int: """Get the scaled text margin.""" return self.pxInt(max(self.textMargin, 0)) - def getTabWidth(self): + def getTabWidth(self) -> int: """Get the scaled tab width.""" return self.pxInt(max(self.tabWidth, 0)) @@ -282,65 +297,70 @@ class Config: # Setters ## - def setMainWinSize(self, newWidth, newHeight): + def setThemeInstance(self, theme: GuiTheme) -> None: + """Set the applications theme instance.""" + self._themeObj = theme + return + + def setMainWinSize(self, width: int, height: int) -> None: """Set the size of the main window, but only if the change is larger than 5 pixels. The OS window manager will sometimes adjust it a bit, and we don't want the main window to shrink or grow each time the app is opened. """ - newWidth = int(newWidth/self.guiScale) - newHeight = int(newHeight/self.guiScale) - if abs(self._mainWinSize[0] - newWidth) > 5: - self._mainWinSize[0] = newWidth - if abs(self._mainWinSize[1] - newHeight) > 5: - self._mainWinSize[1] = newHeight + width = int(width/self.guiScale) + height = int(height/self.guiScale) + if abs(self._mainWinSize[0] - width) > 5: + self._mainWinSize[0] = width + if abs(self._mainWinSize[1] - height) > 5: + self._mainWinSize[1] = height return - def setPreferencesWinSize(self, newWidth, newHeight): + def setPreferencesWinSize(self, width: int, height: int) -> None: """Set the size of the Preferences dialog window.""" - self._prefsWinSize[0] = int(newWidth/self.guiScale) - self._prefsWinSize[1] = int(newHeight/self.guiScale) + self._prefsWinSize[0] = int(width/self.guiScale) + self._prefsWinSize[1] = int(height/self.guiScale) return - def setProjLoadColWidths(self, colWidths): + def setProjLoadColWidths(self, widths: list[int]) -> None: """Set the column widths of the Load Project dialog.""" - self._projLoadCols = [int(x/self.guiScale) for x in colWidths] + self._projLoadCols = [int(x/self.guiScale) for x in widths] return - def setMainPanePos(self, panePos): + def setMainPanePos(self, pos: list[int]) -> None: """Set the position of the main GUI splitter.""" - self._mainPanePos = [int(x/self.guiScale) for x in panePos] + self._mainPanePos = [int(x/self.guiScale) for x in pos] return - def setViewPanePos(self, panePos): + def setViewPanePos(self, pos: list[int]) -> None: """Set the position of the viewer meta data splitter.""" - self._viewPanePos = [int(x/self.guiScale) for x in panePos] + self._viewPanePos = [int(x/self.guiScale) for x in pos] return - def setOutlinePanePos(self, panePos): + def setOutlinePanePos(self, pos: list[int]) -> None: """Set the position of the outline details splitter.""" - self._outlnPanePos = [int(x/self.guiScale) for x in panePos] + self._outlnPanePos = [int(x/self.guiScale) for x in pos] return - def setLastPath(self, lastPath): + def setLastPath(self, path: str | Path) -> None: """Set the last used path. Only the folder is saved, so if the path is not a folder, the parent of the path is used instead. """ - if isinstance(lastPath, (str, Path)): - lastPath = checkPath(lastPath, self._homePath) - if not lastPath.is_dir(): - lastPath = lastPath.parent - if lastPath.is_dir(): - self._lastPath = lastPath + if isinstance(path, (str, Path)): + path = checkPath(path, self._homePath) + if not path.is_dir(): + path = path.parent + if path.is_dir(): + self._lastPath = path logger.debug("Last path updated: %s" % self._lastPath) return - def setBackupPath(self, backupPath: Path | None): + def setBackupPath(self, path: Path | str) -> None: """Set the current backup path.""" - self._backupPath = checkPath(backupPath, None) + self._backupPath = checkPath(path, self._backPath) return - def setTextFont(self, family: str | None, pointSize: int = 12): + def setTextFont(self, family: str | None, pointSize: int = 12) -> None: """Set the text font if it exists. If it doesn't, or is None, set to default font. """ @@ -364,15 +384,11 @@ class Config: ## def pxInt(self, value: int) -> int: - """Used to scale fixed gui sizes by the screen scale factor. - This function returns an int, which is always rounded down. - """ + """Scale fixed gui sizes by the screen scale factor.""" return int(value*self.guiScale) def rpxInt(self, value: int) -> int: - """Used to un-scale fixed gui sizes by the screen scale factor. - This function returns an int, which is always rounded down. - """ + """Un-scale fixed gui sizes by the screen scale factor.""" return int(value/self.guiScale) def dataPath(self, target: str | None = None) -> Path: @@ -395,12 +411,12 @@ class Config: return self._lastPath return self._homePath - def backupPath(self) -> Path | None: + def backupPath(self) -> Path: """Return the backup path.""" if isinstance(self._backupPath, Path): if self._backupPath.is_dir(): return self._backupPath - return None + return self._backPath def errorText(self) -> str: """Compile and return error messages from the initialisation of @@ -442,7 +458,8 @@ class Config: # Config Actions ## - def initConfig(self, confPath: str | Path | None = None, dataPath: str | Path | None = None): + def initConfig(self, confPath: str | Path | None = None, + dataPath: str | Path | None = None) -> None: """Initialise the config class. The manual setting of confPath and dataPath is mainly intended for the test suite. """ @@ -479,16 +496,15 @@ class Config: else: self.saveConfig() - self._recentProj.loadCache() + self._recentObj.loadCache() self._checkOptionalPackages() logger.debug("Config initialisation complete") return - def initLocalisation(self, nwApp): - """Initialise the localisation of the GUI. - """ + def initLocalisation(self, nwApp: QApplication) -> None: + """Initialise the localisation of the GUI.""" self._qLocale = QLocale(self.guiLocale) QLocale.setDefault(self._qLocale) self._qtTrans = {} @@ -509,16 +525,15 @@ class Config: return - def loadConfig(self): - """Load preferences from file and replace default settings. - """ + def loadConfig(self) -> bool: + """Load preferences from file and replace default settings.""" logger.debug("Loading config file") - theConf = NWConfigParser() + conf = NWConfigParser() cnfPath = self._confPath / nwFiles.CONF_FILE try: with open(cnfPath, mode="r", encoding="utf-8") as inFile: - theConf.read_file(inFile) + conf.read_file(inFile) except Exception as exc: logger.error("Could not load config file") logException() @@ -528,98 +543,98 @@ class Config: return False # Main - cnfSec = "Main" - self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme) - self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax) - self.guiFont = theConf.rdStr(cnfSec, "font", self.guiFont) - self.guiFontSize = theConf.rdInt(cnfSec, "fontsize", self.guiFontSize) - self.guiLocale = theConf.rdStr(cnfSec, "localisation", self.guiLocale) - self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll) - self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll) - self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes) - self._lastPath = theConf.rdPath(cnfSec, "lastpath", self._lastPath) + sec = "Main" + self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme) + self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax) + self.guiFont = conf.rdStr(sec, "font", self.guiFont) + self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize) + self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale) + self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll) + self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll) + self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes) + self._lastPath = conf.rdPath(sec, "lastpath", self._lastPath) # Sizes - cnfSec = "Sizes" - self._mainWinSize = theConf.rdIntList(cnfSec, "mainwindow", self._mainWinSize) - self._prefsWinSize = theConf.rdIntList(cnfSec, "preferences", self._prefsWinSize) - self._projLoadCols = theConf.rdIntList(cnfSec, "projloadcols", self._projLoadCols) - self._mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self._mainPanePos) - self._viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self._viewPanePos) - self._outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self._outlnPanePos) + sec = "Sizes" + self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize) + self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize) + self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols) + self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos) + self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos) + self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos) # Project - cnfSec = "Project" - self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj) - self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc) - self.emphLabels = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels) - self._backupPath = theConf.rdPath(cnfSec, "backuppath", self._backupPath) - self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose) - self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup) + sec = "Project" + self.autoSaveProj = conf.rdInt(sec, "autosaveproject", self.autoSaveProj) + self.autoSaveDoc = conf.rdInt(sec, "autosavedoc", self.autoSaveDoc) + self.emphLabels = conf.rdBool(sec, "emphlabels", self.emphLabels) + self._backupPath = conf.rdPath(sec, "backuppath", self._backupPath) + self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose) + self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup) # Editor - cnfSec = "Editor" - self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont) - self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize) - self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth) - self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin) - self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth) - self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth) - self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter) - self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify) - self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect) - self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace) - self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote) - self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote) - self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash) - self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots) - self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd) - self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll) - self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos) - self.fmtSQuoteOpen = theConf.rdStr(cnfSec, "fmtsquoteopen", self.fmtSQuoteOpen) - self.fmtSQuoteClose = theConf.rdStr(cnfSec, "fmtsquoteclose", self.fmtSQuoteClose) - self.fmtDQuoteOpen = theConf.rdStr(cnfSec, "fmtdquoteopen", self.fmtDQuoteOpen) - self.fmtDQuoteClose = theConf.rdStr(cnfSec, "fmtdquoteclose", self.fmtDQuoteClose) - self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore) - self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter) - self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin) - self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage) - self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces) - self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings) - self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces) - self.wordCountTimer = theConf.rdFlt(cnfSec, "wordcounttimer", self.wordCountTimer) - self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit) - self.incNotesWCount = theConf.rdBool(cnfSec, "incnoteswcount", self.incNotesWCount) - self.showFullPath = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath) - self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes) - self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote) - self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote) - self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph) - self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle) - self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime) + sec = "Editor" + self.textFont = conf.rdStr(sec, "textfont", self.textFont) + self.textSize = conf.rdInt(sec, "textsize", self.textSize) + self.textWidth = conf.rdInt(sec, "width", self.textWidth) + self.textMargin = conf.rdInt(sec, "margin", self.textMargin) + self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth) + self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth) + self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter) + self.doJustify = conf.rdBool(sec, "justify", self.doJustify) + self.autoSelect = conf.rdBool(sec, "autoselect", self.autoSelect) + self.doReplace = conf.rdBool(sec, "autoreplace", self.doReplace) + self.doReplaceSQuote = conf.rdBool(sec, "repsquotes", self.doReplaceSQuote) + self.doReplaceDQuote = conf.rdBool(sec, "repdquotes", self.doReplaceDQuote) + self.doReplaceDash = conf.rdBool(sec, "repdash", self.doReplaceDash) + self.doReplaceDots = conf.rdBool(sec, "repdots", self.doReplaceDots) + self.scrollPastEnd = conf.rdInt(sec, "scrollpastend", self.scrollPastEnd) + self.autoScroll = conf.rdBool(sec, "autoscroll", self.autoScroll) + self.autoScrollPos = conf.rdInt(sec, "autoscrollpos", self.autoScrollPos) + self.fmtSQuoteOpen = conf.rdStr(sec, "fmtsquoteopen", self.fmtSQuoteOpen) + self.fmtSQuoteClose = conf.rdStr(sec, "fmtsquoteclose", self.fmtSQuoteClose) + self.fmtDQuoteOpen = conf.rdStr(sec, "fmtdquoteopen", self.fmtDQuoteOpen) + self.fmtDQuoteClose = conf.rdStr(sec, "fmtdquoteclose", self.fmtDQuoteClose) + self.fmtPadBefore = conf.rdStr(sec, "fmtpadbefore", self.fmtPadBefore) + self.fmtPadAfter = conf.rdStr(sec, "fmtpadafter", self.fmtPadAfter) + self.fmtPadThin = conf.rdBool(sec, "fmtpadthin", self.fmtPadThin) + self.spellLanguage = conf.rdStr(sec, "spellcheck", self.spellLanguage) + self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces) + self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings) + self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces) + self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer) + self.bigDocLimit = conf.rdInt(sec, "bigdoclimit", self.bigDocLimit) + self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount) + self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath) + self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes) + self.allowOpenSQuote = conf.rdBool(sec, "allowopensquote", self.allowOpenSQuote) + self.allowOpenDQuote = conf.rdBool(sec, "allowopendquote", self.allowOpenDQuote) + self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph) + self.stopWhenIdle = conf.rdBool(sec, "stopwhenidle", self.stopWhenIdle) + self.userIdleTime = conf.rdInt(sec, "useridletime", self.userIdleTime) # State - cnfSec = "State" - self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel) - self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments) - self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis) - self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase) - self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord) - self.searchRegEx = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx) - self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop) - self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile) - self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap) + sec = "State" + self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel) + self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments) + self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis) + self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase) + self.searchWord = conf.rdBool(sec, "searchword", self.searchWord) + self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx) + self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop) + self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile) + self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap) # Deprecated Settings or Locations as of 2.0 # ToDo: These will be loaded for a few minor releases until the users have converted them - self.guiFont = theConf.rdStr("Main", "guifont", self.guiFont) - self.guiFontSize = theConf.rdInt("Main", "guifontsize", self.guiFontSize) - self.guiLocale = theConf.rdStr("Main", "guilang", self.guiLocale) - self._backupPath = theConf.rdPath("Backup", "backuppath", self._backupPath) - self.backupOnClose = theConf.rdBool("Backup", "backuponclose", self.backupOnClose) - self.askBeforeBackup = theConf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup) - fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", []) - fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", []) + self.guiFont = conf.rdStr("Main", "guifont", self.guiFont) + self.guiFontSize = conf.rdInt("Main", "guifontsize", self.guiFontSize) + self.guiLocale = conf.rdStr("Main", "guilang", self.guiLocale) + self._backupPath = conf.rdPath("Backup", "backuppath", self._backupPath) + self.backupOnClose = conf.rdBool("Backup", "backuponclose", self.backupOnClose) + self.askBeforeBackup = conf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup) + fmtSingleQuotes = conf.rdStrList(sec, "fmtsinglequote", []) + fmtDoubleQuotes = conf.rdStrList(sec, "fmtdoublequote", []) if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2: self.fmtSQuoteOpen = fmtSingleQuotes[0] @@ -631,9 +646,6 @@ class Config: # Check Values # ============ - # Check Certain Values for None - self.spellLanguage = self._checkNone(self.spellLanguage) - # If we're using straight quotes, disable auto-replace if self.fmtSQuoteOpen == self.fmtSQuoteClose == "'" and self.doReplaceSQuote: logger.info("Using straight single quotes, so disabling auto-replace") @@ -645,18 +657,17 @@ class Config: return True - def saveConfig(self): - """Save the current preferences to file. - """ + def saveConfig(self) -> bool: + """Save the current preferences to file.""" logger.debug("Saving config file") - theConf = NWConfigParser() + conf = NWConfigParser() - theConf["Meta"] = { + conf["Meta"] = { "timestamp": formatTimeStamp(time()), } - theConf["Main"] = { + conf["Main"] = { "theme": str(self.guiTheme), "syntax": str(self.guiSyntax), "font": str(self.guiFont), @@ -668,7 +679,7 @@ class Config: "lastpath": str(self._lastPath), } - theConf["Sizes"] = { + conf["Sizes"] = { "mainwindow": self._packList(self._mainWinSize), "preferences": self._packList(self._prefsWinSize), "projloadcols": self._packList(self._projLoadCols), @@ -677,16 +688,16 @@ class Config: "outlinepane": self._packList(self._outlnPanePos), } - theConf["Project"] = { + conf["Project"] = { "autosaveproject": str(self.autoSaveProj), "autosavedoc": str(self.autoSaveDoc), "emphlabels": str(self.emphLabels), - "backuppath": str(self._backupPath or ""), + "backuppath": str(self._backupPath), "backuponclose": str(self.backupOnClose), "askbeforebackup": str(self.askBeforeBackup), } - theConf["Editor"] = { + conf["Editor"] = { "textfont": str(self.textFont), "textsize": str(self.textSize), "width": str(self.textWidth), @@ -727,7 +738,7 @@ class Config: "useridletime": str(self.userIdleTime), } - theConf["State"] = { + conf["State"] = { "showrefpanel": str(self.showRefPanel), "viewcomments": str(self.viewComments), "viewsynopsis": str(self.viewSynopsis), @@ -743,7 +754,7 @@ class Config: cnfPath = self._confPath / nwFiles.CONF_FILE try: with open(cnfPath, mode="w", encoding="utf-8") as outFile: - theConf.write(outFile) + conf.write(outFile) except Exception as exc: logger.error("Could not save config file") logException() @@ -758,26 +769,14 @@ class Config: # Internal Functions ## - def _packList(self, inData): + def _packList(self, data: list) -> str: """Pack a list of items into a comma-separated string for saving to the config file. """ - return ", ".join([str(inVal) for inVal in inData]) + return ", ".join([str(inVal) for inVal in data]) - def _checkNone(self, checkVal): - """Return a NoneType if the value corresponds to None, otherwise - return the value unchanged. - """ - if checkVal is None: - return None - if isinstance(checkVal, str): - if checkVal.lower() == "none": - return None - return checkVal - - def _checkOptionalPackages(self): - """Check if we have the optional packages used by some features. - """ + def _checkOptionalPackages(self) -> None: + """Check optional packages used by some features.""" try: import enchant # noqa: F401 except ImportError: @@ -793,14 +792,13 @@ class Config: class RecentProjects: - def __init__(self, config): + def __init__(self, config: Config) -> None: self._conf = config self._data = {} return - def loadCache(self): - """Load the cache file for recent projects. - """ + def loadCache(self) -> bool: + """Load the cache file for recent projects.""" self._data = {} cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE) @@ -823,9 +821,8 @@ class RecentProjects: return True - def saveCache(self): - """Save the cache dictionary of recent projects. - """ + def saveCache(self) -> bool: + """Save the cache dictionary of recent projects.""" cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE) cacheTemp = cacheFile.with_suffix(".tmp") try: @@ -839,27 +836,27 @@ class RecentProjects: return True - def listEntries(self): - """List all items in the cache. - """ - return [(k, e["title"], e["words"], e["time"]) for k, e in self._data.items()] + def listEntries(self) -> list[tuple[str, str, int, int]]: + """List all items in the cache.""" + return [ + (str(k), str(e["title"]), checkInt(e["words"], 0), checkInt(e["time"], 0)) + for k, e in self._data.items() + ] - def update(self, projPath, projTitle, wordCount, saveTime): - """Add or update recent cache information on a given project. - """ - self._data[str(projPath)] = { - "title": projTitle, - "words": int(wordCount), - "time": int(saveTime), + def update(self, path: str | Path, title: str, words: int, saved: float | int) -> None: + """Add or update recent cache information on a given project.""" + self._data[str(path)] = { + "title": title, + "words": int(words), + "time": int(saved), } self.saveCache() return - def remove(self, projPath): - """Try to remove a path from the recent projects cache. - """ - if self._data.pop(str(projPath), None) is not None: - logger.debug("Removed recent: %s", projPath) + def remove(self, path: str | Path) -> None: + """Try to remove a path from the recent projects cache.""" + if self._data.pop(str(path), None) is not None: + logger.debug("Removed recent: %s", path) self.saveCache() return diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index ff869351..64196ed1 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -153,7 +153,7 @@ class BuildSettings: The settings can be packed/unpacked to/from a dictionary for JSON. """ - def __init__(self): + def __init__(self) -> None: self._name = "" self._uuid = str(uuid.uuid4()) self._path = Path.home() @@ -239,12 +239,12 @@ class BuildSettings: # Setters ## - def setName(self, name: str): + def setName(self, name: str) -> None: """Set the build setting display name.""" self._name = str(name) return - def setBuildID(self, value: str | uuid.UUID): + def setBuildID(self, value: str | uuid.UUID) -> None: """Set a UUID build ID.""" value = checkUuid(value, "") if not value: @@ -253,7 +253,7 @@ class BuildSettings: self._uuid = value return - def setLastPath(self, path: Path | str | None): + def setLastPath(self, path: Path | str | None) -> None: """Set the last used build path.""" if isinstance(path, str): path = Path(path) @@ -264,41 +264,41 @@ class BuildSettings: self._changed = True return - def setLastBuildName(self, name: str): + def setLastBuildName(self, name: str) -> None: """Set the last used build name.""" self._build = str(name).strip() self._changed = True return - def setLastFormat(self, value: nwBuildFmt): + def setLastFormat(self, value: nwBuildFmt) -> None: """Set the last used build format.""" if isinstance(value, nwBuildFmt): self._format = value self._changed = True return - def setFiltered(self, tHandle: str): + def setFiltered(self, tHandle: str) -> None: """Set an item as filtered.""" self._excluded.discard(tHandle) self._included.discard(tHandle) self._changed = True return - def setIncluded(self, tHandle: str): + def setIncluded(self, tHandle: str) -> None: """Set an item as explicitly included.""" self._excluded.discard(tHandle) self._included.add(tHandle) self._changed = True return - def setExcluded(self, tHandle: str): + def setExcluded(self, tHandle: str) -> None: """Set an item as explicitly excluded.""" self._excluded.add(tHandle) self._included.discard(tHandle) self._changed = True return - def setAllowRoot(self, tHandle: str, state: bool): + def setAllowRoot(self, tHandle: str, state: bool) -> None: """Set a specific root folder as allowed or not.""" if state is True: self._skipRoot.discard(tHandle) @@ -386,7 +386,7 @@ class BuildSettings: return result - def resetChangedState(self): + def resetChangedState(self) -> None: """Reset the changed status of the settings object. This must be called when the changes have been safely saved or passed on. """ @@ -410,7 +410,7 @@ class BuildSettings: } } - def unpack(self, data: dict): + def unpack(self, data: dict) -> None: """Unpack a dictionary and populate the class.""" settings = data.get("settings", {}) content = data.get("content", {}) @@ -454,13 +454,13 @@ class BuildCollection: project folder. """ - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._builds = {} self._loadCollection() return - def __len__(self): + def __len__(self) -> int: """Return the number of builds.""" return len(self._builds) @@ -476,7 +476,7 @@ class BuildCollection: build.unpack(self._builds[buildID]) return build - def setBuild(self, build: BuildSettings): + def setBuild(self, build: BuildSettings) -> None: """Set build settings data in the collection.""" if isinstance(build, BuildSettings): buildID = build.buildID @@ -484,7 +484,7 @@ class BuildCollection: self._saveCollection() return - def removeBuild(self, buildID: str): + def removeBuild(self, buildID: str) -> None: """Remove the a build from the collection.""" self._builds.pop(buildID, None) self._saveCollection() diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index 114c92ff..094c8c55 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -80,7 +80,7 @@ class DocMerger: and a new doc label. Calling this function resets the class. """ srcItem = self._project.tree[srcHandle] - if srcItem is None: + if srcItem is None or srcItem.itemParent is None: return None newHandle = self._project.newFile(docLabel, srcItem.itemParent) @@ -210,7 +210,7 @@ class DocSplitter: """An iterator that will write each document in the buffer, and return its new handle, parent handle, and sibling handle. """ - if self._srcHandle is None or self._srcItem is None: + if self._srcHandle is None or self._srcItem is None or self._parHandle is None: return pHandle = self._parHandle @@ -385,9 +385,10 @@ class ProjectBuilder: aDoc = project.storage.getDocument(hChapter) aDoc.writeDocument(f"## {lblNewChapter}\n\n") - hScene = project.newFile(lblNewScene, hChapter) - aDoc = project.storage.getDocument(hScene) - aDoc.writeDocument(f"### {lblNewScene}\n\n") + if hChapter: + hScene = project.newFile(lblNewScene, hChapter) + aDoc = project.storage.getDocument(hScene) + aDoc.writeDocument(f"### {lblNewScene}\n\n") project.newRoot(nwItemClass.PLOT) project.newRoot(nwItemClass.CHARACTER) @@ -418,7 +419,7 @@ class ProjectBuilder: aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n") # Create chapter scenes - if numScenes > 0: + if numScenes > 0 and cHandle: for sc in range(numScenes): scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}") sHandle = project.newFile(scTitle, cHandle) diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index c3301e0f..f5020bba 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -54,7 +54,7 @@ class NWBuildDocument: __slots__ = ("_project", "_build", "_queue", "_error", "_cache") - def __init__(self, project: NWProject, build: BuildSettings): + def __init__(self, project: NWProject, build: BuildSettings) -> None: self._project = project self._build = build self._queue = [] @@ -91,12 +91,12 @@ class NWBuildDocument: # Methods ## - def addDocument(self, tHandle: str): + def addDocument(self, tHandle: str) -> None: """Add a document to the build queue manually.""" self._queue.append(tHandle) return - def queueAll(self): + def queueAll(self) -> None: """Queue all document as defined by the build settings.""" self._queue = [] filtered = self._build.buildItemFilter(self._project) diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index 88450946..c1b2ea2a 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -1,7 +1,6 @@ """ novelWriter – Project Wrapper ============================= -The parent class for a novelWriter project File History: Created: 2018-09-29 [0.0.1] @@ -416,14 +415,6 @@ class NWProject(QObject): logger.info("Backing up project") self.mainGui.setStatus(self.tr("Backing up project ...")) - backupPath = CONFIG.backupPath() - if not isinstance(backupPath, Path): - self.mainGui.makeAlert(self.tr( - "Cannot backup project because no valid backup path is set. " - "Please set a valid backup location in Preferences." - ), level=nwAlert.ERROR) - return False - if not self._data.name: self.mainGui.makeAlert(self.tr( "Cannot backup project because no project name is set. " @@ -432,9 +423,10 @@ class NWProject(QObject): return False cleanName = makeFileNameSafe(self._data.name) + backupPath = CONFIG.backupPath() baseDir = backupPath / cleanName try: - baseDir.mkdir(exist_ok=True) + baseDir.mkdir(exist_ok=True, parents=True) except Exception as exc: self.mainGui.makeAlert(self.tr( "Could not create backup folder." @@ -444,11 +436,12 @@ class NWProject(QObject): timeStamp = formatTimeStamp(time(), fileSafe=True) archName = baseDir / f"{cleanName} {timeStamp}.zip" if self._storage.zipIt(archName, compression=2): - size = archName.stat().st_size + size = formatInt(archName.stat().st_size) if doNotify: - self.mainGui.makeAlert(self.tr( - "Backup archive file written to: {0} [{1}B]" - ).format(str(archName), formatInt(size))) + self.mainGui.makeAlert( + self.tr("Created a backup of your project of size {0}B.").format(size), + info=self.tr("Path: {0}").format(str(backupPath)) + ) else: self.mainGui.makeAlert(self.tr( "Could not write backup archive." diff --git a/novelwriter/core/projectdata.py b/novelwriter/core/projectdata.py index 7deb8d0f..9b0cc36c 100644 --- a/novelwriter/core/projectdata.py +++ b/novelwriter/core/projectdata.py @@ -26,13 +26,16 @@ from __future__ import annotations import uuid import logging -from typing import Any +from typing import TYPE_CHECKING, Any from novelwriter.common import ( checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified ) from novelwriter.core.status import NWStatus +if TYPE_CHECKING: # pragma: no cover + from novelwriter.core.project import NWProject + logger = logging.getLogger(__name__) @@ -43,9 +46,9 @@ class NWProjectData: the list of project items. """ - def __init__(self, theProject): + def __init__(self, project: NWProject) -> None: - self.theProject = theProject + self._project = project # Project Meta self._uuid = "" @@ -184,16 +187,16 @@ class NWProjectData: # Methods ## - def incSaveCount(self): + def incSaveCount(self) -> None: """Increment the save count by one.""" self._saveCount += 1 - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def incAutoCount(self): + def incAutoCount(self) -> None: """Increment the auto save count by one.""" self._autoCount += 1 - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return ## @@ -208,93 +211,93 @@ class NWProjectData: # Setters ## - def setUuid(self, value: Any): + def setUuid(self, value: Any) -> None: """Set the project id.""" value = checkUuid(value, "") if not value: self._uuid = str(uuid.uuid4()) elif value != self._uuid: self._uuid = value - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setName(self, value: str | None): + def setName(self, value: str | None) -> None: """Set a new project name.""" if value != self._name: self._name = simplified(str(value or "")) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setTitle(self, value: str | None): + def setTitle(self, value: str | None) -> None: """Set a new novel title.""" if value != self._title: self._title = simplified(str(value or "")) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setAuthor(self, value: str | None): + def setAuthor(self, value: str | None) -> None: """Set the author value.""" if value != self._title: self._author = simplified(str(value or "")) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setSaveCount(self, value: Any): + def setSaveCount(self, value: Any) -> None: """Set the save count from last session.""" self._saveCount = checkInt(value, 0) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setAutoCount(self, value: Any): + def setAutoCount(self, value: Any) -> None: """Set the auto save count from last session.""" self._autoCount = checkInt(value, 0) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setEditTime(self, value: Any): + def setEditTime(self, value: Any) -> None: """Set the edit time from last session.""" self._editTime = checkInt(value, 0) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setDoBackup(self, value: Any): + def setDoBackup(self, value: Any) -> None: """Set the do write backup flag.""" if value != self._doBackup: self._doBackup = checkBool(value, False) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setLanguage(self, value: str | None): + def setLanguage(self, value: str | None) -> None: """Set the project language.""" if value != self._language: self._language = checkStringNone(value, None) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setSpellCheck(self, value: Any): + def setSpellCheck(self, value: Any) -> None: """Set the spell check flag.""" if value != self._spellCheck: self._spellCheck = checkBool(value, False) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setSpellLang(self, value: str | None): + def setSpellLang(self, value: str | None) -> None: """Set the spell check language.""" if value != self._spellLang: self._spellLang = checkStringNone(value, None) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setLastHandle(self, value: str | None, component: str): + def setLastHandle(self, value: str | None, component: str) -> None: """Set a last used handle into the handle registry for a given component. """ if isinstance(component, str): self._lastHandle[component] = checkStringNone(value, None) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setLastHandles(self, value: dict): + def setLastHandles(self, value: dict) -> None: """Set the full last handles dictionary to a new set of values. This is intended to be used at project load. """ @@ -302,10 +305,10 @@ class NWProjectData: for key, entry in value.items(): if key in self._lastHandle: self._lastHandle[key] = str(entry) if isHandle(entry) else None - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return - def setInitCounts(self, novel: Any = None, notes: Any = None): + def setInitCounts(self, novel: Any = None, notes: Any = None) -> None: """Set the word count totals for novel and note files.""" if novel is not None: self._initCounts[0] = checkInt(novel, 0) @@ -315,7 +318,7 @@ class NWProjectData: self._currCounts[1] = checkInt(notes, 0) return - def setCurrCounts(self, novel: Any = None, notes: Any = None): + def setCurrCounts(self, novel: Any = None, notes: Any = None) -> None: """Set the word count totals for novel and note files.""" if novel is not None: self._currCounts[0] = checkInt(novel, 0) @@ -323,14 +326,14 @@ class NWProjectData: self._currCounts[1] = checkInt(notes, 0) return - def setAutoReplace(self, value: dict): + def setAutoReplace(self, value: dict) -> None: """Set the auto-replace dictionary.""" if isinstance(value, dict): self._autoReplace = {} for key, entry in value.items(): if isinstance(entry, str): self._autoReplace[key] = simplified(entry) - self.theProject.setProjectChanged(True) + self._project.setProjectChanged(True) return # END Class NWProjectData diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py index 055fe4ef..c12e0dc0 100644 --- a/novelwriter/core/projectxml.py +++ b/novelwriter/core/projectxml.py @@ -110,7 +110,7 @@ class ProjectXMLReader: Rev 1: Drops the titleFormat section of settings. """ - def __init__(self, path): + def __init__(self, path: str | Path) -> None: self._path = Path(path) self._state = XMLReadState.NO_ACTION self._root = "" @@ -236,7 +236,7 @@ class ProjectXMLReader: # Internal Functions ## - def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData): + def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData) -> None: """Parse the project section of the XML file.""" logger.debug("Parsing section") @@ -267,7 +267,7 @@ class ProjectXMLReader: return - def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData): + def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None: """Parse the settings section of the XML file.""" logger.debug("Parsing section") @@ -307,7 +307,9 @@ class ProjectXMLReader: return - def _parseProjectContent(self, xSection: ET.Element, data: NWProjectData, content: list): + def _parseProjectContent( + self, xSection: ET.Element, data: NWProjectData, content: list + ) -> None: """Parse the content section of the XML file.""" logger.debug("Parsing section") @@ -362,7 +364,9 @@ class ProjectXMLReader: return - def _parseProjectContentLegacy(self, xSection: ET.Element, data: NWProjectData, content: list): + def _parseProjectContentLegacy( + self, xSection: ET.Element, data: NWProjectData, content: list + ) -> None: """Parse the content section of the XML file for older versions.""" logger.debug("Parsing section (legacy format)") @@ -438,7 +442,7 @@ class ProjectXMLReader: return - def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus): + def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None: """Parse a status or importance entry.""" for xEntry in xItem: if xEntry.tag == "entry": @@ -447,7 +451,7 @@ class ProjectXMLReader: green = checkInt(xEntry.attrib.get("green", 0), 0) blue = checkInt(xEntry.attrib.get("blue", 0), 0) count = checkInt(xEntry.attrib.get("count", 0), 0) - sObject.write(key, xEntry.text, (red, green, blue), count) + sObject.write(key, xEntry.text or "", (red, green, blue), count) return def _parseDictKeyText(self, xItem: ET.Element) -> dict: @@ -460,7 +464,7 @@ class ProjectXMLReader: result[xEntry.attrib["key"]] = checkString(xEntry.text, "") return result - def _parseDictTagText(self, xItem): + def _parseDictTagText(self, xItem) -> dict: """Parse a dictionary stored with key as the tag and the value as the text property. """ @@ -476,7 +480,7 @@ class ProjectXMLWriter: very latest spec. """ - def __init__(self, path): + def __init__(self, path: str | Path) -> None: self._path = Path(path) self._error = None return @@ -585,13 +589,13 @@ class ProjectXMLWriter: def _packSingleValue( self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None - ): + ) -> None: """Pack a single value into an XML element.""" xItem = ET.SubElement(xParent, name, attrib=attrib or {}) xItem.text = str(value) or "" return - def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict): + def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None: """Pack the entries of a dictionary into an XML element.""" xItem = ET.SubElement(xParent, name) for key, value in data.items(): diff --git a/novelwriter/core/sessions.py b/novelwriter/core/sessions.py index aa9da7e8..dfbf720e 100644 --- a/novelwriter/core/sessions.py +++ b/novelwriter/core/sessions.py @@ -47,7 +47,7 @@ class NWSessionLog: format. That is, one JSON object per line. """ - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._start = 0.0 return @@ -65,7 +65,7 @@ class NWSessionLog: # Methods ## - def startSession(self): + def startSession(self) -> None: """Start the writing session.""" self._start = time() return diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index b5cac3a2..d7d55d91 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -45,7 +45,7 @@ class NWSpellEnchant: between spell check tools. """ - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._dictObj = FakeEnchant() self._userDict = UserDictionary(project) @@ -165,7 +165,7 @@ class NWSpellEnchant: class FakeEnchant: """Fallback for when Enchant is selected, but not installed.""" - def __init__(self): + def __init__(self) -> None: class FakeProvider: name = "" @@ -189,7 +189,7 @@ class FakeEnchant: class UserDictionary: - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._words = set() self._path = None @@ -210,7 +210,7 @@ class UserDictionary: self._words.add(word) return True - def load(self): + def load(self) -> None: """Load the user's dictionary.""" self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE) if not isinstance(self._path, Path): @@ -224,7 +224,7 @@ class UserDictionary: logException() return - def save(self): + def save(self) -> None: """Save the user's dictionary.""" if self._path is None: self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE) diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py index a1e99899..fad14027 100644 --- a/novelwriter/core/status.py +++ b/novelwriter/core/status.py @@ -1,7 +1,6 @@ """ novelWriter – Project Item Status Class ======================================= -Data class for the status/importance settings of a project item File History: Created: 2019-05-19 [0.1.3] @@ -23,16 +22,22 @@ 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 . """ +from __future__ import annotations import random import logging +from typing import TYPE_CHECKING, ItemsView, Iterator, KeysView, Literal, ValuesView + from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor from PyQt5.QtCore import QRectF, Qt from novelwriter import CONFIG from novelwriter.common import minmax, simplified +if TYPE_CHECKING: # pragma: no cover + from typing import TypeGuard # Requires Python 3.10 + logger = logging.getLogger(__name__) @@ -41,9 +46,9 @@ class NWStatus: STATUS = 1 IMPORT = 2 - def __init__(self, type): + def __init__(self, kind: Literal[1, 2]) -> None: - self._type = type + self._type = kind self._store = {} self._default = None @@ -66,7 +71,7 @@ class NWStatus: return - def write(self, key, name, col, count=None): + def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str: """Add or update a status entry. If the key is invalid, a new key is generated. """ @@ -96,10 +101,8 @@ class NWStatus: return key - def remove(self, key): - """Remove an entry in the list, but not if the count is larger - than 0. - """ + def remove(self, key: str) -> bool: + """Remove an entry in the list, except if the count > 0.""" if key not in self._store: return False if self._store[key]["count"] > 0: @@ -116,59 +119,48 @@ class NWStatus: return True - def check(self, value): - """Check the key against the stored status names. - """ + def check(self, value: str) -> str: + """Check the key against the stored status names.""" if self._isKey(value) and value in self._store: return value elif self._default is not None: return self._default - else: - return "" + return "" - def name(self, key): - """Return the name associated with a given key. - """ + def name(self, key: str | None) -> str: + """Return the name associated with a given key.""" if key in self._store: return self._store[key]["name"] elif self._default is not None: return self._store[self._default]["name"] - else: - return "" + return "" - def cols(self, key): - """Return the colours associated with a given key. - """ + def cols(self, key: str | None) -> tuple[int, int, int]: + """Return the colours associated with a given key.""" if key in self._store: return self._store[key]["cols"] elif self._default is not None: return self._store[self._default]["cols"] - else: - return (100, 100, 100) + return 100, 100, 100 - def count(self, key): - """Return the count associated with a given key. - """ + def count(self, key: str | None) -> int: + """Return the count associated with a given key.""" if key in self._store: return self._store[key]["count"] elif self._default is not None: return self._store[self._default]["count"] - else: - return 0 + return 0 - def icon(self, key): - """Return the icon associated with a given key. - """ + def icon(self, key: str | None) -> QIcon: + """Return the icon associated with a given key.""" if key in self._store: return self._store[key]["icon"] elif self._default is not None: return self._store[self._default]["icon"] - else: - return self._defaultIcon + return self._defaultIcon - def reorder(self, order): - """Reorder the items according to list. - """ + def reorder(self, order: list[str]) -> bool: + """Reorder the items according to list.""" if len(order) != len(self._store): logger.error("Length mismatch between new and old order") return False @@ -188,23 +180,20 @@ class NWStatus: return True - def resetCounts(self): - """Clear the counts of references to the status entries. - """ + def resetCounts(self) -> None: + """Clear the counts of references to the status entries.""" for key in self._store: self._store[key]["count"] = 0 return - def increment(self, key): - """Increment the counter for a given entry. - """ + def increment(self, key: str) -> None: + """Increment the counter for a given entry.""" if key in self._store: self._store[key]["count"] += 1 return - def pack(self): - """Pack the status entries into a dictionary. - """ + def pack(self) -> Iterator[tuple[str, dict]]: + """Pack the status entries into a dictionary.""" for key, data in self._store.items(): yield (data["name"], { "key": key, @@ -215,25 +204,22 @@ class NWStatus: }) return - def unpack(self, data): - """Unpack a data dictionary and set the class values. - """ + def unpack(self, data: dict) -> None: + """Unpack a data dictionary and set the class values.""" self._store = {} self._default = None - for key, entry in data.items(): label = entry.get("label", "") colour = entry.get("colour", (100, 100, 100)) count = entry.get("count", 0) self.write(key, label, colour, count) - - return True + return ## # Internal Functions ## - def _newKey(self): + def _newKey(self) -> str: """Generate a new key for a status flag. This method is recursive, but should only fail if there is an issue with the random number generator or the user has added a lot of status @@ -245,9 +231,8 @@ class NWStatus: key = self._newKey() return key - def _isKey(self, value): - """Check if a value is a key or not. - """ + def _isKey(self, value: str | None) -> TypeGuard[str]: + """Check if a value is a key or not.""" if not isinstance(value, str): return False if len(value) != 7: @@ -259,9 +244,8 @@ class NWStatus: return False return True - def _createIcon(self, red, green, blue): - """Generate an icon for a status label. - """ + def _createIcon(self, red: int, green: int, blue: int) -> QIcon: + """Generate an icon for a status label.""" pixmap = QPixmap(self._iPX, self._iPX) pixmap.fill(Qt.transparent) @@ -276,22 +260,22 @@ class NWStatus: # Iterator Bits ## - def __len__(self): + def __len__(self) -> int: return len(self._store) - def __getitem__(self, key): + def __getitem__(self, key: str) -> dict: return self._store[key] - def __iter__(self): + def __iter__(self) -> Iterator[dict]: return iter(self._store) - def keys(self): + def keys(self) -> KeysView[str]: return self._store.keys() - def items(self): + def items(self) -> ItemsView[str, dict]: return self._store.items() - def values(self): + def values(self) -> ValuesView[dict]: return self._store.values() # END Class NWStatus diff --git a/novelwriter/core/storage.py b/novelwriter/core/storage.py index 84e4ee35..d8740c0e 100644 --- a/novelwriter/core/storage.py +++ b/novelwriter/core/storage.py @@ -55,7 +55,7 @@ class NWStorage: MODE_INPLACE = 1 MODE_ARCHIVE = 2 - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._storagePath = None self._runtimePath = None @@ -63,7 +63,7 @@ class NWStorage: self._openMode = self.MODE_INACTIVE return - def clear(self): + def clear(self) -> None: """Reset internal variables.""" self._storagePath = None self._runtimePath = None @@ -145,7 +145,7 @@ class NWStorage: return True return True - def closeSession(self): + def closeSession(self) -> None: """Run tasks related to closing the session.""" self.clearLockFile() self.clear() @@ -353,11 +353,11 @@ class _LegacyStorage: file/folder layout to the current project format. """ - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project return - def legacyDataFolder(self, path: Path, child: Path): + def legacyDataFolder(self, path: Path, child: Path) -> None: """Handle the content of a legacy data folder from a version 1.0 project. """ @@ -396,7 +396,7 @@ class _LegacyStorage: return - def deprecatedFiles(self, path: Path): + def deprecatedFiles(self, path: Path) -> None: """Handle files that are no longer used by novelWriter.""" self._convertOldWordList( # Changed in 2.1 Beta 1 path / "meta" / "wordlist.txt", @@ -440,7 +440,7 @@ class _LegacyStorage: # Internal Functions ## - def _convertOldWordList(self, wordList: Path, wordJson: Path): + def _convertOldWordList(self, wordList: Path, wordJson: Path) -> None: """Convert the old word list plain text file to new format.""" if wordJson.exists() or not wordList.exists(): # If the new file already exists, we won't overwrite it @@ -466,7 +466,7 @@ class _LegacyStorage: return - def _convertOldLogFile(self, sessLog: Path, sessJson: Path): + def _convertOldLogFile(self, sessLog: Path, sessJson: Path) -> None: """Convert the old text log file format to the new JSON Lines format. """ @@ -507,7 +507,7 @@ class _LegacyStorage: return - def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path): + def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path) -> None: """Convert the old options state file format to the format.""" if optsNew.exists() or not optsOld.exists(): # If the new file already exists, we won't overwrite it diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 20c01701..8459ae59 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -49,12 +49,12 @@ class ToHtml(Tokenizer): M_EXPORT = 1 # Tweak output for saving to HTML or printing M_EBOOK = 2 # Tweak output for converting to epub - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: super().__init__(project) self._genMode = self.M_EXPORT self._cssStyles = True - self._fullHTML = [] + self._fullHTML: list[str] = [] # Internals self._trMap = {} @@ -67,14 +67,14 @@ class ToHtml(Tokenizer): ## @property - def fullHTML(self): + def fullHTML(self) -> list[str]: return self._fullHTML ## # Setters ## - def setPreview(self, doComments: bool, doSynopsis: bool): + def setPreview(self, doComments: bool, doSynopsis: bool) -> None: """If we're using this class to generate markdown preview, we need to make a few changes to formatting, which is managed by these flags. @@ -85,14 +85,14 @@ class ToHtml(Tokenizer): self._doSynopsis = doSynopsis return - def setStyles(self, cssStyles: bool): + def setStyles(self, cssStyles: bool) -> None: """Enable or disable CSS styling. Some elements may still have class tags. """ self._cssStyles = cssStyles return - def setReplaceUnicode(self, doReplace: bool): + def setReplaceUnicode(self, doReplace: bool) -> None: """Set the translation map to either minimal or full unicode for html entities replacement. """ @@ -113,7 +113,7 @@ class ToHtml(Tokenizer): """Return the size of the full HTML result.""" return sum([len(x) for x in self._fullHTML]) - def doPreProcessing(self): + def doPreProcessing(self) -> None: """Extend the auto-replace to also properly encode some unicode characters into their respective HTML entities. """ @@ -121,7 +121,7 @@ class ToHtml(Tokenizer): self._text = self._text.translate(self._trMap) return - def doConvert(self): + def doConvert(self) -> None: """Convert the list of text tokens into a HTML document saved to _result. """ @@ -299,7 +299,7 @@ class ToHtml(Tokenizer): return - def saveHtml5(self, path: str | Path): + def saveHtml5(self, path: str | Path) -> None: """Save the data to an HTML file.""" with open(path, mode="w", encoding="utf-8") as fObj: fObj.write(( @@ -326,7 +326,7 @@ class ToHtml(Tokenizer): logger.info("Wrote file: %s", path) return - def saveHtmlJson(self, path: str | Path): + def saveHtmlJson(self, path: str | Path) -> None: """Save the data to a JSON file.""" timeStamp = time() data = { @@ -347,7 +347,7 @@ class ToHtml(Tokenizer): logger.info("Wrote file: %s", path) return - def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "): + def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None: """Replace tabs with spaces in the html.""" htmlText = [] tabSpace = spaceChar*nSpaces @@ -357,7 +357,7 @@ class ToHtml(Tokenizer): self._fullHTML = htmlText return - def getStyleSheet(self) -> list: + def getStyleSheet(self) -> list[str]: """Generate a stylesheet for the current settings.""" styles = [] if not self._cssStyles: diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index e1a9aea4..f5d54d29 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -44,7 +44,7 @@ from novelwriter.core.project import NWProject logger = logging.getLogger(__name__) -def stripEscape(text): +def stripEscape(text) -> str: """Helper function to strip escaped Markdown characters from paragraph text. """ @@ -100,7 +100,7 @@ class Tokenizer(ABC): A_IND_L = 0x0100 # Left indentation A_IND_R = 0x0200 # Right indentation - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project @@ -191,116 +191,116 @@ class Tokenizer(ABC): # Setters ## - def setTitleFormat(self, hFormat: str): + def setTitleFormat(self, hFormat: str) -> None: """Set the title format pattern.""" self._fmtTitle = hFormat.strip() return - def setChapterFormat(self, hFormat: str): + def setChapterFormat(self, hFormat: str) -> None: """Set the chapert format pattern.""" self._fmtChapter = hFormat.strip() return - def setUnNumberedFormat(self, hFormat: str): + def setUnNumberedFormat(self, hFormat: str) -> None: """Set the unnumbered format pattern.""" self._fmtUnNum = hFormat.strip() return - def setSceneFormat(self, hFormat: str, hide: bool): + def setSceneFormat(self, hFormat: str, hide: bool) -> None: """Set the scene format pattern and hidden status.""" self._fmtScene = hFormat.strip() self._hideScene = hide return - def setSectionFormat(self, hFormat: str, hide: bool): + def setSectionFormat(self, hFormat: str, hide: bool) -> None: """Set the section format pattern and hidden status.""" self._fmtSection = hFormat.strip() self._hideSection = hide return - def setFont(self, family: str, size: int, isFixed: bool = False): + def setFont(self, family: str, size: int, isFixed: bool = False) -> None: """Set the build font.""" self._textFont = family self._textSize = round(int(size)) self._textFixed = isFixed return - def setLineHeight(self, height: float): + def setLineHeight(self, height: float) -> None: """Set the line height between 0.5 and 5.0.""" self._lineHeight = min(max(float(height), 0.5), 5.0) return - def setBlockIndent(self, indent: float): + def setBlockIndent(self, indent: float) -> None: """Set the block indent between 0.0 and 10.0.""" self._blockIndent = min(max(float(indent), 0.0), 10.0) return - def setJustify(self, state: bool): + def setJustify(self, state: bool) -> None: """Enable or disable text justification.""" self._doJustify = state return - def setTitleMargins(self, upper: float, lower: float): + def setTitleMargins(self, upper: float, lower: float) -> None: """Set the upper and lower title margin.""" self._marginTitle = (float(upper), float(lower)) return - def setHead1Margins(self, upper: float, lower: float): + def setHead1Margins(self, upper: float, lower: float) -> None: """Set the upper and lower header 1 margin.""" self._marginHead1 = (float(upper), float(lower)) return - def setHead2Margins(self, upper: float, lower: float): + def setHead2Margins(self, upper: float, lower: float) -> None: """Set the upper and lower header 2 margin.""" self._marginHead2 = (float(upper), float(lower)) return - def setHead3Margins(self, upper: float, lower: float): + def setHead3Margins(self, upper: float, lower: float) -> None: """Set the upper and lower header 3 margin.""" self._marginHead3 = (float(upper), float(lower)) return - def setHead4Margins(self, upper: float, lower: float): + def setHead4Margins(self, upper: float, lower: float) -> None: """Set the upper and lower header 4 margin.""" self._marginHead4 = (float(upper), float(lower)) return - def setTextMargins(self, upper: float, lower: float): + def setTextMargins(self, upper: float, lower: float) -> None: """Set the upper and lower text margin.""" self._marginText = (float(upper), float(lower)) return - def setMetaMargins(self, upper: float, lower: float): + def setMetaMargins(self, upper: float, lower: float) -> None: """Set the upper and lower meta text margin.""" self._marginMeta = (float(upper), float(lower)) return - def setLinkHeaders(self, state: bool): + def setLinkHeaders(self, state: bool) -> None: """Enable or disable adding an anchor before headers.""" self._linkHeaders = state return - def setBodyText(self, state: bool): + def setBodyText(self, state: bool) -> None: """Include body text in build.""" self._doBodyText = state return - def setSynopsis(self, state: bool): + def setSynopsis(self, state: bool) -> None: """Include synopsis comments in build.""" self._doSynopsis = state return - def setComments(self, state: bool): + def setComments(self, state: bool) -> None: """Include comments in build.""" self._doComments = state return - def setKeywords(self, state: bool): + def setKeywords(self, state: bool) -> None: """Include keywords in build.""" self._doKeywords = state return - def setKeepMarkdown(self, state: bool): + def setKeepMarkdown(self, state: bool) -> None: """Keep original markdown during build.""" self._keepMarkdown = state return @@ -310,7 +310,7 @@ class Tokenizer(ABC): ## @abstractmethod - def doConvert(self): + def doConvert(self) -> None: raise NotImplementedError def addRootHeading(self, tHandle: str) -> bool: @@ -365,7 +365,7 @@ class Tokenizer(ABC): return True - def doPreProcessing(self): + def doPreProcessing(self) -> None: """Run trough the various replace dictionaries.""" # Process the user's auto-replace dictionary autoReplace = self._project.data.autoReplace @@ -382,7 +382,7 @@ class Tokenizer(ABC): return - def tokenizeText(self): + def tokenizeText(self) -> None: """Scan the text for either lines starting with specific characters that indicate headers, comments, commands etc, or just contain plain text. In the case of plain text, apply the @@ -742,14 +742,14 @@ class Tokenizer(ABC): return True - def saveRawMarkdown(self, path: str | Path): + def saveRawMarkdown(self, path: str | Path) -> None: """Save the raw text to a plain text file.""" with open(path, mode="w", encoding="utf-8") as outFile: for nwdPage in self._allMarkdown: outFile.write(nwdPage) return - def saveRawMarkdownJSON(self, path: str | Path): + def saveRawMarkdownJSON(self, path: str | Path) -> None: """Save the raw text to a JSON file.""" timeStamp = time() data = { @@ -773,30 +773,30 @@ class Tokenizer(ABC): class HeadingFormatter: - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: self._project = project self._chCount = 0 self._scChCount = 0 self._scAbsCount = 0 return - def incChapter(self): + def incChapter(self) -> None: """Increment the chapter counter.""" self._chCount += 1 return - def incScene(self): + def incScene(self) -> None: """Increment the scene counters.""" self._scChCount += 1 self._scAbsCount += 1 return - def resetScene(self): + def resetScene(self) -> None: """Reset the chapter scene counter.""" self._scChCount = 0 return - def apply(self, hFormat: str, text: str): + def apply(self, hFormat: str, text: str) -> str: """Apply formatting to a specific heading.""" hFormat = hFormat.replace(nwHeadFmt.TITLE, text) hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount)) diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py index e634de74..c9859318 100644 --- a/novelwriter/core/tomd.py +++ b/novelwriter/core/tomd.py @@ -45,12 +45,10 @@ class ToMarkdown(Tokenizer): M_STD = 0 # Standard Markdown M_GH = 1 # GitHub Markdown - def __init__(self, project: NWProject): + def __init__(self, project: NWProject) -> None: super().__init__(project) - self._genMode = self.M_STD - self._fullMD = [] - + self._fullMD: list[str] = [] return ## @@ -58,7 +56,7 @@ class ToMarkdown(Tokenizer): ## @property - def fullMD(self) -> list: + def fullMD(self) -> list[str]: """Return the markdown as a list.""" return self._fullMD @@ -66,11 +64,11 @@ class ToMarkdown(Tokenizer): # Setters ## - def setStandardMarkdown(self): + def setStandardMarkdown(self) -> None: self._genMode = self.M_STD return - def setGitHubMarkdown(self): + def setGitHubMarkdown(self) -> None: self._genMode = self.M_GH return @@ -82,7 +80,7 @@ class ToMarkdown(Tokenizer): """Return the size of the full Markdown result.""" return sum([len(x) for x in self._fullMD]) - def doConvert(self): + def doConvert(self) -> None: """Convert the list of text tokens into a HTML document saved to theResult. """ @@ -175,14 +173,14 @@ class ToMarkdown(Tokenizer): return - def saveMarkdown(self, path: str | Path): + def saveMarkdown(self, path: str | Path) -> None: """Save the data to a plain text file.""" with open(path, mode="w", encoding="utf-8") as outFile: outFile.write("".join(self._fullMD)) logger.info("Wrote file: %s", path) return - def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "): + def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None: """Replace tabs with spaces.""" spaces = spaceChar*nSpaces self._fullMD = [p.replace("\t", spaces) for p in self._fullMD] diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index 93b8afac..b69af17b 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -98,7 +98,7 @@ class ToOdt(Tokenizer): Test with: https://odfvalidator.org/ """ - def __init__(self, project: NWProject, isFlat: bool): + def __init__(self, project: NWProject, isFlat: bool) -> None: super().__init__(project) self._isFlat = isFlat # Flat: .fodt, otherwise .odt @@ -188,7 +188,7 @@ class ToOdt(Tokenizer): # Setters ## - def setLanguage(self, language: str): + def setLanguage(self, language: str) -> None: """Set language for the document.""" if language: langBits = language.split("_") @@ -197,7 +197,7 @@ class ToOdt(Tokenizer): self._dCountry = langBits[1] return - def setColourHeaders(self, state: bool): + def setColourHeaders(self, state: bool) -> None: """Enable/disable coloured headings and comments.""" self._colourHead = state return @@ -205,7 +205,7 @@ class ToOdt(Tokenizer): def setPageLayout( self, width: int | float, height: int | float, top: int | float, bottom: int | float, left: int | float, right: int | float - ): + ) -> None: """Set the document page size and margins in millimetres.""" self._mDocWidth = f"{width/10.0:.3f}cm" self._mDocHeight = f"{height/10.0:.3f}cm" @@ -219,7 +219,7 @@ class ToOdt(Tokenizer): # Class Methods ## - def initDocument(self): + def initDocument(self) -> None: """Initialises a new open document XML tree.""" # Initialise Variables # ==================== @@ -381,7 +381,7 @@ class ToOdt(Tokenizer): return - def doConvert(self): + def doConvert(self) -> None: """Convert the list of text tokens into XML elements.""" self._result = "" # Not used, but cleared just in case @@ -599,7 +599,7 @@ class ToOdt(Tokenizer): def _addTextPar( self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "", isHead: bool = False, oLevel: str | None = None - ): + ) -> None: """Add a text paragraph to the text XML element.""" tAttr = {} tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle) @@ -726,7 +726,7 @@ class ToOdt(Tokenizer): # Style Elements ## - def _pageStyles(self): + def _pageStyles(self) -> None: """Set the default page style.""" tAttr = {} tAttr[_mkTag("style", "name")] = "PM1" @@ -756,7 +756,7 @@ class ToOdt(Tokenizer): return - def _defaultStyles(self): + def _defaultStyles(self) -> None: """Set the default styles.""" # Add Paragraph Family Style # ========================== @@ -829,7 +829,7 @@ class ToOdt(Tokenizer): return - def _useableStyles(self): + def _useableStyles(self) -> None: """Set the usable styles.""" # Add Text Body Style # =================== @@ -1002,7 +1002,7 @@ class ToOdt(Tokenizer): return - def _writeHeader(self): + def _writeHeader(self) -> None: """Write the header elements.""" tAttr = {} tAttr[_mkTag("style", "name")] = "Standard" @@ -1048,7 +1048,7 @@ class ODTParagraphStyle: VALID_CLASS = ["text", "chapter"] VALID_WEIGHT = ["normal", "inherit", "bold"] - def __init__(self): + def __init__(self) -> None: # Attributes self._mAttr = { @@ -1087,26 +1087,26 @@ class ODTParagraphStyle: # Attribute Setters ## - def setDisplayName(self, value: str | None): + def setDisplayName(self, value: str | None) -> None: self._mAttr["display-name"][1] = value return - def setParentStyleName(self, value: str | None): + def setParentStyleName(self, value: str | None) -> None: self._mAttr["parent-style-name"][1] = value return - def setNextStyleName(self, value: str | None): + def setNextStyleName(self, value: str | None) -> None: self._mAttr["next-style-name"][1] = value return - def setOutlineLevel(self, value: str | None): + def setOutlineLevel(self, value: str | None) -> None: if value in self.VALID_LEVEL: self._mAttr["default-outline-level"][1] = value else: self._mAttr["default-outline-level"][1] = None return - def setClass(self, value: str | None): + def setClass(self, value: str | None) -> None: if value in self.VALID_CLASS: self._mAttr["class"][1] = value else: @@ -1117,41 +1117,41 @@ class ODTParagraphStyle: # Paragraph Setters ## - def setMarginTop(self, value: str | None): + def setMarginTop(self, value: str | None) -> None: self._pAttr["margin-top"][1] = value return - def setMarginBottom(self, value: str | None): + def setMarginBottom(self, value: str | None) -> None: self._pAttr["margin-bottom"][1] = value return - def setMarginLeft(self, value: str | None): + def setMarginLeft(self, value: str | None) -> None: self._pAttr["margin-left"][1] = value return - def setMarginRight(self, value: str | None): + def setMarginRight(self, value: str | None) -> None: self._pAttr["margin-right"][1] = value return - def setLineHeight(self, value: str | None): + def setLineHeight(self, value: str | None) -> None: self._pAttr["line-height"][1] = value return - def setTextAlign(self, value: str | None): + def setTextAlign(self, value: str | None) -> None: if value in self.VALID_ALIGN: self._pAttr["text-align"][1] = value else: self._pAttr["text-align"][1] = None return - def setBreakBefore(self, value: str | None): + def setBreakBefore(self, value: str | None) -> None: if value in self.VALID_BREAK: self._pAttr["break-before"][1] = value else: self._pAttr["break-before"][1] = None return - def setBreakAfter(self, value: str | None): + def setBreakAfter(self, value: str | None) -> None: if value in self.VALID_BREAK: self._pAttr["break-after"][1] = value else: @@ -1162,30 +1162,30 @@ class ODTParagraphStyle: # Text Setters ## - def setFontName(self, value: str | None): + def setFontName(self, value: str | None) -> None: self._tAttr["font-name"][1] = value return - def setFontFamily(self, value: str | None): + def setFontFamily(self, value: str | None) -> None: self._tAttr["font-family"][1] = value return - def setFontSize(self, value: str | None): + def setFontSize(self, value: str | None) -> None: self._tAttr["font-size"][1] = value return - def setFontWeight(self, value: str | None): + def setFontWeight(self, value: str | None) -> None: if value in self.VALID_WEIGHT: self._tAttr["font-weight"][1] = value else: self._tAttr["font-weight"][1] = None return - def setColor(self, value: str | None): + def setColor(self, value: str | None) -> None: self._tAttr["color"][1] = value return - def setOpacity(self, value: str | None): + def setOpacity(self, value: str | None) -> None: self._tAttr["opacity"][1] = value return @@ -1193,7 +1193,7 @@ class ODTParagraphStyle: # Methods ## - def checkNew(self, refStyle: ODTParagraphStyle): + def checkNew(self, refStyle: ODTParagraphStyle) -> bool: """Check if there are new settings in refStyle that differ from those in the current object. """ @@ -1217,7 +1217,7 @@ class ODTParagraphStyle: ) return sha256(theString.encode()).hexdigest() - def packXML(self, xParent: ET.Element, name: str): + def packXML(self, xParent: ET.Element, name: str) -> None: """Pack the content into an xml element.""" theAttr = {} theAttr[_mkTag("style", "name")] = name @@ -1259,8 +1259,7 @@ class ODTTextStyle: VALID_LSTYLE = ["none", "solid"] VALID_LTYPE = ["none", "single", "double"] - def __init__(self): - + def __init__(self) -> None: # Text Attributes self._tAttr = { "font-weight": ["fo", None], @@ -1268,35 +1267,34 @@ class ODTTextStyle: "text-line-through-style": ["style", None], "text-line-through-type": ["style", None], } - return ## # Setters ## - def setFontWeight(self, value: str | None): + def setFontWeight(self, value: str | None) -> None: if value in self.VALID_WEIGHT: self._tAttr["font-weight"][1] = value else: self._tAttr["font-weight"][1] = None return - def setFontStyle(self, value: str | None): + def setFontStyle(self, value: str | None) -> None: if value in self.VALID_STYLE: self._tAttr["font-style"][1] = value else: self._tAttr["font-style"][1] = None return - def setStrikeStyle(self, value: str | None): + def setStrikeStyle(self, value: str | None) -> None: if value in self.VALID_LSTYLE: self._tAttr["text-line-through-style"][1] = value else: self._tAttr["text-line-through-style"][1] = None return - def setStrikeType(self, value: str | None): + def setStrikeType(self, value: str | None) -> None: if value in self.VALID_LTYPE: self._tAttr["text-line-through-type"][1] = value else: @@ -1307,7 +1305,7 @@ class ODTTextStyle: # Methods ## - def packXML(self, xParent: ET.Element, name: str): + def packXML(self, xParent: ET.Element, name: str) -> None: """Pack the content into an xml element.""" theAttr = {} theAttr[_mkTag("style", "name")] = name @@ -1357,7 +1355,7 @@ class XMLParagraph: object and attribute is written to, """ - def __init__(self, xRoot: ET.Element): + def __init__(self, xRoot: ET.Element) -> None: self._xRoot = xRoot self._xTail = ET.Element("") @@ -1370,7 +1368,7 @@ class XMLParagraph: return - def appendText(self, tText: str): + def appendText(self, tText: str) -> None: """Append text to the XML element. We do this one character at the time in order to be able to process line breaks, tabs and spaces separately. Multiple spaces are concatenated into a @@ -1435,7 +1433,7 @@ class XMLParagraph: return - def appendSpan(self, tText: str, tFmt: str): + def appendSpan(self, tText: str, tFmt: str) -> None: """Append a text span to the XML element. The span is always closed since we do not allow nested spans (like Libre Office). Therefore we return to the root element level when we're done @@ -1449,7 +1447,7 @@ class XMLParagraph: self._nState = X_ROOT_TAIL return - def checkError(self): + def checkError(self) -> tuple[int, str]: """Check that the number of characters written matches the number of characters received. """ @@ -1463,7 +1461,7 @@ class XMLParagraph: # Internal Functions ## - def _processSpaces(self, nSpaces: int): + def _processSpaces(self, nSpaces: int) -> None: """Add spaces to paragraph. The first space is always written as-is (unless it's the first character of the paragraph). The second space uses the dedicated tag for spaces, and from the diff --git a/novelwriter/dialogs/about.py b/novelwriter/dialogs/about.py index 36b8bc99..7907882f 100644 --- a/novelwriter/dialogs/about.py +++ b/novelwriter/dialogs/about.py @@ -1,7 +1,6 @@ """ novelWriter – GUI About Box =========================== -The about novelWriter dialog box File History: Created: 2020-05-21 [0.5.2] @@ -22,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 . """ +from __future__ import annotations import logging import novelwriter @@ -31,8 +31,8 @@ from datetime import datetime from PyQt5.QtGui import QCursor from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget, - QTextBrowser, QLabel + qApp, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTabWidget, + QTextBrowser, QVBoxLayout, QWidget ) from novelwriter import CONFIG @@ -44,15 +44,12 @@ logger = logging.getLogger(__name__) class GuiAbout(QDialog): - def __init__(self, mainGui): - super().__init__(parent=mainGui) + def __init__(self, parent: QWidget): + super().__init__(parent=parent) logger.debug("Create: GuiAbout") self.setObjectName("GuiAbout") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.outerBox = QVBoxLayout() self.innerBox = QHBoxLayout() self.innerBox.setSpacing(CONFIG.pxInt(16)) @@ -63,7 +60,7 @@ class GuiAbout(QDialog): nPx = CONFIG.pxInt(96) self.nwIcon = QLabel() - self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) + self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx))) self.lblName = QLabel("novelWriter") self.lblVers = QLabel(f"v{novelwriter.__version__}") self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x")) @@ -231,12 +228,12 @@ class GuiAbout(QDialog): " color: rgb({kColR},{kColG},{kColB});" "}}\n" ).format( - hColR=self.mainGui.mainTheme.colHead[0], - hColG=self.mainGui.mainTheme.colHead[1], - hColB=self.mainGui.mainTheme.colHead[2], - kColR=self.mainTheme.colKey[0], - kColG=self.mainTheme.colKey[1], - kColB=self.mainTheme.colKey[2], + hColR=CONFIG.theme.colHead[0], + hColG=CONFIG.theme.colHead[1], + hColB=CONFIG.theme.colHead[2], + kColR=CONFIG.theme.colKey[0], + kColG=CONFIG.theme.colKey[1], + kColB=CONFIG.theme.colKey[2], ) self.pageAbout.document().setDefaultStyleSheet(styleSheet) self.pageNotes.document().setDefaultStyleSheet(styleSheet) diff --git a/novelwriter/dialogs/docmerge.py b/novelwriter/dialogs/docmerge.py index 49e3d5f1..0faf9dd9 100644 --- a/novelwriter/dialogs/docmerge.py +++ b/novelwriter/dialogs/docmerge.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Doc Merge Dialog ================================== -Custom dialog class for merging documents. File History: Created: 2020-01-23 [0.4.3] @@ -23,6 +22,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 . """ +from __future__ import annotations import logging @@ -49,9 +49,7 @@ class GuiDocMerge(QDialog): logger.debug("Create: GuiDocMerge") self.setObjectName("GuiDocMerge") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._data = {} @@ -60,9 +58,9 @@ class GuiDocMerge(QDialog): self.headLabel = QLabel("{0}".format(self.tr("Documents to Merge"))) self.helpLabel = NHelpLabel(self.tr( "Drag and drop items to change the order, or uncheck to exclude." - ), self.mainTheme.helpText) + ), CONFIG.theme.helpText) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize hSp = CONFIG.pxInt(12) vSp = CONFIG.pxInt(8) bSp = CONFIG.pxInt(12) @@ -157,11 +155,11 @@ class GuiDocMerge(QDialog): self.listBox.clear() for tHandle in itemList: - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] if nwItem is None or not nwItem.isFileType(): continue - itemIcon = self.mainTheme.getItemIcon( + itemIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading ) diff --git a/novelwriter/dialogs/docsplit.py b/novelwriter/dialogs/docsplit.py index 58396879..9542268f 100644 --- a/novelwriter/dialogs/docsplit.py +++ b/novelwriter/dialogs/docsplit.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Doc Split Dialog ================================== -Custom dialog class for splitting documents. File History: Created: 2020-02-01 [0.4.3] @@ -23,6 +22,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 . """ +from __future__ import annotations import logging @@ -51,9 +51,7 @@ class GuiDocSplit(QDialog): logger.debug("Create: GuiDocSplit") self.setObjectName("GuiDocSplit") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._data = {} self._text = [] @@ -63,16 +61,16 @@ class GuiDocSplit(QDialog): self.headLabel = QLabel("{0}".format(self.tr("Document Headers"))) self.helpLabel = NHelpLabel( self.tr("Select the maximum level to split into files."), - self.mainGui.mainTheme.helpText + CONFIG.theme.helpText ) # Values - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize hSp = CONFIG.pxInt(12) vSp = CONFIG.pxInt(8) bSp = CONFIG.pxInt(12) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3) intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True) docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True) @@ -171,7 +169,7 @@ class GuiDocSplit(QDialog): self._data["docHierarchy"] = docHierarchy self._data["moveToTrash"] = moveToTrash - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiDocSplit", "spLevel", spLevel) pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder) pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy) @@ -201,13 +199,13 @@ class GuiDocSplit(QDialog): self.listBox.clear() - nwItem = self.theProject.tree[sHandle] + nwItem = self.mainGui.project.tree[sHandle] if nwItem is None or not nwItem.isFileType(): return spLevel = self.splitLevel.currentData() if not self._text: - inDoc = self.theProject.storage.getDocument(sHandle) + inDoc = self.mainGui.project.storage.getDocument(sHandle) self._text = (inDoc.readDocument() or "").splitlines() for lineNo, aLine in enumerate(self._text): diff --git a/novelwriter/dialogs/editlabel.py b/novelwriter/dialogs/editlabel.py index 58c72890..662c00f6 100644 --- a/novelwriter/dialogs/editlabel.py +++ b/novelwriter/dialogs/editlabel.py @@ -1,7 +1,6 @@ """ novelWriter – Edit Label Dialog =============================== -A simple dialog for editing a label File History: Created: 2022-06-11 [2.0rc1] @@ -22,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 . """ +from __future__ import annotations import logging diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 9878ad51..47d35b08 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Preferences ============================= -GUI classes for the user preferences dialog File History: Created: 2019-06-10 [0.1.5] @@ -22,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 . """ +from __future__ import annotations import logging @@ -49,8 +49,7 @@ class GuiPreferences(NPagedDialog): logger.debug("Create: GuiPreferences") self.setObjectName("GuiPreferences") - self.mainGui = mainGui - self.theProject = mainGui.theProject + self.mainGui = mainGui self.setWindowTitle(self.tr("Preferences")) @@ -160,13 +159,11 @@ class GuiPreferencesGeneral(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.prefsGui = prefsGui - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme + self.prefsGui = prefsGui # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Look and Feel @@ -193,7 +190,7 @@ class GuiPreferencesGeneral(QWidget): # Select Theme self.guiTheme = QComboBox() self.guiTheme.setMinimumWidth(minWidth) - self.theThemes = self.mainTheme.listThemes() + self.theThemes = CONFIG.theme.listThemes() for themeDir, themeName in self.theThemes: self.guiTheme.addItem(themeName, themeDir) themeIdx = self.guiTheme.findData(CONFIG.guiTheme) @@ -209,7 +206,7 @@ class GuiPreferencesGeneral(QWidget): # Editor Theme self.guiSyntax = QComboBox() self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200)) - self.theSyntaxes = self.mainTheme.listSyntax() + self.theSyntaxes = CONFIG.theme.listSyntax() for syntaxFile, syntaxName in self.theSyntaxes: self.guiSyntax.addItem(syntaxName, syntaxFile) syntaxIdx = self.guiSyntax.findData(CONFIG.guiSyntax) @@ -228,7 +225,7 @@ class GuiPreferencesGeneral(QWidget): self.guiFont.setFixedWidth(CONFIG.pxInt(162)) self.guiFont.setText(CONFIG.guiFont) self.fontButton = QPushButton("...") - self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) + self.fontButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("..."))) self.fontButton.clicked.connect(self._selectFont) self.mainForm.addRow( self.tr("Font family"), @@ -342,12 +339,9 @@ class GuiPreferencesProjects(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme - # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Automatic Save @@ -497,12 +491,9 @@ class GuiPreferencesDocuments(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme - # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Text Style @@ -515,7 +506,7 @@ class GuiPreferencesDocuments(QWidget): self.textFont.setFixedWidth(CONFIG.pxInt(162)) self.textFont.setText(CONFIG.textFont) self.fontButton = QPushButton("...") - self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) + self.fontButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("..."))) self.fontButton.clicked.connect(self._selectFont) self.mainForm.addRow( self.tr("Font family"), @@ -654,12 +645,11 @@ class GuiPreferencesEditor(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme + self.mainGui = prefsGui.mainGui # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) mW = CONFIG.pxInt(250) @@ -825,13 +815,11 @@ class GuiPreferencesSyntax(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.prefsGui = prefsGui - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme + self.prefsGui = prefsGui # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Quotes & Dialogue @@ -931,12 +919,9 @@ class GuiPreferencesAutomation(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme - # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Automatic Features @@ -1085,12 +1070,9 @@ class GuiPreferencesQuotes(QWidget): def __init__(self, prefsGui): super().__init__(parent=prefsGui) - self.mainGui = prefsGui.mainGui - self.mainTheme = prefsGui.mainGui.mainTheme - # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) # Quotation Style @@ -1098,7 +1080,7 @@ class GuiPreferencesQuotes(QWidget): self.mainForm.addGroupLabel(self.tr("Quotation Style")) qWidth = CONFIG.pxInt(40) - bWidth = int(2.5*self.mainTheme.getTextWidth("...")) + bWidth = int(2.5*CONFIG.theme.getTextWidth("...")) self.quoteSym = {} # Single Quote Style diff --git a/novelwriter/dialogs/projdetails.py b/novelwriter/dialogs/projdetails.py index e4960b57..5544ec7d 100644 --- a/novelwriter/dialogs/projdetails.py +++ b/novelwriter/dialogs/projdetails.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Project Details ================================= -Class holding the project details dialog File History: Created: 2021-01-03 [1.1rc1] @@ -22,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 . """ +from __future__ import annotations import math import logging @@ -36,9 +36,9 @@ from PyQt5.QtWidgets import ( from novelwriter import CONFIG from novelwriter.common import formatTime, numberToRoman from novelwriter.constants import nwUnicode -from novelwriter.gui.components import NovelSelector from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.pageddialog import NPagedDialog +from novelwriter.extensions.novelselector import NovelSelector logger = logging.getLogger(__name__) @@ -51,14 +51,13 @@ class GuiProjectDetails(NPagedDialog): logger.debug("Create: GuiProjectDetails") self.setObjectName("GuiProjectDetails") - self.mainGui = mainGui - self.theProject = mainGui.theProject + self.mainGui = mainGui self.setWindowTitle(self.tr("Project Details")) wW = CONFIG.pxInt(600) wH = CONFIG.pxInt(400) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.setMinimumWidth(wW) self.setMinimumHeight(wH) @@ -67,8 +66,8 @@ class GuiProjectDetails(NPagedDialog): CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH)) ) - self.tabMain = GuiProjectDetailsMain(self.mainGui, self.theProject) - self.tabContents = GuiProjectDetailsContents(self.mainGui, self.theProject) + self.tabMain = GuiProjectDetailsMain(self.mainGui) + self.tabContents = GuiProjectDetailsContents(self.mainGui) self.addTab(self.tabMain, self.tr("Overview")) self.addTab(self.tabContents, self.tr("Contents")) @@ -125,7 +124,7 @@ class GuiProjectDetails(NPagedDialog): countFrom = self.tabContents.poValue.value() clearDouble = self.tabContents.dblValue.isChecked() - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiProjectDetails", "winWidth", winWidth) pOptions.setValue("GuiProjectDetails", "winHeight", winHeight) pOptions.setValue("GuiProjectDetails", "widthCol0", widthCol0) @@ -144,15 +143,13 @@ class GuiProjectDetails(NPagedDialog): class GuiProjectDetailsMain(QWidget): - def __init__(self, mainGui, theProject): + def __init__(self, mainGui): super().__init__(parent=mainGui) - self.theProject = theProject - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui - fPx = self.mainTheme.fontPixelSize - fPt = self.mainTheme.fontPointSize + fPx = CONFIG.theme.fontPixelSize + fPt = CONFIG.theme.fontPointSize vPx = CONFIG.pxInt(4) hPx = CONFIG.pxInt(12) @@ -247,22 +244,23 @@ class GuiProjectDetailsMain(QWidget): def updateValues(self): """Set all the values. """ - pIndex = self.theProject.index + project = self.mainGui.project + pIndex = project.index hCounts = pIndex.getNovelTitleCounts() nwCount = pIndex.getNovelWordCount() - edTime = self.theProject.getCurrentEditTime() + edTime = project.getCurrentEditTime() - self.bookTitle.setText(self.theProject.data.title or self.theProject.data.name) - self.projName.setText(self.tr("Project: {0}").format(self.theProject.data.name)) - self.bookAuthors.setText(self.tr("By {0}").format(self.theProject.data.author)) + self.bookTitle.setText(project.data.title or project.data.name) + self.projName.setText(self.tr("Project: {0}").format(project.data.name)) + self.bookAuthors.setText(self.tr("By {0}").format(project.data.author)) self.wordCountVal.setText(f"{nwCount:n}") self.chapCountVal.setText(f"{hCounts[2]:n}") self.sceneCountVal.setText(f"{hCounts[3]:n}") - self.revCountVal.setText(f"{self.theProject.data.saveCount:n}") + self.revCountVal.setText(f"{project.data.saveCount:n}") self.editTimeVal.setText(formatTime(edTime)) - self.projPathVal.setText(str(self.theProject.storage.storagePath)) + self.projPathVal.setText(str(project.storage.storagePath)) return @@ -277,28 +275,26 @@ class GuiProjectDetailsContents(QWidget): C_PAGE = 3 C_PROG = 4 - def __init__(self, mainGui, theProject): + def __init__(self, mainGui): super().__init__(parent=mainGui) - self.theProject = theProject - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui # Internal self._theToC = [] self._currentRoot = None - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize hPx = CONFIG.pxInt(12) vPx = CONFIG.pxInt(4) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options # Header # ====== self.tocLabel = QLabel("%s" % self.tr("Table of Contents")) - self.novelValue = NovelSelector(self, self.theProject, self.mainGui) + self.novelValue = NovelSelector(self, self.mainGui) self.novelValue.setMinimumWidth(CONFIG.pxInt(200)) self.novelValue.novelSelectionChanged.connect(self._novelValueChanged) @@ -447,7 +443,7 @@ class GuiProjectDetailsContents(QWidget): """Extract the information from the project index. """ logger.debug("Populating ToC from handle '%s'", rootHandle) - self._theToC = self.theProject.index.getTableOfContents(rootHandle, 2) + self._theToC = self.mainGui.project.index.getTableOfContents(rootHandle, 2) self._theToC.append(("", 0, self.tr("END"), 0)) return @@ -500,7 +496,7 @@ class GuiProjectDetailsContents(QWidget): progPage = f"{cPage:n}" progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%" - hDec = self.mainTheme.getHeaderDecoration(tLevel) + hDec = CONFIG.theme.getHeaderDecoration(tLevel) if tTitle.strip() == "": tTitle = self.tr("Untitled") diff --git a/novelwriter/dialogs/projload.py b/novelwriter/dialogs/projload.py index 916ca820..09fabd4d 100644 --- a/novelwriter/dialogs/projload.py +++ b/novelwriter/dialogs/projload.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Open Project ============================== -GUI class for the load/browse/new project dialog File History: Created: 2020-02-26 [0.4.5] @@ -22,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 . """ +from __future__ import annotations import logging @@ -62,13 +62,12 @@ class GuiProjectLoad(QDialog): self.setObjectName("GuiProjectLoad") self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme self.openState = self.NONE_STATE self.openPath = None sPx = CONFIG.pxInt(16) nPx = CONFIG.pxInt(96) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize self.outerBox = QVBoxLayout() self.innerBox = QHBoxLayout() @@ -80,7 +79,7 @@ class GuiProjectLoad(QDialog): self.setMinimumHeight(CONFIG.pxInt(400)) self.nwIcon = QLabel() - self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) + self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx))) self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop) self.projectForm = QGridLayout() @@ -101,8 +100,9 @@ class GuiProjectLoad(QDialog): self.listBox.setIconSize(QSize(iPx, iPx)) treeHead = self.listBox.headerItem() - treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight) - treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight) + if treeHead: + treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight) + treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight) self.lblRecent = QLabel("%s" % self.tr("Recently Opened Projects")) self.lblPath = QLabel("%s" % self.tr("Path")) @@ -110,7 +110,7 @@ class GuiProjectLoad(QDialog): self.selPath.setReadOnly(True) self.browseButton = QPushButton("...") - self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) + self.browseButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("..."))) self.browseButton.clicked.connect(self._doBrowse) self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3) @@ -268,7 +268,7 @@ class GuiProjectLoad(QDialog): self.listBox.clear() dataList = CONFIG.recentProjects.listEntries() sortList = sorted(dataList, key=lambda x: x[3], reverse=True) - nwxIcon = self.mainGui.mainTheme.getIcon("proj_nwx") + nwxIcon = CONFIG.theme.getIcon("proj_nwx") for path, title, words, time in sortList: newItem = QTreeWidgetItem([""]*4) newItem.setIcon(self.C_NAME, nwxIcon) @@ -279,11 +279,10 @@ class GuiProjectLoad(QDialog): newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter) newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter) newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter) - newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed) + newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed) self.listBox.addTopLevelItem(newItem) - if self.listBox.topLevelItemCount() > 0: - self.listBox.topLevelItem(0).setSelected(True) + self.listBox.setCurrentItem(self.listBox.topLevelItem(0)) projColWidth = CONFIG.projLoadColWidths if len(projColWidth) == 3: diff --git a/novelwriter/dialogs/projsettings.py b/novelwriter/dialogs/projsettings.py index a639f42c..92b22454 100644 --- a/novelwriter/dialogs/projsettings.py +++ b/novelwriter/dialogs/projsettings.py @@ -60,15 +60,13 @@ class GuiProjectSettings(NPagedDialog): logger.debug("Create: GuiProjectSettings") self.setObjectName("GuiProjectSettings") - self.mainGui = mainGui - self.theProject = mainGui.theProject - - self.theProject.countStatus() + self.mainGui = mainGui + self.mainGui.project.countStatus() self.setWindowTitle(self.tr("Project Settings")) wW = CONFIG.pxInt(570) wH = CONFIG.pxInt(375) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.setMinimumWidth(wW) self.setMinimumHeight(wH) @@ -117,34 +115,35 @@ class GuiProjectSettings(NPagedDialog): def _doSave(self): """Save settings and close dialog. """ + project = self.mainGui.project projName = self.tabMain.editName.text() bookTitle = self.tabMain.editTitle.text() bookAuthor = self.tabMain.editAuthor.text() spellLang = self.tabMain.spellLang.currentData() doBackup = not self.tabMain.doBackup.isChecked() - self.theProject.data.setName(projName) - self.theProject.data.setTitle(bookTitle) - self.theProject.data.setAuthor(bookAuthor) - self.theProject.data.setDoBackup(doBackup) + project.data.setName(projName) + project.data.setTitle(bookTitle) + project.data.setAuthor(bookAuthor) + project.data.setDoBackup(doBackup) # Remember this as updating spell dictionary can be expensive - self._spellChanged = self.theProject.data.setSpellLang(spellLang) + self._spellChanged = project.data.setSpellLang(spellLang) if self.tabStatus.colChanged: newList, delList = self.tabStatus.getNewList() - self.theProject.setStatusColours(newList, delList) + project.setStatusColours(newList, delList) if self.tabImport.colChanged: newList, delList = self.tabImport.getNewList() - self.theProject.setImportColours(newList, delList) + project.setImportColours(newList, delList) if self.tabStatus.colChanged or self.tabImport.colChanged: self.mainGui.rebuildTrees() if self.tabReplace.arChanged: newList = self.tabReplace.getNewList() - self.theProject.data.setAutoReplace(newList) + project.data.setAutoReplace(newList) self._saveGuiSettings() self.accept() @@ -184,7 +183,7 @@ class GuiProjectSettings(NPagedDialog): statusColW = CONFIG.rpxInt(self.tabStatus.listBox.columnWidth(0)) importColW = CONFIG.rpxInt(self.tabImport.listBox.columnWidth(0)) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiProjectSettings", "winWidth", winWidth) pOptions.setValue("GuiProjectSettings", "winHeight", winHeight) pOptions.setValue("GuiProjectSettings", "replaceColW", replaceColW) @@ -201,22 +200,22 @@ class GuiProjectEditMain(QWidget): def __init__(self, projGui): super().__init__(parent=projGui) - self.mainGui = projGui.mainGui - self.theProject = projGui.theProject + self.mainGui = projGui.mainGui # The Form self.mainForm = NConfigLayout() - self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText) + self.mainForm.setHelpTextStyle(CONFIG.theme.helpText) self.setLayout(self.mainForm) self.mainForm.addGroupLabel(self.tr("Project Settings")) xW = CONFIG.pxInt(250) + pData = self.mainGui.project.data self.editName = QLineEdit() self.editName.setMaxLength(200) self.editName.setMaximumWidth(xW) - self.editName.setText(self.theProject.data.name) + self.editName.setText(pData.name) self.mainForm.addRow( self.tr("Project name"), self.editName, @@ -226,7 +225,7 @@ class GuiProjectEditMain(QWidget): self.editTitle = QLineEdit() self.editTitle.setMaxLength(200) self.editTitle.setMaximumWidth(xW) - self.editTitle.setText(self.theProject.data.title) + self.editTitle.setText(pData.title) self.mainForm.addRow( self.tr("Novel title"), self.editTitle, @@ -236,7 +235,7 @@ class GuiProjectEditMain(QWidget): self.editAuthor = QLineEdit() self.editAuthor.setMaxLength(200) self.editAuthor.setMaximumWidth(xW) - self.editAuthor.setText(self.theProject.data.author) + self.editAuthor.setText(pData.author) self.mainForm.addRow( self.tr("Author(s)"), self.editAuthor, @@ -259,13 +258,13 @@ class GuiProjectEditMain(QWidget): ) spellIdx = 0 - if self.theProject.data.spellLang is not None: - spellIdx = self.spellLang.findData(self.theProject.data.spellLang) + if pData.spellLang is not None: + spellIdx = self.spellLang.findData(pData.spellLang) if spellIdx != -1: self.spellLang.setCurrentIndex(spellIdx) self.doBackup = NSwitch(self) - self.doBackup.setChecked(not self.theProject.data.doBackup) + self.doBackup.setChecked(not pData.doBackup) self.mainForm.addRow( self.tr("No backup on close"), self.doBackup, @@ -289,28 +288,26 @@ class GuiProjectEditStatus(QWidget): def __init__(self, projGui, isStatus): super().__init__(parent=projGui) - self.mainGui = projGui.mainGui - self.theProject = projGui.theProject - self.mainTheme = projGui.mainGui.mainTheme + self.mainGui = projGui.mainGui if isStatus: - self.theStatus = self.theProject.data.itemStatus + self.theStatus = self.mainGui.project.data.itemStatus pageLabel = self.tr("Novel File Status Levels") colSetting = "statusColW" else: - self.theStatus = self.theProject.data.itemImport + self.theStatus = self.mainGui.project.data.itemImport pageLabel = self.tr("Note File Importance Levels") colSetting = "importColW" wCol0 = CONFIG.pxInt( - self.theProject.options.getInt("GuiProjectSettings", colSetting, 130) + self.mainGui.project.options.getInt("GuiProjectSettings", colSetting, 130) ) self.colDeleted = [] self.colChanged = False self.selColour = QColor(100, 100, 100) - self.iPx = self.mainTheme.baseIconSize + self.iPx = CONFIG.theme.baseIconSize # The List # ======== @@ -329,16 +326,16 @@ class GuiProjectEditStatus(QWidget): # List Controls # ============= - self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") + self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "") self.addButton.clicked.connect(self._newItem) - self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") + self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "") self.delButton.clicked.connect(self._delItem) - self.upButton = QPushButton(self.mainTheme.getIcon("up"), "") + self.upButton = QPushButton(CONFIG.theme.getIcon("up"), "") self.upButton.clicked.connect(lambda: self._moveItem(-1)) - self.dnButton = QPushButton(self.mainTheme.getIcon("down"), "") + self.dnButton = QPushButton(CONFIG.theme.getIcon("down"), "") self.dnButton.clicked.connect(lambda: self._moveItem(1)) # Edit Form @@ -577,13 +574,11 @@ class GuiProjectEditReplace(QWidget): def __init__(self, projGui): super().__init__(parent=projGui) - self.mainGui = projGui.mainGui - self.mainTheme = projGui.mainGui.mainTheme - self.theProject = projGui.theProject - self.arChanged = False + self.mainGui = projGui.mainGui + self.arChanged = False wCol0 = CONFIG.pxInt( - self.theProject.options.getInt("GuiProjectSettings", "replaceColW", 130) + self.mainGui.project.options.getInt("GuiProjectSettings", "replaceColW", 130) ) pageLabel = self.tr("Text Replace List for Preview and Export") @@ -599,7 +594,7 @@ class GuiProjectEditReplace(QWidget): self.listBox.setColumnWidth(self.COL_KEY, wCol0) self.listBox.setIndentation(0) - for aKey, aVal in self.theProject.data.autoReplace.items(): + for aKey, aVal in self.mainGui.project.data.autoReplace.items(): newItem = QTreeWidgetItem(["<%s>" % aKey, aVal]) self.listBox.addTopLevelItem(newItem) @@ -609,10 +604,10 @@ class GuiProjectEditReplace(QWidget): # List Controls # ============= - self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") + self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "") self.addButton.clicked.connect(self._addEntry) - self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") + self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "") self.delButton.clicked.connect(self._delEntry) # Edit Form diff --git a/novelwriter/dialogs/quotes.py b/novelwriter/dialogs/quotes.py index 4f5ba9eb..6282c6b6 100644 --- a/novelwriter/dialogs/quotes.py +++ b/novelwriter/dialogs/quotes.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Quotes Dialog =============================== -GUI class for quotes dialog File History: Created: 2020-06-18 [0.9] @@ -22,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 . """ +from __future__ import annotations import logging diff --git a/novelwriter/dialogs/updates.py b/novelwriter/dialogs/updates.py index 4b8afad1..9553fa8d 100644 --- a/novelwriter/dialogs/updates.py +++ b/novelwriter/dialogs/updates.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Updates ========================= -A dialog box for checking for latest updates File History: Created: 2021-08-21 [1.5b1] @@ -22,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 . """ +from __future__ import annotations import json import logging @@ -49,9 +49,6 @@ class GuiUpdates(QDialog): logger.debug("Create: GuiUpdates") self.setObjectName("GuiUpdates") - - self.mainGui = mainGui - self.setWindowTitle(self.tr("Check for Updates")) nPx = CONFIG.pxInt(96) @@ -61,7 +58,7 @@ class GuiUpdates(QDialog): # Left Box self.nwIcon = QLabel() - self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) + self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx))) self.leftBox = QVBoxLayout() self.leftBox.addWidget(self.nwIcon) diff --git a/novelwriter/dialogs/wordlist.py b/novelwriter/dialogs/wordlist.py index 9f807162..ad549298 100644 --- a/novelwriter/dialogs/wordlist.py +++ b/novelwriter/dialogs/wordlist.py @@ -50,17 +50,14 @@ class GuiWordList(QDialog): logger.debug("Create: GuiWordList") self.setObjectName("GuiWordList") - - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject - self.setWindowTitle(self.tr("Project Word List")) + self.mainGui = mainGui + mS = CONFIG.pxInt(250) wW = CONFIG.pxInt(320) wH = CONFIG.pxInt(340) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.setMinimumWidth(mS) self.setMinimumHeight(mS) @@ -80,10 +77,10 @@ class GuiWordList(QDialog): self.newEntry = QLineEdit() - self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") + self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "") self.addButton.clicked.connect(self._doAdd) - self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") + self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "") self.delButton.clicked.connect(self._doDelete) self.editBox = QHBoxLayout() @@ -152,7 +149,7 @@ class GuiWordList(QDialog): def _doSave(self): """Save the new word list and close.""" self._saveGuiSettings() - userDict = UserDictionary(self.theProject) + userDict = UserDictionary(self.mainGui.project) for i in range(self.listBox.count()): item = self.listBox.item(i) if isinstance(item, QListWidgetItem): @@ -175,7 +172,7 @@ class GuiWordList(QDialog): def _loadWordList(self): """Load the project's word list, if it exists.""" - userDict = UserDictionary(self.theProject) + userDict = UserDictionary(self.mainGui.project) userDict.load() self.listBox.clear() for word in userDict: @@ -188,7 +185,7 @@ class GuiWordList(QDialog): winWidth = CONFIG.rpxInt(self.width()) winHeight = CONFIG.rpxInt(self.height()) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiWordList", "winWidth", winWidth) pOptions.setValue("GuiWordList", "winHeight", winHeight) diff --git a/novelwriter/enum.py b/novelwriter/enum.py index aeab3f5e..1abc29c7 100644 --- a/novelwriter/enum.py +++ b/novelwriter/enum.py @@ -1,7 +1,6 @@ """ novelWriter – Enums =================== -Global enum values File History: Created: 2018-11-02 [0.0.1] diff --git a/novelwriter/extensions/circularprogress.py b/novelwriter/extensions/circularprogress.py index 79dacfd1..b53bf6bd 100644 --- a/novelwriter/extensions/circularprogress.py +++ b/novelwriter/extensions/circularprogress.py @@ -42,7 +42,7 @@ class NProgressCircle(QProgressBar): "_cPen", "_bPen", "_tColor" ) - def __init__(self, parent: QWidget, size: int, point: int): + def __init__(self, parent: QWidget, size: int, point: int) -> None: super().__init__(parent=parent) self._text = None self._point = point @@ -60,10 +60,8 @@ class NProgressCircle(QProgressBar): self.setFixedHeight(size) return - def setColours( - self, back: QColor | None = None, track: QColor | None = None, - bar: QColor | None = None, text: QColor | None = None - ): + def setColours(self, back: QColor | None = None, track: QColor | None = None, + bar: QColor | None = None, text: QColor | None = None) -> None: """Set the colours of the widget.""" if isinstance(back, QColor): self._dPen = QPen(back) @@ -76,13 +74,13 @@ class NProgressCircle(QProgressBar): self._tColor = text return - def setCentreText(self, text: str | None): + def setCentreText(self, text: str | None) -> None: """Replace the progress text with a custom string.""" self._text = text self.setValue(self.value()) # Triggers a redraw return - def paintEvent(self, event: QPaintEvent): + def paintEvent(self, event: QPaintEvent) -> None: """Custom painter for the progress bar.""" progress = 100.0*self.value()/self.maximum() angle = ceil(16*3.6*progress) diff --git a/novelwriter/extensions/configlayout.py b/novelwriter/extensions/configlayout.py index 93d348be..3ab6b87b 100644 --- a/novelwriter/extensions/configlayout.py +++ b/novelwriter/extensions/configlayout.py @@ -1,7 +1,6 @@ """ novelWriter – Custom Widget: Config Layout ========================================== -A custom grid layout for config pages File History: Created: 2020-05-03 [0.4.5] @@ -38,7 +37,7 @@ FONT_SCALE = 0.9 class NConfigLayout(QGridLayout): - def __init__(self): + def __init__(self) -> None: super().__init__() self._nextRow = 0 @@ -57,7 +56,8 @@ class NConfigLayout(QGridLayout): # Getters and Setters ## - def setHelpTextStyle(self, color: QColor | list | tuple, fontScale: float = FONT_SCALE): + def setHelpTextStyle(self, color: QColor | list | tuple, + fontScale: float = FONT_SCALE) -> None: """Set the text color for the help text.""" if isinstance(color, QColor): self._helpCol = color @@ -66,7 +66,7 @@ class NConfigLayout(QGridLayout): self._fontScale = fontScale return - def setHelpText(self, row: int, text: str): + def setHelpText(self, row: int, text: str) -> None: """Set the text for the help label.""" if row in self._itemMap: qHelp = self._itemMap[row][1] @@ -74,7 +74,7 @@ class NConfigLayout(QGridLayout): qHelp.setText(text) return - def setLabelText(self, row: int, text: str): + def setLabelText(self, row: int, text: str) -> None: """Set the text for the main label.""" if row in self._itemMap: self._itemMap[row](0).setText(text) @@ -84,7 +84,7 @@ class NConfigLayout(QGridLayout): # Class Methods ## - def addGroupLabel(self, label: str): + def addGroupLabel(self, label: str) -> None: """Add a text label to separate groups of settings.""" hM = CONFIG.pxInt(4) qLabel = QLabel("%s" % label) @@ -95,10 +95,8 @@ class NConfigLayout(QGridLayout): self._nextRow += 1 return - def addRow( - self, label: str, widget: QWidget, helpText: str | None = None, - unit: str | None = None, button: QWidget | None = None - ) -> int: + def addRow(self, label: str, widget: QWidget, helpText: str | None = None, + unit: str | None = None, button: QWidget | None = None) -> int: """Add a label and a widget as a new row of the grid.""" wSp = CONFIG.pxInt(8) qLabel = QLabel(label) @@ -156,7 +154,7 @@ class NSimpleLayout(QGridLayout): column layout. """ - def __init__(self): + def __init__(self) -> None: super().__init__() self._nextRow = 0 @@ -171,7 +169,7 @@ class NSimpleLayout(QGridLayout): # Methods ## - def addGroupLabel(self, label: str): + def addGroupLabel(self, label: str) -> None: """Add a text label to separate groups of settings.""" hM = CONFIG.pxInt(4) qLabel = QLabel("%s" % label) @@ -182,7 +180,7 @@ class NSimpleLayout(QGridLayout): self._nextRow += 1 return - def addRow(self, label: str, widget: QWidget): + def addRow(self, label: str, widget: QWidget) -> None: """Add a label and a widget as a new row of the grid.""" wSp = CONFIG.pxInt(8) qLabel = QLabel(label) @@ -209,7 +207,8 @@ class NSimpleLayout(QGridLayout): class NHelpLabel(QLabel): - def __init__(self, text: str, color: QColor | list | tuple, fontSize: float = FONT_SCALE): + def __init__(self, text: str, color: QColor | list | tuple, + fontSize: float = FONT_SCALE) -> None: super().__init__(text) if isinstance(color, QColor): diff --git a/novelwriter/gui/components.py b/novelwriter/extensions/novelselector.py similarity index 54% rename from novelwriter/gui/components.py rename to novelwriter/extensions/novelselector.py index 3556ae9c..8bb2c4c2 100644 --- a/novelwriter/gui/components.py +++ b/novelwriter/extensions/novelselector.py @@ -1,11 +1,9 @@ """ -novelWriter – GUI Components Module -=================================== -A module of various small GUI components +novelWriter – Custom Widget: Novel Selector +=========================================== File History: -Created: 2020-05-17 [0.5.1] StatusLED -Created: 2022-11-17 [2.0] NovelSelector +Created: 2022-11-17 [2.0] This file is a part of novelWriter Copyright 2018–2023, Veronica Berglyd Olsen @@ -23,16 +21,22 @@ 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 . """ +from __future__ import annotations import logging -from PyQt5.QtGui import QPainter -from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import QAbstractButton, QComboBox +from typing import TYPE_CHECKING +from PyQt5.QtCore import pyqtSignal, pyqtSlot +from PyQt5.QtWidgets import QComboBox, QWidget + +from novelwriter import CONFIG from novelwriter.enum import nwItemClass from novelwriter.constants import nwLabels +if TYPE_CHECKING: # pragma: no cover + from novelwriter.guimain import GuiMain + logger = logging.getLogger(__name__) @@ -40,17 +44,12 @@ class NovelSelector(QComboBox): novelSelectionChanged = pyqtSignal(str) - def __init__(self, parent, project, mainGui): + def __init__(self, parent: QWidget, mainGui: GuiMain) -> None: super().__init__(parent=parent) - self._mainGui = mainGui - self._project = project - self._theme = mainGui.mainTheme self._blockSignal = False self._firstHandle = None - self.currentIndexChanged.connect(self._indexChanged) - return ## @@ -58,20 +57,19 @@ class NovelSelector(QComboBox): ## @property - def handle(self): + def handle(self) -> str: return self.currentData() @property - def firstHandle(self): + def firstHandle(self) -> str | None: return self._firstHandle ## # Methods ## - def setHandle(self, tHandle, blockSignal=True): - """Set the currently selected handle. - """ + def setHandle(self, tHandle: str, blockSignal: bool = True) -> None: + """Set the currently selected handle.""" self._blockSignal = blockSignal if tHandle is None: index = self.count() - 1 @@ -82,16 +80,15 @@ class NovelSelector(QComboBox): self._blockSignal = False return - def updateList(self, includeAll=False, prefix=None): - """Rebuild the list of novel items. - """ + def updateList(self, includeAll: bool = False, prefix: str | None = None) -> None: + """Rebuild the list of novel items.""" self._blockSignal = True self._firstHandle = None self.clear() - icon = self._theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]) + icon = CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]) handle = self.currentData() - for tHandle, nwItem in self._project.tree.iterRoots(nwItemClass.NOVEL): + for tHandle, nwItem in self._mainGui.project.tree.iterRoots(nwItemClass.NOVEL): if prefix: name = prefix.format(nwItem.itemName) self.addItem(name, tHandle) @@ -116,67 +113,10 @@ class NovelSelector(QComboBox): ## @pyqtSlot(int) - def _indexChanged(self, index): - """Re-emit the change of selected novel signal, unless blocked. - """ + def _indexChanged(self, index: int) -> None: + """Re-emit the change of selection signal, unless blocked.""" if not self._blockSignal: self.novelSelectionChanged.emit(self.currentData()) return # END Class NovelSelector - - -class StatusLED(QAbstractButton): - - S_NONE = 0 - S_BAD = 1 - S_GOOD = 2 - - def __init__(self, colNone, colGood, colBad, sW, sH, parent=None): - super().__init__(parent=parent) - - self._colNone = colNone - self._colGood = colGood - self._colBad = colBad - self._theCol = colNone - - self.setFixedWidth(sW) - self.setFixedHeight(sH) - - return - - ## - # Setters - ## - - def setState(self, theState): - """Set the colour state. - """ - if theState == self.S_GOOD: - self._theCol = self._colGood - elif theState == self.S_BAD: - self._theCol = self._colBad - else: - self._theCol = self._colNone - - self.update() - - return - - ## - # Events - ## - - def paintEvent(self, _): - """Drawing the LED. - """ - qPalette = self.palette() - qPaint = QPainter(self) - qPaint.setRenderHint(QPainter.Antialiasing, True) - qPaint.setPen(qPalette.dark().color()) - qPaint.setBrush(self._theCol) - qPaint.setOpacity(1.0) - qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2) - return - -# END Class StatusLED diff --git a/novelwriter/extensions/pageddialog.py b/novelwriter/extensions/pageddialog.py index 28bd817c..a4170a2a 100644 --- a/novelwriter/extensions/pageddialog.py +++ b/novelwriter/extensions/pageddialog.py @@ -1,7 +1,6 @@ """ novelWriter – Custom Widget: Paged Dialog ========================================= -A custom dialog with tabs and a vertical tab bar File History: Created: 2020-05-17 [0.5.1] @@ -22,11 +21,13 @@ 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 . """ +from __future__ import annotations -from PyQt5.QtCore import QRect, QPoint +from PyQt5.QtGui import QPaintEvent +from PyQt5.QtCore import QRect, QPoint, QSize from PyQt5.QtWidgets import ( QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar, - QTabWidget, QVBoxLayout + QTabWidget, QVBoxLayout, QWidget ) from novelwriter import CONFIG @@ -34,7 +35,7 @@ from novelwriter import CONFIG class NPagedDialog(QDialog): - def __init__(self, parent=None): + def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) self._tabBar = NVerticalTabBar(self) @@ -67,21 +68,18 @@ class NPagedDialog(QDialog): return - def addTab(self, widget, label): - """Forward the adding of tabs to the QTabWidget. - """ + def addTab(self, widget: QWidget, label: str) -> None: + """Forward the adding of tabs to the QTabWidget.""" self._tabBox.addTab(widget, label) return - def addControls(self, buttonBar): - """Add a button bar to the dialog. - """ + def addControls(self, buttonBar: QWidget) -> None: + """Add a button bar to the dialog.""" self._buttonBox.addWidget(buttonBar) return - def setCurrentWidget(self, widget): - """Forward the changing of tab to the QTabWidget. - """ + def setCurrentWidget(self, widget: QWidget) -> None: + """Forward the changing of tab to the QTabWidget.""" self._tabBox.setCurrentWidget(widget) return @@ -90,20 +88,19 @@ class NPagedDialog(QDialog): class NVerticalTabBar(QTabBar): - def __init__(self, parent=None): + def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) self._mW = CONFIG.pxInt(150) return - def tabSizeHint(self, index): - """Return a transposed size hint for the rotated bar. - """ + def tabSizeHint(self, index: int) -> QSize: + """Return a transposed size hint for the rotated bar.""" tSize = super().tabSizeHint(index) tSize.transpose() tSize.setWidth(min(tSize.width(), self._mW)) return tSize - def paintEvent(self, event): + def paintEvent(self, event: QPaintEvent) -> None: """Custom implementation of the label painter that rotates the label 90 degrees. """ diff --git a/novelwriter/extensions/simpleprogress.py b/novelwriter/extensions/simpleprogress.py index 2dbf4e43..30a11168 100644 --- a/novelwriter/extensions/simpleprogress.py +++ b/novelwriter/extensions/simpleprogress.py @@ -35,11 +35,11 @@ class NProgressSimple(QProgressBar): A custom widget that paints a plain bar with no other styling. """ - def __init__(self, parent: QWidget): + def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) return - def paintEvent(self, event: QPaintEvent): + def paintEvent(self, event: QPaintEvent) -> None: """Custom painter for the progress bar.""" if self.value() == 0: return diff --git a/novelwriter/extensions/statusled.py b/novelwriter/extensions/statusled.py new file mode 100644 index 00000000..8a5ba4e6 --- /dev/null +++ b/novelwriter/extensions/statusled.py @@ -0,0 +1,77 @@ +""" +novelWriter – Custom Widget: Status LED +======================================= + +File History: +Created: 2020-05-17 [0.5.1] + +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 . +""" +from __future__ import annotations + +import logging +from typing import Literal + +from PyQt5.QtGui import QColor, QPaintEvent, QPainter +from PyQt5.QtWidgets import QAbstractButton, QWidget + +logger = logging.getLogger(__name__) + + +class StatusLED(QAbstractButton): + + S_NONE = 0 + S_BAD = 1 + S_GOOD = 2 + + def __init__(self, colNone: QColor, colGood: QColor, colBad: QColor, + sW: int, sH: int, parent: QWidget | None = None) -> None: + super().__init__(parent=parent) + + self._colNone = colNone + self._colGood = colGood + self._colBad = colBad + self._theCol = colNone + + self.setFixedWidth(sW) + self.setFixedHeight(sH) + + return + + def setState(self, state: Literal[0, 1, 2]) -> None: + """Set the colour state.""" + if state == self.S_GOOD: + self._theCol = self._colGood + elif state == self.S_BAD: + self._theCol = self._colBad + else: + self._theCol = self._colNone + self.update() + return + + def paintEvent(self, event: QPaintEvent) -> None: + """Drawing the LED.""" + qPalette = self.palette() + qPaint = QPainter(self) + qPaint.setRenderHint(QPainter.Antialiasing, True) + qPaint.setPen(qPalette.dark().color()) + qPaint.setBrush(self._theCol) + qPaint.setOpacity(1.0) + qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2) + return + +# END Class StatusLED diff --git a/novelwriter/extensions/switch.py b/novelwriter/extensions/switch.py index 529db854..e702a6b6 100644 --- a/novelwriter/extensions/switch.py +++ b/novelwriter/extensions/switch.py @@ -1,7 +1,6 @@ """ novelWriter – Custom Widget: Switch =================================== -A custom switch widget File History: Created: 2020-05-03 [0.4.5] @@ -22,10 +21,11 @@ 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 . """ +from __future__ import annotations -from PyQt5.QtGui import QPainter -from PyQt5.QtCore import Qt, QRectF, QPropertyAnimation, pyqtProperty -from PyQt5.QtWidgets import QSizePolicy, QAbstractButton +from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent +from PyQt5.QtCore import QEvent, QPropertyAnimation, QRectF, Qt, pyqtProperty +from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget from novelwriter import CONFIG from novelwriter.constants import nwUnicode @@ -33,7 +33,8 @@ from novelwriter.constants import nwUnicode class NSwitch(QAbstractButton): - def __init__(self, parent=None, width=None, height=None): + def __init__(self, parent: QWidget | None = None, + width: int | None = None, height: int | None = None) -> None: super().__init__(parent=parent) if width is None: @@ -64,12 +65,12 @@ class NSwitch(QAbstractButton): # Properties ## - @pyqtProperty(int) - def offset(self): + @pyqtProperty(int) # type: ignore + def offset(self) -> int: # type: ignore return self._offset - @offset.setter - def offset(self, offset): + @offset.setter # type: ignore + def offset(self, offset: int): self._offset = offset self.update() return @@ -78,33 +79,30 @@ class NSwitch(QAbstractButton): # Getters and Setters ## - def setChecked(self, checked): - """Overload setChecked to also alter the offset. - """ + def setChecked(self, checked: bool) -> None: + """Overload setChecked to also alter the offset.""" super().setChecked(checked) if checked: - self.offset = self._xW - self._xR + self._offset = self._xW - self._xR else: - self.offset = self._xR + self._offset = self._xR return ## # Events ## - def resizeEvent(self, event): - """Overload resize to ensure correct offset. - """ + def resizeEvent(self, event: QResizeEvent) -> None: + """Overload resize to ensure correct offset.""" super().resizeEvent(event) if self.isChecked(): - self.offset = self._xW - self._xR + self._offset = self._xW - self._xR else: - self.offset = self._xR + self._offset = self._xR return - def paintEvent(self, event): - """Drawing the switch itself. - """ + def paintEvent(self, event: QPaintEvent) -> None: + """Drawing the switch itself.""" qPaint = QPainter(self) qPaint.setRenderHint(QPainter.Antialiasing, True) qPaint.setPen(Qt.NoPen) @@ -134,27 +132,26 @@ class NSwitch(QAbstractButton): qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR) qPaint.setBrush(thumbBrush) - qPaint.drawEllipse(self.offset - self._rR, self._rB, self._rH, self._rH) + qPaint.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH) theFont = qPaint.font() theFont.setPixelSize(self._xT) qPaint.setPen(textColor) qPaint.setFont(theFont) qPaint.drawText( - QRectF(self.offset - self._rR, self._rB, self._rH, self._rH), + QRectF(self._offset - self._rR, self._rB, self._rH, self._rH), Qt.AlignCenter, thumbText ) return - def mouseReleaseEvent(self, event): - """Animate the switch on mouse release. - """ + def mouseReleaseEvent(self, event: QMouseEvent) -> None: + """Animate the switch on mouse release.""" super().mouseReleaseEvent(event) if event.button() == Qt.LeftButton: doAnim = QPropertyAnimation(self, b"offset", self) doAnim.setDuration(120) - doAnim.setStartValue(self.offset) + doAnim.setStartValue(self._offset) if self.isChecked(): doAnim.setEndValue(self._xW - self._xR) else: @@ -162,9 +159,8 @@ class NSwitch(QAbstractButton): doAnim.start() return - def enterEvent(self, event): - """Change the cursor when hovering the button. - """ + def enterEvent(self, event: QEvent) -> None: + """Change the cursor when hovering the button.""" self.setCursor(Qt.PointingHandCursor) super().enterEvent(event) return diff --git a/novelwriter/extensions/switchbox.py b/novelwriter/extensions/switchbox.py index f7385058..98db295a 100644 --- a/novelwriter/extensions/switchbox.py +++ b/novelwriter/extensions/switchbox.py @@ -23,8 +23,8 @@ along with this program. If not, see . """ from __future__ import annotations -from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QIcon +from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget from novelwriter.extensions.switch import NSwitch @@ -39,7 +39,7 @@ class NSwitchBox(QScrollArea): switchToggled = pyqtSignal(str, bool) - def __init__(self, parent: QWidget, baseSize: int): + def __init__(self, parent: QWidget, baseSize: int) -> None: super().__init__(parent=parent) self._index = 0 self._hSwitch = baseSize @@ -49,7 +49,7 @@ class NSwitchBox(QScrollArea): self.clear() return - def clear(self): + def clear(self) -> None: """Rebuild the content of the core widget.""" self._index = 0 self._widgets = [] @@ -66,7 +66,7 @@ class NSwitchBox(QScrollArea): return - def addLabel(self, text: str): + def addLabel(self, text: str) -> None: """Add a header label to the content box.""" label = QLabel(text) font = label.font() @@ -77,7 +77,7 @@ class NSwitchBox(QScrollArea): self._bumpIndex() return - def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False): + def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False) -> None: """Add an item to the content box.""" icon = QLabel("") icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter) @@ -97,7 +97,7 @@ class NSwitchBox(QScrollArea): return - def addSeparator(self): + def addSeparator(self) -> None: """Add a blank entry in the content box.""" spacer = QWidget() spacer.setFixedHeight(int(0.5*self._sIcon)) @@ -106,7 +106,7 @@ class NSwitchBox(QScrollArea): self._bumpIndex() return - def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int): + def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int) -> None: """Set the contents margins of the inner layout.""" self._content.setContentsMargins(left, top, right, bottom) return @@ -115,12 +115,12 @@ class NSwitchBox(QScrollArea): # Internal Functions ## - def _emitSwitchSignal(self, identifier: str, state: bool): + def _emitSwitchSignal(self, identifier: str, state: bool) -> None: """Emit a signal for a switch toggle.""" self.switchToggled.emit(identifier, state) return - def _bumpIndex(self): + def _bumpIndex(self) -> None: """Increase the index counter and make sure only the last columns is stretching. """ diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 9734bc45..876172e1 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -84,9 +84,7 @@ class GuiDocEditor(QTextEdit): logger.debug("Create: GuiDocEditor") # Class Variables - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._nwDocument = None self._nwItem = None @@ -134,7 +132,7 @@ class GuiDocEditor(QTextEdit): self.docSearch = GuiDocEditSearch(self) # Syntax - self.spEnchant = NWSpellEnchant(self.theProject) + self.spEnchant = NWSpellEnchant(self.mainGui.project) self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant) # Context Menu @@ -229,14 +227,14 @@ class GuiDocEditor(QTextEdit): """Update the syntax highlighting theme. """ mainPalette = self.palette() - mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) - mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack)) + mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(mainPalette) docPalette = self.viewport().palette() - docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) - docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack)) + docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.viewport().setPalette(docPalette) self.docHeader.matchColours() @@ -342,7 +340,7 @@ class GuiDocEditor(QTextEdit): document is new (empty string), we set up the editor for editing the file. """ - self._nwDocument = self.theProject.storage.getDocument(tHandle) + self._nwDocument = self.mainGui.project.storage.getDocument(tHandle) self._nwItem = self._nwDocument.getCurrentItem() theDoc = self._nwDocument.readDocument() @@ -518,10 +516,10 @@ class GuiDocEditor(QTextEdit): self.setDocumentChanged(False) oldHeader = self._nwItem.mainHeading - oldCount = self.theProject.index.getHandleHeaderCount(tHandle) - self.theProject.index.scanText(tHandle, docText) + oldCount = self.mainGui.project.index.getHandleHeaderCount(tHandle) + self.mainGui.project.index.scanText(tHandle, docText) newHeader = self._nwItem.mainHeading - newCount = self.theProject.index.getHandleHeaderCount(tHandle) + newCount = self.mainGui.project.index.getHandleHeaderCount(tHandle) if self._nwItem.itemClass == nwItemClass.NOVEL: if oldCount == newCount: @@ -700,10 +698,10 @@ class GuiDocEditor(QTextEdit): """Set the spell checker dictionary language, and emit the dictionary changed signal. """ - if self.theProject.data.spellLang is None: + if self.mainGui.project.data.spellLang is None: theLang = CONFIG.spellLanguage else: - theLang = self.theProject.data.spellLang + theLang = self.mainGui.project.data.spellLang self.spEnchant.setLanguage(theLang) _, theProvider = self.spEnchant.describeDict() @@ -736,7 +734,7 @@ class GuiDocEditor(QTextEdit): self._spellCheck = theMode self.mainGui.mainMenu.setSpellCheck(theMode) - self.theProject.data.setSpellCheck(theMode) + self.mainGui.project.data.setSpellCheck(theMode) self.highLight.setSpellCheck(theMode) if not self._bigDoc or theMode is False: # We don't run the spell checker automatically on big docs @@ -1918,7 +1916,7 @@ class GuiDocEditor(QTextEdit): if theText.startswith("@"): - isGood, tBits, tPos = self.theProject.index.scanThis(theText) + isGood, tBits, tPos = self.mainGui.project.index.scanThis(theText) if not isGood: return False @@ -2223,10 +2221,8 @@ class GuiDocEditSearch(QFrame): logger.debug("Create: GuiDocEditSearch") - self.docEditor = docEditor - self.mainGui = docEditor.mainGui - self.theProject = docEditor.theProject - self.mainTheme = docEditor.mainTheme + self.docEditor = docEditor + self.mainGui = docEditor.mainGui self.repVisible = False self.isCaseSense = CONFIG.searchCase @@ -2237,9 +2233,9 @@ class GuiDocEditSearch(QFrame): self.doMatchCap = CONFIG.searchMatchCap mPx = CONFIG.pxInt(6) - tPx = int(0.8*self.mainTheme.fontPixelSize) - self.boxFont = self.mainTheme.guiFont - self.boxFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + tPx = int(0.8*CONFIG.theme.fontPixelSize) + self.boxFont = CONFIG.theme.guiFont + self.boxFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) self.setContentsMargins(0, 0, 0, 0) self.setAutoFillBackground(True) @@ -2272,7 +2268,7 @@ class GuiDocEditSearch(QFrame): self.resultLabel = QLabel("?/?") self.resultLabel.setFont(self.boxFont) - self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont)) + self.resultLabel.setMinimumWidth(CONFIG.theme.getTextWidth("?/?", self.boxFont)) self.toggleCase = QAction(self.tr("Case Sensitive"), self) self.toggleCase.setCheckable(True) @@ -2378,15 +2374,15 @@ class GuiDocEditSearch(QFrame): self.replaceBox.setPalette(qPalette) # Set icons - self.toggleCase.setIcon(self.mainTheme.getIcon("search_case")) - self.toggleWord.setIcon(self.mainTheme.getIcon("search_word")) - self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex")) - self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop")) - self.toggleProject.setIcon(self.mainTheme.getIcon("search_project")) - self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve")) - self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel")) - self.searchButton.setIcon(self.mainTheme.getIcon("search")) - self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace")) + self.toggleCase.setIcon(CONFIG.theme.getIcon("search_case")) + self.toggleWord.setIcon(CONFIG.theme.getIcon("search_word")) + self.toggleRegEx.setIcon(CONFIG.theme.getIcon("search_regex")) + self.toggleLoop.setIcon(CONFIG.theme.getIcon("search_loop")) + self.toggleProject.setIcon(CONFIG.theme.getIcon("search_project")) + self.toggleMatchCap.setIcon(CONFIG.theme.getIcon("search_preserve")) + self.cancelSearch.setIcon(CONFIG.theme.getIcon("search_cancel")) + self.searchButton.setIcon(CONFIG.theme.getIcon("search")) + self.replaceButton.setIcon(CONFIG.theme.getIcon("search_replace")) # Set stylesheets self.searchOpt.setStyleSheet("QToolBar {padding: 0;}") @@ -2478,7 +2474,7 @@ class GuiDocEditSearch(QFrame): """ currRes = "?" if currRes is None else currRes resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount - minWidth = self.mainTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont) + minWidth = CONFIG.theme.getTextWidth(f"{resCount}//{resCount}", self.boxFont) self.resultLabel.setText(f"{currRes}/{resCount}") self.resultLabel.setMinimumWidth(minWidth) self.adjustSize() @@ -2638,14 +2634,12 @@ class GuiDocEditHeader(QWidget): logger.debug("Create: GuiDocEditHeader") - self.docEditor = docEditor - self.mainGui = docEditor.mainGui - self.theProject = docEditor.theProject - self.mainTheme = docEditor.mainTheme + self.docEditor = docEditor + self.mainGui = docEditor.mainGui self._docHandle = None - fPx = int(0.9*self.mainTheme.fontPixelSize) + fPx = int(0.9*CONFIG.theme.fontPixelSize) hSp = CONFIG.pxInt(6) # Main Widget Settings @@ -2662,7 +2656,7 @@ class GuiDocEditHeader(QWidget): self.theTitle.setFixedHeight(fPx) lblFont = self.theTitle.font() - lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) self.theTitle.setFont(lblFont) # Buttons @@ -2732,15 +2726,15 @@ class GuiDocEditHeader(QWidget): def updateTheme(self): """Update theme elements. """ - self.editButton.setIcon(self.mainTheme.getIcon("edit")) - self.searchButton.setIcon(self.mainTheme.getIcon("search")) - self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise")) - self.closeButton.setIcon(self.mainTheme.getIcon("close")) + self.editButton.setIcon(CONFIG.theme.getIcon("edit")) + self.searchButton.setIcon(CONFIG.theme.getIcon("search")) + self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise")) + self.closeButton.setIcon(CONFIG.theme.getIcon("close")) buttonStyle = ( "QToolButton {{border: none; background: transparent;}} " "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" - ).format(*self.mainTheme.colText) + ).format(*CONFIG.theme.colText) self.editButton.setStyleSheet(buttonStyle) self.searchButton.setStyleSheet(buttonStyle) @@ -2756,9 +2750,9 @@ class GuiDocEditHeader(QWidget): theme rather than the main GUI. """ thePalette = QPalette() - thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) - thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText)) + thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(thePalette) self.theTitle.setPalette(thePalette) @@ -2778,17 +2772,18 @@ class GuiDocEditHeader(QWidget): self.minmaxButton.setVisible(False) return True + pTree = self.mainGui.project.tree if CONFIG.showFullPath: tTitle = [] - tTree = self.theProject.tree.getItemPath(tHandle) + tTree = pTree.getItemPath(tHandle) for aHandle in reversed(tTree): - nwItem = self.theProject.tree[aHandle] + nwItem = pTree[aHandle] if nwItem is not None: tTitle.append(nwItem.itemName) sSep = " %s " % nwUnicode.U_RSAQUO self.theTitle.setText(sSep.join(tTitle)) else: - nwItem = self.theProject.tree[tHandle] + nwItem = pTree[tHandle] if nwItem is None: return False self.theTitle.setText(nwItem.itemName) @@ -2806,9 +2801,9 @@ class GuiDocEditHeader(QWidget): toggleFocusMode function and should not be activated directly. """ if self.mainGui.isFocusMode: - self.minmaxButton.setIcon(self.mainTheme.getIcon("minimise")) + self.minmaxButton.setIcon(CONFIG.theme.getIcon("minimise")) else: - self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise")) + self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise")) return ## @@ -2873,23 +2868,21 @@ class GuiDocEditFooter(QWidget): logger.debug("Create: GuiDocEditFooter") - self.docEditor = docEditor - self.mainGui = docEditor.mainGui - self.theProject = docEditor.theProject - self.mainTheme = docEditor.mainTheme + self.docEditor = docEditor + self.mainGui = docEditor.mainGui self._theItem = None self._docHandle = None self._docSelection = False - self.sPx = int(round(0.9*self.mainTheme.baseIconSize)) - fPx = int(0.9*self.mainTheme.fontPixelSize) + self.sPx = int(round(0.9*CONFIG.theme.baseIconSize)) + fPx = int(0.9*CONFIG.theme.fontPixelSize) bSp = CONFIG.pxInt(4) hSp = CONFIG.pxInt(6) lblFont = self.font() - lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) # Main Widget Settings self.setContentsMargins(0, 0, 0, 0) @@ -2976,8 +2969,8 @@ class GuiDocEditFooter(QWidget): def updateTheme(self): """Update theme elements. """ - self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx))) - self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx))) + self.linesIcon.setPixmap(CONFIG.theme.getPixmap("status_lines", (self.sPx, self.sPx))) + self.wordsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (self.sPx, self.sPx))) self.matchColours() @@ -2988,9 +2981,9 @@ class GuiDocEditFooter(QWidget): theme rather than the main GUI. """ thePalette = QPalette() - thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) - thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText)) + thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(thePalette) self.statusText.setPalette(thePalette) @@ -3007,7 +3000,7 @@ class GuiDocEditFooter(QWidget): logger.debug("No handle set, so clearing the editor footer") self._theItem = None else: - self._theItem = self.theProject.tree[self._docHandle] + self._theItem = self.mainGui.project.tree[self._docHandle] self.setHasSelection(False) self.updateInfo() diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py index 46904842..1032c886 100644 --- a/novelwriter/gui/dochighlight.py +++ b/novelwriter/gui/dochighlight.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Syntax Highlighter ==================================== -Class for the main document editor syntax highlighter File History: Created: 2019-04-06 [0.0.1] @@ -22,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 . """ +from __future__ import annotations import logging @@ -54,8 +54,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.theDoc = theDoc self.spEnchant = spEnchant self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject self.theHandle = None self.spellCheck = False self.spellRx = None @@ -87,24 +85,24 @@ class GuiDocHighlighter(QSyntaxHighlighter): """ logger.debug("Setting up highlighting rules") - self.colHead = QColor(*self.mainTheme.colHead) - self.colHeadH = QColor(*self.mainTheme.colHeadH) - self.colDialN = QColor(*self.mainTheme.colDialN) - self.colDialD = QColor(*self.mainTheme.colDialD) - self.colDialS = QColor(*self.mainTheme.colDialS) - self.colHidden = QColor(*self.mainTheme.colHidden) - self.colKey = QColor(*self.mainTheme.colKey) - self.colVal = QColor(*self.mainTheme.colVal) - self.colSpell = QColor(*self.mainTheme.colSpell) - self.colError = QColor(*self.mainTheme.colError) - self.colRepTag = QColor(*self.mainTheme.colRepTag) - self.colMod = QColor(*self.mainTheme.colMod) - self.colBreak = QColor(*self.mainTheme.colEmph) + self.colHead = QColor(*CONFIG.theme.colHead) + self.colHeadH = QColor(*CONFIG.theme.colHeadH) + self.colDialN = QColor(*CONFIG.theme.colDialN) + self.colDialD = QColor(*CONFIG.theme.colDialD) + self.colDialS = QColor(*CONFIG.theme.colDialS) + self.colHidden = QColor(*CONFIG.theme.colHidden) + self.colKey = QColor(*CONFIG.theme.colKey) + self.colVal = QColor(*CONFIG.theme.colVal) + self.colSpell = QColor(*CONFIG.theme.colSpell) + self.colError = QColor(*CONFIG.theme.colError) + self.colRepTag = QColor(*CONFIG.theme.colRepTag) + self.colMod = QColor(*CONFIG.theme.colMod) + self.colBreak = QColor(*CONFIG.theme.colEmph) self.colBreak.setAlpha(64) self.colEmph = None if CONFIG.highlightEmph: - self.colEmph = QColor(*self.mainTheme.colEmph) + self.colEmph = QColor(*CONFIG.theme.colEmph) self.hStyles = { "header1": self._makeFormat(self.colHead, "bold", 1.8), @@ -287,8 +285,8 @@ class GuiDocHighlighter(QSyntaxHighlighter): if theText.startswith("@"): # Keywords and commands self.setCurrentBlockState(self.BLOCK_META) - pIndex = self.theProject.index - tItem = self.mainGui.theProject.tree[self.theHandle] + pIndex = self.mainGui.project.index + tItem = self.mainGui.project.tree[self.theHandle] isValid, theBits, thePos = pIndex.scanThis(theText) isGood = pIndex.checkThese(theBits, tItem) if isValid: diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 168030b2..fdb5435f 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Document Viewer ================================= -GUI classes for the main document viewer File History: Created: 2019-05-10 [0.0.1] GuiDocViewer @@ -26,6 +25,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 . """ +from __future__ import annotations import logging @@ -59,9 +59,7 @@ class GuiDocViewer(QTextBrowser): logger.debug("Create: GuiDocViewer") # Class Variables - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui # Internal Variables self._docHandle = None @@ -121,14 +119,14 @@ class GuiDocViewer(QTextBrowser): # Set the widget colours to match syntax theme mainPalette = self.palette() - mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) - mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack)) + mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(mainPalette) docPalette = self.viewport().palette() - docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) - docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack)) + docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.viewport().setPalette(docPalette) self.docHeader.matchColours() @@ -164,7 +162,7 @@ class GuiDocViewer(QTextBrowser): def loadText(self, tHandle, updateHistory=True): """Load text into the viewer from an item handle. """ - if not self.theProject.tree.checkType(tHandle, nwItemType.FILE): + if not self.mainGui.project.tree.checkType(tHandle, nwItemType.FILE): logger.warning("Item not found") return False @@ -172,7 +170,7 @@ class GuiDocViewer(QTextBrowser): qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) sPos = self.verticalScrollBar().value() - aDoc = ToHtml(self.theProject) + aDoc = ToHtml(self.mainGui.project) aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis) aDoc.setLinkHeaders(True) @@ -212,7 +210,7 @@ class GuiDocViewer(QTextBrowser): self.verticalScrollBar().setValue(sPos) self._docHandle = tHandle - self.theProject._data.setLastHandle(tHandle, "viewer") + self.mainGui.project.data.setLastHandle(tHandle, "viewer") self.docHeader.setTitleFromHandle(self._docHandle) self.updateDocMargins() @@ -508,27 +506,27 @@ class GuiDocViewer(QTextBrowser): " text-align: center;" "}}\n" ).format( - tColR=self.mainTheme.colText[0], - tColG=self.mainTheme.colText[1], - tColB=self.mainTheme.colText[2], - hColR=self.mainTheme.colHead[0], - hColG=self.mainTheme.colHead[1], - hColB=self.mainTheme.colHead[2], - aColR=self.mainTheme.colVal[0], - aColG=self.mainTheme.colVal[1], - aColB=self.mainTheme.colVal[2], - eColR=self.mainTheme.colEmph[0], - eColG=self.mainTheme.colEmph[1], - eColB=self.mainTheme.colEmph[2], - kColR=self.mainTheme.colKey[0], - kColG=self.mainTheme.colKey[1], - kColB=self.mainTheme.colKey[2], - cColR=self.mainTheme.colHidden[0], - cColG=self.mainTheme.colHidden[1], - cColB=self.mainTheme.colHidden[2], - mColR=self.mainTheme.colMod[0], - mColG=self.mainTheme.colMod[1], - mColB=self.mainTheme.colMod[2], + tColR=CONFIG.theme.colText[0], + tColG=CONFIG.theme.colText[1], + tColB=CONFIG.theme.colText[2], + hColR=CONFIG.theme.colHead[0], + hColG=CONFIG.theme.colHead[1], + hColB=CONFIG.theme.colHead[2], + aColR=CONFIG.theme.colVal[0], + aColG=CONFIG.theme.colVal[1], + aColB=CONFIG.theme.colVal[2], + eColR=CONFIG.theme.colEmph[0], + eColG=CONFIG.theme.colEmph[1], + eColB=CONFIG.theme.colEmph[2], + kColR=CONFIG.theme.colKey[0], + kColG=CONFIG.theme.colKey[1], + kColB=CONFIG.theme.colKey[2], + cColR=CONFIG.theme.colHidden[0], + cColG=CONFIG.theme.colHidden[1], + cColB=CONFIG.theme.colHidden[2], + mColR=CONFIG.theme.colMod[0], + mColG=CONFIG.theme.colMod[1], + mColB=CONFIG.theme.colMod[2], ) self.document().setDefaultStyleSheet(styleSheet) @@ -681,15 +679,13 @@ class GuiDocViewHeader(QWidget): logger.debug("Create: GuiDocViewHeader") - self.docViewer = docViewer - self.mainGui = docViewer.mainGui - self.theProject = docViewer.theProject - self.mainTheme = docViewer.mainTheme + self.docViewer = docViewer + self.mainGui = docViewer.mainGui # Internal Variables self._docHandle = None - fPx = int(0.9*self.mainTheme.fontPixelSize) + fPx = int(0.9*CONFIG.theme.fontPixelSize) hSp = CONFIG.pxInt(6) # Main Widget Settings @@ -706,7 +702,7 @@ class GuiDocViewHeader(QWidget): self.theTitle.setFixedHeight(fPx) lblFont = self.theTitle.font() - lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) self.theTitle.setFont(lblFont) # Buttons @@ -777,15 +773,15 @@ class GuiDocViewHeader(QWidget): def updateTheme(self): """Update theme elements. """ - self.backButton.setIcon(self.mainTheme.getIcon("backward")) - self.forwardButton.setIcon(self.mainTheme.getIcon("forward")) - self.refreshButton.setIcon(self.mainTheme.getIcon("refresh")) - self.closeButton.setIcon(self.mainTheme.getIcon("close")) + self.backButton.setIcon(CONFIG.theme.getIcon("backward")) + self.forwardButton.setIcon(CONFIG.theme.getIcon("forward")) + self.refreshButton.setIcon(CONFIG.theme.getIcon("refresh")) + self.closeButton.setIcon(CONFIG.theme.getIcon("close")) buttonStyle = ( "QToolButton {{border: none; background: transparent;}} " "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" - ).format(*self.mainTheme.colText) + ).format(*CONFIG.theme.colText) self.backButton.setStyleSheet(buttonStyle) self.forwardButton.setStyleSheet(buttonStyle) @@ -801,9 +797,9 @@ class GuiDocViewHeader(QWidget): theme rather than the main GUI. """ thePalette = QPalette() - thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) - thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText)) + thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(thePalette) self.theTitle.setPalette(thePalette) @@ -823,17 +819,18 @@ class GuiDocViewHeader(QWidget): self.refreshButton.setVisible(False) return True + pTree = self.mainGui.project.tree if CONFIG.showFullPath: tTitle = [] - tTree = self.theProject.tree.getItemPath(tHandle) + tTree = pTree.getItemPath(tHandle) for aHandle in reversed(tTree): - nwItem = self.theProject.tree[aHandle] + nwItem = pTree[aHandle] if nwItem is not None: tTitle.append(nwItem.itemName) sSep = " %s " % nwUnicode.U_RSAQUO self.theTitle.setText(sSep.join(tTitle)) else: - nwItem = self.theProject.tree[tHandle] + nwItem = pTree[tHandle] if nwItem is None: return False self.theTitle.setText(nwItem.itemName) @@ -900,13 +897,12 @@ class GuiDocViewFooter(QWidget): self.docViewer = docViewer self.mainGui = docViewer.mainGui - self.mainTheme = docViewer.mainTheme self.viewMeta = docViewer.mainGui.viewMeta # Internal Variables self._docHandle = None - fPx = int(0.9*self.mainTheme.fontPixelSize) + fPx = int(0.9*CONFIG.theme.fontPixelSize) bSp = CONFIG.pxInt(2) hSp = CONFIG.pxInt(8) @@ -991,7 +987,7 @@ class GuiDocViewFooter(QWidget): self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop) lblFont = self.font() - lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) self.lblRefs.setFont(lblFont) self.lblSticky.setFont(lblFont) self.lblComments.setFont(lblFont) @@ -1036,21 +1032,21 @@ class GuiDocViewFooter(QWidget): """ # Icons - fPx = int(0.9*self.mainTheme.fontPixelSize) + fPx = int(0.9*CONFIG.theme.fontPixelSize) - stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx)) - stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx)) + stickyOn = CONFIG.theme.getPixmap("sticky-on", (fPx, fPx)) + stickyOff = CONFIG.theme.getPixmap("sticky-off", (fPx, fPx)) stickyIcon = QIcon() stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On) stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off) - bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx)) - bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx)) + bulletOn = CONFIG.theme.getPixmap("bullet-on", (fPx, fPx)) + bulletOff = CONFIG.theme.getPixmap("bullet-off", (fPx, fPx)) bulletIcon = QIcon() bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On) bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off) - self.showHide.setIcon(self.mainTheme.getIcon("reference")) + self.showHide.setIcon(CONFIG.theme.getIcon("reference")) self.stickyRefs.setIcon(stickyIcon) self.showComments.setIcon(bulletIcon) self.showSynopsis.setIcon(bulletIcon) @@ -1060,7 +1056,7 @@ class GuiDocViewFooter(QWidget): buttonStyle = ( "QToolButton {{border: none; background: transparent;}} " "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" - ).format(*self.mainTheme.colText) + ).format(*CONFIG.theme.colText) self.showHide.setStyleSheet(buttonStyle) self.stickyRefs.setStyleSheet(buttonStyle) @@ -1076,9 +1072,9 @@ class GuiDocViewFooter(QWidget): theme rather than the main GUI. """ thePalette = QPalette() - thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) - thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) - thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) + thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack)) + thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText)) + thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText)) self.setPalette(thePalette) self.lblRefs.setPalette(thePalette) @@ -1141,9 +1137,7 @@ class GuiDocViewDetails(QScrollArea): logger.debug("Create: GuiDocViewDetails") - self.mainGui = mainGui - self.theProject = mainGui.theProject - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui self.refList = QLabel("") self.refList.setWordWrap(True) @@ -1151,9 +1145,7 @@ class GuiDocViewDetails(QScrollArea): self.refList.setScaledContents(True) self.refList.linkActivated.connect(self._linkClicked) - self.linkStyle = "style='color: rgb({0},{1},{2})'".format( - *self.mainTheme.colLink - ) + self.linkStyle = "style='color: rgb({0},{1},{2})'".format(*CONFIG.theme.colLink) # Assemble self.outerWidget = QWidget() @@ -1180,10 +1172,10 @@ class GuiDocViewDetails(QScrollArea): if self.mainGui.docViewer.stickyRef: return - theRefs = self.theProject.index.getBackReferenceList(tHandle) + theRefs = self.mainGui.project.index.getBackReferenceList(tHandle) theList = [] for tHandle in theRefs: - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None: theList.append("%s" % ( tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py index 163e0d4d..df11ce2c 100644 --- a/novelwriter/gui/itemdetails.py +++ b/novelwriter/gui/itemdetails.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Item Details Panel ==================================== -GUI class for the project tree item details panel File History: Created: 2019-04-24 [0.0.1] @@ -22,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 . """ +from __future__ import annotations import logging @@ -42,9 +42,7 @@ class GuiItemDetails(QWidget): logger.debug("Create: GuiItemDetails") - self.mainGui = mainGui - self.theProject = mainGui.theProject - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui # Internal Variables self._itemHandle = None @@ -53,7 +51,7 @@ class GuiItemDetails(QWidget): hSp = CONFIG.pxInt(6) vSp = CONFIG.pxInt(1) mPx = CONFIG.pxInt(6) - fPt = self.mainTheme.fontPointSize + fPt = CONFIG.theme.fontPointSize fntLabel = QFont() fntLabel.setBold(True) @@ -178,8 +176,8 @@ class GuiItemDetails(QWidget): self.updateTheme() # Make sure the columns for flags and counts don't resize too often - flagWidth = self.mainTheme.getTextWidth("Mm", fntValue) - countWidth = self.mainTheme.getTextWidth("99,999", fntValue) + flagWidth = CONFIG.theme.getTextWidth("Mm", fntValue) + countWidth = CONFIG.theme.getTextWidth("99,999", fntValue) self.mainBox.setColumnMinimumWidth(1, flagWidth) self.mainBox.setColumnMinimumWidth(4, countWidth) @@ -235,13 +233,13 @@ class GuiItemDetails(QWidget): self.clearDetails() return - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] if nwItem is None: self.clearDetails() return self._itemHandle = tHandle - iPx = int(round(0.8*self.mainTheme.baseIconSize)) + iPx = int(round(0.8*CONFIG.theme.baseIconSize)) # Label # ===== @@ -252,11 +250,11 @@ class GuiItemDetails(QWidget): if nwItem.isFileType(): if nwItem.isActive: - self.labelIcon.setPixmap(self.mainTheme.getPixmap("checked", (iPx, iPx))) + self.labelIcon.setPixmap(CONFIG.theme.getPixmap("checked", (iPx, iPx))) else: - self.labelIcon.setPixmap(self.mainTheme.getPixmap("unchecked", (iPx, iPx))) + self.labelIcon.setPixmap(CONFIG.theme.getPixmap("unchecked", (iPx, iPx))) else: - self.labelIcon.setPixmap(self.mainTheme.getPixmap("noncheckable", (iPx, iPx))) + self.labelIcon.setPixmap(CONFIG.theme.getPixmap("noncheckable", (iPx, iPx))) self.labelData.setText(theLabel) @@ -270,14 +268,14 @@ class GuiItemDetails(QWidget): # Class # ===== - classIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]) + classIcon = CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]) self.classIcon.setPixmap(classIcon.pixmap(iPx, iPx)) self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass])) # Layout # ====== - usageIcon = self.mainTheme.getItemIcon( + usageIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading ) self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx)) diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 80b1f8ae..dce2ca55 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Main Menu =========================== -GUI class for the main window menu File History: Created: 2019-04-27 [0.0.1] @@ -22,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 . """ +from __future__ import annotations import logging @@ -51,8 +51,7 @@ class GuiMainMenu(QMenuBar): logger.debug("Create: GuiMainMenu") - self.mainGui = mainGui - self.theProject = mainGui.theProject + self.mainGui = mainGui # Build Menu self._buildProjectMenu() @@ -380,10 +379,10 @@ class GuiMainMenu(QMenuBar): """Assemble the Insert menu. """ # Insert - self.insertMenu = self.addMenu(self.tr("&Insert")) + self.insMenu = self.addMenu(self.tr("&Insert")) # Insert > Dashes and Dots - self.mInsDashes = self.insertMenu.addMenu(self.tr("Dashes")) + self.mInsDashes = self.insMenu.addMenu(self.tr("Dashes")) # Insert > Short Dash self.aInsENDash = QAction(self.tr("Short Dash"), self) @@ -410,7 +409,7 @@ class GuiMainMenu(QMenuBar): self.mInsDashes.addAction(self.aInsFigDash) # Insert > Quote Marks - self.mInsQuotes = self.insertMenu.addMenu(self.tr("Quote Marks")) + self.mInsQuotes = self.insMenu.addMenu(self.tr("Quote Marks")) # Insert > Left Single Quote self.aInsQuoteLS = QAction(self.tr("Left Single Quote"), self) @@ -443,7 +442,7 @@ class GuiMainMenu(QMenuBar): self.mInsQuotes.addAction(self.aInsMSApos) # Insert > Symbols - self.mInsPunct = self.insertMenu.addMenu(self.tr("General Punctuation")) + self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation")) # Insert > Ellipsis self.aInsEllipsis = QAction(self.tr("Ellipsis"), self) @@ -464,7 +463,7 @@ class GuiMainMenu(QMenuBar): self.mInsPunct.addAction(self.aInsDPrime) # Insert > White Spaces - self.mInsSpace = self.insertMenu.addMenu(self.tr("White Spaces")) + self.mInsSpace = self.insMenu.addMenu(self.tr("White Spaces")) # Insert > Non-Breaking Space self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self) @@ -485,7 +484,7 @@ class GuiMainMenu(QMenuBar): self.mInsSpace.addAction(self.aInsThinNBSpace) # Insert > Symbols - self.mInsSymbol = self.insertMenu.addMenu(self.tr("Other Symbols")) + self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols")) # Insert > List Bullet self.aInsBullet = QAction(self.tr("List Bullet"), self) @@ -536,7 +535,7 @@ class GuiMainMenu(QMenuBar): self.mInsSymbol.addAction(self.aInsDivide) # Insert > Tags and References - self.mInsKeywords = self.insertMenu.addMenu(self.tr("Tags and References")) + self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References")) self.mInsKWItems = {} self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G") self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V") @@ -557,7 +556,7 @@ class GuiMainMenu(QMenuBar): self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0]) # Insert > Special Comments - self.mInsComments = self.insertMenu.addMenu(self.tr("Special Comments")) + self.mInsComments = self.insMenu.addMenu(self.tr("Special Comments")) # Insert > Synopsis Comment self.aInsSynopsis = QAction(self.tr("Synopsis Comment"), self) @@ -566,7 +565,7 @@ class GuiMainMenu(QMenuBar): self.mInsComments.addAction(self.aInsSynopsis) # Insert > Symbols - self.mInsBreaks = self.insertMenu.addMenu(self.tr("Page Break and Space")) + self.mInsBreaks = self.insMenu.addMenu(self.tr("Page Break and Space")) # Insert > New Page self.aInsNewPage = QAction(self.tr("Page Break"), self) @@ -586,7 +585,7 @@ class GuiMainMenu(QMenuBar): # Insert > Placeholder Text self.aLipsumText = QAction(self.tr("Placeholder Text"), self) self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog()) - self.insertMenu.addAction(self.aLipsumText) + self.insMenu.addAction(self.aLipsumText) return @@ -796,7 +795,7 @@ class GuiMainMenu(QMenuBar): # Tools > Check Spelling self.aSpellCheck = QAction(self.tr("Check Spelling"), self) self.aSpellCheck.setCheckable(True) - self.aSpellCheck.setChecked(self.theProject.data.spellCheck) + self.aSpellCheck.setChecked(self.mainGui.project.data.spellCheck) self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled! self.aSpellCheck.setShortcut("Ctrl+F7") self.toolsMenu.addAction(self.aSpellCheck) @@ -826,7 +825,7 @@ class GuiMainMenu(QMenuBar): # Tools > Backup Project self.aBackupProject = QAction(self.tr("Backup Project"), self) - self.aBackupProject.triggered.connect(lambda: self.theProject.backupProject(True)) + self.aBackupProject.triggered.connect(lambda: self.mainGui.project.backupProject(True)) self.toolsMenu.addAction(self.aBackupProject) # Tools > Build Manuscript diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py index cd0fd593..98d36fe9 100644 --- a/novelwriter/gui/noveltree.py +++ b/novelwriter/gui/noveltree.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Novel Tree ============================ -GUI class for the main window novel tree File History: Created: 2020-12-20 [1.1rc1] GuiNovelTree @@ -24,6 +23,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 . """ +from __future__ import annotations import logging @@ -42,7 +42,7 @@ from novelwriter import CONFIG from novelwriter.enum import nwDocMode, nwItemClass, nwOutline from novelwriter.common import minmax from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst -from novelwriter.gui.components import NovelSelector +from novelwriter.extensions.novelselector import NovelSelector logger = logging.getLogger(__name__) @@ -66,8 +66,7 @@ class GuiNovelView(QWidget): def __init__(self, mainGui): super().__init__(parent=mainGui) - self.mainGui = mainGui - self.theProject = mainGui.theProject + self.mainGui = mainGui # Build GUI self.novelTree = GuiNovelTree(self) @@ -118,16 +117,16 @@ class GuiNovelView(QWidget): def openProjectTasks(self): """Run open project tasks. """ - lastNovel = self.theProject.data.getLastHandle("novelTree") - if lastNovel not in self.theProject.tree: - lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL) + lastNovel = self.mainGui.project.data.getLastHandle("novelTree") + if lastNovel not in self.mainGui.project.tree: + lastNovel = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL) logger.debug("Setting novel tree to root item '%s'", lastNovel) - lastCol = self.theProject.options.getEnum( + lastCol = self.mainGui.project.options.getEnum( "GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN ) - lastColSize = self.theProject.options.getInt( + lastColSize = self.mainGui.project.options.getInt( "GuiNovelView", "lastColSize", 25 ) @@ -147,8 +146,9 @@ class GuiNovelView(QWidget): """ lastColType = self.novelTree.lastColType lastColSize = self.novelTree.lastColSize - self.theProject.options.setValue("GuiNovelView", "lastCol", lastColType) - self.theProject.options.setValue("GuiNovelView", "lastColSize", lastColSize) + pOptions = self.mainGui.project.options + pOptions.setValue("GuiNovelView", "lastCol", lastColType) + pOptions.setValue("GuiNovelView", "lastColSize", lastColSize) return def setTreeFocus(self): @@ -170,7 +170,7 @@ class GuiNovelView(QWidget): def refreshTree(self): """Refresh the current tree. """ - self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree")) + self.novelTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("novelTree")) return @pyqtSlot(str) @@ -198,12 +198,10 @@ class GuiNovelToolBar(QWidget): logger.debug("Create: GuiNovelToolBar") - self.novelView = novelView - self.mainGui = novelView.mainGui - self.theProject = novelView.mainGui.theProject - self.mainTheme = novelView.mainGui.mainTheme + self.novelView = novelView + self.mainGui = novelView.mainGui - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize mPx = CONFIG.pxInt(2) self.setContentsMargins(0, 0, 0, 0) @@ -213,7 +211,7 @@ class GuiNovelToolBar(QWidget): selFont = self.font() selFont.setWeight(QFont.Bold) self.novelPrefix = self.tr("Outline of {0}") - self.novelValue = NovelSelector(self, self.theProject, self.mainGui) + self.novelValue = NovelSelector(self, self.mainGui) self.novelValue.setFont(selFont) self.novelValue.setMinimumWidth(CONFIG.pxInt(150)) self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) @@ -276,9 +274,9 @@ class GuiNovelToolBar(QWidget): """Update theme elements. """ # Icons - self.tbNovel.setIcon(self.mainTheme.getIcon("cls_novel")) - self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh")) - self.tbMore.setIcon(self.mainTheme.getIcon("menu")) + self.tbNovel.setIcon(CONFIG.theme.getIcon("cls_novel")) + self.tbRefresh.setIcon(CONFIG.theme.getIcon("refresh")) + self.tbMore.setIcon(CONFIG.theme.getIcon("menu")) qPalette = self.palette() qPalette.setBrush(QPalette.Window, qPalette.base()) @@ -347,7 +345,7 @@ class GuiNovelToolBar(QWidget): def _refreshNovelTree(self): """Rebuild the current tree. """ - rootHandle = self.theProject.data.getLastHandle("novelTree") + rootHandle = self.mainGui.project.data.getLastHandle("novelTree") self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True) return @@ -399,10 +397,8 @@ class GuiNovelTree(QTreeWidget): logger.debug("Create: GuiNovelTree") - self.novelView = novelView - self.mainGui = novelView.mainGui - self.mainTheme = novelView.mainGui.mainTheme - self.theProject = novelView.mainGui.theProject + self.novelView = novelView + self.mainGui = novelView.mainGui # Internal Variables self._treeMap = {} @@ -419,7 +415,7 @@ class GuiNovelTree(QTreeWidget): # Build GUI # ========= - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize cMg = CONFIG.pxInt(6) self.setIconSize(QSize(iPx, iPx)) @@ -485,8 +481,8 @@ class GuiNovelTree(QTreeWidget): def updateTheme(self): """Update theme elements. """ - iPx = self.mainTheme.baseIconSize - self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx) + iPx = CONFIG.theme.baseIconSize + self._pMore = CONFIG.theme.loadDecoration("deco_doc_more", pxH=iPx) return ## @@ -518,10 +514,10 @@ class GuiNovelTree(QTreeWidget): """ logger.debug("Requesting refresh of the novel tree") if rootHandle is None: - rootHandle = self.theProject.tree.findRoot(nwItemClass.NOVEL) + rootHandle = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL) treeChanged = self.mainGui.projView.changedSince(self._lastBuild) - indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild) + indexChanged = self.mainGui.project.index.rootChangedSince(rootHandle, self._lastBuild) if not (treeChanged or indexChanged or overRide): logger.debug("No changes have been made to the novel index") return @@ -532,7 +528,7 @@ class GuiNovelTree(QTreeWidget): titleKey = selItem[0].data(self.C_DATA, self.D_KEY) self._populateTree(rootHandle) - self.theProject.data.setLastHandle(rootHandle, "novelTree") + self.mainGui.project.data.setLastHandle(rootHandle, "novelTree") if titleKey is not None and titleKey in self._treeMap: self._treeMap[titleKey].setSelected(True) @@ -542,7 +538,7 @@ class GuiNovelTree(QTreeWidget): def refreshHandle(self, tHandle): """Refresh the data for a given handle. """ - idxData = self.theProject.index.getItemData(tHandle) + idxData = self.mainGui.project.index.getItemData(tHandle) if idxData is None: return @@ -579,7 +575,7 @@ class GuiNovelTree(QTreeWidget): self._lastCol = colType self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN) if doRefresh: - lastNovel = self.theProject.data.getLastHandle("novelTree") + lastNovel = self.mainGui.project.data.getLastHandle("novelTree") self.refreshTree(rootHandle=lastNovel, overRide=True) return @@ -711,7 +707,7 @@ class GuiNovelTree(QTreeWidget): tStart = time() logger.debug("Building novel tree for root item '%s'", rootHandle) - novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True) + novStruct = self.mainGui.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True) for tKey, tHandle, sTitle, novIdx in novStruct: if novIdx.level == "H0": continue @@ -737,7 +733,7 @@ class GuiNovelTree(QTreeWidget): """Set the tree item values from the index entry. """ iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0) - hDec = self.mainTheme.getHeaderDecoration(iLevel) + hDec = CONFIG.theme.getHeaderDecoration(iLevel) trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec) trItem.setText(self.C_TITLE, idxItem.title) @@ -763,7 +759,7 @@ class GuiNovelTree(QTreeWidget): refData = [] refName = "" - theRefs = self.theProject.index.getReferences(tHandle, sTitle) + theRefs = self.mainGui.project.index.getReferences(tHandle, sTitle) if self._lastCol == NovelTreeColumn.POV: refData = theRefs[nwKeyWords.POV_KEY] refName = self._povLabel @@ -787,7 +783,7 @@ class GuiNovelTree(QTreeWidget): """ logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle) - pIndex = self.theProject.index + pIndex = self.mainGui.project.index novIdx = pIndex.getItemHeader(tHandle, sTitle) refTags = pIndex.getReferences(tHandle, sTitle) diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index e19b19a3..73440f1f 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Project Outline ================================= -GUI class for the project outline view File History: Created: 2022-05-15 [2.0rc1] GuiOutlineView @@ -26,6 +25,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 . """ +from __future__ import annotations import logging @@ -48,7 +48,7 @@ from novelwriter.enum import ( from novelwriter.error import logException from novelwriter.common import checkInt from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels -from novelwriter.gui.components import NovelSelector +from novelwriter.extensions.novelselector import NovelSelector logger = logging.getLogger(__name__) @@ -62,8 +62,7 @@ class GuiOutlineView(QWidget): def __init__(self, mainGui): super().__init__(parent=mainGui) - self.mainGui = mainGui - self.theProject = mainGui.theProject + self.mainGui = mainGui # Build GUI self.outlineTree = GuiOutlineTree(self) @@ -118,7 +117,7 @@ class GuiOutlineView(QWidget): def refreshTree(self): """Refresh the current tree. """ - self.outlineTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("outline")) + self.outlineTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("outline")) return def clearProject(self): @@ -131,9 +130,9 @@ class GuiOutlineView(QWidget): def openProjectTasks(self): """Run open project tasks. """ - lastOutline = self.theProject.data.getLastHandle("outline") - if not (lastOutline in self.theProject.tree or lastOutline is None): - lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL) + lastOutline = self.mainGui.project.data.getLastHandle("outline") + if not (lastOutline in self.mainGui.project.tree or lastOutline is None): + lastOutline = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL) logger.debug("Setting outline tree to root item '%s'", lastOutline) @@ -215,9 +214,7 @@ class GuiOutlineToolBar(QToolBar): logger.debug("Create: GuiOutlineToolBar") - self.mainGui = theOutline.mainGui - self.theProject = theOutline.mainGui.theProject - self.mainTheme = theOutline.mainGui.mainTheme + self.mainGui = theOutline.mainGui iPx = CONFIG.pxInt(22) mPx = CONFIG.pxInt(12) @@ -233,7 +230,7 @@ class GuiOutlineToolBar(QToolBar): self.novelLabel = QLabel(self.tr("Outline of")) self.novelLabel.setContentsMargins(0, 0, mPx, 0) - self.novelValue = NovelSelector(self, self.theProject, self.mainGui) + self.novelValue = NovelSelector(self, self.mainGui) self.novelValue.setMinimumWidth(CONFIG.pxInt(200)) self.novelValue.novelSelectionChanged.connect(self._novelValueChanged) @@ -275,8 +272,8 @@ class GuiOutlineToolBar(QToolBar): self.setStyleSheet("QToolBar {border: 0px;}") self.novelValue.updateList(includeAll=True) - self.aRefresh.setIcon(self.mainTheme.getIcon("refresh")) - self.tbColumns.setIcon(self.mainTheme.getIcon("menu")) + self.aRefresh.setIcon(CONFIG.theme.getIcon("refresh")) + self.tbColumns.setIcon(CONFIG.theme.getIcon("menu")) return @@ -374,8 +371,6 @@ class GuiOutlineTree(QTreeWidget): self.outlineView = outlineView self.mainGui = outlineView.mainGui - self.theProject = outlineView.mainGui.theProject - self.mainTheme = outlineView.mainGui.mainTheme self.setUniformRowHeights(True) self.setFrameStyle(QFrame.NoFrame) @@ -386,7 +381,7 @@ class GuiOutlineTree(QTreeWidget): self.itemDoubleClicked.connect(self._treeDoubleClick) self.itemSelectionChanged.connect(self._itemSelected) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize self.setIconSize(QSize(iPx, iPx)) self.setIndentation(0) @@ -403,11 +398,11 @@ class GuiOutlineTree(QTreeWidget): self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()] self._dIcon = { - "H0": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"), - "H1": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"), - "H2": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"), - "H3": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"), - "H4": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"), + "H0": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"), + "H1": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"), + "H2": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"), + "H3": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"), + "H4": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"), } # Internals @@ -493,13 +488,13 @@ class GuiOutlineTree(QTreeWidget): # If the novel index or novel tree has changed since the tree # was last built, we rebuild the tree from the updated index. - indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild) + indexChanged = self.mainGui.project.index.rootChangedSince(rootHandle, self._lastBuild) if not (novelChanged or indexChanged or overRide): logger.debug("No changes have been made to the novel index") return self._populateTree(rootHandle) - self.theProject.data.setLastHandle(rootHandle or None, "outline") + self.mainGui.project.data.setLastHandle(rootHandle or None, "outline") return @@ -579,7 +574,7 @@ class GuiOutlineTree(QTreeWidget): """ # Load whatever we saved last time, regardless of wether it # contains the correct names or number of columns. - colState = self.theProject.options.getValue("GuiOutline", "columnState", {}) + colState = self.mainGui.project.options.getValue("GuiOutline", "columnState", {}) tmpOrder = [] tmpHidden = {} @@ -630,7 +625,7 @@ class GuiOutlineTree(QTreeWidget): logHidden, orgWidth if logHidden and logWidth == 0 else logWidth ] - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiOutline", "columnState", colState) pOptions.saveSettings() @@ -666,7 +661,7 @@ class GuiOutlineTree(QTreeWidget): headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight) headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight) - novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True) + novStruct = self.mainGui.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True) for _, tHandle, sTitle, novIdx in novStruct: iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0) @@ -674,8 +669,8 @@ class GuiOutlineTree(QTreeWidget): continue trItem = QTreeWidgetItem() - nwItem = self.theProject.tree[tHandle] - hDec = self.mainTheme.getHeaderDecoration(iLevel) + nwItem = self.mainGui.project.tree[tHandle] + hDec = CONFIG.theme.getHeaderDecoration(iLevel) trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, hDec) trItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title) @@ -694,7 +689,7 @@ class GuiOutlineTree(QTreeWidget): trItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight) trItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight) - refs = self.theProject.index.getReferences(tHandle, sTitle) + refs = self.mainGui.project.index.getReferences(tHandle, sTitle) trItem.setText(self._colIdx[nwOutline.POV], ", ".join(refs[nwKeyWords.POV_KEY])) trItem.setText(self._colIdx[nwOutline.FOCUS], ", ".join(refs[nwKeyWords.FOCUS_KEY])) trItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(refs[nwKeyWords.CHAR_KEY])) @@ -776,13 +771,11 @@ class GuiOutlineDetails(QScrollArea): self.theOutline = theOutline self.mainGui = theOutline.mainGui - self.theProject = theOutline.mainGui.theProject - self.mainTheme = theOutline.mainGui.mainTheme # Sizes - minTitle = 30*self.mainTheme.textNWidth - maxTitle = 40*self.mainTheme.textNWidth - wCount = self.mainTheme.getTextWidth("999,999") + minTitle = 30*CONFIG.theme.textNWidth + maxTitle = 40*CONFIG.theme.textNWidth + wCount = CONFIG.theme.getTextWidth("999,999") hSpace = int(CONFIG.pxInt(10)) vSpace = int(CONFIG.pxInt(4)) @@ -1012,8 +1005,8 @@ class GuiOutlineDetails(QScrollArea): """Update the content of the tree with the given handle and line number pointing to a header. """ - pIndex = self.theProject.index - nwItem = self.theProject.tree[tHandle] + pIndex = self.mainGui.project.index + nwItem = self.mainGui.project.tree[tHandle] novIdx = pIndex.getItemHeader(tHandle, sTitle) theRefs = pIndex.getReferences(tHandle, sTitle) if nwItem is None or novIdx is None: @@ -1056,7 +1049,7 @@ class GuiOutlineDetails(QScrollArea): def updateClasses(self): """Update the visibility status of class details. """ - usedClasses = self.theProject.tree.rootClasses() + usedClasses = self.mainGui.project.tree.rootClasses() pltVisible = nwItemClass.PLOT in usedClasses timVisible = nwItemClass.TIMELINE in usedClasses diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index de547135..a4dedd46 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Project Tree ============================== -GUI classes for the main window project tree File History: Created: 2018-09-29 [0.0.1] GuiProjectTree @@ -233,13 +232,11 @@ class GuiProjectToolBar(QWidget): logger.debug("Create: GuiProjectToolBar") - self.projView = projView - self.projTree = projView.projTree - self.mainGui = projView.mainGui - self.theProject = projView.mainGui.theProject - self.mainTheme = projView.mainGui.mainTheme + self.projView = projView + self.projTree = projView.projTree + self.mainGui = projView.mainGui - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize mPx = CONFIG.pxInt(2) self.setContentsMargins(0, 0, 0, 0) @@ -370,16 +367,16 @@ class GuiProjectToolBar(QWidget): self.tbAdd.setStyleSheet(buttonStyle) self.tbMore.setStyleSheet(buttonStyle) - self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark")) - self.tbMoveU.setIcon(self.mainTheme.getIcon("up")) - self.tbMoveD.setIcon(self.mainTheme.getIcon("down")) - self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document")) - self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter")) - self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene")) - self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note")) - self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder")) - self.tbAdd.setIcon(self.mainTheme.getIcon("add")) - self.tbMore.setIcon(self.mainTheme.getIcon("menu")) + self.tbQuick.setIcon(CONFIG.theme.getIcon("bookmark")) + self.tbMoveU.setIcon(CONFIG.theme.getIcon("up")) + self.tbMoveD.setIcon(CONFIG.theme.getIcon("down")) + self.aAddEmpty.setIcon(CONFIG.theme.getIcon("proj_document")) + self.aAddChap.setIcon(CONFIG.theme.getIcon("proj_chapter")) + self.aAddScene.setIcon(CONFIG.theme.getIcon("proj_scene")) + self.aAddNote.setIcon(CONFIG.theme.getIcon("proj_note")) + self.aAddFolder.setIcon(CONFIG.theme.getIcon("proj_folder")) + self.tbAdd.setIcon(CONFIG.theme.getIcon("add")) + self.tbMore.setIcon(CONFIG.theme.getIcon("menu")) self.buildQuickLinkMenu() self._buildRootMenu() @@ -395,10 +392,10 @@ class GuiProjectToolBar(QWidget): """Build the quick link menu.""" logger.debug("Rebuilding quick links menu") self.mQuick.clear() - for n, (tHandle, nwItem) in enumerate(self.theProject.tree.iterRoots(None)): + for n, (tHandle, nwItem) in enumerate(self.mainGui.project.tree.iterRoots(None)): aRoot = self.mQuick.addAction(nwItem.itemName) aRoot.setData(tHandle) - aRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])) + aRoot.setIcon(CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])) aRoot.triggered.connect( lambda n, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True) ) @@ -412,7 +409,7 @@ class GuiProjectToolBar(QWidget): """Build the rood folder menu.""" def addClass(itemClass): aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass])) - aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass])) + aNew.setIcon(CONFIG.theme.getIcon(nwLabels.CLASS_ICON[itemClass])) aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass)) self.mAddRoot.addAction(aNew) return @@ -441,7 +438,7 @@ class GuiProjectToolBar(QWidget): documents. They should only be visible if novel documents can actually be added. """ - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed() self.aAddEmpty.setVisible(allowDoc) self.aAddChap.setVisible(allowDoc) @@ -467,10 +464,8 @@ class GuiProjectTree(QTreeWidget): logger.debug("Create: GuiProjectTree") - self.projView = projView - self.mainGui = projView.mainGui - self.mainTheme = projView.mainGui.mainTheme - self.theProject = projView.mainGui.theProject + self.projView = projView + self.mainGui = projView.mainGui # Internal Variables self._treeMap = {} @@ -485,7 +480,7 @@ class GuiProjectTree(QTreeWidget): self.customContextMenuRequested.connect(self._openContextMenu) # Tree Settings - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize cMg = CONFIG.pxInt(6) self.setIconSize(QSize(iPx, iPx)) @@ -577,15 +572,15 @@ class GuiProjectTree(QTreeWidget): if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass): - tHandle = self.theProject.newRoot(itemClass) + tHandle = self.mainGui.project.newRoot(itemClass) sHandle = self.getSelectedHandle() - pItem = self.theProject.tree[sHandle] if sHandle else None + pItem = self.mainGui.project.tree[sHandle] if sHandle else None nHandle = pItem.itemRoot if pItem else None elif itemType in (nwItemType.FILE, nwItemType.FOLDER): sHandle = self.getSelectedHandle() - pItem = self.theProject.tree[sHandle] if sHandle else None + pItem = self.mainGui.project.tree[sHandle] if sHandle else None if sHandle is None or pItem is None: self.mainGui.makeAlert(self.tr( "Did not find anywhere to add the file or folder!" @@ -597,7 +592,7 @@ class GuiProjectTree(QTreeWidget): sLevel = nwHeaders.H_LEVEL.get(pItem.mainHeading, 0) sIsParent = False if qItem is None else qItem.childCount() > 0 - if self.theProject.tree.isTrash(sHandle): + if self.mainGui.project.tree.isTrash(sHandle): self.mainGui.makeAlert(self.tr( "Cannot add new files or folders to the Trash folder." ), level=nwAlert.ERROR) @@ -640,9 +635,9 @@ class GuiProjectTree(QTreeWidget): # Add the file or folder if itemType == nwItemType.FILE: - tHandle = self.theProject.newFile(newLabel, sHandle) + tHandle = self.mainGui.project.newFile(newLabel, sHandle) else: - tHandle = self.theProject.newFolder(newLabel, sHandle) + tHandle = self.mainGui.project.newFolder(newLabel, sHandle) else: logger.error("Failed to add new item") @@ -655,7 +650,7 @@ class GuiProjectTree(QTreeWidget): # Handle new file creation if itemType == nwItemType.FILE and hLevel > 0: - self.theProject.writeNewFile(tHandle, hLevel, not isNote) + self.mainGui.project.writeNewFile(tHandle, hLevel, not isNote) # Add the new item to the project tree self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True) @@ -666,7 +661,7 @@ class GuiProjectTree(QTreeWidget): def revealNewTreeItem(self, tHandle: str | None, nHandle: str | None = None, wordCount: bool = False) -> bool: """Reveal a newly added project item in the project tree.""" - nwItem = self.theProject.tree[tHandle] if tHandle else None + nwItem = self.mainGui.project.tree[tHandle] if tHandle else None if tHandle is None or nwItem is None: return False @@ -675,7 +670,7 @@ class GuiProjectTree(QTreeWidget): return False if nwItem.isFileType() and wordCount: - wC = self.theProject.index.getCounts(tHandle)[1] + wC = self.mainGui.project.index.getCounts(tHandle)[1] self.propagateCount(tHandle, wC) self.projView.wordCountsChanged.emit() @@ -750,7 +745,7 @@ class GuiProjectTree(QTreeWidget): def renameTreeItem(self, tHandle: str) -> bool: """Open a dialog to edit the label of an item.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is None: return False @@ -774,7 +769,7 @@ class GuiProjectTree(QTreeWidget): if isinstance(item, QTreeWidgetItem): theList = self._scanChildren(theList, item, i) logger.debug("Saving project tree item order") - self.theProject.setTreeOrder(theList) + self.mainGui.project.setTreeOrder(theList) return def getTreeFromHandle(self, tHandle: str) -> list[str]: @@ -807,16 +802,16 @@ class GuiProjectTree(QTreeWidget): logger.error("There is no item to delete") return False - trashHandle = self.theProject.tree.trashRoot() + trashHandle = self.mainGui.project.tree.trashRoot() if tHandle == trashHandle: logger.error("Cannot delete the Trash folder") return False - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] if nwItem is None: return False - if self.theProject.tree.isTrash(tHandle) or nwItem.isRootType(): + if self.mainGui.project.tree.isTrash(tHandle) or nwItem.isRootType(): status = self.permDeleteItem(tHandle) else: status = self.moveItemToTrash(tHandle) @@ -832,7 +827,7 @@ class GuiProjectTree(QTreeWidget): logger.error("No project open") return False - trashHandle = self.theProject.tree.trashRoot() + trashHandle = self.mainGui.project.tree.trashRoot() logger.debug("Emptying Trash folder") if trashHandle is None: @@ -875,13 +870,13 @@ class GuiProjectTree(QTreeWidget): so such a request is cancelled. """ trItemS = self._getTreeItem(tHandle) - nwItemS = self.theProject.tree[tHandle] + nwItemS = self.mainGui.project.tree[tHandle] if trItemS is None or nwItemS is None: logger.error("Could not find tree item for deletion") return False - if self.theProject.tree.isTrash(tHandle): + if self.mainGui.project.tree.isTrash(tHandle): logger.error("Item is already in the Trash folder") return False @@ -925,7 +920,7 @@ class GuiProjectTree(QTreeWidget): Root items are handled a little different than other items. """ trItemS = self._getTreeItem(tHandle) - nwItemS = self.theProject.tree[tHandle] + nwItemS = self.mainGui.project.tree[tHandle] if trItemS is None or nwItemS is None: logger.error("Could not find tree item for deletion") return False @@ -942,7 +937,7 @@ class GuiProjectTree(QTreeWidget): tIndex = self.indexOfTopLevelItem(trItemS) self.takeTopLevelItem(tIndex) - self.theProject.removeItem(tHandle) + self.mainGui.project.removeItem(tHandle) self._treeMap.pop(tHandle, None) self._alertTreeChange(tHandle, flush=True) @@ -971,7 +966,7 @@ class GuiProjectTree(QTreeWidget): for dHandle in reversed(self.getTreeFromHandle(tHandle)): if self.mainGui.docEditor.docHandle() == dHandle: self.mainGui.closeDocument() - self.theProject.removeItem(dHandle) + self.mainGui.project.removeItem(dHandle) self._treeMap.pop(dHandle, None) self._alertTreeChange(tHandle, flush=flush) @@ -989,13 +984,13 @@ class GuiProjectTree(QTreeWidget): already coming from the project tree. """ trItem = self._getTreeItem(tHandle) - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] if trItem is None or nwItem is None: return itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True) hLevel = nwItem.mainHeading - itemIcon = self.mainTheme.getItemIcon( + itemIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel ) @@ -1011,7 +1006,7 @@ class GuiProjectTree(QTreeWidget): else: iconName = "noncheckable" - trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) + trItem.setIcon(self.C_ACTIVE, CONFIG.theme.getIcon(iconName)) if CONFIG.emphLabels and nwItem.isDocumentLayout(): trFont = trItem.font(self.C_NAME) @@ -1051,10 +1046,10 @@ class GuiProjectTree(QTreeWidget): pHandle = pItem.data(self.C_DATA, self.D_HANDLE) if pHandle: - if self.theProject.tree.checkType(pHandle, nwItemType.FILE): + if self.mainGui.project.tree.checkType(pHandle, nwItemType.FILE): # A file has an internal word count we need to account # for, but a folder always has 0 words on its own. - pCount += self.theProject.index.getCounts(pHandle)[1] + pCount += self.mainGui.project.index.getCounts(pHandle)[1] self.propagateCount(pHandle, pCount, countChildren=False) @@ -1069,7 +1064,7 @@ class GuiProjectTree(QTreeWidget): logger.debug("Building the project tree ...") self.clearTree() count = 0 - for nwItem in self.theProject.getProjectItems(): + for nwItem in self.mainGui.project.getProjectItems(): count += 1 self._addTreeItem(nwItem) if count > 0: @@ -1182,7 +1177,7 @@ class GuiProjectTree(QTreeWidget): if tHandle is None: return - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is None: return @@ -1204,7 +1199,7 @@ class GuiProjectTree(QTreeWidget): selItem = self.itemAt(clickPos) if isinstance(selItem, QTreeWidgetItem): tHandle = selItem.data(self.C_DATA, self.D_HANDLE) - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] hasChild = selItem.childCount() > 0 if tItem is None or tHandle is None: @@ -1216,7 +1211,7 @@ class GuiProjectTree(QTreeWidget): # Trash Folder # ============ - trashHandle = self.theProject.tree.trashRoot() + trashHandle = self.mainGui.project.tree.trashRoot() if tItem.itemHandle == trashHandle and trashHandle is not None: # The trash folder only has one option aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash")) @@ -1255,7 +1250,7 @@ class GuiProjectTree(QTreeWidget): checkMark = f" ({nwUnicode.U_CHECK})" if tItem.isNovelLike(): mStatus = ctxMenu.addMenu(self.tr("Set Status to ...")) - for n, (key, entry) in enumerate(self.theProject.data.itemStatus.items()): + for n, (key, entry) in enumerate(self.mainGui.project.data.itemStatus.items()): entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "") aStatus = mStatus.addAction(entry["icon"], entryName) aStatus.triggered.connect( @@ -1268,7 +1263,7 @@ class GuiProjectTree(QTreeWidget): ) else: mImport = ctxMenu.addMenu(self.tr("Set Importance to ...")) - for n, (key, entry) in enumerate(self.theProject.data.itemImport.items()): + for n, (key, entry) in enumerate(self.mainGui.project.data.itemImport.items()): entryName = entry["name"] + (checkMark if tItem.itemImport == key else "") aImport = mImport.addAction(entry["icon"], entryName) aImport.triggered.connect( @@ -1380,7 +1375,7 @@ class GuiProjectTree(QTreeWidget): return tHandle = selItem.data(self.C_DATA, self.D_HANDLE) - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is None: return @@ -1424,7 +1419,7 @@ class GuiProjectTree(QTreeWidget): def _postItemMove(self, tHandle: str, wCount: int) -> bool: """Run various maintenance tasks for a moved item.""" trItemS = self._getTreeItem(tHandle) - nwItemS = self.theProject.tree[tHandle] + nwItemS = self.mainGui.project.tree[tHandle] trItemP = trItemS.parent() if trItemS else None if trItemP is None or nwItemS is None: logger.error("Failed to find new parent item of '%s'", tHandle) @@ -1441,13 +1436,13 @@ class GuiProjectTree(QTreeWidget): logger.debug("A total of %d item(s) were moved", len(mHandles)) for mHandle in mHandles: logger.debug("Updating item '%s'", mHandle) - self.theProject.tree.updateItemData(mHandle) + self.mainGui.project.tree.updateItemData(mHandle) # Update the index if nwItemS.isInactiveClass(): - self.theProject.index.deleteHandle(mHandle) + self.mainGui.project.index.deleteHandle(mHandle) else: - self.theProject.index.reIndexHandle(mHandle) + self.mainGui.project.index.reIndexHandle(mHandle) self.setTreeItemValues(mHandle) @@ -1467,7 +1462,7 @@ class GuiProjectTree(QTreeWidget): def _toggleItemActive(self, tHandle: str) -> None: """Toggle the active status of an item.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None: tItem.setActive(not tItem.isActive) self.setTreeItemValues(tItem.itemHandle) @@ -1488,7 +1483,7 @@ class GuiProjectTree(QTreeWidget): def _changeItemStatus(self, tHandle: str, tStatus: str) -> None: """Set a new status value of an item.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None: tItem.setStatus(tStatus) self.setTreeItemValues(tItem.itemHandle) @@ -1497,7 +1492,7 @@ class GuiProjectTree(QTreeWidget): def _changeItemImport(self, tHandle: str, tImport: str) -> None: """Set a new importance value of an item.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None: tItem.setImport(tImport) self.setTreeItemValues(tItem.itemHandle) @@ -1506,7 +1501,7 @@ class GuiProjectTree(QTreeWidget): def _changeItemLayout(self, tHandle: str, itemLayout: nwItemLayout) -> None: """Set a new item layout value of an item.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None: if itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed(): tItem.setLayout(nwItemLayout.DOCUMENT) @@ -1520,7 +1515,7 @@ class GuiProjectTree(QTreeWidget): def _covertFolderToFile(self, tHandle: str, itemLayout: nwItemLayout) -> None: """Convert a folder to a note or document.""" - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is not None and tItem.isFolderType(): msgYes = self.mainGui.askQuestion(self.tr( "Do you want to convert the folder to a {0}? " @@ -1545,7 +1540,7 @@ class GuiProjectTree(QTreeWidget): logger.info("Request to merge items under handle '%s'", tHandle) itemList = self.getTreeFromHandle(tHandle) - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is None: return False @@ -1571,7 +1566,7 @@ class GuiProjectTree(QTreeWidget): self.mainGui.saveDocument() # Create merge object, and append docs - docMerger = DocMerger(self.theProject) + docMerger = DocMerger(self.mainGui.project) mLabel = self.tr("Merged") if newFile: @@ -1593,7 +1588,7 @@ class GuiProjectTree(QTreeWidget): ) return False - self.theProject.index.reIndexHandle(mHandle) + self.mainGui.project.index.reIndexHandle(mHandle) if newFile: self.revealNewTreeItem(mHandle, nHandle=tHandle, wordCount=True) @@ -1618,7 +1613,7 @@ class GuiProjectTree(QTreeWidget): """Split a document into multiple documents.""" logger.info("Request to split items with handle '%s'", tHandle) - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem is None: return False @@ -1637,7 +1632,7 @@ class GuiProjectTree(QTreeWidget): intoFolder = splitData.get("intoFolder", False) docHierarchy = splitData.get("docHierarchy", False) - docSplit = DocSplitter(self.theProject, tHandle) + docSplit = DocSplitter(self.mainGui.project, tHandle) if intoFolder: fHandle = docSplit.newParentFolder(tItem.itemParent, tItem.itemName) self.revealNewTreeItem(fHandle, nHandle=tHandle) @@ -1647,7 +1642,7 @@ class GuiProjectTree(QTreeWidget): docSplit.splitDocument(headerList, splitText) for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy): - self.theProject.index.reIndexHandle(dHandle) + self.mainGui.project.index.reIndexHandle(dHandle) self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True) self._alertTreeChange(dHandle, flush=False) if not writeOk: @@ -1681,10 +1676,10 @@ class GuiProjectTree(QTreeWidget): if not self.mainGui.askQuestion(question): return False - docDup = DocDuplicator(self.theProject) + docDup = DocDuplicator(self.mainGui.project) dupCount = 0 for dHandle, nHandle in docDup.duplicate(itemTree): - self.theProject.index.reIndexHandle(dHandle) + self.mainGui.project.index.reIndexHandle(dHandle) self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True) self._alertTreeChange(dHandle, flush=False) dupCount += 1 @@ -1704,7 +1699,7 @@ class GuiProjectTree(QTreeWidget): cCount = tItem.childCount() # Update tree-related meta data - nwItem = self.theProject.tree[tHandle] + nwItem = self.mainGui.project.tree[tHandle] if nwItem is not None: nwItem.setExpanded(tItem.isExpanded() and cCount > 0) nwItem.setOrder(tIndex) @@ -1771,13 +1766,13 @@ class GuiProjectTree(QTreeWidget): """Adds the trash root folder if it doesn't already exist in the project tree. """ - trashHandle = self.theProject.trashFolder() + trashHandle = self.mainGui.project.trashFolder() if trashHandle is None: return None trItem = self._getTreeItem(trashHandle) if trItem is None: - trItem = self._addTreeItem(self.theProject.tree[trashHandle]) + trItem = self._addTreeItem(self.mainGui.project.tree[trashHandle]) if trItem is not None: trItem.setExpanded(True) self._alertTreeChange(trashHandle, flush=True) @@ -1790,14 +1785,14 @@ class GuiProjectTree(QTreeWidget): deleted. """ self._timeChanged = time() - self.theProject.setProjectChanged(True) + self.mainGui.project.setProjectChanged(True) if flush: self.saveTreeOrder() - if tHandle is None or tHandle not in self.theProject.tree: + if tHandle is None or tHandle not in self.mainGui.project.tree: return - tItem = self.theProject.tree[tHandle] + tItem = self.mainGui.project.tree[tHandle] if tItem and tItem.isRootType(): self.projView.rootFolderChanged.emit(tHandle) diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index 637e11a5..6ec9f308 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Main Window SideBar ===================================== -GUI class for the main window side bar File History: Created: 2022-05-10 [2.0rc1] @@ -22,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 . """ +from __future__ import annotations import logging @@ -45,15 +45,14 @@ class GuiSideBar(QToolBar): logger.debug("Create: GuiSideBar") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui # Style iPx = CONFIG.pxInt(22) mPx = CONFIG.pxInt(60) - lblFont = self.mainTheme.guiFont - lblFont.setPointSizeF(0.65*self.mainTheme.fontPointSize) + lblFont = CONFIG.theme.guiFont + lblFont.setPointSizeF(0.65*CONFIG.theme.fontPointSize) self.setMovable(False) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) @@ -131,13 +130,13 @@ class GuiSideBar(QToolBar): """ self.setStyleSheet("QToolBar {border: 0px;}") - self.aProject.setIcon(self.mainTheme.getIcon("view_editor")) - self.aNovel.setIcon(self.mainTheme.getIcon("view_novel")) - self.aOutline.setIcon(self.mainTheme.getIcon("view_outline")) - self.aBuild.setIcon(self.mainTheme.getIcon("view_build")) - self.aDetails.setIcon(self.mainTheme.getIcon("proj_details")) - self.aStats.setIcon(self.mainTheme.getIcon("proj_stats")) - self.tbSettings.setIcon(self.mainTheme.getIcon("settings")) + self.aProject.setIcon(CONFIG.theme.getIcon("view_editor")) + self.aNovel.setIcon(CONFIG.theme.getIcon("view_novel")) + self.aOutline.setIcon(CONFIG.theme.getIcon("view_outline")) + self.aBuild.setIcon(CONFIG.theme.getIcon("view_build")) + self.aDetails.setIcon(CONFIG.theme.getIcon("proj_details")) + self.aStats.setIcon(CONFIG.theme.getIcon("proj_stats")) + self.tbSettings.setIcon(CONFIG.theme.getIcon("settings")) return diff --git a/novelwriter/gui/statusbar.py b/novelwriter/gui/statusbar.py index 10f16c08..138d1cd3 100644 --- a/novelwriter/gui/statusbar.py +++ b/novelwriter/gui/statusbar.py @@ -1,7 +1,6 @@ """ novelWriter – GUI Main Window Status Bar ======================================== -GUI class for the main window status bar File History: Created: 2019-04-20 [0.0.1] GuiMainStatus @@ -23,6 +22,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 . """ +from __future__ import annotations import logging @@ -34,7 +34,7 @@ from PyQt5.QtWidgets import qApp, QStatusBar, QLabel from novelwriter import CONFIG from novelwriter.common import formatTime -from novelwriter.gui.components import StatusLED +from novelwriter.extensions.statusled import StatusLED logger = logging.getLogger(__name__) @@ -47,15 +47,14 @@ class GuiMainStatus(QStatusBar): logger.debug("Create: GuiMainStatus") self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme self.refTime = None self.userIdle = False - colNone = QColor(*self.mainTheme.statNone) - colSaved = QColor(*self.mainTheme.statSaved) - colUnsaved = QColor(*self.mainTheme.statUnsaved) + colNone = QColor(*CONFIG.theme.statNone) + colSaved = QColor(*CONFIG.theme.statSaved) + colUnsaved = QColor(*CONFIG.theme.statUnsaved) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize # Permanent Widgets # ================= @@ -99,7 +98,7 @@ class GuiMainStatus(QStatusBar): self.timeIcon = QLabel() self.timeText = QLabel("") self.timeText.setToolTip(self.tr("Session Time")) - self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:")) + self.timeText.setMinimumWidth(CONFIG.theme.getTextWidth("00:00:00:")) self.timeIcon.setContentsMargins(0, 0, 0, 0) self.timeText.setContentsMargins(0, 0, 0, 0) self.addPermanentWidget(self.timeIcon) @@ -129,13 +128,13 @@ class GuiMainStatus(QStatusBar): def updateTheme(self): """Update theme elements. """ - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize - self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx))) - self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx))) + self.langIcon.setPixmap(CONFIG.theme.getPixmap("status_lang", (iPx, iPx))) + self.statsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (iPx, iPx))) - self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx)) - self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx)) + self.timePixmap = CONFIG.theme.getPixmap("status_time", (iPx, iPx)) + self.idlePixmap = CONFIG.theme.getPixmap("status_idle", (iPx, iPx)) self.timeIcon.setPixmap(self.timePixmap) diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index c65fad5d..a91d9ae1 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -1,7 +1,6 @@ """ novelWriter – Theme and Icons Classes ===================================== -Classes managing and caching themes and icons File History: Created: 2019-05-18 [0.1.3] GuiTheme diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 6076d120..ab728dfe 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -113,8 +113,8 @@ class GuiMain(QMainWindow): # ============ # Core Classes - self.mainTheme = GuiTheme() - self.theProject = NWProject(self) + CONFIG.setThemeInstance(GuiTheme()) + self._project = NWProject(self) # Core Settings self.hasProject = False @@ -135,7 +135,7 @@ class GuiMain(QMainWindow): # ============= # Sizes - iPx = self.mainTheme.fontPixelSize + iPx = CONFIG.theme.fontPixelSize mPx = CONFIG.pxInt(4) hWd = CONFIG.pxInt(4) @@ -238,7 +238,7 @@ class GuiMain(QMainWindow): # Connect Signals # =============== - self.theProject.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus) + self._project.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus) self.viewsBar.viewChangeRequested.connect(self._changeView) @@ -307,10 +307,10 @@ class GuiMain(QMainWindow): # Cache Alert Pixmaps pxSize = (2*iPx, 2*iPx) self.alertPix: dict[nwAlert, QPixmap] = { - nwAlert.INFO: self.mainTheme.getPixmap("alert_info", pxSize), - nwAlert.WARN: self.mainTheme.getPixmap("alert_warn", pxSize), - nwAlert.ERROR: self.mainTheme.getPixmap("alert_error", pxSize), - nwAlert.ASK: self.mainTheme.getPixmap("alert_question", pxSize), + nwAlert.INFO: CONFIG.theme.getPixmap("alert_info", pxSize), + nwAlert.WARN: CONFIG.theme.getPixmap("alert_warn", pxSize), + nwAlert.ERROR: CONFIG.theme.getPixmap("alert_error", pxSize), + nwAlert.ASK: CONFIG.theme.getPixmap("alert_question", pxSize), } # Check that config loaded fine @@ -382,6 +382,15 @@ class GuiMain(QMainWindow): return + ## + # Properties + ## + + @property + def project(self) -> NWProject: + """The project instance.""" + return self._project + ## # Project Actions ## @@ -444,7 +453,7 @@ class GuiMain(QMainWindow): saveOK = self.saveProject() doBackup = False - if self.theProject.data.doBackup and CONFIG.backupOnClose: + if self._project.data.doBackup and CONFIG.backupOnClose: doBackup = True if CONFIG.askBeforeBackup: msgYes = self.askQuestion(self.tr("Backup the current project?")) @@ -452,7 +461,7 @@ class GuiMain(QMainWindow): doBackup = False if doBackup: - self.theProject.backupProject(False) + self._project.backupProject(False) if saveOK: self.closeDocument() @@ -460,7 +469,7 @@ class GuiMain(QMainWindow): self.outlineView.closeProjectTasks() self.novelView.closeProjectTasks() - self.theProject.closeProject(self.idleTime) + self._project.closeProject(self.idleTime) self.idleRefTime = time() self.idleTime = 0.0 @@ -484,9 +493,9 @@ class GuiMain(QMainWindow): self._changeView(nwView.PROJECT) # Try to open the project - if not self.theProject.openProject(projFile): + if not self._project.openProject(projFile): # The project open failed. - lockStatus = self.theProject.getLockStatus() + lockStatus = self._project.getLockStatus() if lockStatus is None: # The project is not locked, so failed for some other # reason handled by the project class. @@ -516,7 +525,7 @@ class GuiMain(QMainWindow): lockDetails = "" if self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN): - if not self.theProject.openProject(projFile, overrideLock=True): + if not self._project.openProject(projFile, overrideLock=True): return False else: return False @@ -527,11 +536,11 @@ class GuiMain(QMainWindow): self.idleTime = 0.0 # Update GUI - self._updateWindowTitle(self.theProject.data.name) + self._updateWindowTitle(self._project.data.name) self.rebuildTrees() self.docEditor.setDictionaries() - self.docEditor.toggleSpellCheck(self.theProject.data.spellCheck) - self.mainStatus.setRefTime(self.theProject.projOpened) + self.docEditor.toggleSpellCheck(self._project.data.spellCheck) + self.mainStatus.setRefTime(self._project.projOpened) self.projView.openProjectTasks() self.novelView.openProjectTasks() self.outlineView.openProjectTasks() @@ -539,9 +548,9 @@ class GuiMain(QMainWindow): # Restore previously open documents, if any # If none was recorded, open the first document found - lastEdited = self.theProject.data.getLastHandle("editor") + lastEdited = self._project.data.getLastHandle("editor") if lastEdited is None: - for nwItem in self.theProject.tree: + for nwItem in self._project.tree: if nwItem and nwItem.isFileType(): lastEdited = nwItem.itemHandle break @@ -549,19 +558,19 @@ class GuiMain(QMainWindow): if lastEdited is not None: self.openDocument(lastEdited, doScroll=True) - lastViewed = self.theProject.data.getLastHandle("viewer") + lastViewed = self._project.data.getLastHandle("viewer") if lastViewed is not None: self.viewDocument(lastViewed) # Check if we need to rebuild the index - if self.theProject.index.indexBroken: + if self._project.index.indexBroken: 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 qApp.processEvents() self.docEditor.setDocumentChanged(False) - self.theProject.setProjectChanged(False) + self._project.setProjectChanged(False) logger.debug("Project load complete") @@ -573,7 +582,7 @@ class GuiMain(QMainWindow): logger.error("No project open") return False self.projView.saveProjectTasks() - self.theProject.saveProject(autoSave=autoSave) + self._project.saveProject(autoSave=autoSave) return True ## @@ -606,7 +615,7 @@ class GuiMain(QMainWindow): logger.error("No project open") return False - if not tHandle or not self.theProject.tree.checkType(tHandle, nwItemType.FILE): + if not tHandle or not self._project.tree.checkType(tHandle, nwItemType.FILE): logger.debug("Requested item '%s' is not a document", tHandle) return False @@ -620,7 +629,7 @@ class GuiMain(QMainWindow): self.closeDocument(beforeOpen=True) if self.docEditor.loadText(tHandle, tLine): - self.theProject.data.setLastHandle(tHandle, "editor") + self._project.data.setLastHandle(tHandle, "editor") self.projView.setSelectedHandle(tHandle, doScroll=doScroll) self.novelView.setActiveHandle(tHandle) if changeFocus: @@ -641,7 +650,7 @@ class GuiMain(QMainWindow): nHandle = None # The next handle after tHandle fHandle = None # The first file handle we encounter foundIt = False # We've found tHandle, pick the next we see - for tItem in self.theProject.tree: + for tItem in self._project.tree: if not tItem.isFileType(): continue if fHandle is None: @@ -687,7 +696,7 @@ class GuiMain(QMainWindow): tHandle = self.projView.getSelectedHandle() if tHandle is None: - tHandle = self.theProject.data.getLastHandle("viewer") + tHandle = self._project.data.getLastHandle("viewer") if tHandle is None: logger.debug("No document to view, giving up") @@ -806,7 +815,7 @@ class GuiMain(QMainWindow): return False if tHandle is not None and sTitle is not None: - hItem = self.theProject.index.getItemHeader(tHandle, sTitle) + hItem = self._project.index.getItemHeader(tHandle, sTitle) if hItem is not None: tLine = hItem.line @@ -843,7 +852,7 @@ class GuiMain(QMainWindow): tStart = time() self.projView.saveProjectTasks() - self.theProject.index.rebuildIndex() + self._project.index.rebuildIndex() self.projView.populateTree() self.novelView.refreshTree() @@ -912,7 +921,7 @@ class GuiMain(QMainWindow): if dlgConf.updateTheme: # We are doing this manually instead of connecting to # qApp.paletteChanged since the processing order matters - self.mainTheme.loadTheme() + CONFIG.theme.loadTheme() self.docEditor.updateTheme() self.docViewer.updateTheme() self.viewsBar.updateTheme() @@ -923,7 +932,7 @@ class GuiMain(QMainWindow): self.mainStatus.updateTheme() if dlgConf.updateSyntax: - self.mainTheme.loadSyntax() + CONFIG.theme.loadSyntax() self.docEditor.updateSyntaxColours() self.docEditor.initEditor() @@ -951,7 +960,7 @@ class GuiMain(QMainWindow): if dlgProj.spellChanged: self.docEditor.setDictionaries() self.itemDetails.refreshDetails() - self._updateWindowTitle(self.theProject.data.name) + self._updateWindowTitle(self._project.data.name) return True @@ -1197,7 +1206,7 @@ class GuiMain(QMainWindow): def closeDocEditor(self) -> None: """Close the document editor. This does not hide the editor.""" self.closeDocument() - self.theProject.data.setLastHandle(None, "editor") + self._project.data.setLastHandle(None, "editor") return def closeDocViewer(self, byUser: bool = True) -> bool: @@ -1205,7 +1214,7 @@ class GuiMain(QMainWindow): self.docViewer.clearViewer() if byUser: # Only reset the last handle if the user called this - self.theProject.data.setLastHandle(None, "viewer") + self._project.data.setLastHandle(None, "viewer") # Hide the panel bPos = self.splitMain.sizes() @@ -1391,7 +1400,7 @@ class GuiMain(QMainWindow): """Handle the index lookup of a tag and display an alert if the tag cannot be found. """ - tHandle, sTitle = self.theProject.index.getTagSource(tag) + tHandle, sTitle = self._project.index.getTagSource(tag) if tHandle is None: self.makeAlert(self.tr( "Could not find the reference for tag '{0}'. It either doesn't " @@ -1438,7 +1447,7 @@ class GuiMain(QMainWindow): if tHandle is not None: if mode == nwDocMode.EDIT: tLine = None - hItem = self.theProject.index.getItemHeader(tHandle, sTitle) + hItem = self._project.index.getItemHeader(tHandle, sTitle) if hItem is not None: tLine = hItem.line self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus) @@ -1491,8 +1500,8 @@ class GuiMain(QMainWindow): def _autoSaveProject(self) -> None: """Autosave of the project. This is a timer-activated slot.""" doSave = self.hasProject - doSave &= self.theProject.projChanged - doSave &= self.theProject.storage.isOpen() + doSave &= self._project.projChanged + doSave &= self._project.storage.isOpen() if doSave: logger.debug("Autosaving project") self.saveProject(autoSave=True) @@ -1512,14 +1521,14 @@ class GuiMain(QMainWindow): if not self.hasProject: self.mainStatus.setProjectStats(0, 0) - self.theProject.updateWordCounts() + self._project.updateWordCounts() if CONFIG.incNotesWCount: - iTotal = sum(self.theProject.data.initCounts) - cTotal = sum(self.theProject.data.currCounts) + iTotal = sum(self._project.data.initCounts) + cTotal = sum(self._project.data.currCounts) self.mainStatus.setProjectStats(cTotal, cTotal - iTotal) else: - iNovel, _ = self.theProject.data.initCounts - cNovel, _ = self.theProject.data.currCounts + iNovel, _ = self._project.data.initCounts + cNovel, _ = self._project.data.currCounts self.mainStatus.setProjectStats(cNovel, cNovel - iNovel) return diff --git a/novelwriter/tools/lipsum.py b/novelwriter/tools/lipsum.py index c78e20de..d7aa9d51 100644 --- a/novelwriter/tools/lipsum.py +++ b/novelwriter/tools/lipsum.py @@ -1,7 +1,6 @@ """ novelWriter – Lorem Ipsum Tool ============================== -Simple tool for inserting placeholder text in a document File History: Created: 2022-04-02 [2.0rc1] @@ -22,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 . """ +from __future__ import annotations import random import logging @@ -49,8 +49,7 @@ class GuiLipsum(QDialog): if CONFIG.osDarwin: self.setWindowFlag(Qt.WindowType.Tool) - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui self.setWindowTitle(self.tr("Insert Placeholder Text")) @@ -61,7 +60,7 @@ class GuiLipsum(QDialog): nPx = CONFIG.pxInt(64) vSp = CONFIG.pxInt(4) self.docIcon = QLabel() - self.docIcon.setPixmap(self.mainTheme.getPixmap("proj_document", (nPx, nPx))) + self.docIcon.setPixmap(CONFIG.theme.getPixmap("proj_document", (nPx, nPx))) self.leftBox = QVBoxLayout() self.leftBox.setSpacing(vSp) diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py index 81df33c2..2fb91269 100644 --- a/novelwriter/tools/manusbuild.py +++ b/novelwriter/tools/manusbuild.py @@ -65,9 +65,7 @@ class GuiManuscriptBuild(QDialog): logger.debug("Create: GuiManuscriptBuild") self.setObjectName("GuiManuscriptBuild") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._parent = parent self._build = build @@ -76,14 +74,14 @@ class GuiManuscriptBuild(QDialog): self.setMinimumWidth(CONFIG.pxInt(500)) self.setMinimumHeight(CONFIG.pxInt(300)) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize sp4 = CONFIG.pxInt(4) sp8 = CONFIG.pxInt(8) sp16 = CONFIG.pxInt(16) wWin = CONFIG.pxInt(620) hWin = CONFIG.pxInt(360) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.resize( CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin)) @@ -148,7 +146,7 @@ class GuiManuscriptBuild(QDialog): # Build Path self.lblPath = QLabel(self.tr("Path")) self.buildPath = QLineEdit(self) - self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "") + self.btnBrowse = QPushButton(CONFIG.theme.getIcon("browse"), "") self.pathBox = QHBoxLayout() self.pathBox.addWidget(self.buildPath) @@ -158,7 +156,7 @@ class GuiManuscriptBuild(QDialog): # Build Name self.lblName = QLabel(self.tr("File Name")) self.buildName = QLineEdit(self) - self.btnReset = QPushButton(self.mainTheme.getIcon("revert"), "") + self.btnReset = QPushButton(CONFIG.theme.getIcon("revert"), "") self.btnReset.setToolTip(self.tr("Reset file name to default")) self.nameBox = QHBoxLayout() @@ -183,7 +181,7 @@ class GuiManuscriptBuild(QDialog): self.buildBox.setVerticalSpacing(sp4) # Dialog Buttons - self.btnBuild = QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build")) + self.btnBuild = QPushButton(CONFIG.theme.getIcon("export"), self.tr("&Build")) self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close) self.dlgButtons.addButton(self.btnBuild, QDialogButtonBox.ActionRole) @@ -281,7 +279,7 @@ class GuiManuscriptBuild(QDialog): @pyqtSlot() def _doResetBuildName(self): """Generate a default build name.""" - bName = f"{self.theProject.data.name} - {self._build.name}" + bName = f"{self.mainGui.project.data.name} - {self._build.name}" self.buildName.setText(bName) self._build.setLastBuildName(bName) return @@ -322,7 +320,7 @@ class GuiManuscriptBuild(QDialog): ): return False - docBuild = NWBuildDocument(self.theProject, self._build) + docBuild = NWBuildDocument(self.mainGui.project, self._build) docBuild.queueAll() self.buildProgress.setMaximum(len(docBuild)) @@ -355,7 +353,7 @@ class GuiManuscriptBuild(QDialog): fmtWidth = CONFIG.rpxInt(mainSplit[0]) sumWidth = CONFIG.rpxInt(mainSplit[1]) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth) pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight) pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth) @@ -367,9 +365,9 @@ class GuiManuscriptBuild(QDialog): def _populateContentList(self): """Build the content list.""" rootMap = {} - filtered = self._build.buildItemFilter(self.theProject) + filtered = self._build.buildItemFilter(self.mainGui.project) self.listContent.clear() - for nwItem in self.theProject.tree: + for nwItem in self.mainGui.project.tree: tHandle = nwItem.itemHandle rHandle = nwItem.itemRoot @@ -378,11 +376,11 @@ class GuiManuscriptBuild(QDialog): if filtered.get(tHandle, (False, 0))[0]: if rHandle not in rootMap: - rItem = self.theProject.tree[rHandle] + rItem = self.mainGui.project.tree[rHandle] if isinstance(rItem, NWItem): rootMap[rHandle] = rItem.itemName - itemIcon = self.mainTheme.getItemIcon( + itemIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading ) diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index e767dced..8666e47d 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -72,22 +72,20 @@ class GuiManuscript(QDialog): if CONFIG.osDarwin: self.setWindowFlag(Qt.WindowType.Tool) - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui - self._builds = BuildCollection(self.theProject) + self._builds = BuildCollection(self.mainGui.project) self._buildMap: dict[str, QListWidgetItem] = {} self.setWindowTitle(self.tr("Build Manuscript")) self.setMinimumWidth(CONFIG.pxInt(600)) self.setMinimumHeight(CONFIG.pxInt(500)) - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize wWin = CONFIG.pxInt(900) hWin = CONFIG.pxInt(600) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.resize( CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin)) @@ -107,21 +105,21 @@ class GuiManuscript(QDialog): ).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue()) self.tbAdd = QToolButton(self) - self.tbAdd.setIcon(self.mainTheme.getIcon("add")) + self.tbAdd.setIcon(CONFIG.theme.getIcon("add")) self.tbAdd.setIconSize(QSize(iPx, iPx)) self.tbAdd.setToolTip(self.tr("Add New Build")) self.tbAdd.setStyleSheet(buttonStyle) self.tbAdd.clicked.connect(self._createNewBuild) self.tbDel = QToolButton(self) - self.tbDel.setIcon(self.mainTheme.getIcon("remove")) + self.tbDel.setIcon(CONFIG.theme.getIcon("remove")) self.tbDel.setIconSize(QSize(iPx, iPx)) self.tbDel.setToolTip(self.tr("Delete Selected Build")) self.tbDel.setStyleSheet(buttonStyle) self.tbDel.clicked.connect(self._deleteSelectedBuild) self.tbEdit = QToolButton(self) - self.tbEdit.setIcon(self.mainTheme.getIcon("edit")) + self.tbEdit.setIcon(CONFIG.theme.getIcon("edit")) self.tbEdit.setIconSize(QSize(iPx, iPx)) self.tbEdit.setToolTip(self.tr("Edit Selected Build")) self.tbEdit.setStyleSheet(buttonStyle) @@ -212,7 +210,7 @@ class GuiManuscript(QDialog): self._updateBuildsList() logger.debug("Loading build cache") - cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json" + cache = CONFIG.dataPath("cache") / f"build_{self.mainGui.project.data.uuid}.json" if cache.is_file(): try: with open(cache, mode="r", encoding="utf-8") as fObj: @@ -291,7 +289,7 @@ class GuiManuscript(QDialog): if build is None: return - docBuild = NWBuildDocument(self.theProject, build) + docBuild = NWBuildDocument(self.mainGui.project, build) docBuild.queueAll() self.docPreview.beginNewBuild(len(docBuild)) @@ -311,7 +309,7 @@ class GuiManuscript(QDialog): self._updatePreview(result, build) logger.debug("Saving build cache") - cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json" + cache = CONFIG.dataPath("cache") / f"build_{self.mainGui.project.data.uuid}.json" try: with open(cache, mode="w+", encoding="utf-8") as outFile: outFile.write(json.dumps(result, indent=2)) @@ -392,7 +390,7 @@ class GuiManuscript(QDialog): optsWidth = CONFIG.rpxInt(mainSplit[0]) viewWidth = CONFIG.rpxInt(mainSplit[1]) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiManuscript", "winWidth", winWidth) pOptions.setValue("GuiManuscript", "winHeight", winHeight) pOptions.setValue("GuiManuscript", "optsWidth", optsWidth) @@ -428,7 +426,7 @@ class GuiManuscript(QDialog): for key, name in self._builds.builds(): bItem = QListWidgetItem() bItem.setText(name) - bItem.setIcon(self.mainTheme.getIcon("export")) + bItem.setIcon(CONFIG.theme.getIcon("export")) bItem.setData(self.D_KEY, key) self.buildList.addItem(bItem) self._buildMap[key] = bItem @@ -451,9 +449,7 @@ class _PreviewWidget(QTextBrowser): def __init__(self, mainGui: GuiMain): super().__init__(parent=mainGui) - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._docTime = 0 self._buildName = "" @@ -464,7 +460,7 @@ class _PreviewWidget(QTextBrowser): dPalette.setColor(QPalette.Text, QColor(0, 0, 0)) self.setPalette(dPalette) - self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth) + self.setMinimumWidth(40*CONFIG.theme.textNWidth) self.setTextFont(CONFIG.textFont, CONFIG.textSize) self.setTabStopDistance(CONFIG.getTabWidth()) self.setOpenExternalLinks(False) @@ -482,7 +478,7 @@ class _PreviewWidget(QTextBrowser): aPalette.setColor(QPalette.Foreground, aPalette.toolTipText().color()) aFont = self.font() - aFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) + aFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize) self.ageLabel = QLabel("", self) self.ageLabel.setIndent(0) @@ -490,7 +486,7 @@ class _PreviewWidget(QTextBrowser): self.ageLabel.setPalette(aPalette) self.ageLabel.setAutoFillBackground(True) self.ageLabel.setAlignment(Qt.AlignCenter) - self.ageLabel.setFixedHeight(int(2.1*self.mainTheme.fontPixelSize)) + self.ageLabel.setFixedHeight(int(2.1*CONFIG.theme.fontPixelSize)) # Progress self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16)) @@ -526,12 +522,12 @@ class _PreviewWidget(QTextBrowser): def setJustify(self, state: bool): """Enable/disable the justify text option.""" - options = self.document().defaultTextOption() + pOptions = self.document().defaultTextOption() if state: - options.setAlignment(Qt.AlignJustify) + pOptions.setAlignment(Qt.AlignJustify) else: - options.setAlignment(Qt.AlignAbsolute) - self.document().setDefaultTextOption(options) + pOptions.setAlignment(Qt.AlignAbsolute) + self.document().setDefaultTextOption(pOptions) return def setTextFont(self, family: str, size: int): diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index fb5eeba0..62eaba27 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -49,7 +49,6 @@ from novelwriter.extensions.pagedsidebar import NPagedSideBar if TYPE_CHECKING: # pragma: no cover from novelwriter.guimain import GuiMain - from novelwriter.gui.theme import GuiTheme logger = logging.getLogger(__name__) @@ -77,9 +76,7 @@ class GuiBuildSettings(QDialog): if CONFIG.osDarwin: self.setWindowFlag(Qt.WindowType.Tool) - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self._build = build @@ -91,7 +88,7 @@ class GuiBuildSettings(QDialog): wWin = CONFIG.pxInt(750) hWin = CONFIG.pxInt(550) - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.resize( CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winHeight", hWin)) @@ -103,7 +100,7 @@ class GuiBuildSettings(QDialog): self.optSideBar = NPagedSideBar(self) self.optSideBar.setMinimumWidth(mPx) self.optSideBar.setMaximumWidth(mPx) - self.optSideBar.setLabelColor(self.mainTheme.helpText) + self.optSideBar.setLabelColor(CONFIG.theme.helpText) self.optSideBar.addLabel(self.tr("Options")) self.optSideBar.addButton(self.tr("Selection"), self.OPT_FILTERS) @@ -265,7 +262,7 @@ class GuiBuildSettings(QDialog): treeWidth, filterWidth = self.optTabSelect.mainSplitSizes() - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiBuildSettings", "winWidth", winWidth) pOptions.setValue("GuiBuildSettings", "winHeight", winHeight) pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) @@ -306,18 +303,16 @@ class _FilterTab(QWidget): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: super().__init__(parent=buildMain) - self.mainGui = buildMain.mainGui - self.mainTheme = buildMain.mainGui.mainTheme - self.theProject = buildMain.mainGui.theProject + self.mainGui = buildMain.mainGui self._treeMap: dict[str, QTreeWidgetItem] = {} self._build = build self._statusFlags: dict[int, QIcon] = { self.F_NONE: QIcon(), - self.F_FILTERED: self.mainTheme.getIcon("build_filtered"), - self.F_INCLUDED: self.mainTheme.getIcon("build_included"), - self.F_EXCLUDED: self.mainTheme.getIcon("build_excluded"), + self.F_FILTERED: CONFIG.theme.getIcon("build_filtered"), + self.F_INCLUDED: CONFIG.theme.getIcon("build_included"), + self.F_EXCLUDED: CONFIG.theme.getIcon("build_excluded"), } self._trIncluded = self.tr("Included in manuscript") @@ -327,7 +322,7 @@ class _FilterTab(QWidget): # ============ # Tree Settings - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize cMg = CONFIG.pxInt(6) # Tree Widget @@ -365,7 +360,7 @@ class _FilterTab(QWidget): self.resetButton = QToolButton(self) self.resetButton.setToolTip(self.tr("Reset to default")) - self.resetButton.setIcon(self.mainTheme.getIcon("revert")) + self.resetButton.setIcon(CONFIG.theme.getIcon("revert")) self.resetButton.clicked.connect(lambda: self._setSelectedMode(self.F_FILTERED)) self.modeBox = QHBoxLayout() @@ -384,7 +379,7 @@ class _FilterTab(QWidget): # Assemble GUI # ============ - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.selectionBox = QVBoxLayout() self.selectionBox.addWidget(self.optTree) @@ -450,7 +445,7 @@ class _FilterTab(QWidget): logger.debug("Building project tree") self._treeMap = {} self.optTree.clear() - for nwItem in self.theProject.getProjectItems(): + for nwItem in self.mainGui.project.getProjectItems(): tHandle = nwItem.itemHandle pHandle = nwItem.itemParent @@ -466,7 +461,7 @@ class _FilterTab(QWidget): continue hLevel = nwItem.mainHeading - itemIcon = self.mainTheme.getItemIcon( + itemIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel ) @@ -480,7 +475,7 @@ class _FilterTab(QWidget): trItem.setText(self.C_NAME, nwItem.itemName) trItem.setData(self.C_DATA, self.D_HANDLE, tHandle) trItem.setData(self.C_DATA, self.D_FILE, isFile) - trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) + trItem.setIcon(self.C_ACTIVE, CONFIG.theme.getIcon(iconName)) trItem.setTextAlignment(self.C_NAME, Qt.AlignLeft) @@ -504,19 +499,19 @@ class _FilterTab(QWidget): self.filterOpt.clear() self.filterOpt.addLabel(self._build.getLabel("filter")) self.filterOpt.addItem( - self.mainTheme.getIcon("proj_scene"), + CONFIG.theme.getIcon("proj_scene"), self._build.getLabel("filter.includeNovel"), "doc:filter.includeNovel", default=self._build.getBool("filter.includeNovel") ) self.filterOpt.addItem( - self.mainTheme.getIcon("proj_note"), + CONFIG.theme.getIcon("proj_note"), self._build.getLabel("filter.includeNotes"), "doc:filter.includeNotes", default=self._build.getBool("filter.includeNotes") ) self.filterOpt.addItem( - self.mainTheme.getIcon("unchecked"), + CONFIG.theme.getIcon("unchecked"), self._build.getLabel("filter.includeInactive"), "doc:filter.includeInactive", default=self._build.getBool("filter.includeInactive") @@ -526,9 +521,9 @@ class _FilterTab(QWidget): # Root Classes self.filterOpt.addLabel(self.tr("Select Root Folders")) - for tHandle, nwItem in self.theProject.tree.iterRoots(None): + for tHandle, nwItem in self.mainGui.project.tree.iterRoots(None): if not nwItem.isInactiveClass(): - itemIcon = self.mainTheme.getItemIcon( + itemIcon = CONFIG.theme.getItemIcon( nwItem.itemType, nwItem.itemClass, nwItem.itemLayout ) self.filterOpt.addItem( @@ -562,7 +557,7 @@ class _FilterTab(QWidget): def _setTreeItemMode(self) -> None: """Update the filtered mode icon on all items.""" - filtered = self._build.buildItemFilter(self.theProject) + filtered = self._build.buildItemFilter(self.mainGui.project) for tHandle, item in self._treeMap.items(): allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN)) if mode == FilterMode.INCLUDED: @@ -602,14 +597,12 @@ class _HeadingsTab(QWidget): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: super().__init__(parent=buildMain) - self.mainGui = buildMain.mainGui - self.mainTheme = buildMain.mainGui.mainTheme - self.theProject = buildMain.mainGui.theProject + self.mainGui = buildMain.mainGui self._build = build self._editing = 0 - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize vSp = CONFIG.pxInt(12) bSp = CONFIG.pxInt(6) @@ -623,7 +616,7 @@ class _HeadingsTab(QWidget): self.fmtTitle = QLineEdit("") self.fmtTitle.setReadOnly(True) self.btnTitle = QToolButton() - self.btnTitle.setIcon(self.mainTheme.getIcon("edit")) + self.btnTitle.setIcon(CONFIG.theme.getIcon("edit")) self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE)) wrapTitle = QHBoxLayout() @@ -639,7 +632,7 @@ class _HeadingsTab(QWidget): self.fmtChapter = QLineEdit("") self.fmtChapter.setReadOnly(True) self.btnChapter = QToolButton() - self.btnChapter.setIcon(self.mainTheme.getIcon("edit")) + self.btnChapter.setIcon(CONFIG.theme.getIcon("edit")) self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER)) wrapChapter = QHBoxLayout() @@ -655,7 +648,7 @@ class _HeadingsTab(QWidget): self.fmtUnnumbered = QLineEdit("") self.fmtUnnumbered.setReadOnly(True) self.btnUnnumbered = QToolButton() - self.btnUnnumbered.setIcon(self.mainTheme.getIcon("edit")) + self.btnUnnumbered.setIcon(CONFIG.theme.getIcon("edit")) self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM)) wrapUnnumbered = QHBoxLayout() @@ -672,7 +665,7 @@ class _HeadingsTab(QWidget): self.fmtScene = QLineEdit("") self.fmtScene.setReadOnly(True) self.btnScene = QToolButton() - self.btnScene.setIcon(self.mainTheme.getIcon("edit")) + self.btnScene.setIcon(CONFIG.theme.getIcon("edit")) self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE)) self.hdeScene = QLabel(self.tr("Hide")) self.hdeScene.setToolTip(sceneHideTip) @@ -699,7 +692,7 @@ class _HeadingsTab(QWidget): self.fmtSection = QLineEdit("") self.fmtSection.setReadOnly(True) self.btnSection = QToolButton() - self.btnSection.setIcon(self.mainTheme.getIcon("edit")) + self.btnSection.setIcon(CONFIG.theme.getIcon("edit")) self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION)) self.hdeSection = QLabel(self.tr("Hide")) self.hdeSection.setToolTip(sectionHideTip) @@ -729,7 +722,7 @@ class _HeadingsTab(QWidget): self.editTextBox.setFixedHeight(5*iPx) self.editTextBox.setEnabled(False) - self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document(), self.mainTheme) + self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document()) self.menuInsert = QMenu() self.aInsTitle = self.menuInsert.addAction(self.tr("Title")) @@ -872,12 +865,12 @@ class _HeadingsTab(QWidget): class _HeadingSyntaxHighlighter(QSyntaxHighlighter): - def __init__(self, document: QTextDocument, mainTheme: GuiTheme) -> None: + def __init__(self, document: QTextDocument) -> None: super().__init__(document) self._fmtSymbol = QTextCharFormat() - self._fmtSymbol.setForeground(QColor(*mainTheme.colHead)) + self._fmtSymbol.setForeground(QColor(*CONFIG.theme.colHead)) self._fmtFormat = QTextCharFormat() - self._fmtFormat.setForeground(QColor(*mainTheme.colEmph)) + self._fmtFormat.setForeground(QColor(*CONFIG.theme.colEmph)) return def highlightBlock(self, text: str) -> None: @@ -901,12 +894,9 @@ class _ContentTab(QWidget): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: super().__init__(parent=buildMain) - self.mainGui = buildMain.mainGui - self.mainTheme = buildMain.mainGui.mainTheme - self._build = build - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize # Left Form # ========= @@ -973,16 +963,15 @@ class _FormatTab(QWidget): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: super().__init__(parent=buildMain) - self.buildMain = buildMain - self.mainGui = buildMain.mainGui - self.mainTheme = buildMain.mainGui.mainTheme + self.buildMain = buildMain + self.mainGui = buildMain.mainGui self._build = build self._unitScale = 1.0 - iPx = self.mainTheme.baseIconSize - spW = 6*self.mainTheme.textNWidth - dbW = 8*self.mainTheme.textNWidth + iPx = CONFIG.theme.baseIconSize + spW = 6*CONFIG.theme.textNWidth + dbW = 8*CONFIG.theme.textNWidth # Text Format Form # ================ @@ -1003,7 +992,7 @@ class _FormatTab(QWidget): self.textFont = QLineEdit() self.textFont.setReadOnly(True) self.btnTextFont = QPushButton("...") - self.btnTextFont.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) + self.btnTextFont.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("..."))) self.btnTextFont.clicked.connect(self._selectFont) self.formFormat.addRow( self._build.getLabel("format.textFont"), self.textFont, button=self.btnTextFont @@ -1287,12 +1276,9 @@ class _OutputTab(QWidget): def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: super().__init__(parent=buildMain) - self.mainGui = buildMain.mainGui - self.mainTheme = buildMain.mainGui.mainTheme - self._build = build - iPx = self.mainTheme.baseIconSize + iPx = CONFIG.theme.baseIconSize # Left Form # ========= diff --git a/novelwriter/tools/projwizard.py b/novelwriter/tools/projwizard.py index f147dd0f..6564320a 100644 --- a/novelwriter/tools/projwizard.py +++ b/novelwriter/tools/projwizard.py @@ -1,7 +1,6 @@ """ novelWriter – GUI New Project Wizard ==================================== -GUI classes for the new project wizard dialog File History: Created: 2020-07-11 [0.10.1] @@ -22,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 . """ +from __future__ import annotations import os import logging @@ -53,10 +53,9 @@ class GuiProjectWizard(QWizard): logger.debug("Create: GuiProjectWizard") self.setObjectName("GuiProjectWizard") - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme + self.mainGui = mainGui - self.sideImage = self.mainTheme.loadDecoration( + self.sideImage = CONFIG.theme.loadDecoration( "wiz-back", None, CONFIG.pxInt(370) ) self.setWizardStyle(QWizard.ModernStyle) @@ -92,9 +91,6 @@ class ProjWizardIntroPage(QWizardPage): def __init__(self, theWizard): super().__init__() - self.theWizard = theWizard - self.mainTheme = theWizard.mainTheme - self.setTitle(self.tr("Create New Project")) self.theText = QLabel(self.tr( "Provide at least a project name. The project name should not " @@ -108,7 +104,7 @@ class ProjWizardIntroPage(QWizardPage): "Peter Mitterhofer", "CC BY-SA 4.0" )) lblFont = self.imgCredit.font() - lblFont.setPointSizeF(0.6*self.mainTheme.fontPointSize) + lblFont.setPointSizeF(0.6*CONFIG.theme.fontPointSize) self.imgCredit.setFont(lblFont) xW = CONFIG.pxInt(300) @@ -160,9 +156,6 @@ class ProjWizardFolderPage(QWizardPage): def __init__(self, theWizard): super().__init__() - self.theWizard = theWizard - self.mainTheme = theWizard.mainTheme - self.setTitle(self.tr("Select Project Folder")) self.theText = QLabel(self.tr( "Select a location to store the project. A new project folder " @@ -179,7 +172,7 @@ class ProjWizardFolderPage(QWizardPage): self.projPath.setPlaceholderText(self.tr("Required")) self.browseButton = QPushButton("...") - self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) + self.browseButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("..."))) self.browseButton.clicked.connect(self._doBrowse) self.errLabel = QLabel("") @@ -257,8 +250,6 @@ class ProjWizardPopulatePage(QWizardPage): def __init__(self, theWizard): super().__init__() - self.theWizard = theWizard - self.setTitle(self.tr("Populate Project")) self.theText = QLabel(self.tr( "Choose how to pre-fill the project. Either with a minimal set of " @@ -312,8 +303,6 @@ class ProjWizardCustomPage(QWizardPage): def __init__(self, theWizard): super().__init__() - self.theWizard = theWizard - self.setTitle(self.tr("Custom Project Options")) self.theText = QLabel(self.tr( "Select which additional elements to populate the project with. " @@ -412,8 +401,6 @@ class ProjWizardFinalPage(QWizardPage): def __init__(self, theWizard): super().__init__() - self.theWizard = theWizard - self.setTitle(self.tr("Summary")) self.theText = QLabel("") self.theText.setWordWrap(True) diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py index c3b0b1b1..fb995ef2 100644 --- a/novelwriter/tools/writingstats.py +++ b/novelwriter/tools/writingstats.py @@ -72,16 +72,14 @@ class GuiWritingStats(QDialog): if CONFIG.osDarwin: self.setWindowFlag(Qt.WindowType.Tool) - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.mainGui = mainGui self.logData = [] self.filterData = [] self.timeFilter = 0.0 self.wordOffset = 0 - pOptions = self.theProject.options + pOptions = self.mainGui.project.options self.setWindowTitle(self.tr("Writing Statistics")) self.setMinimumWidth(CONFIG.pxInt(420)) @@ -134,7 +132,7 @@ class GuiWritingStats(QDialog): self.listBox.setSortingEnabled(True) # Word Bar - self.barHeight = int(round(0.5*self.mainTheme.fontPixelSize)) + self.barHeight = int(round(0.5*CONFIG.theme.fontPixelSize)) self.barWidth = CONFIG.pxInt(200) self.barImage = QPixmap(self.barHeight, self.barHeight) self.barImage.fill(self.palette().highlight().color()) @@ -145,27 +143,27 @@ class GuiWritingStats(QDialog): self.infoBox.setLayout(self.infoForm) self.labelTotal = QLabel(formatTime(0)) - self.labelTotal.setFont(self.mainTheme.guiFontFixed) + self.labelTotal.setFont(CONFIG.theme.guiFontFixed) self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelIdleT = QLabel(formatTime(0)) - self.labelIdleT.setFont(self.mainTheme.guiFontFixed) + self.labelIdleT.setFont(CONFIG.theme.guiFontFixed) self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelFilter = QLabel(formatTime(0)) - self.labelFilter.setFont(self.mainTheme.guiFontFixed) + self.labelFilter.setFont(CONFIG.theme.guiFontFixed) self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.novelWords = QLabel("0") - self.novelWords.setFont(self.mainTheme.guiFontFixed) + self.novelWords.setFont(CONFIG.theme.guiFontFixed) self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.notesWords = QLabel("0") - self.notesWords.setFont(self.mainTheme.guiFontFixed) + self.notesWords.setFont(CONFIG.theme.guiFontFixed) self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.totalWords = QLabel("0") - self.totalWords.setFont(self.mainTheme.guiFontFixed) + self.totalWords.setFont(CONFIG.theme.guiFontFixed) self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) lblTTime = QLabel(self.tr("Total Time:")) @@ -192,7 +190,7 @@ class GuiWritingStats(QDialog): self.infoForm.setRowStretch(6, 1) # Filter Options - sPx = self.mainTheme.baseIconSize + sPx = CONFIG.theme.baseIconSize self.filterBox = QGroupBox(self.tr("Filters"), self) self.filterForm = QGridLayout(self) @@ -335,7 +333,7 @@ class GuiWritingStats(QDialog): showIdleTime = self.showIdleTime.isChecked() histMax = self.histMax.value() - pOptions = self.theProject.options + pOptions = self.mainGui.project.options pOptions.setValue("GuiWritingStats", "winWidth", winWidth) pOptions.setValue("GuiWritingStats", "winHeight", winHeight) pOptions.setValue("GuiWritingStats", "widthCol0", widthCol0) @@ -443,7 +441,7 @@ class GuiWritingStats(QDialog): ttTime = 0 ttIdle = 0 - for record in self.theProject.session.iterRecords(): + for record in self.mainGui.project.session.iterRecords(): rType = record.get("type") if rType == "initial": self.wordOffset = checkInt(record.get("offset"), 0) @@ -589,13 +587,13 @@ class GuiWritingStats(QDialog): newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight) newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter) - newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed) - newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed) - newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed) + newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed) + newItem.setFont(self.C_LENGTH, CONFIG.theme.guiFontFixed) + newItem.setFont(self.C_COUNT, CONFIG.theme.guiFontFixed) if showIdleTime: - newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed) + newItem.setFont(self.C_IDLE, CONFIG.theme.guiFontFixed) else: - newItem.setFont(self.C_IDLE, self.mainTheme.guiFont) + newItem.setFont(self.C_IDLE, CONFIG.theme.guiFont) self.listBox.addTopLevelItem(newItem) self.timeFilter += sDiff diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index c37f48cd..cb3d212e 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Sample Project Jane Smith diff --git a/tests/mocked.py b/tests/mocked.py index b9a64fb0..cb25a51b 100644 --- a/tests/mocked.py +++ b/tests/mocked.py @@ -31,8 +31,9 @@ class MockGuiMain(QObject): def __init__(self): super().__init__() + self._project = None + self.hasProject = True - self.theProject = None self.mainStatus = MockStatusBar() self.projPath = "" @@ -43,6 +44,10 @@ class MockGuiMain(QObject): return + @property + def project(self): + return self._project + def postLaunchTasks(self, cmdOpen): return diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf index 50f12524..e1554e94 100644 --- a/tests/reference/baseConfig_novelwriter.conf +++ b/tests/reference/baseConfig_novelwriter.conf @@ -1,5 +1,5 @@ [Meta] -timestamp = 2023-08-02 14:53:36 +timestamp = 2023-08-08 19:01:25 [Main] theme = default @@ -10,7 +10,7 @@ localisation = en_GB hidevscroll = False hidehscroll = False lastnotes = 0x0 -lastpath = /home/vkbo +lastpath = [Sizes] mainwindow = 1200, 650 diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py index 3f0d94c7..0f953f81 100644 --- a/tests/test_base/test_base_config.py +++ b/tests/test_base/test_base_config.py @@ -111,7 +111,7 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths): # Check that we have a default file copyfile(confFile, testFile) - ignore = ("timestamp", "lastnotes", "localisation", "lastpath") + ignore = ("timestamp", "lastnotes", "localisation", "lastpath", "backuppath") assert cmpFiles(testFile, compFile, ignoreStart=ignore) tstConf.errorText() # This clears the error cache @@ -366,14 +366,6 @@ def testBaseConfig_Internal(monkeypatch, fncPath): # Function _packList assert tstConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False" - # Function _checkNone - assert tstConf._checkNone(None) is None - assert tstConf._checkNone("None") is None - assert tstConf._checkNone("none") is None - assert tstConf._checkNone("NONE") is None - assert tstConf._checkNone("NoNe") is None - assert tstConf._checkNone(123456) == 123456 - # Function _checkOptionalPackages # (Assumes enchant package exists and is importable) tstConf._checkOptionalPackages() diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index 598f861e..b13b75c4 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -590,10 +590,6 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tstPaths): # Invalid Settings # ================ - # Invalid path - CONFIG._backupPath = None - assert theProject.backupProject(doNotify=False) is False - # Missing project name CONFIG._backupPath = tstPaths.tmpDir theProject.data.setName("") diff --git a/tests/test_core/test_core_status.py b/tests/test_core/test_core_status.py index fdfa1588..a42701b9 100644 --- a/tests/test_core/test_core_status.py +++ b/tests/test_core/test_core_status.py @@ -344,7 +344,7 @@ def testCoreStatus_PackUnpack(mockRnd): # Unpack theStatus = NWStatus(NWStatus.STATUS) - assert theStatus.unpack({ + theStatus.unpack({ statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]}, statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]}, statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]}, diff --git a/tests/test_dialogs/test_dlg_about.py b/tests/test_dialogs/test_dlg_about.py index 1ae7e028..d2ab97a6 100644 --- a/tests/test_dialogs/test_dlg_about.py +++ b/tests/test_dialogs/test_dlg_about.py @@ -34,8 +34,6 @@ from novelwriter.dialogs.about import GuiAbout def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI): """Test the novelWriter about dialogs.""" # NW About - nwGUI.mainTheme.themeName = "A Theme" - nwGUI.mainTheme.themeAuthor = "An Author" assert nwGUI.showAboutNWDialog(showNotes=True) is True qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000) diff --git a/tests/test_dialogs/test_dlg_docsplit.py b/tests/test_dialogs/test_dlg_docsplit.py index 63d21e51..fc6ceccf 100644 --- a/tests/test_dialogs/test_dlg_docsplit.py +++ b/tests/test_dialogs/test_dlg_docsplit.py @@ -35,7 +35,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd): # Create a new project buildTestProject(nwGUI, projPath) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree docText = ( diff --git a/tests/test_dialogs/test_dlg_projdetails.py b/tests/test_dialogs/test_dlg_projdetails.py index 8afd668e..af234423 100644 --- a/tests/test_dialogs/test_dlg_projdetails.py +++ b/tests/test_dialogs/test_dlg_projdetails.py @@ -54,7 +54,7 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum): assert projDet.tabMain.wordCountVal.text() == f"{3000:n}" assert projDet.tabMain.chapCountVal.text() == f"{3:n}" assert projDet.tabMain.sceneCountVal.text() == f"{5:n}" - assert projDet.tabMain.revCountVal.text() == f"{nwGUI.theProject.data.saveCount:n}" + assert projDet.tabMain.revCountVal.text() == f"{nwGUI.project.data.saveCount:n}" assert projDet.tabMain.projPathVal.text() == str(prjLipsum) diff --git a/tests/test_dialogs/test_dlg_projsettings.py b/tests/test_dialogs/test_dlg_projsettings.py index a2c36d2f..c3137265 100644 --- a/tests/test_dialogs/test_dlg_projsettings.py +++ b/tests/test_dialogs/test_dlg_projsettings.py @@ -51,7 +51,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI): # Pretend we have a project nwGUI.hasProject = True - nwGUI.theProject.data.setSpellLang("en") + nwGUI.project.data.setSpellLang("en") # Get the dialog object nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) @@ -95,7 +95,7 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR CONFIG.setBackupPath(fncPath) # Set some values - theProject = nwGUI.theProject + theProject = nwGUI.project theProject.data.setSpellLang("en") theProject.data.setAuthor("Jane Smith") theProject.data.setAutoReplace({"A": "B", "C": "D"}) @@ -160,7 +160,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat CONFIG.setBackupPath(fncPath) # Set some values - theProject = nwGUI.theProject + theProject = nwGUI.project theProject.tree[C.hTitlePage].setStatus(C.sFinished) theProject.tree[C.hChapterDoc].setStatus(C.sDraft) theProject.tree[C.hSceneDoc].setStatus(C.sDraft) @@ -361,7 +361,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo CONFIG.setBackupPath(fncPath) # Set some values - theProject = nwGUI.theProject + theProject = nwGUI.project theProject.data.setAutoReplace({ "A": "B", "C": "D" }) diff --git a/tests/test_dialogs/test_dlg_wordlist.py b/tests/test_dialogs/test_dlg_wordlist.py index 8d698851..35b1b1cf 100644 --- a/tests/test_dialogs/test_dlg_wordlist.py +++ b/tests/test_dialogs/test_dlg_wordlist.py @@ -55,7 +55,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath): assert wList.listBox.count() == 0 # Add words - userDict = UserDictionary(nwGUI.theProject) + userDict = UserDictionary(nwGUI.project) userDict.add("word_a") userDict.add("word_c") userDict.add("word_g") diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py index 25769dbf..8452623f 100644 --- a/tests/test_gui/test_gui_doceditor.py +++ b/tests/test_gui/test_gui_doceditor.py @@ -163,10 +163,10 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, projPath, ipsumTex assert "Could not save document." in caplog.text # Change header level - assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT + assert nwGUI.project.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT nwGUI.docEditor.replaceText(longText[1:]) assert nwGUI.docEditor.saveText() is True - assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT + assert nwGUI.project.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT # Regular save assert nwGUI.docEditor.saveText() is True @@ -203,9 +203,9 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd): assert nwGUI.docEditor.setCursorPosition(None) is False assert nwGUI.docEditor.setCursorPosition(10) is True assert nwGUI.docEditor.getCursorPosition() == 10 - assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos != 10 + assert nwGUI.project.tree[C.hSceneDoc].cursorPos != 10 nwGUI.docEditor.saveCursorPosition() - assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos == 10 + assert nwGUI.project.tree[C.hSceneDoc].cursorPos == 10 assert nwGUI.docEditor.setCursorLine(None) is False assert nwGUI.docEditor.setCursorLine(3) is True @@ -1067,7 +1067,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd): # Create Character theText = "### Jane Doe\n\n@tag: Jane\n\n" + ipsumText[1] + "\n\n" - cHandle = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot) + cHandle = nwGUI.project.newFile("Jane Doe", C.hCharRoot) assert nwGUI.openDocument(cHandle) is True assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.saveDocument() is True @@ -1145,8 +1145,8 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)" # Open a document and populate it - nwGUI.theProject.tree[C.hSceneDoc]._initCount = 0 # Clear item's count - nwGUI.theProject.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count + nwGUI.project.tree[C.hSceneDoc]._initCount = 0 # Clear item's count + nwGUI.project.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count assert nwGUI.openDocument(C.hSceneDoc) is True theText = "\n\n".join(ipsumText) @@ -1170,9 +1170,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m nwGUI.docEditor.wCounterDoc.run() # nwGUI.docEditor._updateDocCounts(cC, wC, pC) - assert nwGUI.theProject.tree[C.hSceneDoc]._charCount == cC - assert nwGUI.theProject.tree[C.hSceneDoc]._wordCount == wC - assert nwGUI.theProject.tree[C.hSceneDoc]._paraCount == pC + assert nwGUI.project.tree[C.hSceneDoc]._charCount == cC + assert nwGUI.project.tree[C.hSceneDoc]._wordCount == wC + assert nwGUI.project.tree[C.hSceneDoc]._paraCount == pC assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})" # Select all text diff --git a/tests/test_gui/test_gui_docviewer.py b/tests/test_gui/test_gui_docviewer.py index 45d3caec..69b11039 100644 --- a/tests/test_gui/test_gui_docviewer.py +++ b/tests/test_gui/test_gui_docviewer.py @@ -40,8 +40,8 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum): # Rebuild the index nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger) - assert nwGUI.theProject.index._tagsIndex._tags != {} - assert nwGUI.theProject.index._itemIndex._items != {} + assert nwGUI.project.index._tagsIndex._tags != {} + assert nwGUI.project.index._itemIndex._items != {} # Select a document in the project tree nwGUI.projView.setSelectedHandle("88243afbe5ed8") @@ -128,7 +128,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum): nwGUI.docViewer.reloadText() # Change document title - nwItem = nwGUI.theProject.tree["4c4f28287af27"] + nwItem = nwGUI.project.tree["4c4f28287af27"] nwItem.setName("Test Title") assert nwItem.itemName == "Test Title" nwGUI.docViewer.updateDocInfo("4c4f28287af27") diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py index 5ab8ea25..c46cac96 100644 --- a/tests/test_gui/test_gui_guimain.py +++ b/tests/test_gui/test_gui_guimain.py @@ -202,14 +202,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd): assert nwGUI.saveProject() assert nwGUI.closeProject() - assert len(nwGUI.theProject.tree) == 0 - assert len(nwGUI.theProject.tree._treeOrder) == 0 - assert len(nwGUI.theProject.tree._treeRoots) == 0 - assert nwGUI.theProject.tree.trashRoot() is None - assert nwGUI.theProject.data.name == "" - assert nwGUI.theProject.data.title == "" - assert nwGUI.theProject.data.author == "" - assert nwGUI.theProject.data.spellCheck is False + assert len(nwGUI.project.tree) == 0 + assert len(nwGUI.project.tree._treeOrder) == 0 + assert len(nwGUI.project.tree._treeRoots) == 0 + assert nwGUI.project.tree.trashRoot() is None + assert nwGUI.project.data.name == "" + assert nwGUI.project.data.title == "" + assert nwGUI.project.data.author == "" + assert nwGUI.project.data.spellCheck is False # Check the files projFile = projPath / "nwProject.nwx" @@ -222,14 +222,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd): assert nwGUI.openProject(projPath) # Check that we loaded the data - assert len(nwGUI.theProject.tree) == 8 - assert len(nwGUI.theProject.tree._treeOrder) == 8 - assert len(nwGUI.theProject.tree._treeRoots) == 4 - assert nwGUI.theProject.tree.trashRoot() is None - assert nwGUI.theProject.data.name == "New Project" - assert nwGUI.theProject.data.title == "New Novel" - assert nwGUI.theProject.data.author == "Jane Doe" - assert nwGUI.theProject.data.spellCheck is False + assert len(nwGUI.project.tree) == 8 + assert len(nwGUI.project.tree._treeOrder) == 8 + assert len(nwGUI.project.tree._treeRoots) == 4 + assert nwGUI.project.tree.trashRoot() is None + assert nwGUI.project.data.name == "New Project" + assert nwGUI.project.data.title == "New Novel" + assert nwGUI.project.data.author == "Jane Doe" + assert nwGUI.project.data.spellCheck is False # Check that tree items have been created assert nwGUI.projView.projTree._getTreeItem(C.hNovelRoot) is not None diff --git a/tests/test_gui/test_gui_noveltree.py b/tests/test_gui/test_gui_noveltree.py index 789f6225..86675cf8 100644 --- a/tests/test_gui/test_gui_noveltree.py +++ b/tests/test_gui/test_gui_noveltree.py @@ -48,7 +48,7 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd): nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) - contentPath = nwGUI.theProject.storage.contentPath + contentPath = nwGUI.project.storage.contentPath assert isinstance(contentPath, Path) (contentPath / "0000000000010.nwd").write_text( diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py index 5aaf3117..b1bb74f8 100644 --- a/tests/test_gui/test_gui_outline.py +++ b/tests/test_gui/test_gui_outline.py @@ -71,7 +71,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, projPath): # Option State # ============ - pOptions = nwGUI.theProject.options + pOptions = nwGUI.project.options colNames = [h.name for h in nwOutline] colItems = [h for h in nwOutline] colWidth = {h: outlineTree.DEF_WIDTH[h] for h in nwOutline} @@ -181,7 +181,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum): assert outlineBar.novelValue.itemData(2) == "" # All novels # Add a second novel folder - newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL) + newHandle = nwGUI.project.newRoot(nwItemClass.NOVEL) nwGUI.projView.projTree.revealNewTreeItem(newHandle) # Check new values in dropdown list @@ -198,7 +198,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum): ("Section 4", 4), ] for dTitle, hLevel in docList: - aHandle = nwGUI.theProject.newFile(dTitle, newHandle) + aHandle = nwGUI.project.newFile(dTitle, newHandle) hHash = "#"*hLevel writeFile(prjLipsum / "content" / f"{aHandle}.nwd", f"{hHash} {dTitle}\n\n") nwGUI.projView.projTree.revealNewTreeItem(aHandle) diff --git a/tests/test_gui/test_gui_projtree.py b/tests/test_gui/test_gui_projtree.py index bd645619..c5428506 100644 --- a/tests/test_gui/test_gui_projtree.py +++ b/tests/test_gui/test_gui_projtree.py @@ -46,7 +46,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn projView = nwGUI.projView projTree = nwGUI.projView.projTree - theProject = nwGUI.theProject + theProject = nwGUI.project # Try to add item with no project assert projView.projTree.newTreeItem(nwItemType.FILE) is False @@ -260,19 +260,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd): # =========== projView.setSelectedHandle(C.hNovelRoot) - assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 + assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0 # Move novel folder up assert projTree.moveTreeItem(-1) is False - assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 + assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0 # Move novel folder down assert projTree.moveTreeItem(1) is True - assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1 + assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 1 # Move novel folder up again assert projTree.moveTreeItem(-1) is True - assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 + assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0 # Clean up # qtbot.stop() @@ -348,7 +348,7 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, projPat C.hChapterDir, C.hChapterDoc, C.hSceneDoc, "0000000000010" ] - trashHandle = nwGUI.theProject.tree.trashRoot() + trashHandle = nwGUI.project.tree.trashRoot() assert projTree.getTreeFromHandle(trashHandle) == [ trashHandle, "0000000000012", "0000000000011" ] @@ -368,7 +368,7 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, projPath, """Test moving items to Trash.""" monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree # Create a project @@ -420,7 +420,7 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, pro """Test permanently deleting items.""" monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree # Create a project @@ -471,7 +471,7 @@ def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, projPath, mock """Test emptying Trash.""" monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree # No project open @@ -541,16 +541,16 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd): projTree.setExpandedFromHandle(None, True) projTree._addTrashRoot() - hTrashRoot = projTree.theProject.tree.trashRoot() + hTrashRoot = nwGUI.project.tree.trashRoot() projTree.setSelectedHandle(C.hCharRoot) projTree.newTreeItem(nwItemType.FILE) projTree.setSelectedHandle(C.hNovelRoot) projTree.newTreeItem(nwItemType.FILE, isNote=True) - nwGUI.theProject.newFile("SubNote", hNovelNote) + nwGUI.project.newFile("SubNote", hNovelNote) projTree.revealNewTreeItem(hSubNote) - assert nwGUI.theProject.tree[hSubNote].itemParent == hNovelNote + assert nwGUI.project.tree[hSubNote].itemParent == hNovelNote def itemPos(tHandle): return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center() @@ -578,7 +578,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd): # Direct Edit Functions # ===================== # Trigger the dedicated functions the menu entries connect to - nwItem = projTree.theProject.tree[hNovelNote] + nwItem = nwGUI.project.tree[hNovelNote] # Toggle active flag assert nwItem.isActive is True @@ -619,17 +619,17 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd): with monkeypatch.context() as mp: mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT) - assert nwGUI.theProject.tree[hNewFolderOne].isFolderType() + assert nwGUI.project.tree[hNewFolderOne].isFolderType() # Convert the first folder to a document projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT) - assert nwGUI.theProject.tree[hNewFolderOne].isFileType() - assert nwGUI.theProject.tree[hNewFolderOne].isDocumentLayout() + assert nwGUI.project.tree[hNewFolderOne].isFileType() + assert nwGUI.project.tree[hNewFolderOne].isDocumentLayout() # Convert the second folder to a note projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE) - assert nwGUI.theProject.tree[hNewFolderTwo].isFileType() - assert nwGUI.theProject.tree[hNewFolderTwo].isNoteLayout() + assert nwGUI.project.tree[hNewFolderTwo].isFileType() + assert nwGUI.project.tree[hNewFolderTwo].isNoteLayout() # qtbot.stop() @@ -649,7 +649,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd, # Create a project buildTestProject(nwGUI, projPath) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree mergedDoc1 = "0000000000014" @@ -751,7 +751,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd, # Create a project buildTestProject(nwGUI, projPath) - theProject = nwGUI.theProject + theProject = nwGUI.project projTree = nwGUI.projView.projTree docText = ( @@ -852,7 +852,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock """Test the duplicate items function.""" # Create a project buildTestProject(nwGUI, projPath) - assert len(nwGUI.theProject.tree) == 8 + assert len(nwGUI.project.tree) == 8 projTree = nwGUI.projView.projTree projTree._getTreeItem(C.hNovelRoot).setExpanded(True) # type: ignore @@ -860,28 +860,28 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock # Nothing to do assert projTree._duplicateFromHandle(C.hInvalid) is False - assert len(nwGUI.theProject.tree) == 8 + assert len(nwGUI.project.tree) == 8 # Duplicate title page, but select no with monkeypatch.context() as mp: mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) assert projTree._duplicateFromHandle(C.hTitlePage) is False - assert len(nwGUI.theProject.tree) == 8 + assert len(nwGUI.project.tree) == 8 # Duplicate title page assert projTree._duplicateFromHandle(C.hTitlePage) is True - assert len(nwGUI.theProject.tree) == 9 + assert len(nwGUI.project.tree) == 9 # Duplicate folder assert projTree._duplicateFromHandle(C.hChapterDir) is True - assert len(nwGUI.theProject.tree) == 12 + assert len(nwGUI.project.tree) == 12 # Duplicate novel root assert projTree._duplicateFromHandle(C.hNovelRoot) is True - assert len(nwGUI.theProject.tree) == 21 + assert len(nwGUI.project.tree) == 21 # Check tree order that all items are next to eachother - assert nwGUI.theProject.tree._treeOrder == [ + assert nwGUI.project.tree._treeOrder == [ C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc, "0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015", "0000000000016", "0000000000017", "0000000000018", "0000000000019", "000000000001a", @@ -889,7 +889,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock ] # Make the duplicator stop early - content = nwGUI.theProject.storage.contentPath + content = nwGUI.project.storage.contentPath assert isinstance(content, Path) (content / "000000000001e.nwd").touch() assert (content / "000000000001e.nwd").exists() @@ -897,7 +897,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock # Should only create the folder, and skip the two files because the # next handle is already a file assert projTree._duplicateFromHandle(C.hChapterDir) is True - assert len(nwGUI.theProject.tree) == 22 + assert len(nwGUI.project.tree) == 22 # qtbot.stop() @@ -938,13 +938,13 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd) assert projTree.revealNewTreeItem(C.hInvalid) is False # Try to add an orphaned file to the tree - nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot) - nwGUI.theProject.tree[nHandle].setParent(None) # type: ignore + nHandle = nwGUI.project.newFile("Test", C.hNovelRoot) + nwGUI.project.tree[nHandle].setParent(None) # type: ignore assert projTree.revealNewTreeItem(nHandle) is False # Try to add an item with unknown parent to the tree - nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot) - nwGUI.theProject.tree[nHandle].setParent(C.hInvalid) # type: ignore + nHandle = nwGUI.project.newFile("Test", C.hNovelRoot) + nwGUI.project.tree[nHandle].setParent(C.hInvalid) # type: ignore assert projTree.revealNewTreeItem(nHandle) is False # Method: undoLastMove diff --git a/tests/test_gui/test_gui_statusbar.py b/tests/test_gui/test_gui_statusbar.py index b19afa45..b227fb61 100644 --- a/tests/test_gui/test_gui_statusbar.py +++ b/tests/test_gui/test_gui_statusbar.py @@ -25,7 +25,7 @@ import pytest from tools import C, buildTestProject from novelwriter import CONFIG -from novelwriter.gui.statusbar import StatusLED +from novelwriter.extensions.statusled import StatusLED @pytest.mark.gui @@ -33,8 +33,8 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd): """Test the the various features of the status bar. """ buildTestProject(nwGUI, projPath) - cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot) - newDoc = nwGUI.theProject.storage.getDocument(cHandle) + cHandle = nwGUI.project.newFile("A Note", C.hCharRoot) + newDoc = nwGUI.project.storage.getDocument(cHandle) newDoc.writeDocument("# A Note\n\n") nwGUI.projView.projTree.revealNewTreeItem(cHandle) nwGUI.rebuildIndex(beQuiet=True) diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py index 9d193480..2f012229 100644 --- a/tests/test_gui/test_gui_theme.py +++ b/tests/test_gui/test_gui_theme.py @@ -33,14 +33,12 @@ from PyQt5.QtWidgets import QApplication from novelwriter import CONFIG from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.constants import nwLabels -from novelwriter.gui.theme import GuiIcons, GuiTheme @pytest.mark.gui def testGuiTheme_Main(qtbot, nwGUI, tstPaths): - """Test the theme class init. - """ - mainTheme: GuiTheme = nwGUI.mainTheme + """Test the theme class init.""" + mainTheme = CONFIG.theme # Methods # ======= @@ -123,7 +121,7 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths): @pytest.mark.gui def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI): """Test the theme part of the class.""" - mainTheme: GuiTheme = nwGUI.mainTheme + mainTheme = CONFIG.theme # List Themes # =========== @@ -200,9 +198,8 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI): @pytest.mark.gui def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI): - """Test the syntax part of the class. - """ - mainTheme: GuiTheme = nwGUI.mainTheme + """Test the syntax part of the class.""" + mainTheme = CONFIG.theme # List Themes # =========== @@ -266,9 +263,8 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI): @pytest.mark.gui def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths): - """Test the icon cache class. - """ - iconCache: GuiIcons = nwGUI.mainTheme.iconCache + """Test the icon cache class.""" + iconCache = CONFIG.theme.iconCache # Load Theme # ========== diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py index cb5aedb1..9d58554d 100644 --- a/tests/test_tools/test_tools_manuscript.py +++ b/tests/test_tools/test_tools_manuscript.py @@ -45,7 +45,7 @@ def testManuscript_Init(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: Pat """Test the init/main functionality of the GuiManuscript dialog.""" buildTestProject(nwGUI, projPath) nwGUI.openProject(projPath) - nwGUI.theProject.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi") + nwGUI.project.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi") allText = "New Novel\nBy Jane Doe\nA Chapter\n\t\tHi\n* * *" manus = GuiManuscript(nwGUI) @@ -159,7 +159,7 @@ def testManuscript_Features(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: manus.show() manus.loadContent() - cacheFile = CONFIG.dataPath("cache") / f"build_{nwGUI.theProject.data.uuid}.json" + cacheFile = CONFIG.dataPath("cache") / f"build_{nwGUI.project.data.uuid}.json" manus.buildList.setCurrentRow(0) build = manus._getSelectedBuild() assert isinstance(build, BuildSettings) diff --git a/tests/test_tools/test_tools_manussettings.py b/tests/test_tools/test_tools_manussettings.py index f84b2e3d..deccca1c 100644 --- a/tests/test_tools/test_tools_manussettings.py +++ b/tests/test_tools/test_tools_manussettings.py @@ -128,11 +128,11 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR "worldRoot": 9, } - hPlotDoc = nwGUI.theProject.newFile("Main Plot", C.hPlotRoot) - hCharDoc = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot) + hPlotDoc = nwGUI.project.newFile("Main Plot", C.hPlotRoot) + hCharDoc = nwGUI.project.newFile("Jane Doe", C.hCharRoot) nwGUI.projView.projTree.revealNewTreeItem(hPlotDoc) nwGUI.projView.projTree.revealNewTreeItem(hCharDoc) - nwGUI.theProject.tree[hPlotDoc].setActive(False) # type: ignore + nwGUI.project.tree[hPlotDoc].setActive(False) # type: ignore # Create the dialog and populate it bSettings = GuiBuildSettings(nwGUI, build) @@ -167,7 +167,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR # Switch off novel docs filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False) - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (False, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -182,7 +182,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR # Switch on note docs filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True) - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (False, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -197,7 +197,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR # Switch on inactive docs filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True) - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (False, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -214,7 +214,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR filterTab._treeMap[C.hChapterDoc].setSelected(True) filterTab._treeMap[C.hSceneDoc].setSelected(True) filterTab.includedButton.click() - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (False, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -232,7 +232,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab.excludedButton.click() - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (False, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -247,7 +247,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR # Switch on novel docs filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True) - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled C.hChapterDir: (False, FilterMode.SKIPPED), @@ -264,7 +264,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR filterTab.optTree.clearSelection() filterTab._treeMap[C.hNovelRoot].setSelected(True) filterTab.resetButton.click() - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (True, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -284,7 +284,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab.resetButton.click() - assert build.buildItemFilter(nwGUI.theProject) == { + assert build.buildItemFilter(nwGUI.project) == { C.hNovelRoot: (False, FilterMode.SKIPPED), C.hTitlePage: (True, FilterMode.FILTERED), C.hChapterDir: (False, FilterMode.SKIPPED), @@ -302,8 +302,8 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hPlotRoot, hPlotDoc, C.hCharRoot, hCharDoc, ] - nwGUI.theProject.tree[hCharDoc].setRoot(None) # type: ignore - nwGUI.theProject.tree[hPlotDoc].setParent(None) # type: ignore + nwGUI.project.tree[hCharDoc].setRoot(None) # type: ignore + nwGUI.project.tree[hPlotDoc].setParent(None) # type: ignore filterTab._populateTree() assert list(filterTab._treeMap.keys()) == [ C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc, diff --git a/tests/test_tools/test_tools_writingstats.py b/tests/test_tools/test_tools_writingstats.py index 50ab2e1d..3674004c 100644 --- a/tests/test_tools/test_tools_writingstats.py +++ b/tests/test_tools/test_tools_writingstats.py @@ -39,7 +39,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, projPath, tstPaths): """ # Create a project to work on buildTestProject(nwGUI, projPath) - project = nwGUI.theProject + project = nwGUI.project qtbot.wait(100) assert nwGUI.saveProject() diff --git a/tests/tools.py b/tests/tools.py index e07912ae..f8f3f03c 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -154,59 +154,59 @@ def cleanProject(path: str | Path): return -def buildTestProject(theObject, projPath): - """Build a standard test project in projPath using theProject +def buildTestProject(obj, projPath): + """Build a standard test project in projPath using the project object as the parent. """ from novelwriter.enum import nwItemClass from novelwriter.core.project import NWProject - if isinstance(theObject, NWProject): - theGUI = None - theProject = theObject + if isinstance(obj, NWProject): + nwGUI = None + project = obj else: - theGUI = theObject - theProject = theObject.theProject + nwGUI = obj + project = obj.project - theProject.clearProject() - theProject.storage.openProjectInPlace(projPath) - theProject.setDefaultStatusImport() + project.clearProject() + project.storage.openProjectInPlace(projPath) + project.setDefaultStatusImport() - theProject.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed") - theProject.data.setName("New Project") - theProject.data.setTitle("New Novel") - theProject.data.setAuthor("Jane Doe") + project.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed") + project.data.setName("New Project") + project.data.setTitle("New Novel") + project.data.setAuthor("Jane Doe") # Creating a minimal project with a few root folders and a # single chapter folder with a single file. xHandle = {} - xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel") - xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot") - xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters") - xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World") - xHandle[5] = theProject.newFile("Title Page", xHandle[1]) - xHandle[6] = theProject.newFolder("New Chapter", xHandle[1]) - xHandle[7] = theProject.newFile("New Chapter", xHandle[6]) - xHandle[8] = theProject.newFile("New Scene", xHandle[6]) + xHandle[1] = project.newRoot(nwItemClass.NOVEL, "Novel") + xHandle[2] = project.newRoot(nwItemClass.PLOT, "Plot") + xHandle[3] = project.newRoot(nwItemClass.CHARACTER, "Characters") + xHandle[4] = project.newRoot(nwItemClass.WORLD, "World") + xHandle[5] = project.newFile("Title Page", xHandle[1]) + xHandle[6] = project.newFolder("New Chapter", xHandle[1]) + xHandle[7] = project.newFile("New Chapter", xHandle[6]) + xHandle[8] = project.newFile("New Scene", xHandle[6]) - aDoc = theProject.storage.getDocument(xHandle[5]) + aDoc = project.storage.getDocument(xHandle[5]) aDoc.writeDocument("#! New Novel\n\n>> By Jane Doe <<\n") - theProject.index.reIndexHandle(xHandle[5]) + project.index.reIndexHandle(xHandle[5]) - aDoc = theProject.storage.getDocument(xHandle[7]) - aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter")) - theProject.index.reIndexHandle(xHandle[7]) + aDoc = project.storage.getDocument(xHandle[7]) + aDoc.writeDocument("## %s\n\n" % project.tr("New Chapter")) + project.index.reIndexHandle(xHandle[7]) - aDoc = theProject.storage.getDocument(xHandle[8]) - aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene")) - theProject.index.reIndexHandle(xHandle[8]) + aDoc = project.storage.getDocument(xHandle[8]) + aDoc.writeDocument("### %s\n\n" % project.tr("New Scene")) + project.index.reIndexHandle(xHandle[8]) - theProject.session.startSession() - theProject.setProjectChanged(True) - theProject.saveProject(autoSave=True) + project.session.startSession() + project.setProjectChanged(True) + project.saveProject(autoSave=True) - if theGUI is not None: - theGUI.hasProject = True - theGUI.rebuildTrees() + if nwGUI is not None: + nwGUI.hasProject = True + nwGUI.rebuildTrees() return