Annotate config class and extensions
This commit is contained in:
+171
-190
@@ -33,12 +33,13 @@ from pathlib import Path
|
|||||||
|
|
||||||
from PyQt5.QtGui import QFontDatabase
|
from PyQt5.QtGui import QFontDatabase
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths,
|
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
|
||||||
QSysInfo, QLocale, QLibraryInfo, QTranslator
|
QLocale, QStandardPaths, QSysInfo, QTranslator
|
||||||
)
|
)
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
from novelwriter.error import logException, formatException
|
from novelwriter.error import formatException, logException
|
||||||
from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser
|
from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp
|
||||||
from novelwriter.constants import nwFiles, nwUnicode
|
from novelwriter.constants import nwFiles, nwUnicode
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
@@ -52,7 +53,7 @@ class Config:
|
|||||||
LANG_NW = 1
|
LANG_NW = 1
|
||||||
LANG_PROJ = 2
|
LANG_PROJ = 2
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
|
|
||||||
# Initialisation
|
# Initialisation
|
||||||
# ==============
|
# ==============
|
||||||
@@ -236,11 +237,11 @@ class Config:
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hasError(self):
|
def hasError(self) -> bool:
|
||||||
return self._hasError
|
return self._hasError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def recentProjects(self):
|
def recentProjects(self) -> RecentProjects:
|
||||||
return self._recentObj
|
return self._recentObj
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -250,45 +251,45 @@ class Config:
|
|||||||
return self._themeObj
|
return self._themeObj
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mainWinSize(self):
|
def mainWinSize(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._mainWinSize]
|
return [int(x*self.guiScale) for x in self._mainWinSize]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preferencesWinSize(self):
|
def preferencesWinSize(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._prefsWinSize]
|
return [int(x*self.guiScale) for x in self._prefsWinSize]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def projLoadColWidths(self):
|
def projLoadColWidths(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._projLoadCols]
|
return [int(x*self.guiScale) for x in self._projLoadCols]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mainPanePos(self):
|
def mainPanePos(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._mainPanePos]
|
return [int(x*self.guiScale) for x in self._mainPanePos]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def viewPanePos(self):
|
def viewPanePos(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._viewPanePos]
|
return [int(x*self.guiScale) for x in self._viewPanePos]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def outlinePanePos(self):
|
def outlinePanePos(self) -> list[int]:
|
||||||
return [int(x*self.guiScale) for x in self._outlnPanePos]
|
return [int(x*self.guiScale) for x in self._outlnPanePos]
|
||||||
|
|
||||||
##
|
##
|
||||||
# Getters
|
# Getters
|
||||||
##
|
##
|
||||||
|
|
||||||
def getTextWidth(self, focusMode=False):
|
def getTextWidth(self, focusMode: bool = False) -> int:
|
||||||
"""Get the text with for the correct editor mode."""
|
"""Get the text with for the correct editor mode."""
|
||||||
if focusMode:
|
if focusMode:
|
||||||
return self.pxInt(max(self.focusWidth, 200))
|
return self.pxInt(max(self.focusWidth, 200))
|
||||||
else:
|
else:
|
||||||
return self.pxInt(max(self.textWidth, 200))
|
return self.pxInt(max(self.textWidth, 200))
|
||||||
|
|
||||||
def getTextMargin(self):
|
def getTextMargin(self) -> int:
|
||||||
"""Get the scaled text margin."""
|
"""Get the scaled text margin."""
|
||||||
return self.pxInt(max(self.textMargin, 0))
|
return self.pxInt(max(self.textMargin, 0))
|
||||||
|
|
||||||
def getTabWidth(self):
|
def getTabWidth(self) -> int:
|
||||||
"""Get the scaled tab width."""
|
"""Get the scaled tab width."""
|
||||||
return self.pxInt(max(self.tabWidth, 0))
|
return self.pxInt(max(self.tabWidth, 0))
|
||||||
|
|
||||||
@@ -301,65 +302,65 @@ class Config:
|
|||||||
self._themeObj = theme
|
self._themeObj = theme
|
||||||
return
|
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
|
"""Set the size of the main window, but only if the change is
|
||||||
larger than 5 pixels. The OS window manager will sometimes
|
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
|
adjust it a bit, and we don't want the main window to shrink or
|
||||||
grow each time the app is opened.
|
grow each time the app is opened.
|
||||||
"""
|
"""
|
||||||
newWidth = int(newWidth/self.guiScale)
|
width = int(width/self.guiScale)
|
||||||
newHeight = int(newHeight/self.guiScale)
|
height = int(height/self.guiScale)
|
||||||
if abs(self._mainWinSize[0] - newWidth) > 5:
|
if abs(self._mainWinSize[0] - width) > 5:
|
||||||
self._mainWinSize[0] = newWidth
|
self._mainWinSize[0] = width
|
||||||
if abs(self._mainWinSize[1] - newHeight) > 5:
|
if abs(self._mainWinSize[1] - height) > 5:
|
||||||
self._mainWinSize[1] = newHeight
|
self._mainWinSize[1] = height
|
||||||
return
|
return
|
||||||
|
|
||||||
def setPreferencesWinSize(self, newWidth, newHeight):
|
def setPreferencesWinSize(self, width: int, height: int) -> None:
|
||||||
"""Set the size of the Preferences dialog window."""
|
"""Set the size of the Preferences dialog window."""
|
||||||
self._prefsWinSize[0] = int(newWidth/self.guiScale)
|
self._prefsWinSize[0] = int(width/self.guiScale)
|
||||||
self._prefsWinSize[1] = int(newHeight/self.guiScale)
|
self._prefsWinSize[1] = int(height/self.guiScale)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setProjLoadColWidths(self, colWidths):
|
def setProjLoadColWidths(self, widths: list[int]) -> None:
|
||||||
"""Set the column widths of the Load Project dialog."""
|
"""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
|
return
|
||||||
|
|
||||||
def setMainPanePos(self, panePos):
|
def setMainPanePos(self, pos: list[int]) -> None:
|
||||||
"""Set the position of the main GUI splitter."""
|
"""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
|
return
|
||||||
|
|
||||||
def setViewPanePos(self, panePos):
|
def setViewPanePos(self, pos: list[int]) -> None:
|
||||||
"""Set the position of the viewer meta data splitter."""
|
"""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
|
return
|
||||||
|
|
||||||
def setOutlinePanePos(self, panePos):
|
def setOutlinePanePos(self, pos: list[int]) -> None:
|
||||||
"""Set the position of the outline details splitter."""
|
"""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
|
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
|
"""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.
|
path is not a folder, the parent of the path is used instead.
|
||||||
"""
|
"""
|
||||||
if isinstance(lastPath, (str, Path)):
|
if isinstance(path, (str, Path)):
|
||||||
lastPath = checkPath(lastPath, self._homePath)
|
path = checkPath(path, self._homePath)
|
||||||
if not lastPath.is_dir():
|
if not path.is_dir():
|
||||||
lastPath = lastPath.parent
|
path = path.parent
|
||||||
if lastPath.is_dir():
|
if path.is_dir():
|
||||||
self._lastPath = lastPath
|
self._lastPath = path
|
||||||
logger.debug("Last path updated: %s" % self._lastPath)
|
logger.debug("Last path updated: %s" % self._lastPath)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBackupPath(self, backupPath: Path | str):
|
def setBackupPath(self, path: Path | str) -> None:
|
||||||
"""Set the current backup path."""
|
"""Set the current backup path."""
|
||||||
self._backupPath = checkPath(backupPath, self._backPath)
|
self._backupPath = checkPath(path, self._backPath)
|
||||||
return
|
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 the text font if it exists. If it doesn't, or is None,
|
||||||
set to default font.
|
set to default font.
|
||||||
"""
|
"""
|
||||||
@@ -383,15 +384,11 @@ class Config:
|
|||||||
##
|
##
|
||||||
|
|
||||||
def pxInt(self, value: int) -> int:
|
def pxInt(self, value: int) -> int:
|
||||||
"""Used to scale fixed gui sizes by the screen scale factor.
|
"""Scale fixed gui sizes by the screen scale factor."""
|
||||||
This function returns an int, which is always rounded down.
|
|
||||||
"""
|
|
||||||
return int(value*self.guiScale)
|
return int(value*self.guiScale)
|
||||||
|
|
||||||
def rpxInt(self, value: int) -> int:
|
def rpxInt(self, value: int) -> int:
|
||||||
"""Used to un-scale fixed gui sizes by the screen scale factor.
|
"""Un-scale fixed gui sizes by the screen scale factor."""
|
||||||
This function returns an int, which is always rounded down.
|
|
||||||
"""
|
|
||||||
return int(value/self.guiScale)
|
return int(value/self.guiScale)
|
||||||
|
|
||||||
def dataPath(self, target: str | None = None) -> Path:
|
def dataPath(self, target: str | None = None) -> Path:
|
||||||
@@ -461,7 +458,8 @@ class Config:
|
|||||||
# Config Actions
|
# 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
|
"""Initialise the config class. The manual setting of confPath
|
||||||
and dataPath is mainly intended for the test suite.
|
and dataPath is mainly intended for the test suite.
|
||||||
"""
|
"""
|
||||||
@@ -505,9 +503,8 @@ class Config:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def initLocalisation(self, nwApp):
|
def initLocalisation(self, nwApp: QApplication) -> None:
|
||||||
"""Initialise the localisation of the GUI.
|
"""Initialise the localisation of the GUI."""
|
||||||
"""
|
|
||||||
self._qLocale = QLocale(self.guiLocale)
|
self._qLocale = QLocale(self.guiLocale)
|
||||||
QLocale.setDefault(self._qLocale)
|
QLocale.setDefault(self._qLocale)
|
||||||
self._qtTrans = {}
|
self._qtTrans = {}
|
||||||
@@ -528,16 +525,15 @@ class Config:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def loadConfig(self):
|
def loadConfig(self) -> bool:
|
||||||
"""Load preferences from file and replace default settings.
|
"""Load preferences from file and replace default settings."""
|
||||||
"""
|
|
||||||
logger.debug("Loading config file")
|
logger.debug("Loading config file")
|
||||||
|
|
||||||
theConf = NWConfigParser()
|
conf = NWConfigParser()
|
||||||
cnfPath = self._confPath / nwFiles.CONF_FILE
|
cnfPath = self._confPath / nwFiles.CONF_FILE
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||||
theConf.read_file(inFile)
|
conf.read_file(inFile)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Could not load config file")
|
logger.error("Could not load config file")
|
||||||
logException()
|
logException()
|
||||||
@@ -547,98 +543,98 @@ class Config:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
cnfSec = "Main"
|
sec = "Main"
|
||||||
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
|
self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
|
||||||
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
|
self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
|
||||||
self.guiFont = theConf.rdStr(cnfSec, "font", self.guiFont)
|
self.guiFont = conf.rdStr(sec, "font", self.guiFont)
|
||||||
self.guiFontSize = theConf.rdInt(cnfSec, "fontsize", self.guiFontSize)
|
self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize)
|
||||||
self.guiLocale = theConf.rdStr(cnfSec, "localisation", self.guiLocale)
|
self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale)
|
||||||
self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll)
|
self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll)
|
||||||
self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll)
|
self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll)
|
||||||
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
|
self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes)
|
||||||
self._lastPath = theConf.rdPath(cnfSec, "lastpath", self._lastPath)
|
self._lastPath = conf.rdPath(sec, "lastpath", self._lastPath)
|
||||||
|
|
||||||
# Sizes
|
# Sizes
|
||||||
cnfSec = "Sizes"
|
sec = "Sizes"
|
||||||
self._mainWinSize = theConf.rdIntList(cnfSec, "mainwindow", self._mainWinSize)
|
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
|
||||||
self._prefsWinSize = theConf.rdIntList(cnfSec, "preferences", self._prefsWinSize)
|
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
|
||||||
self._projLoadCols = theConf.rdIntList(cnfSec, "projloadcols", self._projLoadCols)
|
self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
|
||||||
self._mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self._mainPanePos)
|
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
|
||||||
self._viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self._viewPanePos)
|
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
|
||||||
self._outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self._outlnPanePos)
|
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
cnfSec = "Project"
|
sec = "Project"
|
||||||
self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj)
|
self.autoSaveProj = conf.rdInt(sec, "autosaveproject", self.autoSaveProj)
|
||||||
self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc)
|
self.autoSaveDoc = conf.rdInt(sec, "autosavedoc", self.autoSaveDoc)
|
||||||
self.emphLabels = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels)
|
self.emphLabels = conf.rdBool(sec, "emphlabels", self.emphLabels)
|
||||||
self._backupPath = theConf.rdPath(cnfSec, "backuppath", self._backupPath)
|
self._backupPath = conf.rdPath(sec, "backuppath", self._backupPath)
|
||||||
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
|
self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
|
||||||
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)
|
self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
|
||||||
|
|
||||||
# Editor
|
# Editor
|
||||||
cnfSec = "Editor"
|
sec = "Editor"
|
||||||
self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont)
|
self.textFont = conf.rdStr(sec, "textfont", self.textFont)
|
||||||
self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize)
|
self.textSize = conf.rdInt(sec, "textsize", self.textSize)
|
||||||
self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth)
|
self.textWidth = conf.rdInt(sec, "width", self.textWidth)
|
||||||
self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin)
|
self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
|
||||||
self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth)
|
self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
|
||||||
self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth)
|
self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth)
|
||||||
self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter)
|
self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter)
|
||||||
self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify)
|
self.doJustify = conf.rdBool(sec, "justify", self.doJustify)
|
||||||
self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect)
|
self.autoSelect = conf.rdBool(sec, "autoselect", self.autoSelect)
|
||||||
self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace)
|
self.doReplace = conf.rdBool(sec, "autoreplace", self.doReplace)
|
||||||
self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote)
|
self.doReplaceSQuote = conf.rdBool(sec, "repsquotes", self.doReplaceSQuote)
|
||||||
self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote)
|
self.doReplaceDQuote = conf.rdBool(sec, "repdquotes", self.doReplaceDQuote)
|
||||||
self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash)
|
self.doReplaceDash = conf.rdBool(sec, "repdash", self.doReplaceDash)
|
||||||
self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots)
|
self.doReplaceDots = conf.rdBool(sec, "repdots", self.doReplaceDots)
|
||||||
self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd)
|
self.scrollPastEnd = conf.rdInt(sec, "scrollpastend", self.scrollPastEnd)
|
||||||
self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
|
self.autoScroll = conf.rdBool(sec, "autoscroll", self.autoScroll)
|
||||||
self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos)
|
self.autoScrollPos = conf.rdInt(sec, "autoscrollpos", self.autoScrollPos)
|
||||||
self.fmtSQuoteOpen = theConf.rdStr(cnfSec, "fmtsquoteopen", self.fmtSQuoteOpen)
|
self.fmtSQuoteOpen = conf.rdStr(sec, "fmtsquoteopen", self.fmtSQuoteOpen)
|
||||||
self.fmtSQuoteClose = theConf.rdStr(cnfSec, "fmtsquoteclose", self.fmtSQuoteClose)
|
self.fmtSQuoteClose = conf.rdStr(sec, "fmtsquoteclose", self.fmtSQuoteClose)
|
||||||
self.fmtDQuoteOpen = theConf.rdStr(cnfSec, "fmtdquoteopen", self.fmtDQuoteOpen)
|
self.fmtDQuoteOpen = conf.rdStr(sec, "fmtdquoteopen", self.fmtDQuoteOpen)
|
||||||
self.fmtDQuoteClose = theConf.rdStr(cnfSec, "fmtdquoteclose", self.fmtDQuoteClose)
|
self.fmtDQuoteClose = conf.rdStr(sec, "fmtdquoteclose", self.fmtDQuoteClose)
|
||||||
self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore)
|
self.fmtPadBefore = conf.rdStr(sec, "fmtpadbefore", self.fmtPadBefore)
|
||||||
self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter)
|
self.fmtPadAfter = conf.rdStr(sec, "fmtpadafter", self.fmtPadAfter)
|
||||||
self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin)
|
self.fmtPadThin = conf.rdBool(sec, "fmtpadthin", self.fmtPadThin)
|
||||||
self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage)
|
self.spellLanguage = conf.rdStr(sec, "spellcheck", self.spellLanguage)
|
||||||
self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces)
|
self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
|
||||||
self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings)
|
self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
|
||||||
self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces)
|
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
|
||||||
self.wordCountTimer = theConf.rdFlt(cnfSec, "wordcounttimer", self.wordCountTimer)
|
self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
|
||||||
self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit)
|
self.bigDocLimit = conf.rdInt(sec, "bigdoclimit", self.bigDocLimit)
|
||||||
self.incNotesWCount = theConf.rdBool(cnfSec, "incnoteswcount", self.incNotesWCount)
|
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
|
||||||
self.showFullPath = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath)
|
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
||||||
self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes)
|
self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
|
||||||
self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote)
|
self.allowOpenSQuote = conf.rdBool(sec, "allowopensquote", self.allowOpenSQuote)
|
||||||
self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote)
|
self.allowOpenDQuote = conf.rdBool(sec, "allowopendquote", self.allowOpenDQuote)
|
||||||
self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph)
|
self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
|
||||||
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
|
self.stopWhenIdle = conf.rdBool(sec, "stopwhenidle", self.stopWhenIdle)
|
||||||
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)
|
self.userIdleTime = conf.rdInt(sec, "useridletime", self.userIdleTime)
|
||||||
|
|
||||||
# State
|
# State
|
||||||
cnfSec = "State"
|
sec = "State"
|
||||||
self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel)
|
self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel)
|
||||||
self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments)
|
self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
|
||||||
self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis)
|
self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
|
||||||
self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase)
|
self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
|
||||||
self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord)
|
self.searchWord = conf.rdBool(sec, "searchword", self.searchWord)
|
||||||
self.searchRegEx = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx)
|
self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx)
|
||||||
self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop)
|
self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop)
|
||||||
self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile)
|
self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
|
||||||
self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap)
|
self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
|
||||||
|
|
||||||
# Deprecated Settings or Locations as of 2.0
|
# Deprecated Settings or Locations as of 2.0
|
||||||
# ToDo: These will be loaded for a few minor releases until the users have converted them
|
# 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.guiFont = conf.rdStr("Main", "guifont", self.guiFont)
|
||||||
self.guiFontSize = theConf.rdInt("Main", "guifontsize", self.guiFontSize)
|
self.guiFontSize = conf.rdInt("Main", "guifontsize", self.guiFontSize)
|
||||||
self.guiLocale = theConf.rdStr("Main", "guilang", self.guiLocale)
|
self.guiLocale = conf.rdStr("Main", "guilang", self.guiLocale)
|
||||||
self._backupPath = theConf.rdPath("Backup", "backuppath", self._backupPath)
|
self._backupPath = conf.rdPath("Backup", "backuppath", self._backupPath)
|
||||||
self.backupOnClose = theConf.rdBool("Backup", "backuponclose", self.backupOnClose)
|
self.backupOnClose = conf.rdBool("Backup", "backuponclose", self.backupOnClose)
|
||||||
self.askBeforeBackup = theConf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
|
self.askBeforeBackup = conf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
|
||||||
fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", [])
|
fmtSingleQuotes = conf.rdStrList(sec, "fmtsinglequote", [])
|
||||||
fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", [])
|
fmtDoubleQuotes = conf.rdStrList(sec, "fmtdoublequote", [])
|
||||||
|
|
||||||
if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2:
|
if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2:
|
||||||
self.fmtSQuoteOpen = fmtSingleQuotes[0]
|
self.fmtSQuoteOpen = fmtSingleQuotes[0]
|
||||||
@@ -661,18 +657,17 @@ class Config:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveConfig(self):
|
def saveConfig(self) -> bool:
|
||||||
"""Save the current preferences to file.
|
"""Save the current preferences to file."""
|
||||||
"""
|
|
||||||
logger.debug("Saving config file")
|
logger.debug("Saving config file")
|
||||||
|
|
||||||
theConf = NWConfigParser()
|
conf = NWConfigParser()
|
||||||
|
|
||||||
theConf["Meta"] = {
|
conf["Meta"] = {
|
||||||
"timestamp": formatTimeStamp(time()),
|
"timestamp": formatTimeStamp(time()),
|
||||||
}
|
}
|
||||||
|
|
||||||
theConf["Main"] = {
|
conf["Main"] = {
|
||||||
"theme": str(self.guiTheme),
|
"theme": str(self.guiTheme),
|
||||||
"syntax": str(self.guiSyntax),
|
"syntax": str(self.guiSyntax),
|
||||||
"font": str(self.guiFont),
|
"font": str(self.guiFont),
|
||||||
@@ -684,7 +679,7 @@ class Config:
|
|||||||
"lastpath": str(self._lastPath),
|
"lastpath": str(self._lastPath),
|
||||||
}
|
}
|
||||||
|
|
||||||
theConf["Sizes"] = {
|
conf["Sizes"] = {
|
||||||
"mainwindow": self._packList(self._mainWinSize),
|
"mainwindow": self._packList(self._mainWinSize),
|
||||||
"preferences": self._packList(self._prefsWinSize),
|
"preferences": self._packList(self._prefsWinSize),
|
||||||
"projloadcols": self._packList(self._projLoadCols),
|
"projloadcols": self._packList(self._projLoadCols),
|
||||||
@@ -693,7 +688,7 @@ class Config:
|
|||||||
"outlinepane": self._packList(self._outlnPanePos),
|
"outlinepane": self._packList(self._outlnPanePos),
|
||||||
}
|
}
|
||||||
|
|
||||||
theConf["Project"] = {
|
conf["Project"] = {
|
||||||
"autosaveproject": str(self.autoSaveProj),
|
"autosaveproject": str(self.autoSaveProj),
|
||||||
"autosavedoc": str(self.autoSaveDoc),
|
"autosavedoc": str(self.autoSaveDoc),
|
||||||
"emphlabels": str(self.emphLabels),
|
"emphlabels": str(self.emphLabels),
|
||||||
@@ -702,7 +697,7 @@ class Config:
|
|||||||
"askbeforebackup": str(self.askBeforeBackup),
|
"askbeforebackup": str(self.askBeforeBackup),
|
||||||
}
|
}
|
||||||
|
|
||||||
theConf["Editor"] = {
|
conf["Editor"] = {
|
||||||
"textfont": str(self.textFont),
|
"textfont": str(self.textFont),
|
||||||
"textsize": str(self.textSize),
|
"textsize": str(self.textSize),
|
||||||
"width": str(self.textWidth),
|
"width": str(self.textWidth),
|
||||||
@@ -743,7 +738,7 @@ class Config:
|
|||||||
"useridletime": str(self.userIdleTime),
|
"useridletime": str(self.userIdleTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
theConf["State"] = {
|
conf["State"] = {
|
||||||
"showrefpanel": str(self.showRefPanel),
|
"showrefpanel": str(self.showRefPanel),
|
||||||
"viewcomments": str(self.viewComments),
|
"viewcomments": str(self.viewComments),
|
||||||
"viewsynopsis": str(self.viewSynopsis),
|
"viewsynopsis": str(self.viewSynopsis),
|
||||||
@@ -759,7 +754,7 @@ class Config:
|
|||||||
cnfPath = self._confPath / nwFiles.CONF_FILE
|
cnfPath = self._confPath / nwFiles.CONF_FILE
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||||
theConf.write(outFile)
|
conf.write(outFile)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Could not save config file")
|
logger.error("Could not save config file")
|
||||||
logException()
|
logException()
|
||||||
@@ -774,26 +769,14 @@ class Config:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _packList(self, inData):
|
def _packList(self, data: list) -> str:
|
||||||
"""Pack a list of items into a comma-separated string for saving
|
"""Pack a list of items into a comma-separated string for saving
|
||||||
to the config file.
|
to the config file.
|
||||||
"""
|
"""
|
||||||
return ", ".join([str(inVal) for inVal in inData])
|
return ", ".join([str(inVal) for inVal in data])
|
||||||
|
|
||||||
def _checkNone(self, checkVal):
|
def _checkOptionalPackages(self) -> None:
|
||||||
"""Return a NoneType if the value corresponds to None, otherwise
|
"""Check optional packages used by some features."""
|
||||||
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.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
import enchant # noqa: F401
|
import enchant # noqa: F401
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -809,14 +792,13 @@ class Config:
|
|||||||
|
|
||||||
class RecentProjects:
|
class RecentProjects:
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config: Config) -> None:
|
||||||
self._conf = config
|
self._conf = config
|
||||||
self._data = {}
|
self._data = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
def loadCache(self):
|
def loadCache(self) -> bool:
|
||||||
"""Load the cache file for recent projects.
|
"""Load the cache file for recent projects."""
|
||||||
"""
|
|
||||||
self._data = {}
|
self._data = {}
|
||||||
|
|
||||||
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
||||||
@@ -839,9 +821,8 @@ class RecentProjects:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveCache(self):
|
def saveCache(self) -> bool:
|
||||||
"""Save the cache dictionary of recent projects.
|
"""Save the cache dictionary of recent projects."""
|
||||||
"""
|
|
||||||
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
||||||
cacheTemp = cacheFile.with_suffix(".tmp")
|
cacheTemp = cacheFile.with_suffix(".tmp")
|
||||||
try:
|
try:
|
||||||
@@ -855,27 +836,27 @@ class RecentProjects:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def listEntries(self):
|
def listEntries(self) -> list[tuple[str, str, int, int]]:
|
||||||
"""List all items in the cache.
|
"""List all items in the cache."""
|
||||||
"""
|
return [
|
||||||
return [(k, e["title"], e["words"], e["time"]) for k, e in self._data.items()]
|
(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):
|
def update(self, path: str | Path, title: str, words: int, saved: float | int) -> None:
|
||||||
"""Add or update recent cache information on a given project.
|
"""Add or update recent cache information on a given project."""
|
||||||
"""
|
self._data[str(path)] = {
|
||||||
self._data[str(projPath)] = {
|
"title": title,
|
||||||
"title": projTitle,
|
"words": int(words),
|
||||||
"words": int(wordCount),
|
"time": int(saved),
|
||||||
"time": int(saveTime),
|
|
||||||
}
|
}
|
||||||
self.saveCache()
|
self.saveCache()
|
||||||
return
|
return
|
||||||
|
|
||||||
def remove(self, projPath):
|
def remove(self, path: str | Path) -> None:
|
||||||
"""Try to remove a path from the recent projects cache.
|
"""Try to remove a path from the recent projects cache."""
|
||||||
"""
|
if self._data.pop(str(path), None) is not None:
|
||||||
if self._data.pop(str(projPath), None) is not None:
|
logger.debug("Removed recent: %s", path)
|
||||||
logger.debug("Removed recent: %s", projPath)
|
|
||||||
self.saveCache()
|
self.saveCache()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -100,8 +100,9 @@ class GuiProjectLoad(QDialog):
|
|||||||
self.listBox.setIconSize(QSize(iPx, iPx))
|
self.listBox.setIconSize(QSize(iPx, iPx))
|
||||||
|
|
||||||
treeHead = self.listBox.headerItem()
|
treeHead = self.listBox.headerItem()
|
||||||
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
if treeHead:
|
||||||
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
|
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
||||||
|
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
|
||||||
|
|
||||||
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
|
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
|
||||||
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
|
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
|
||||||
@@ -281,8 +282,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed)
|
newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed)
|
||||||
self.listBox.addTopLevelItem(newItem)
|
self.listBox.addTopLevelItem(newItem)
|
||||||
|
|
||||||
if self.listBox.topLevelItemCount() > 0:
|
self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
|
||||||
self.listBox.topLevelItem(0).setSelected(True)
|
|
||||||
|
|
||||||
projColWidth = CONFIG.projLoadColWidths
|
projColWidth = CONFIG.projLoadColWidths
|
||||||
if len(projColWidth) == 3:
|
if len(projColWidth) == 3:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class NProgressCircle(QProgressBar):
|
|||||||
"_cPen", "_bPen", "_tColor"
|
"_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)
|
super().__init__(parent=parent)
|
||||||
self._text = None
|
self._text = None
|
||||||
self._point = point
|
self._point = point
|
||||||
@@ -60,10 +60,8 @@ class NProgressCircle(QProgressBar):
|
|||||||
self.setFixedHeight(size)
|
self.setFixedHeight(size)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setColours(
|
def setColours(self, back: QColor | None = None, track: QColor | None = None,
|
||||||
self, back: QColor | None = None, track: QColor | None = None,
|
bar: QColor | None = None, text: QColor | None = None) -> None:
|
||||||
bar: QColor | None = None, text: QColor | None = None
|
|
||||||
):
|
|
||||||
"""Set the colours of the widget."""
|
"""Set the colours of the widget."""
|
||||||
if isinstance(back, QColor):
|
if isinstance(back, QColor):
|
||||||
self._dPen = QPen(back)
|
self._dPen = QPen(back)
|
||||||
@@ -76,13 +74,13 @@ class NProgressCircle(QProgressBar):
|
|||||||
self._tColor = text
|
self._tColor = text
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCentreText(self, text: str | None):
|
def setCentreText(self, text: str | None) -> None:
|
||||||
"""Replace the progress text with a custom string."""
|
"""Replace the progress text with a custom string."""
|
||||||
self._text = text
|
self._text = text
|
||||||
self.setValue(self.value()) # Triggers a redraw
|
self.setValue(self.value()) # Triggers a redraw
|
||||||
return
|
return
|
||||||
|
|
||||||
def paintEvent(self, event: QPaintEvent):
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
"""Custom painter for the progress bar."""
|
"""Custom painter for the progress bar."""
|
||||||
progress = 100.0*self.value()/self.maximum()
|
progress = 100.0*self.value()/self.maximum()
|
||||||
angle = ceil(16*3.6*progress)
|
angle = ceil(16*3.6*progress)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ FONT_SCALE = 0.9
|
|||||||
|
|
||||||
class NConfigLayout(QGridLayout):
|
class NConfigLayout(QGridLayout):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._nextRow = 0
|
self._nextRow = 0
|
||||||
@@ -56,7 +56,8 @@ class NConfigLayout(QGridLayout):
|
|||||||
# Getters and Setters
|
# 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."""
|
"""Set the text color for the help text."""
|
||||||
if isinstance(color, QColor):
|
if isinstance(color, QColor):
|
||||||
self._helpCol = color
|
self._helpCol = color
|
||||||
@@ -65,7 +66,7 @@ class NConfigLayout(QGridLayout):
|
|||||||
self._fontScale = fontScale
|
self._fontScale = fontScale
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHelpText(self, row: int, text: str):
|
def setHelpText(self, row: int, text: str) -> None:
|
||||||
"""Set the text for the help label."""
|
"""Set the text for the help label."""
|
||||||
if row in self._itemMap:
|
if row in self._itemMap:
|
||||||
qHelp = self._itemMap[row][1]
|
qHelp = self._itemMap[row][1]
|
||||||
@@ -73,7 +74,7 @@ class NConfigLayout(QGridLayout):
|
|||||||
qHelp.setText(text)
|
qHelp.setText(text)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLabelText(self, row: int, text: str):
|
def setLabelText(self, row: int, text: str) -> None:
|
||||||
"""Set the text for the main label."""
|
"""Set the text for the main label."""
|
||||||
if row in self._itemMap:
|
if row in self._itemMap:
|
||||||
self._itemMap[row](0).setText(text)
|
self._itemMap[row](0).setText(text)
|
||||||
@@ -83,7 +84,7 @@ class NConfigLayout(QGridLayout):
|
|||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def addGroupLabel(self, label: str):
|
def addGroupLabel(self, label: str) -> None:
|
||||||
"""Add a text label to separate groups of settings."""
|
"""Add a text label to separate groups of settings."""
|
||||||
hM = CONFIG.pxInt(4)
|
hM = CONFIG.pxInt(4)
|
||||||
qLabel = QLabel("<b>%s</b>" % label)
|
qLabel = QLabel("<b>%s</b>" % label)
|
||||||
@@ -94,10 +95,8 @@ class NConfigLayout(QGridLayout):
|
|||||||
self._nextRow += 1
|
self._nextRow += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def addRow(
|
def addRow(self, label: str, widget: QWidget, helpText: str | None = None,
|
||||||
self, label: str, widget: QWidget, helpText: str | None = None,
|
unit: str | None = None, button: QWidget | None = None) -> int:
|
||||||
unit: str | None = None, button: QWidget | None = None
|
|
||||||
) -> int:
|
|
||||||
"""Add a label and a widget as a new row of the grid."""
|
"""Add a label and a widget as a new row of the grid."""
|
||||||
wSp = CONFIG.pxInt(8)
|
wSp = CONFIG.pxInt(8)
|
||||||
qLabel = QLabel(label)
|
qLabel = QLabel(label)
|
||||||
@@ -155,7 +154,7 @@ class NSimpleLayout(QGridLayout):
|
|||||||
column layout.
|
column layout.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._nextRow = 0
|
self._nextRow = 0
|
||||||
|
|
||||||
@@ -170,7 +169,7 @@ class NSimpleLayout(QGridLayout):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def addGroupLabel(self, label: str):
|
def addGroupLabel(self, label: str) -> None:
|
||||||
"""Add a text label to separate groups of settings."""
|
"""Add a text label to separate groups of settings."""
|
||||||
hM = CONFIG.pxInt(4)
|
hM = CONFIG.pxInt(4)
|
||||||
qLabel = QLabel("<b>%s</b>" % label)
|
qLabel = QLabel("<b>%s</b>" % label)
|
||||||
@@ -181,7 +180,7 @@ class NSimpleLayout(QGridLayout):
|
|||||||
self._nextRow += 1
|
self._nextRow += 1
|
||||||
return
|
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."""
|
"""Add a label and a widget as a new row of the grid."""
|
||||||
wSp = CONFIG.pxInt(8)
|
wSp = CONFIG.pxInt(8)
|
||||||
qLabel = QLabel(label)
|
qLabel = QLabel(label)
|
||||||
@@ -208,7 +207,8 @@ class NSimpleLayout(QGridLayout):
|
|||||||
|
|
||||||
class NHelpLabel(QLabel):
|
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)
|
super().__init__(text)
|
||||||
|
|
||||||
if isinstance(color, QColor):
|
if isinstance(color, QColor):
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
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 (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
|
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
|
||||||
QTabWidget, QVBoxLayout
|
QTabWidget, QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
@@ -34,7 +35,7 @@ from novelwriter import CONFIG
|
|||||||
|
|
||||||
class NPagedDialog(QDialog):
|
class NPagedDialog(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent: QWidget) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self._tabBar = NVerticalTabBar(self)
|
self._tabBar = NVerticalTabBar(self)
|
||||||
@@ -67,21 +68,18 @@ class NPagedDialog(QDialog):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addTab(self, widget, label):
|
def addTab(self, widget: QWidget, label: str) -> None:
|
||||||
"""Forward the adding of tabs to the QTabWidget.
|
"""Forward the adding of tabs to the QTabWidget."""
|
||||||
"""
|
|
||||||
self._tabBox.addTab(widget, label)
|
self._tabBox.addTab(widget, label)
|
||||||
return
|
return
|
||||||
|
|
||||||
def addControls(self, buttonBar):
|
def addControls(self, buttonBar: QWidget) -> None:
|
||||||
"""Add a button bar to the dialog.
|
"""Add a button bar to the dialog."""
|
||||||
"""
|
|
||||||
self._buttonBox.addWidget(buttonBar)
|
self._buttonBox.addWidget(buttonBar)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCurrentWidget(self, widget):
|
def setCurrentWidget(self, widget: QWidget) -> None:
|
||||||
"""Forward the changing of tab to the QTabWidget.
|
"""Forward the changing of tab to the QTabWidget."""
|
||||||
"""
|
|
||||||
self._tabBox.setCurrentWidget(widget)
|
self._tabBox.setCurrentWidget(widget)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -90,20 +88,19 @@ class NPagedDialog(QDialog):
|
|||||||
|
|
||||||
class NVerticalTabBar(QTabBar):
|
class NVerticalTabBar(QTabBar):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent: QWidget) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
self._mW = CONFIG.pxInt(150)
|
self._mW = CONFIG.pxInt(150)
|
||||||
return
|
return
|
||||||
|
|
||||||
def tabSizeHint(self, index):
|
def tabSizeHint(self, index: int) -> QSize:
|
||||||
"""Return a transposed size hint for the rotated bar.
|
"""Return a transposed size hint for the rotated bar."""
|
||||||
"""
|
|
||||||
tSize = super().tabSizeHint(index)
|
tSize = super().tabSizeHint(index)
|
||||||
tSize.transpose()
|
tSize.transpose()
|
||||||
tSize.setWidth(min(tSize.width(), self._mW))
|
tSize.setWidth(min(tSize.width(), self._mW))
|
||||||
return tSize
|
return tSize
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
"""Custom implementation of the label painter that rotates the
|
"""Custom implementation of the label painter that rotates the
|
||||||
label 90 degrees.
|
label 90 degrees.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ class NProgressSimple(QProgressBar):
|
|||||||
A custom widget that paints a plain bar with no other styling.
|
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)
|
super().__init__(parent=parent)
|
||||||
return
|
return
|
||||||
|
|
||||||
def paintEvent(self, event: QPaintEvent):
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
"""Custom painter for the progress bar."""
|
"""Custom painter for the progress bar."""
|
||||||
if self.value() == 0:
|
if self.value() == 0:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from PyQt5.QtGui import QPainter
|
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
||||||
from PyQt5.QtCore import Qt, QRectF, QPropertyAnimation, pyqtProperty
|
from PyQt5.QtCore import QEvent, QPropertyAnimation, QRectF, Qt, pyqtProperty
|
||||||
from PyQt5.QtWidgets import QSizePolicy, QAbstractButton
|
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.constants import nwUnicode
|
from novelwriter.constants import nwUnicode
|
||||||
@@ -33,7 +33,8 @@ from novelwriter.constants import nwUnicode
|
|||||||
|
|
||||||
class NSwitch(QAbstractButton):
|
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)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
if width is None:
|
if width is None:
|
||||||
@@ -64,12 +65,12 @@ class NSwitch(QAbstractButton):
|
|||||||
# Properties
|
# Properties
|
||||||
##
|
##
|
||||||
|
|
||||||
@pyqtProperty(int)
|
@pyqtProperty(int) # type: ignore
|
||||||
def offset(self):
|
def offset(self) -> int: # type: ignore
|
||||||
return self._offset
|
return self._offset
|
||||||
|
|
||||||
@offset.setter
|
@offset.setter # type: ignore
|
||||||
def offset(self, offset):
|
def offset(self, offset: int):
|
||||||
self._offset = offset
|
self._offset = offset
|
||||||
self.update()
|
self.update()
|
||||||
return
|
return
|
||||||
@@ -78,33 +79,30 @@ class NSwitch(QAbstractButton):
|
|||||||
# Getters and Setters
|
# Getters and Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setChecked(self, checked):
|
def setChecked(self, checked: bool) -> None:
|
||||||
"""Overload setChecked to also alter the offset.
|
"""Overload setChecked to also alter the offset."""
|
||||||
"""
|
|
||||||
super().setChecked(checked)
|
super().setChecked(checked)
|
||||||
if checked:
|
if checked:
|
||||||
self.offset = self._xW - self._xR
|
self._offset = self._xW - self._xR
|
||||||
else:
|
else:
|
||||||
self.offset = self._xR
|
self._offset = self._xR
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Events
|
# Events
|
||||||
##
|
##
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||||
"""Overload resize to ensure correct offset.
|
"""Overload resize to ensure correct offset."""
|
||||||
"""
|
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
if self.isChecked():
|
if self.isChecked():
|
||||||
self.offset = self._xW - self._xR
|
self._offset = self._xW - self._xR
|
||||||
else:
|
else:
|
||||||
self.offset = self._xR
|
self._offset = self._xR
|
||||||
return
|
return
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
"""Drawing the switch itself.
|
"""Drawing the switch itself."""
|
||||||
"""
|
|
||||||
qPaint = QPainter(self)
|
qPaint = QPainter(self)
|
||||||
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
qPaint.setPen(Qt.NoPen)
|
qPaint.setPen(Qt.NoPen)
|
||||||
@@ -134,27 +132,26 @@ class NSwitch(QAbstractButton):
|
|||||||
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
||||||
|
|
||||||
qPaint.setBrush(thumbBrush)
|
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 = qPaint.font()
|
||||||
theFont.setPixelSize(self._xT)
|
theFont.setPixelSize(self._xT)
|
||||||
qPaint.setPen(textColor)
|
qPaint.setPen(textColor)
|
||||||
qPaint.setFont(theFont)
|
qPaint.setFont(theFont)
|
||||||
qPaint.drawText(
|
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
|
Qt.AlignCenter, thumbText
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
|
||||||
"""Animate the switch on mouse release.
|
"""Animate the switch on mouse release."""
|
||||||
"""
|
|
||||||
super().mouseReleaseEvent(event)
|
super().mouseReleaseEvent(event)
|
||||||
if event.button() == Qt.LeftButton:
|
if event.button() == Qt.LeftButton:
|
||||||
doAnim = QPropertyAnimation(self, b"offset", self)
|
doAnim = QPropertyAnimation(self, b"offset", self)
|
||||||
doAnim.setDuration(120)
|
doAnim.setDuration(120)
|
||||||
doAnim.setStartValue(self.offset)
|
doAnim.setStartValue(self._offset)
|
||||||
if self.isChecked():
|
if self.isChecked():
|
||||||
doAnim.setEndValue(self._xW - self._xR)
|
doAnim.setEndValue(self._xW - self._xR)
|
||||||
else:
|
else:
|
||||||
@@ -162,9 +159,8 @@ class NSwitch(QAbstractButton):
|
|||||||
doAnim.start()
|
doAnim.start()
|
||||||
return
|
return
|
||||||
|
|
||||||
def enterEvent(self, event):
|
def enterEvent(self, event: QEvent) -> None:
|
||||||
"""Change the cursor when hovering the button.
|
"""Change the cursor when hovering the button."""
|
||||||
"""
|
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
super().enterEvent(event)
|
super().enterEvent(event)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal
|
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||||||
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
|
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
|
||||||
|
|
||||||
from novelwriter.extensions.switch import NSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
@@ -39,7 +39,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
|
|
||||||
switchToggled = pyqtSignal(str, bool)
|
switchToggled = pyqtSignal(str, bool)
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, baseSize: int):
|
def __init__(self, parent: QWidget, baseSize: int) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
self._index = 0
|
self._index = 0
|
||||||
self._hSwitch = baseSize
|
self._hSwitch = baseSize
|
||||||
@@ -49,7 +49,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
self.clear()
|
self.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
def clear(self):
|
def clear(self) -> None:
|
||||||
"""Rebuild the content of the core widget."""
|
"""Rebuild the content of the core widget."""
|
||||||
self._index = 0
|
self._index = 0
|
||||||
self._widgets = []
|
self._widgets = []
|
||||||
@@ -66,7 +66,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addLabel(self, text: str):
|
def addLabel(self, text: str) -> None:
|
||||||
"""Add a header label to the content box."""
|
"""Add a header label to the content box."""
|
||||||
label = QLabel(text)
|
label = QLabel(text)
|
||||||
font = label.font()
|
font = label.font()
|
||||||
@@ -77,7 +77,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
self._bumpIndex()
|
self._bumpIndex()
|
||||||
return
|
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."""
|
"""Add an item to the content box."""
|
||||||
icon = QLabel("")
|
icon = QLabel("")
|
||||||
icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
@@ -97,7 +97,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addSeparator(self):
|
def addSeparator(self) -> None:
|
||||||
"""Add a blank entry in the content box."""
|
"""Add a blank entry in the content box."""
|
||||||
spacer = QWidget()
|
spacer = QWidget()
|
||||||
spacer.setFixedHeight(int(0.5*self._sIcon))
|
spacer.setFixedHeight(int(0.5*self._sIcon))
|
||||||
@@ -106,7 +106,7 @@ class NSwitchBox(QScrollArea):
|
|||||||
self._bumpIndex()
|
self._bumpIndex()
|
||||||
return
|
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."""
|
"""Set the contents margins of the inner layout."""
|
||||||
self._content.setContentsMargins(left, top, right, bottom)
|
self._content.setContentsMargins(left, top, right, bottom)
|
||||||
return
|
return
|
||||||
@@ -115,12 +115,12 @@ class NSwitchBox(QScrollArea):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _emitSwitchSignal(self, identifier: str, state: bool):
|
def _emitSwitchSignal(self, identifier: str, state: bool) -> None:
|
||||||
"""Emit a signal for a switch toggle."""
|
"""Emit a signal for a switch toggle."""
|
||||||
self.switchToggled.emit(identifier, state)
|
self.switchToggled.emit(identifier, state)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _bumpIndex(self):
|
def _bumpIndex(self) -> None:
|
||||||
"""Increase the index counter and make sure only the last
|
"""Increase the index counter and make sure only the last
|
||||||
columns is stretching.
|
columns is stretching.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -366,14 +366,6 @@ def testBaseConfig_Internal(monkeypatch, fncPath):
|
|||||||
# Function _packList
|
# Function _packList
|
||||||
assert tstConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
|
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
|
# Function _checkOptionalPackages
|
||||||
# (Assumes enchant package exists and is importable)
|
# (Assumes enchant package exists and is importable)
|
||||||
tstConf._checkOptionalPackages()
|
tstConf._checkOptionalPackages()
|
||||||
|
|||||||
Reference in New Issue
Block a user