Make further config class updates (#1229)

This commit is contained in:
Veronica Berglyd Olsen
2022-11-10 13:37:09 +01:00
committed by GitHub
19 changed files with 431 additions and 469 deletions
+1 -6
View File
@@ -161,9 +161,6 @@ def main(sysArgs=None):
elif inOpt == "--testmode":
testMode = True
# Set Config Options
CONFIG.cmdOpen = cmdOpen
# Set Logging
cHandle = logging.StreamHandler()
cHandle.setFormatter(logging.Formatter(fmt=logFormat, style="{"))
@@ -256,9 +253,7 @@ def main(sysArgs=None):
# Launch main GUI
CONFIG.initLocalisation(nwApp)
nwGUI = GuiMain()
if not nwGUI.hasProject:
nwGUI.showProjectLoadDialog()
nwGUI.releaseNotes()
nwGUI.postLaunchTasks(cmdOpen)
sys.exit(nwApp.exec_())
+16
View File
@@ -124,6 +124,17 @@ def checkUuid(value, default):
return default
def checkPath(value, default):
"""Check if a value is a valid path. Non-empty strings are accepted.
"""
if isinstance(value, Path):
return value
elif isinstance(value, str):
if value.strip():
return Path(value)
return default
# =============================================================================================== #
# Validator Functions
# =============================================================================================== #
@@ -552,6 +563,11 @@ class NWConfigParser(ConfigParser):
logger.error("Could not read '%s':'%s' from config", section, option)
return default
def rdPath(self, section, option, default):
"""Read a path value.
"""
return checkPath(self.get(section, option, fallback=default), default)
def rdStrList(self, section, option, default):
"""Read string list.
"""
+233 -299
View File
@@ -37,7 +37,7 @@ from PyQt5.QtCore import (
)
from novelwriter.error import logException, formatException
from novelwriter.common import splitVersionNumber, formatTimeStamp, NWConfigParser
from novelwriter.common import checkPath, splitVersionNumber, formatTimeStamp, NWConfigParser
from novelwriter.constants import nwFiles, nwUnicode
logger = logging.getLogger(__name__)
@@ -63,7 +63,7 @@ class Config:
self._confPath = confRoot.absolute() / self.appHandle # The user config location
self._dataPath = dataRoot.absolute() / self.appHandle # The user data location
self._lastPath = Path.home().absolute() # The user's last used path
self._homePath = Path.home().absolute() # The user's home directory
self._appPath = Path(__file__).parent.absolute()
self._appRoot = self._appPath.parent
@@ -73,13 +73,12 @@ class Config:
self._appPath = self._appRoot
# Runtime Settings and Variables
self._hasError = False # True if the config class encountered an error
self._errData = [] # List of error messages
self.confChanged = False # True whenever the config has chenged, false after save
self.cmdOpen = None # Path from command line for project to be opened on launch
self._hasError = False # True if the config class encountered an error
self._errData = [] # List of error messages
# Localisation Info
self._qLocal = QLocale.system()
# Localisation
# Note that these paths must be strings
self._qLocale = QLocale.system()
self._qtTrans = {}
self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
self._nwLangPath = str(self._appPath / "assets" / "i18n")
@@ -91,38 +90,35 @@ class Config:
# User Settings
# =============
self._recentProj = RecentProjects(self._dataPath)
self._recentProj = RecentProjects(self)
# General GUI Settings
self.guiLang = self._qLocal.name()
self.guiTheme = "" # GUI theme
self.guiSyntax = "" # Syntax theme
self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class
self.lastNotes = "0x0" # The latest release notes that have been shown
self.setDefaultGuiTheme()
self.setDefaultSyntaxTheme()
self.guiLocale = self._qLocale.name()
self.guiTheme = "default" # GUI theme
self.guiSyntax = "default_light" # Syntax theme
self.guiFont = "" # Defaults to system default font in theme class
self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class
self.hideVScroll = False # Hide vertical scroll bars on main widgets
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
self.lastNotes = "0x0" # The latest release notes that have been shown
self._lastPath = self._homePath # The user's last used path
# Size Settings
self.winGeometry = [1200, 650]
self.prefGeometry = [700, 615]
self.projColWidth = [200, 60, 140]
self.mainPanePos = [300, 800]
self.docPanePos = [400, 400]
self.viewPanePos = [500, 150]
self.outlnPanePos = [500, 150]
self.isFullScreen = False
# Feature Settings
self.hideVScroll = False # Hide vertical scroll bars on main widgets
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
self.emphLabels = True # Add emphasis to H1 and H2 item labels
self._mainWinSize = [1200, 650] # Last size of the main GUI window
self._prefsWinSize = [700, 615] # Last size of the Preferences dialog
self._projLoadCols = [280, 60, 160] # Last columns withs of the Project Load dialog
self._mainPanePos = [300, 800] # Last position of the main window splitter
self._viewPanePos = [500, 150] # Last position of the document viewer splitter
self._outlnPanePos = [500, 150] # Last position of the outline panel splitter
# Project Settings
self.autoSaveProj = 60 # Interval for auto-saving project in seconds
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
self.autoSaveProj = 60 # Interval for auto-saving project, in seconds
self.autoSaveDoc = 30 # Interval for auto-saving document, in seconds
self.emphLabels = True # Add emphasis to H1 and H2 item labels
self._backupPath = None # Backup path to use, can be none
self.backupOnClose = False # Flag for running automatic backups
self.askBeforeBackup = True # Flag for asking before running automatic backup
# Text Editor Settings
self.textFont = None # Editor font
@@ -174,6 +170,12 @@ class Config:
# Spell Checking Settings
self.spellLanguage = "en"
# State
self.isFullScreen = False # Last fullscreen state
self.showRefPanel = True # The reference panel for the viewer is visible
self.viewComments = True # Comments are shown in the viewer
self.viewSynopsis = True # Synopsis is shown in the viewer
# Search Bar Switches
self.searchCase = False
self.searchWord = False
@@ -182,16 +184,6 @@ class Config:
self.searchNextFile = False
self.searchMatchCap = False
# Backup Settings
self._backupPath = None
self.backupOnClose = False
self.askBeforeBackup = True
# State
self.showRefPanel = True # The reference panel for the viewer is visible
self.viewComments = True # Comments are shown in the viewer
self.viewSynopsis = True # Synopsis is shown in the viewer
# System and App Information
# ==========================
@@ -258,6 +250,111 @@ class Config:
def recentProjects(self):
return self._recentProj
@property
def mainWinSize(self):
return [int(x*self.guiScale) for x in self._mainWinSize]
@property
def preferencesWinSize(self):
return [int(x*self.guiScale) for x in self._prefsWinSize]
@property
def projLoadColWidths(self):
return [int(x*self.guiScale) for x in self._projLoadCols]
@property
def mainPanePos(self):
return [int(x*self.guiScale) for x in self._mainPanePos]
@property
def viewPanePos(self):
return [int(x*self.guiScale) for x in self._viewPanePos]
@property
def outlinePanePos(self):
return [int(x*self.guiScale) for x in self._outlnPanePos]
##
# Getters
##
def getTextWidth(self, focusMode=False):
"""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):
"""Get the scaled text margin."""
return self.pxInt(max(self.textMargin, 0))
def getTabWidth(self):
"""Get the scaled tab width."""
return self.pxInt(max(self.tabWidth, 0))
##
# Setters
##
def setMainWinSize(self, newWidth, newHeight):
"""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
return
def setPreferencesWinSize(self, newWidth, newHeight):
"""Set the size of the Preferences dialog window."""
self._prefsWinSize[0] = int(newWidth/self.guiScale)
self._prefsWinSize[1] = int(newHeight/self.guiScale)
return
def setProjLoadColWidths(self, colWidths):
"""Set the column widths of the Load Project dialog."""
self._projLoadCols = [int(x/self.guiScale) for x in colWidths]
return
def setMainPanePos(self, panePos):
"""Set the position of the main GUI splitter."""
self._mainPanePos = [int(x/self.guiScale) for x in panePos]
return
def setViewPanePos(self, panePos):
"""Set the position of the viewer meta data splitter."""
self._viewPanePos = [int(x/self.guiScale) for x in panePos]
return
def setOutlinePanePos(self, panePos):
"""Set the position of the outline details splitter."""
self._outlnPanePos = [int(x/self.guiScale) for x in panePos]
return
def setLastPath(self, lastPath):
"""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
logger.debug("Last path updated: %s" % self._lastPath)
return
def setBackupPath(self, backupPath):
"""Set the current backup path."""
self._backupPath = checkPath(backupPath, None)
return
##
# Methods
##
@@ -275,15 +372,13 @@ class Config:
return int(theSize/self.guiScale)
def dataPath(self, target=None):
"""Return a path in the data folder.
"""
"""Return a path in the data folder."""
if isinstance(target, str):
return self._dataPath / target
return self._dataPath
def assetPath(self, target=None):
"""Return a path in the assets folder.
"""
"""Return a path in the assets folder."""
if isinstance(target, str):
return self._appPath / "assets" / target
return self._appPath / "assets"
@@ -291,13 +386,13 @@ class Config:
def lastPath(self):
"""Return the last path used by the user, but ensure it exists.
"""
if self._lastPath.is_dir():
return self._lastPath
return Path.home().absolute()
if isinstance(self._lastPath, Path):
if self._lastPath.is_dir():
return self._lastPath
return self._homePath
def backupPath(self):
"""Return the backup path.
"""
"""Return the backup path."""
if isinstance(self._backupPath, Path):
if self._backupPath.is_dir():
return self._backupPath
@@ -312,6 +407,33 @@ class Config:
self._errData = []
return errMessage
def listLanguages(self, lngSet):
"""List localisation files in the i18n folder. The default GUI
language is British English (en_GB).
"""
if lngSet == self.LANG_NW:
fPre = "nw_"
fExt = ".qm"
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
elif lngSet == self.LANG_PROJ:
fPre = "project_"
fExt = ".json"
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
else:
return []
for qmFile in Path(self._nwLangPath).iterdir():
qmName = qmFile.name
if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
continue
qmLang = qmName[len(fPre):-len(fExt)]
qmName = QLocale(qmLang).nativeLanguageName().title()
if qmLang and qmName and qmLang != "en_GB":
langList[qmLang] = qmName
return sorted(langList.items(), key=lambda x: x[0])
##
# Config Actions
##
@@ -361,54 +483,26 @@ class Config:
def initLocalisation(self, nwApp):
"""Initialise the localisation of the GUI.
"""
self._qLocal = QLocale(self.guiLang)
QLocale.setDefault(self._qLocal)
self._qLocale = QLocale(self.guiLocale)
QLocale.setDefault(self._qLocale)
self._qtTrans = {}
langList = [
(self._qtLangPath, "qtbase"), # Qt 5.x
(self._nwLangPath, "qtbase"), # Alternative Qt 5.x
(self._nwLangPath, "nw"), # novelWriter
]
for lngPath, lngBase in langList:
for lngCode in self._qLocal.uiLanguages():
for lngCode in self._qLocale.uiLanguages():
qTrans = QTranslator()
lngFile = "%s_%s" % (lngBase, lngCode.replace("-", "_"))
if lngFile not in self._qtTrans:
if qTrans.load(lngFile, str(lngPath)):
logger.debug("Loaded: %s/%s", lngPath, lngFile)
if qTrans.load(lngFile, lngPath):
logger.debug("Loaded: %s.qm", lngFile)
nwApp.installTranslator(qTrans)
self._qtTrans[lngFile] = qTrans
return
def listLanguages(self, lngSet):
"""List localisation files in the i18n folder. The default GUI
language is British English (en_GB).
"""
if lngSet == self.LANG_NW:
fPre = "nw_"
fExt = ".qm"
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
elif lngSet == self.LANG_PROJ:
fPre = "project_"
fExt = ".json"
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
else:
return []
for qmFile in Path(self._nwLangPath).iterdir():
qmName = qmFile.name
if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
continue
qmLang = qmName[len(fPre):-len(fExt)]
qmName = QLocale(qmLang).nativeLanguageName().title()
if qmLang and qmName and qmLang != "en_GB":
langList[qmLang] = qmName
return sorted(langList.items(), key=lambda x: x[0])
def loadConfig(self):
"""Load preferences from file and replace default settings.
"""
@@ -431,29 +525,31 @@ class Config:
cnfSec = "Main"
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
self.guiLang = theConf.rdStr(cnfSec, "guilang", self.guiLang)
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)
# Sizes
cnfSec = "Sizes"
self.winGeometry = theConf.rdIntList(cnfSec, "geometry", self.winGeometry)
self.prefGeometry = theConf.rdIntList(cnfSec, "preferences", self.prefGeometry)
self.projColWidth = theConf.rdIntList(cnfSec, "projcols", self.projColWidth)
self.mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self.mainPanePos)
self.docPanePos = theConf.rdIntList(cnfSec, "docpane", self.docPanePos)
self.viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self.viewPanePos)
self.outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self.outlnPanePos)
self.isFullScreen = theConf.rdBool(cnfSec, "fullscreen", self.isFullScreen)
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)
# 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.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)
# Editor
cnfSec = "Editor"
@@ -494,15 +590,9 @@ class Config:
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)
# Backup
cnfSec = "Backup"
backupPath = theConf.rdStr(cnfSec, "backuppath", None)
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)
self.setBackupPath(backupPath)
# State
cnfSec = "State"
self.isFullScreen = theConf.rdBool(cnfSec, "fullscreen", self.isFullScreen)
self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel)
self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments)
self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis)
@@ -513,9 +603,14 @@ class Config:
self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile)
self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap)
# Path
cnfSec = "Path"
self._lastPath = Path(theConf.rdStr(cnfSec, "lastpath", self._lastPath))
# Deprecated Settings or Locations as of 2.0
# 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)
# Check Certain Values for None
self.spellLanguage = self._checkNone(self.spellLanguage)
@@ -538,33 +633,38 @@ class Config:
theConf = NWConfigParser()
theConf["Meta"] = {
"timestamp": formatTimeStamp(time()),
}
theConf["Main"] = {
"timestamp": formatTimeStamp(time()),
"theme": str(self.guiTheme),
"syntax": str(self.guiSyntax),
"guifont": str(self.guiFont),
"guifontsize": str(self.guiFontSize),
"lastnotes": str(self.lastNotes),
"guilang": str(self.guiLang),
"hidevscroll": str(self.hideVScroll),
"hidehscroll": str(self.hideHScroll),
"theme": str(self.guiTheme),
"syntax": str(self.guiSyntax),
"font": str(self.guiFont),
"fontsize": str(self.guiFontSize),
"localisation": str(self.guiLocale),
"hidevscroll": str(self.hideVScroll),
"hidehscroll": str(self.hideHScroll),
"lastnotes": str(self.lastNotes),
"lastpath": str(self._lastPath),
}
theConf["Sizes"] = {
"geometry": self._packList(self.winGeometry),
"preferences": self._packList(self.prefGeometry),
"projcols": self._packList(self.projColWidth),
"mainpane": self._packList(self.mainPanePos),
"docpane": self._packList(self.docPanePos),
"viewpane": self._packList(self.viewPanePos),
"outlinepane": self._packList(self.outlnPanePos),
"fullscreen": str(self.isFullScreen),
"mainwindow": self._packList(self._mainWinSize),
"preferences": self._packList(self._prefsWinSize),
"projloadcols": self._packList(self._projLoadCols),
"mainpane": self._packList(self._mainPanePos),
"viewpane": self._packList(self._viewPanePos),
"outlinepane": self._packList(self._outlnPanePos),
}
theConf["Project"] = {
"autosaveproject": str(self.autoSaveProj),
"autosavedoc": str(self.autoSaveDoc),
"emphlabels": str(self.emphLabels),
"backuppath": str(self._backupPath or ""),
"backuponclose": str(self.backupOnClose),
"askbeforebackup": str(self.askBeforeBackup),
}
theConf["Editor"] = {
@@ -606,13 +706,8 @@ class Config:
"useridletime": str(self.userIdleTime),
}
theConf["Backup"] = {
"backuppath": str(self._backupPath or ""),
"backuponclose": str(self.backupOnClose),
"askbeforebackup": str(self.askBeforeBackup),
}
theConf["State"] = {
"fullscreen": str(self.isFullScreen),
"showrefpanel": str(self.showRefPanel),
"viewcomments": str(self.viewComments),
"viewsynopsis": str(self.viewSynopsis),
@@ -624,16 +719,11 @@ class Config:
"searchmatchcap": str(self.searchMatchCap),
}
theConf["Path"] = {
"lastpath": str(self._lastPath),
}
# Write config file
cnfPath = self._confPath / nwFiles.CONF_FILE
try:
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
theConf.write(outFile)
self.confChanged = False
except Exception as exc:
logger.error("Could not save config file")
logException()
@@ -644,162 +734,6 @@ class Config:
return True
##
# Setters
##
def setLastPath(self, lastPath):
"""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 = Path(lastPath)
if not lastPath.is_dir():
lastPath = lastPath.parent
if lastPath.is_dir():
self._lastPath = lastPath
logger.debug("Last path updated: %s" % self._lastPath)
return
def setBackupPath(self, backupPath):
"""Set the current backup path.
"""
self._backupPath = None
if isinstance(backupPath, (str, Path)):
self._backupPath = Path(backupPath)
return
def setWinSize(self, newWidth, newHeight):
"""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.winGeometry[0] - newWidth) > 5:
self.winGeometry[0] = newWidth
self.confChanged = True
if abs(self.winGeometry[1] - newHeight) > 5:
self.winGeometry[1] = newHeight
self.confChanged = True
return
def setPreferencesSize(self, newWidth, newHeight):
"""Sat the size of the Preferences dialog window.
"""
self.prefGeometry[0] = int(newWidth/self.guiScale)
self.prefGeometry[1] = int(newHeight/self.guiScale)
self.confChanged = True
return
def setProjColWidths(self, colWidths):
"""Set the column widths of the Load Project dialog.
"""
self.projColWidth = [int(x/self.guiScale) for x in colWidths]
self.confChanged = True
return
def setMainPanePos(self, panePos):
"""Set the position of the main GUI splitter.
"""
self.mainPanePos = [int(x/self.guiScale) for x in panePos]
self.confChanged = True
return
def setDocPanePos(self, panePos):
"""Set the position of the main editor/viewer splitter.
"""
self.docPanePos = [int(x/self.guiScale) for x in panePos]
self.confChanged = True
return
def setViewPanePos(self, panePos):
"""Set the position of the viewer meta data splitter.
"""
self.viewPanePos = [int(x/self.guiScale) for x in panePos]
self.confChanged = True
return
def setOutlinePanePos(self, panePos):
"""Set the position of the outline details splitter.
"""
self.outlnPanePos = [int(x/self.guiScale) for x in panePos]
self.confChanged = True
return
def setShowRefPanel(self, checkState):
"""Set the visibility state of the reference panel.
"""
self.showRefPanel = checkState
self.confChanged = True
return
def setViewComments(self, viewState):
"""Set the visibility state of comments in the viewer.
"""
self.viewComments = viewState
self.confChanged = True
return
def setViewSynopsis(self, viewState):
"""Set the visibility state of synopsis comments in the viewer.
"""
self.viewSynopsis = viewState
self.confChanged = True
return
##
# Default Setters
##
def setDefaultGuiTheme(self):
"""Reset the GUI theme to default value.
"""
self.guiTheme = "default"
def setDefaultSyntaxTheme(self):
"""Reset the syntax theme to default value.
"""
self.guiSyntax = "default_light"
##
# Getters
##
def getWinSize(self):
return [int(x*self.guiScale) for x in self.winGeometry]
def getPreferencesSize(self):
return [int(x*self.guiScale) for x in self.prefGeometry]
def getProjColWidths(self):
return [int(x*self.guiScale) for x in self.projColWidth]
def getMainPanePos(self):
return [int(x*self.guiScale) for x in self.mainPanePos]
def getDocPanePos(self):
return [int(x*self.guiScale) for x in self.docPanePos]
def getViewPanePos(self):
return [int(x*self.guiScale) for x in self.viewPanePos]
def getOutlinePanePos(self):
return [int(x*self.guiScale) for x in self.outlnPanePos]
def getTextWidth(self, focusMode=False):
if focusMode:
return self.pxInt(max(self.focusWidth, 200))
else:
return self.pxInt(max(self.textWidth, 200))
def getTextMargin(self):
return self.pxInt(max(self.textMargin, 0))
def getTabWidth(self):
return self.pxInt(max(self.tabWidth, 0))
##
# Internal Functions
##
@@ -839,8 +773,8 @@ class Config:
class RecentProjects:
def __init__(self, dataPath):
self._dataPath = dataPath
def __init__(self, mainConf):
self.mainConf = mainConf
self._data = {}
return
@@ -849,7 +783,7 @@ class RecentProjects:
"""
self._data = {}
cacheFile = self._dataPath / nwFiles.RECENT_FILE
cacheFile = self.mainConf.dataPath(nwFiles.RECENT_FILE)
if not cacheFile.is_file():
return True
@@ -872,7 +806,7 @@ class RecentProjects:
def saveCache(self):
"""Save the cache dictionary of recent projects.
"""
cacheFile = self._dataPath / nwFiles.RECENT_FILE
cacheFile = self.mainConf.dataPath(nwFiles.RECENT_FILE)
cacheTemp = cacheFile.with_suffix(".tmp")
try:
with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
+12 -26
View File
@@ -74,7 +74,7 @@ class GuiPreferences(PagedDialog):
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
self.resize(*self.mainConf.getPreferencesSize())
self.resize(*self.mainConf.preferencesWinSize)
# Settings
self._updateTheme = False
@@ -125,6 +125,7 @@ class GuiPreferences(PagedDialog):
self.tabQuote.saveValues()
self._saveWindowSize()
self.mainConf.saveConfig()
self.accept()
return
@@ -143,7 +144,7 @@ class GuiPreferences(PagedDialog):
def _saveWindowSize(self):
"""Save the dialog window size.
"""
self.mainConf.setPreferencesSize(self.width(), self.height())
self.mainConf.setPreferencesWinSize(self.width(), self.height())
return
# END Class GuiPreferences
@@ -170,18 +171,18 @@ class GuiPreferencesGeneral(QWidget):
minWidth = self.mainConf.pxInt(200)
# Select Locale
self.guiLang = QComboBox()
self.guiLang.setMinimumWidth(minWidth)
self.guiLocale = QComboBox()
self.guiLocale.setMinimumWidth(minWidth)
theLangs = self.mainConf.listLanguages(self.mainConf.LANG_NW)
for lang, langName in theLangs:
self.guiLang.addItem(langName, lang)
langIdx = self.guiLang.findData(self.mainConf.guiLang)
self.guiLocale.addItem(langName, lang)
langIdx = self.guiLocale.findData(self.mainConf.guiLocale)
if langIdx != -1:
self.guiLang.setCurrentIndex(langIdx)
self.guiLocale.setCurrentIndex(langIdx)
self.mainForm.addRow(
self.tr("Main GUI language"),
self.guiLang,
self.guiLocale,
self.tr("Requires restart to take effect.")
)
@@ -286,7 +287,7 @@ class GuiPreferencesGeneral(QWidget):
def saveValues(self):
"""Save the values set for this tab.
"""
guiLang = self.guiLang.currentData()
guiLocale = self.guiLocale.currentData()
guiTheme = self.guiTheme.currentData()
guiSyntax = self.guiSyntax.currentData()
guiFont = self.guiFont.text()
@@ -296,12 +297,12 @@ class GuiPreferencesGeneral(QWidget):
# Update Flags
self.prefsGui._updateTheme |= self.mainConf.guiTheme != guiTheme
self.prefsGui._updateSyntax |= self.mainConf.guiSyntax != guiSyntax
self.prefsGui._needsRestart |= self.mainConf.guiLang != guiLang
self.prefsGui._needsRestart |= self.mainConf.guiLocale != guiLocale
self.prefsGui._needsRestart |= self.mainConf.guiFont != guiFont
self.prefsGui._needsRestart |= self.mainConf.guiFontSize != guiFontSize
self.prefsGui._refreshTree |= self.mainConf.emphLabels != emphLabels
self.mainConf.guiLang = guiLang
self.mainConf.guiLocale = guiLocale
self.mainConf.guiTheme = guiTheme
self.mainConf.guiSyntax = guiSyntax
self.mainConf.guiFont = guiFont
@@ -311,8 +312,6 @@ class GuiPreferencesGeneral(QWidget):
self.mainConf.hideVScroll = self.hideVScroll.isChecked()
self.mainConf.hideHScroll = self.hideHScroll.isChecked()
self.mainConf.confChanged = True
return
##
@@ -458,8 +457,6 @@ class GuiPreferencesProjects(QWidget):
self.mainConf.stopWhenIdle = self.stopWhenIdle.isChecked()
self.mainConf.userIdleTime = round(self.userIdleTime.value() * 60)
self.mainConf.confChanged = True
return
##
@@ -629,8 +626,6 @@ class GuiPreferencesDocuments(QWidget):
self.mainConf.textMargin = self.textMargin.value()
self.mainConf.tabWidth = self.tabWidth.value()
self.mainConf.confChanged = True
return
##
@@ -820,8 +815,6 @@ class GuiPreferencesEditor(QWidget):
self.mainConf.autoScroll = self.autoScroll.isChecked()
self.mainConf.autoScrollPos = self.autoScrollPos.value()
self.mainConf.confChanged = True
return
# END Class GuiPreferencesEditor
@@ -911,8 +904,6 @@ class GuiPreferencesSyntax(QWidget):
# Text Errors
self.mainConf.showMultiSpaces = self.showMultiSpaces.isChecked()
self.mainConf.confChanged = True
return
##
@@ -1065,8 +1056,6 @@ class GuiPreferencesAutomation(QWidget):
self.mainConf.fmtPadAfter = self.fmtPadAfter.text().strip()
self.mainConf.fmtPadThin = self.fmtPadThin.isChecked()
self.mainConf.confChanged = True
return
##
@@ -1185,9 +1174,6 @@ class GuiPreferencesQuotes(QWidget):
self.mainConf.fmtSingleQuotes[1] = self.quoteSym["SC"].text()
self.mainConf.fmtDoubleQuotes[0] = self.quoteSym["DO"].text()
self.mainConf.fmtDoubleQuotes[1] = self.quoteSym["DC"].text()
self.mainConf.confChanged = True
return
##
+2 -2
View File
@@ -258,7 +258,7 @@ class GuiProjectLoad(QDialog):
colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
self.mainConf.setProjColWidths(colWidths)
self.mainConf.setProjLoadColWidths(colWidths)
return
def _populateList(self):
@@ -284,7 +284,7 @@ class GuiProjectLoad(QDialog):
if self.listBox.topLevelItemCount() > 0:
self.listBox.topLevelItem(0).setSelected(True)
projColWidth = self.mainConf.getProjColWidths()
projColWidth = self.mainConf.projLoadColWidths
if len(projColWidth) == 3:
self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
+2 -2
View File
@@ -1148,7 +1148,7 @@ class GuiDocViewFooter(QWidget):
def _doToggleComments(self, theState):
"""Toggle the view comment button and reload the document.
"""
self.mainConf.setViewComments(theState)
self.mainConf.viewComments = theState
self.docViewer.reloadText()
return
@@ -1156,7 +1156,7 @@ class GuiDocViewFooter(QWidget):
def _doToggleSynopsis(self, theState):
"""Toggle the view synopsis button and reload the document.
"""
self.mainConf.setViewSynopsis(theState)
self.mainConf.viewSynopsis = theState
self.docViewer.reloadText()
return
+1 -1
View File
@@ -71,7 +71,7 @@ class GuiOutlineView(QWidget):
self.splitOutline = QSplitter(Qt.Vertical)
self.splitOutline.addWidget(self.outlineTree)
self.splitOutline.addWidget(self.outlineData)
self.splitOutline.setSizes(self.mainConf.getOutlinePanePos())
self.splitOutline.setSizes(self.mainConf.outlinePanePos)
# Assemble
self.outerBox = QVBoxLayout()
+12 -2
View File
@@ -185,9 +185,14 @@ class GuiTheme:
"""Load the currently specified GUI theme.
"""
guiTheme = self.mainConf.guiTheme
if guiTheme not in self._availThemes:
logger.error("Could not find GUI theme '%s'", guiTheme)
guiTheme = "default"
self.mainConf.guiTheme = guiTheme
themeFile = self._availThemes.get(guiTheme, None)
if themeFile is None:
logger.error("Could not find GUI theme '%s'", guiTheme)
logger.error("Could not load GUI theme")
return False
# Config File
@@ -266,9 +271,14 @@ class GuiTheme:
"""Load the currently specified syntax highlighter theme.
"""
guiSyntax = self.mainConf.guiSyntax
if guiSyntax not in self._availSyntax:
logger.error("Could not find syntax theme '%s'", guiSyntax)
guiSyntax = "default_light"
self.mainConf.guiSyntax = guiSyntax
syntaxFile = self._availSyntax.get(guiSyntax, None)
if syntaxFile is None:
logger.error("Could not find syntax theme '%s'", guiSyntax)
logger.error("Could not load syntax theme")
return False
logger.info("Loading syntax theme '%s'", guiSyntax)
+18 -15
View File
@@ -79,7 +79,7 @@ class GuiMain(QMainWindow):
logger.info("Qt5: %s (%d)", self.mainConf.verQtString, self.mainConf.verQtValue)
logger.info("PyQt5: %s (%d)", self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
logger.info("Python: %s (0x%x)", self.mainConf.verPyString, self.mainConf.verPyHexVal)
logger.info("GUI Language: %s", self.mainConf.guiLang)
logger.info("GUI Language: %s", self.mainConf.guiLocale)
# Core Classes
# ============
@@ -93,7 +93,7 @@ class GuiMain(QMainWindow):
self.idleTime = 0.0
# Prepare Main Window
self.resize(*self.mainConf.getWinSize())
self.resize(*self.mainConf.mainWinSize)
self._updateWindowTitle()
nwIcon = self.mainConf.assetPath("icons") / "novelwriter.svg"
@@ -140,7 +140,7 @@ class GuiMain(QMainWindow):
self.splitView.addWidget(self.docViewer)
self.splitView.addWidget(self.viewMeta)
self.splitView.setHandleWidth(hWd)
self.splitView.setSizes(self.mainConf.getViewPanePos())
self.splitView.setSizes(self.mainConf.viewPanePos)
# Splitter : Document Editor / Document Viewer
self.splitDocs = QSplitter(Qt.Horizontal)
@@ -154,7 +154,7 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.splitDocs)
self.splitMain.setHandleWidth(hWd)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
self.splitMain.setSizes(self.mainConf.mainPanePos)
# Main Stack : Editor / Outline
self.mainStack = QStackedWidget()
@@ -290,11 +290,6 @@ class GuiMain(QMainWindow):
"and make sure you take regular backups."
), nwAlert.WARN)
# If a project path was provided at command line, open it
if self.mainConf.cmdOpen is not None:
logger.debug("Opening project from additional command line option")
self.openProject(self.mainConf.cmdOpen)
logger.info("novelWriter is ready ...")
self.setStatus(self.tr("novelWriter is ready ..."))
@@ -327,13 +322,22 @@ class GuiMain(QMainWindow):
self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000))
return True
def releaseNotes(self):
"""Determine whether release notes need to be shown, and show
them by calling the About dialog.
def postLaunchTasks(self, cmdOpen):
"""This function is called after the main window is created to
determine what to open or show after initialisation.
"""
if cmdOpen:
logger.info("Command line path: %s", cmdOpen)
self.openProject(cmdOpen)
if not self.hasProject:
self.showProjectLoadDialog()
# Determine whether release notes need to be shown or not
if hexToInt(self.mainConf.lastNotes) < hexToInt(novelwriter.__hexversion__):
self.mainConf.lastNotes = novelwriter.__hexversion__
self.showAboutNWDialog(showNotes=True)
return
##
@@ -1169,14 +1173,13 @@ class GuiMain(QMainWindow):
if not self.isFocusMode:
self.mainConf.setMainPanePos(self.splitMain.sizes())
self.mainConf.setDocPanePos(self.splitDocs.sizes())
self.mainConf.setOutlinePanePos(self.outlineView.splitSizes())
if self.viewMeta.isVisible():
self.mainConf.setViewPanePos(self.splitView.sizes())
self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
self.mainConf.showRefPanel = self.viewMeta.isVisible()
if not self.mainConf.isFullScreen:
self.mainConf.setWinSize(self.width(), self.height())
self.mainConf.setMainWinSize(self.width(), self.height())
if self.hasProject:
self.closeProject(True)
+2 -2
View File
@@ -113,7 +113,7 @@ def tmpConf(tmpPath):
theConf = Config()
theConf.initConfig(tmpPath, tmpPath)
theConf.setLastPath(tmpPath)
theConf.guiLang = "en_GB"
theConf.guiLocale = "en_GB"
return theConf
@@ -127,7 +127,7 @@ def fncConf(fncPath):
theConf = Config()
theConf.initConfig(fncPath, fncPath)
theConf.setLastPath(fncPath)
theConf.guiLang = "en_GB"
theConf.guiLocale = "en_GB"
return theConf
+3 -1
View File
@@ -35,6 +35,7 @@ class MockGuiMain(QObject):
self.hasProject = True
self.theProject = None
self.mainStatus = MockStatusBar()
self.projPath = ""
# Test Variables
self.askResponse = True
@@ -43,7 +44,7 @@ class MockGuiMain(QObject):
return
def releaseNotes(self):
def postLaunchTasks(self, cmdOpen):
return
def makeAlert(self, message, level=0, exception=None):
@@ -61,6 +62,7 @@ class MockGuiMain(QObject):
return
def openProject(self, projPath):
self.projPath = projPath
return
def rebuildIndex(self):
+14 -17
View File
@@ -1,28 +1,32 @@
[Meta]
timestamp = 2022-11-10 11:10:10
[Main]
timestamp = 2022-10-26 11:19:49
theme = default
syntax = default_light
guifont =
guifontsize = 11
lastnotes = 0x0
guilang = en_GB
font =
fontsize = 11
localisation = en_GB
hidevscroll = False
hidehscroll = False
lastnotes = 0x0
lastpath = /home/vkbo
[Sizes]
geometry = 1200, 650
mainwindow = 1200, 650
preferences = 700, 615
projcols = 200, 60, 140
projloadcols = 280, 60, 160
mainpane = 300, 800
docpane = 400, 400
viewpane = 500, 150
outlinepane = 500, 150
fullscreen = False
[Project]
autosaveproject = 60
autosavedoc = 30
emphlabels = True
backuppath =
backuponclose = False
askbeforebackup = True
[Editor]
textfont = None
@@ -62,12 +66,8 @@ highlightemph = True
stopwhenidle = True
useridletime = 300
[Backup]
backuppath =
backuponclose = False
askbeforebackup = True
[State]
fullscreen = False
showrefpanel = True
viewcomments = True
viewsynopsis = True
@@ -78,6 +78,3 @@ searchloop = False
searchnextfile = False
searchmatchcap = False
[Path]
lastpath =
+14 -17
View File
@@ -1,28 +1,32 @@
[Meta]
timestamp = 2022-11-10 11:10:13
[Main]
timestamp = 2022-10-26 11:19:51
theme = default
syntax = default_light
guifont = Cantarell
guifontsize = 12
lastnotes = 0x0
guilang = en_GB
font = Cantarell
fontsize = 12
localisation = en_GB
hidevscroll = True
hidehscroll = True
lastnotes = 0x0
lastpath = /home/vkbo/Code/novelWriter/Source/tests/temp/function
[Sizes]
geometry = 1200, 650
mainwindow = 1200, 650
preferences = 699, 614
projcols = 200, 60, 140
projloadcols = 280, 60, 160
mainpane = 300, 800
docpane = 400, 400
viewpane = 500, 150
outlinepane = 500, 150
fullscreen = False
[Project]
autosaveproject = 40
autosavedoc = 20
emphlabels = True
backuppath = some/dir
backuponclose = True
askbeforebackup = True
[Editor]
textfont = None
@@ -62,12 +66,8 @@ highlightemph = False
stopwhenidle = True
useridletime = 300
[Backup]
backuppath = some/dir
backuponclose = True
askbeforebackup = True
[State]
fullscreen = False
showrefpanel = True
viewcomments = True
viewsynopsis = True
@@ -78,6 +78,3 @@ searchloop = False
searchnextfile = False
searchmatchcap = False
[Path]
lastpath =
+18 -3
View File
@@ -23,15 +23,17 @@ import time
import pytest
import hashlib
from pathlib import Path
from mock import causeOSError
from tools import writeFile
from novelwriter.guimain import GuiMain
from novelwriter.common import (
checkStringNone, checkString, checkInt, checkFloat, checkBool, checkHandle,
checkUuid, isHandle, isTitleTag, isItemClass, isItemType, isItemLayout,
hexToInt, minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime,
simplified, yesNo, splitVersionNumber, transferCase, fuzzyTime,
checkUuid, checkPath, isHandle, isTitleTag, isItemClass, isItemType,
isItemLayout, hexToInt, minmax, checkIntTuple, formatInt, formatTimeStamp,
formatTime, simplified, yesNo, splitVersionNumber, transferCase, fuzzyTime,
numberToRoman, jsonEncode, readTextFile, makeFileNameSafe, sha256sum,
getGuiItem, NWConfigParser
)
@@ -175,6 +177,19 @@ def testBaseCommon_CheckUuid():
# END Test testBaseCommon_CheckUuid
@pytest.mark.base
def testBaseCommon_CheckPath():
"""Test the checkPath function.
"""
assert checkPath(Path("test"), None) == Path("test")
assert checkPath("test", None) == Path("test")
assert checkPath(None, None) is None
assert checkPath("", None) is None
assert checkPath(" ", None) is None
# END Test testBaseCommon_CheckPath
@pytest.mark.base
def testBaseCommon_IsHandle():
"""Test the isHandle function.
+37 -68
View File
@@ -112,7 +112,7 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
# Check that we have a default file
copyfile(confFile, testFile)
ignore = ("timestamp", "lastnotes", "guilang", "lastpath")
ignore = ("timestamp", "lastnotes", "localisation", "lastpath")
assert cmpFiles(testFile, compFile, ignoreStart=ignore)
tstConf.errorText() # This clears the error cache
@@ -166,7 +166,7 @@ def testBaseConfig_Localisation(fncPath, tstPaths):
i18nDir = fncPath / "i18n"
i18nDir.mkdir()
tstConf._nwLangPath = i18nDir
tstConf._nwLangPath = str(i18nDir)
copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_en_GB.qm")
writeFile(i18nDir / "nw_en_GB.ts", "")
@@ -253,96 +253,83 @@ def testBaseConfig_SettersGetters(tmpConf):
# Window Size
tmpConf.guiScale = 1.0
tmpConf.setWinSize(1205, 655)
assert tmpConf.confChanged is False
tmpConf.setMainWinSize(1205, 655)
assert tmpConf.mainWinSize == [1200, 650]
tmpConf.guiScale = 2.0
tmpConf.setWinSize(70, 70)
assert tmpConf.getWinSize() == [70, 70]
assert tmpConf.winGeometry == [35, 35]
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._mainWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setWinSize(70, 70)
assert tmpConf.getWinSize() == [70, 70]
assert tmpConf.winGeometry == [70, 70]
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._mainWinSize == [70, 70]
tmpConf.setWinSize(1200, 650)
tmpConf.setMainWinSize(1200, 650)
# Preferences Size
tmpConf.guiScale = 2.0
tmpConf.setPreferencesSize(70, 70)
assert tmpConf.getPreferencesSize() == [70, 70]
assert tmpConf.prefGeometry == [35, 35]
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefsWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setPreferencesSize(70, 70)
assert tmpConf.getPreferencesSize() == [70, 70]
assert tmpConf.prefGeometry == [70, 70]
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefsWinSize == [70, 70]
tmpConf.setPreferencesSize(700, 615)
tmpConf.setPreferencesWinSize(700, 615)
# Project Settings Tree Columns
tmpConf.guiScale = 2.0
tmpConf.setProjColWidths([10, 20, 30])
assert tmpConf.getProjColWidths() == [10, 20, 30]
assert tmpConf.projColWidth == [5, 10, 15]
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projLoadCols == [5, 10, 15]
tmpConf.guiScale = 1.0
tmpConf.setProjColWidths([10, 20, 30])
assert tmpConf.getProjColWidths() == [10, 20, 30]
assert tmpConf.projColWidth == [10, 20, 30]
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projLoadCols == [10, 20, 30]
tmpConf.setProjColWidths([200, 60, 140])
tmpConf.setProjLoadColWidths([200, 60, 140])
# Main Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setMainPanePos([200, 700])
assert tmpConf.getMainPanePos() == [200, 700]
assert tmpConf.mainPanePos == [100, 350]
assert tmpConf.mainPanePos == [200, 700]
assert tmpConf._mainPanePos == [100, 350]
tmpConf.guiScale = 1.0
tmpConf.setMainPanePos([200, 700])
assert tmpConf.getMainPanePos() == [200, 700]
assert tmpConf.mainPanePos == [200, 700]
assert tmpConf._mainPanePos == [200, 700]
tmpConf.setMainPanePos([300, 800])
# Doc Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setDocPanePos([300, 300])
assert tmpConf.getDocPanePos() == [300, 300]
assert tmpConf.docPanePos == [150, 150]
tmpConf.guiScale = 1.0
tmpConf.setDocPanePos([300, 300])
assert tmpConf.getDocPanePos() == [300, 300]
assert tmpConf.docPanePos == [300, 300]
tmpConf.setDocPanePos([400, 400])
# View Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setViewPanePos([400, 250])
assert tmpConf.getViewPanePos() == [400, 250]
assert tmpConf.viewPanePos == [200, 125]
assert tmpConf.viewPanePos == [400, 250]
assert tmpConf._viewPanePos == [200, 125]
tmpConf.guiScale = 1.0
tmpConf.setViewPanePos([400, 250])
assert tmpConf.getViewPanePos() == [400, 250]
assert tmpConf.viewPanePos == [400, 250]
assert tmpConf._viewPanePos == [400, 250]
tmpConf.setViewPanePos([500, 150])
# Outline Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setOutlinePanePos([400, 250])
assert tmpConf.getOutlinePanePos() == [400, 250]
assert tmpConf.outlnPanePos == [200, 125]
assert tmpConf.outlinePanePos == [400, 250]
assert tmpConf._outlnPanePos == [200, 125]
tmpConf.guiScale = 1.0
tmpConf.setOutlinePanePos([400, 250])
assert tmpConf.getOutlinePanePos() == [400, 250]
assert tmpConf.outlnPanePos == [400, 250]
assert tmpConf.outlinePanePos == [400, 250]
assert tmpConf._outlnPanePos == [400, 250]
tmpConf.setOutlinePanePos([500, 150])
@@ -361,24 +348,6 @@ def testBaseConfig_SettersGetters(tmpConf):
assert tmpConf.getTextMargin() == 80
assert tmpConf.getTabWidth() == 80
# Flag Setters
# ============
tmpConf.setShowRefPanel(False)
assert tmpConf.showRefPanel is False
tmpConf.setShowRefPanel(True)
assert tmpConf.showRefPanel is True
tmpConf.setViewComments(False)
assert tmpConf.viewComments is False
tmpConf.setViewComments(True)
assert tmpConf.viewComments is True
tmpConf.setViewSynopsis(False)
assert tmpConf.viewSynopsis is False
tmpConf.setViewSynopsis(True)
assert tmpConf.viewSynopsis is True
# END Test testBaseConfig_SettersGetters
@@ -411,11 +380,11 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
@pytest.mark.base
def testBaseConfig_RecentCache(monkeypatch, fncPath):
def testBaseConfig_RecentCache(monkeypatch, fncConf, fncPath):
"""Test recent cache file.
"""
cacheFile = fncPath / nwFiles.RECENT_FILE
recent = RecentProjects(fncPath)
recent = RecentProjects(fncConf)
# Load when there is no file should pass, but load nothing
assert not cacheFile.exists()
-1
View File
@@ -138,7 +138,6 @@ def testBaseInit_Options(monkeypatch, tmpPath):
nwGUI = novelwriter.main(
["--testmode", f"--config={tmpPath}", f"--data={tmpPath}", "sample/"]
)
assert novelwriter.CONFIG.cmdOpen == "sample/"
assert nwGUI.closeMain() == "closeMain"
# END Test testBaseInit_Options
+1 -3
View File
@@ -215,15 +215,13 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
nwPrefs._doClose()
assert theConf.confChanged
assert nwGUI.mainConf.saveConfig()
projFile = fncPath / "novelwriter.conf"
testFile = tstPaths.outDir / "guiPreferences_novelwriter.conf"
compFile = tstPaths.refDir / "guiPreferences_novelwriter.conf"
copyfile(projFile, testFile)
ignTuple = (
"timestamp", "guifont", "lastnotes", "guilang", "geometry",
"timestamp", "font", "lastnotes", "localisation", "geometry",
"preferences", "projcols", "mainpane", "docpane", "viewpane",
"outlinepane", "textfont", "textsize", "lastpath", "backuppath"
)
+39 -4
View File
@@ -21,15 +21,18 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest
from tools import C, cmpFiles, buildTestProject, XML_IGNORE, writeFile
from shutil import copyfile
from tools import (
C, cmpFiles, buildTestProject, XML_IGNORE, getGuiItem, writeFile
)
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QInputDialog
from PyQt5.QtWidgets import QDialog, QMessageBox, QInputDialog
from novelwriter.enum import nwItemType, nwView, nwWidget
from novelwriter.tools import GuiProjectWizard
from novelwriter.dialogs import GuiEditLabel
from novelwriter.dialogs import GuiEditLabel, GuiAbout, GuiProjectLoad
from novelwriter.constants import nwFiles
from novelwriter.gui.outline import GuiOutlineView
from novelwriter.gui.projtree import GuiProjectTree
@@ -62,7 +65,39 @@ def testGuiMain_ProjectBlocker(nwGUI):
assert nwGUI.showProjectWordListDialog() is False
assert nwGUI.showWritingStatsDialog() is False
# END Test testGuiMain_NoProject
# END Test testGuiMain_ProjectBlocker
@pytest.mark.gui
def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, prjLipsum):
"""Test the handling of launch tasks.
"""
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
nwGUI.mainConf.lastNotes = "0x0"
# Open Lipsum project
nwGUI.postLaunchTasks(prjLipsum)
nwGUI.closeProject()
# Check that release notes opened
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
msgAbout = getGuiItem("GuiAbout")
assert isinstance(msgAbout, GuiAbout)
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
msgAbout.accept()
# Check that project open dialog launches
nwGUI.postLaunchTasks(None)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
nwLoad.reject()
# qtbot.stop()
# END Test testGuiMain_Launch
@pytest.mark.gui
+6
View File
@@ -150,7 +150,10 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
# Check handling of broken theme settings
mainConf.guiTheme = "not_a_theme"
availThemes = mainTheme._availThemes
mainTheme._availThemes = {}
assert mainTheme.loadTheme() is False
mainTheme._availThemes = availThemes
# Check handling of unreadable file
mainConf.guiTheme = "default"
@@ -216,8 +219,11 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
assert mainTheme.listSyntax() == mainTheme._syntaxList
# Check handling of broken theme settings
availSyntax = mainTheme._availSyntax
mainTheme._availSyntax = {}
mainConf.guiSyntax = "not_a_syntax"
assert mainTheme.loadSyntax() is False
mainTheme._availSyntax = availSyntax
# Check handling of unreadable file
mainConf.guiSyntax = "default_light"