diff --git a/novelwriter/config.py b/novelwriter/config.py
index c60efee1..cafa3e36 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -33,12 +33,13 @@ 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
@@ -52,7 +53,7 @@ class Config:
LANG_NW = 1
LANG_PROJ = 2
- def __init__(self):
+ def __init__(self) -> None:
# Initialisation
# ==============
@@ -236,11 +237,11 @@ class Config:
##
@property
- def hasError(self):
+ def hasError(self) -> bool:
return self._hasError
@property
- def recentProjects(self):
+ def recentProjects(self) -> RecentProjects:
return self._recentObj
@property
@@ -250,45 +251,45 @@ class Config:
return self._themeObj
@property
- def mainWinSize(self):
+ 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))
@@ -301,65 +302,65 @@ class Config:
self._themeObj = theme
return
- def setMainWinSize(self, newWidth, newHeight):
+ 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 | str):
+ def setBackupPath(self, path: Path | str) -> None:
"""Set the current backup path."""
- self._backupPath = checkPath(backupPath, self._backPath)
+ 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.
"""
@@ -383,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:
@@ -461,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.
"""
@@ -505,9 +503,8 @@ class Config:
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 = {}
@@ -528,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()
@@ -547,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]
@@ -661,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),
@@ -684,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),
@@ -693,7 +688,7 @@ class Config:
"outlinepane": self._packList(self._outlnPanePos),
}
- theConf["Project"] = {
+ conf["Project"] = {
"autosaveproject": str(self.autoSaveProj),
"autosavedoc": str(self.autoSaveDoc),
"emphlabels": str(self.emphLabels),
@@ -702,7 +697,7 @@ class Config:
"askbeforebackup": str(self.askBeforeBackup),
}
- theConf["Editor"] = {
+ conf["Editor"] = {
"textfont": str(self.textFont),
"textsize": str(self.textSize),
"width": str(self.textWidth),
@@ -743,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),
@@ -759,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()
@@ -774,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:
@@ -809,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)
@@ -839,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:
@@ -855,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/dialogs/projload.py b/novelwriter/dialogs/projload.py
index 1ae6fb50..09fabd4d 100644
--- a/novelwriter/dialogs/projload.py
+++ b/novelwriter/dialogs/projload.py
@@ -100,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"))
@@ -281,8 +282,7 @@ class GuiProjectLoad(QDialog):
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/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 68ac39e2..3ab6b87b 100644
--- a/novelwriter/extensions/configlayout.py
+++ b/novelwriter/extensions/configlayout.py
@@ -37,7 +37,7 @@ FONT_SCALE = 0.9
class NConfigLayout(QGridLayout):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
self._nextRow = 0
@@ -56,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
@@ -65,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]
@@ -73,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)
@@ -83,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)
@@ -94,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)
@@ -155,7 +154,7 @@ class NSimpleLayout(QGridLayout):
column layout.
"""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
self._nextRow = 0
@@ -170,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)
@@ -181,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)
@@ -208,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/extensions/pageddialog.py b/novelwriter/extensions/pageddialog.py
index aa8de6e2..a4170a2a 100644
--- a/novelwriter/extensions/pageddialog.py
+++ b/novelwriter/extensions/pageddialog.py
@@ -23,10 +23,11 @@ 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/switch.py b/novelwriter/extensions/switch.py
index be90bec7..e702a6b6 100644
--- a/novelwriter/extensions/switch.py
+++ b/novelwriter/extensions/switch.py
@@ -23,9 +23,9 @@ 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/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index 524cd8a1..0f953f81 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -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()