Clean up config file format, modify how projects are launched, and add broken theme setting handling

This commit is contained in:
Veronica Berglyd Olsen
2022-11-10 13:20:26 +01:00
parent 15fec91358
commit b46ad86c31
15 changed files with 277 additions and 329 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_())
+152 -217
View File
@@ -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")
@@ -94,34 +93,32 @@ class Config:
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._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
@@ -173,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
@@ -181,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
# ==========================
@@ -259,15 +252,15 @@ class Config:
@property
def mainWinSize(self):
return [int(x*self.guiScale) for x in self._winGeometry]
return [int(x*self.guiScale) for x in self._mainWinSize]
@property
def preferencesWinSize(self):
return [int(x*self.guiScale) for x in self._prefGeometry]
return [int(x*self.guiScale) for x in self._prefsWinSize]
@property
def projLoadColWidths(self):
return [int(x*self.guiScale) for x in self._projColWidth]
return [int(x*self.guiScale) for x in self._projLoadCols]
@property
def mainPanePos(self):
@@ -281,6 +274,25 @@ class Config:
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
##
@@ -293,48 +305,36 @@ class Config:
"""
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
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):
"""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
"""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._projColWidth = [int(x/self.guiScale) for x in colWidths]
self._confChanged = True
"""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.
"""
"""Set the position of the main GUI splitter."""
self._mainPanePos = [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.
"""
"""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.
"""
"""Set the position of the outline details splitter."""
self._outlnPanePos = [int(x/self.guiScale) for x in panePos]
self._confChanged = True
return
def setLastPath(self, lastPath):
@@ -342,7 +342,7 @@ class Config:
path is not a folder, the parent of the path is used instead.
"""
if isinstance(lastPath, (str, Path)):
lastPath = Path(lastPath)
lastPath = checkPath(lastPath, self._homePath)
if not lastPath.is_dir():
lastPath = lastPath.parent
if lastPath.is_dir():
@@ -351,15 +351,10 @@ class Config:
return
def setBackupPath(self, backupPath):
"""Set the current backup path.
"""
"""Set the current backup path."""
self._backupPath = checkPath(backupPath, None)
return
def setConfigChanged(self, value):
self._confChanged = bool(value)
return
##
# Methods
##
@@ -377,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"
@@ -396,11 +389,10 @@ class Config:
if isinstance(self._lastPath, Path):
if self._lastPath.is_dir():
return self._lastPath
return Path.home().absolute()
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
@@ -415,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
##
@@ -464,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.
"""
@@ -534,28 +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._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)
self.isFullScreen = theConf.rdBool(cnfSec, "fullscreen", self.isFullScreen)
# 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"
@@ -596,14 +590,9 @@ class Config:
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)
# Backup
cnfSec = "Backup"
self._backupPath = theConf.rdPath(cnfSec, "backuppath", self._backupPath)
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)
# 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)
@@ -614,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 = theConf.rdPath(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)
@@ -639,32 +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),
"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"] = {
@@ -706,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),
@@ -724,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()
@@ -744,61 +734,6 @@ class Config:
return True
##
# Setters
##
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 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
##
+10 -24
View File
@@ -125,6 +125,7 @@ class GuiPreferences(PagedDialog):
self.tabQuote.saveValues()
self._saveWindowSize()
self.mainConf.saveConfig()
self.accept()
return
@@ -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.setConfigChanged(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.setConfigChanged(True)
return
##
@@ -629,8 +626,6 @@ class GuiPreferencesDocuments(QWidget):
self.mainConf.textMargin = self.textMargin.value()
self.mainConf.tabWidth = self.tabWidth.value()
self.mainConf.setConfigChanged(True)
return
##
@@ -820,8 +815,6 @@ class GuiPreferencesEditor(QWidget):
self.mainConf.autoScroll = self.autoScroll.isChecked()
self.mainConf.autoScrollPos = self.autoScrollPos.value()
self.mainConf.setConfigChanged(True)
return
# END Class GuiPreferencesEditor
@@ -911,8 +904,6 @@ class GuiPreferencesSyntax(QWidget):
# Text Errors
self.mainConf.showMultiSpaces = self.showMultiSpaces.isChecked()
self.mainConf.setConfigChanged(True)
return
##
@@ -1065,8 +1056,6 @@ class GuiPreferencesAutomation(QWidget):
self.mainConf.fmtPadAfter = self.fmtPadAfter.text().strip()
self.mainConf.fmtPadThin = self.fmtPadThin.isChecked()
self.mainConf.setConfigChanged(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.setConfigChanged(True)
return
##
+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
+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)
+14 -10
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
# ============
@@ -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
##
@@ -1173,7 +1177,7 @@ class GuiMain(QMainWindow):
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.setMainWinSize(self.width(), self.height())
+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 -16
View File
@@ -1,27 +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
viewpane = 500, 150
outlinepane = 500, 150
fullscreen = False
[Project]
autosaveproject = 60
autosavedoc = 30
emphlabels = True
backuppath =
backuponclose = False
askbeforebackup = True
[Editor]
textfont = None
@@ -61,12 +66,8 @@ highlightemph = True
stopwhenidle = True
useridletime = 300
[Backup]
backuppath =
backuponclose = False
askbeforebackup = True
[State]
fullscreen = False
showrefpanel = True
viewcomments = True
viewsynopsis = True
@@ -77,6 +78,3 @@ searchloop = False
searchnextfile = False
searchmatchcap = False
[Path]
lastpath =
+14 -16
View File
@@ -1,27 +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
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
@@ -61,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
@@ -77,6 +78,3 @@ searchloop = False
searchnextfile = False
searchmatchcap = False
[Path]
lastpath =
+8 -26
View File
@@ -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", "")
@@ -254,17 +254,17 @@ def testBaseConfig_SettersGetters(tmpConf):
# Window Size
tmpConf.guiScale = 1.0
tmpConf.setMainWinSize(1205, 655)
assert tmpConf._confChanged is False
assert tmpConf.mainWinSize == [1200, 650]
tmpConf.guiScale = 2.0
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._winGeometry == [35, 35]
assert tmpConf._mainWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._winGeometry == [70, 70]
assert tmpConf._mainWinSize == [70, 70]
tmpConf.setMainWinSize(1200, 650)
@@ -272,12 +272,12 @@ def testBaseConfig_SettersGetters(tmpConf):
tmpConf.guiScale = 2.0
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefGeometry == [35, 35]
assert tmpConf._prefsWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefGeometry == [70, 70]
assert tmpConf._prefsWinSize == [70, 70]
tmpConf.setPreferencesWinSize(700, 615)
@@ -285,12 +285,12 @@ def testBaseConfig_SettersGetters(tmpConf):
tmpConf.guiScale = 2.0
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projColWidth == [5, 10, 15]
assert tmpConf._projLoadCols == [5, 10, 15]
tmpConf.guiScale = 1.0
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projColWidth == [10, 20, 30]
assert tmpConf._projLoadCols == [10, 20, 30]
tmpConf.setProjLoadColWidths([200, 60, 140])
@@ -348,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
-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
@@ -215,8 +215,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
nwPrefs._doClose()
assert theConf._confChanged is True
assert nwGUI.mainConf.saveConfig()
projFile = fncPath / "novelwriter.conf"
testFile = tstPaths.outDir / "guiPreferences_novelwriter.conf"
+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"