From 9d3291aba66872e07a26b1d7d35c4222811ab2f7 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 9 Nov 2022 17:44:20 +0100
Subject: [PATCH] Change assetPath to a Path object
---
novelwriter/__init__.py | 2 -
novelwriter/config.py | 120 +++++++++++++------------
novelwriter/core/coretools.py | 5 +-
novelwriter/core/project.py | 6 +-
novelwriter/dialogs/about.py | 5 +-
novelwriter/gui/mainmenu.py | 12 +--
novelwriter/gui/theme.py | 37 ++++----
novelwriter/guimain.py | 9 +-
novelwriter/tools/lipsum.py | 3 +-
tests/test_base/test_base_config.py | 8 +-
tests/test_core/test_core_coretools.py | 12 +--
tests/test_dialogs/test_dlg_about.py | 17 ++--
tests/test_gui/test_gui_theme.py | 57 +++++-------
13 files changed, 142 insertions(+), 151 deletions(-)
diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py
index 55e73ce9..68a049f4 100644
--- a/novelwriter/__init__.py
+++ b/novelwriter/__init__.py
@@ -27,7 +27,6 @@ import sys
import getopt
import logging
-from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QErrorMessage
from novelwriter.error import exceptionHandler, logException
@@ -249,7 +248,6 @@ def main(sysArgs=None):
nwApp = QApplication([CONFIG.appName, (f"-style={qtStyle}")])
nwApp.setApplicationName(CONFIG.appName)
nwApp.setApplicationVersion(__version__)
- nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
nwApp.setOrganizationDomain(__domain__)
# Connect the exception handler before making the main GUI
diff --git a/novelwriter/config.py b/novelwriter/config.py
index 17b99530..ef342f4d 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -51,6 +51,9 @@ class Config:
def __init__(self):
+ # Initialisation
+ # ==============
+
# Set Application Variables
self.appName = "novelWriter"
self.appHandle = "novelwriter"
@@ -62,20 +65,37 @@ class Config:
self._confPath = confRoot.absolute() / self.appHandle # The user config location
self._dataPath = dataRoot.absolute() / self.appHandle # The user data location
- self.cmdOpen = None # Path from command line for project to be opened on launch
- self.lastPath = None # The last user-selected folder (browse dialogs)
- self.appPath = None # The full path to the novelwriter package folder
- self.appRoot = None # The full path to the novelwriter root folder
- self.appIcon = None # The full path to the novelwriter icon file
- self.assetPath = None # The full path to the novelwriter/assets folder
- self.pdfDocs = None # The location of the PDF manual, if it exists
+ if hasattr(sys, "_MEIPASS"):
+ self._appPath = Path(sys._MEIPASS).absolute()
+ else:
+ self._appPath = Path(__file__).parent.absolute()
+
+ self._appRoot = self._appPath.parent
+ if self._appRoot.is_file():
+ # novelWriter is packaged as a single file
+ self._appRoot = self._appRoot.parent
+ self._appPath = self._appRoot
+
+ self.cmdOpen = None # Path from command line for project to be opened on launch
+ self.lastPath = None # The last user-selected folder (browse dialogs)
+ self.pdfDocs = None # The location of the PDF manual, if it exists
# 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
- # General
+ # Localisation Info
+ self._qLocal = QLocale.system()
+ self._qtTrans = {}
+ self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
+ self._nwLangPath = str(self._appPath / "assets" / "i18n")
+
+ # User Settings
+ # =============
+
+ # General GUI Settings
+ self.guiLang = self._qLocal.name()
self.guiTheme = "" # GUI theme
self.guiSyntax = "" # Syntax theme
self.guiFont = "" # Defaults to system default font
@@ -86,14 +106,7 @@ class Config:
self.setDefaultGuiTheme()
self.setDefaultSyntaxTheme()
- # Localisation
- self.qLocal = QLocale.system()
- self.guiLang = self.qLocal.name()
- self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
- self.nwLangPath = None
- self.qtTrans = {}
-
- # Sizes
+ # Size Settings
self.winGeometry = [1200, 650]
self.prefGeometry = [700, 615]
self.projColWidth = [200, 60, 140]
@@ -103,16 +116,16 @@ class Config:
self.outlnPanePos = [500, 150]
self.isFullScreen = False
- # Features
+ # 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
- # Project
+ # Project Settings
self.autoSaveProj = 60 # Interval for auto-saving project in seconds
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
- # Text Editor
+ # Text Editor Settings
self.textFont = None # Editor font
self.textSize = 12 # Editor font size
self.textWidth = 700 # Editor text width
@@ -151,7 +164,7 @@ class Config:
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
self.userIdleTime = 300 # Time of inactivity to consider user idle
- # User-Selected Symbols
+ # User-Selected Symbol Settings
self.fmtApostrophe = nwUnicode.U_RSQUO
self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
@@ -159,7 +172,7 @@ class Config:
self.fmtPadAfter = ""
self.fmtPadThin = False
- # Spell Checking
+ # Spell Checking Settings
self.spellLanguage = None
# Search Bar Switches
@@ -170,7 +183,7 @@ class Config:
self.searchNextFile = False
self.searchMatchCap = False
- # Backup
+ # Backup Settings
self.backupPath = ""
self.backupOnClose = False
self.askBeforeBackup = True
@@ -180,6 +193,9 @@ class Config:
self.viewComments = True # Comments are shown in the viewer
self.viewSynopsis = True # Synopsis is shown in the viewer
+ # System and App Information
+ # ==========================
+
# Check Qt5 Versions
verQt = splitVersionNumber(QT_VERSION_STR)
self.verQtString = QT_VERSION_STR
@@ -254,6 +270,13 @@ class Config:
return self._dataPath / target
return self._dataPath
+ def getAssetPath(self, target=None):
+ """Return a path in the assets folder.
+ """
+ if isinstance(target, str):
+ return self._appPath / "assets" / target
+ return self._appPath / "assets"
+
##
# Config Actions
##
@@ -271,29 +294,12 @@ class Config:
logger.info("Setting data path from alternative path: %s", dataPath)
self._dataPath = Path(dataPath)
- logger.debug("Config path: %s", self._confPath)
- logger.debug("Data path: %s", self._dataPath)
+ logger.debug("Config Path: %s", self._confPath)
+ logger.debug("Data Path: %s", self._dataPath)
+ logger.debug("App Root: %s", self._appRoot)
+ logger.debug("App Path: %s", self._appPath)
self.lastPath = os.path.expanduser("~")
- self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
- self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))
-
- if os.path.isfile(self.appRoot):
- # novelWriter is packaged as a single file, so the app and
- # root paths are the same, and equal to the folder that
- # contains the single executable.
- self.appRoot = os.path.dirname(self.appRoot)
- self.appPath = self.appRoot
-
- # Assets
- self.assetPath = os.path.join(self.appPath, "assets")
- self.appIcon = os.path.join(self.assetPath, "icons", "novelwriter.svg")
-
- # Internationalisation
- self.nwLangPath = os.path.join(self.assetPath, "i18n")
-
- logger.debug("Assets: %s", self.assetPath)
- logger.debug("App path: %s", self.appPath)
logger.debug("Last path: %s", self.lastPath)
# If the config and data folders don't not exist, create them
@@ -324,9 +330,9 @@ class Config:
self.spellLanguage = "en"
# Look for a PDF version of the manual
- pdfDocs = os.path.join(self.assetPath, "manual.pdf")
- if os.path.isfile(pdfDocs):
- logger.debug("Found manual: %s", pdfDocs)
+ pdfDocs = self._appPath / "assets" / "manual.pdf"
+ if pdfDocs.is_file():
+ logger.debug("Found PDF manual: %s", pdfDocs)
self.pdfDocs = pdfDocs
logger.debug("Config initialisation complete")
@@ -336,24 +342,24 @@ class Config:
def initLocalisation(self, nwApp):
"""Initialise the localisation of the GUI.
"""
- self.qLocal = QLocale(self.guiLang)
- QLocale.setDefault(self.qLocal)
- self.qtTrans = {}
+ self._qLocal = QLocale(self.guiLang)
+ QLocale.setDefault(self._qLocal)
+ self._qtTrans = {}
langList = [
- (self.qtLangPath, "qtbase"), # Qt 5.x
- (self.nwLangPath, "qtbase"), # Alternative Qt 5.x
- (self.nwLangPath, "nw"), # novelWriter
+ (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._qLocal.uiLanguages():
qTrans = QTranslator()
lngFile = "%s_%s" % (lngBase, lngCode.replace("-", "_"))
- if lngFile not in self.qtTrans:
+ if lngFile not in self._qtTrans:
if qTrans.load(lngFile, lngPath):
logger.debug("Loaded: %s", os.path.join(lngPath, lngFile))
nwApp.installTranslator(qTrans)
- self.qtTrans[lngFile] = qTrans
+ self._qtTrans[lngFile] = qTrans
return
@@ -372,8 +378,8 @@ class Config:
else:
return []
- for qmFile in os.listdir(self.nwLangPath):
- if not os.path.isfile(os.path.join(self.nwLangPath, qmFile)):
+ for qmFile in os.listdir(self._nwLangPath):
+ if not os.path.isfile(os.path.join(self._nwLangPath, qmFile)):
continue
if not qmFile.startswith(fPre) or not qmFile.endswith(fExt):
continue
diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py
index 4f9d4954..97bab0de 100644
--- a/novelwriter/core/coretools.py
+++ b/novelwriter/core/coretools.py
@@ -24,7 +24,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import shutil
import logging
import novelwriter
@@ -431,8 +430,8 @@ class ProjectBuilder:
logger.error("No project path set for the example project")
return False
- pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip")
- if os.path.isfile(pkgSample):
+ pkgSample = self.mainConf.getAssetPath("sample.zip")
+ if pkgSample.is_file():
try:
shutil.unpack_archive(pkgSample, projPath)
except Exception as exc:
diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py
index a0e3ac8d..49df331a 100644
--- a/novelwriter/core/project.py
+++ b/novelwriter/core/project.py
@@ -698,13 +698,13 @@ class NWProject(QObject):
def _loadProjectLocalisation(self):
"""Load the language data for the current project language.
"""
- if self._data.language is None or self.mainConf.nwLangPath is None:
+ if self._data.language is None or self.mainConf._nwLangPath is None:
self._langData = {}
return False
- langFile = Path(self.mainConf.nwLangPath) / f"project_{self._data.language}.json"
+ langFile = Path(self.mainConf._nwLangPath) / f"project_{self._data.language}.json"
if not langFile.is_file():
- langFile = Path(self.mainConf.nwLangPath) / "project_en_GB.json"
+ langFile = Path(self.mainConf._nwLangPath) / "project_en_GB.json"
try:
with open(langFile, mode="r", encoding="utf-8") as inFile:
diff --git a/novelwriter/dialogs/about.py b/novelwriter/dialogs/about.py
index 5b3a509e..8d4523b1 100644
--- a/novelwriter/dialogs/about.py
+++ b/novelwriter/dialogs/about.py
@@ -23,7 +23,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import logging
import novelwriter
@@ -233,7 +232,7 @@ class GuiAbout(QDialog):
def _fillNotesPage(self):
"""Load the content for the Release Notes page.
"""
- docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
+ docPath = self.mainConf.getAssetPath("text") / "release_notes.htm"
docText = readTextFile(docPath)
if docText:
self.pageNotes.setHtml(docText)
@@ -244,7 +243,7 @@ class GuiAbout(QDialog):
def _fillLicensePage(self):
"""Load the content for the Licence page.
"""
- docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
+ docPath = self.mainConf.getAssetPath("text") / "gplv3_en.htm"
docText = readTextFile(docPath)
if docText:
self.pageLicense.setHtml(docText)
diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py
index a3b7eca0..b4acd6f2 100644
--- a/novelwriter/gui/mainmenu.py
+++ b/novelwriter/gui/mainmenu.py
@@ -26,6 +26,7 @@ along with this program. If not, see .
import logging
import novelwriter
+from pathlib import Path
from urllib.parse import urljoin
from urllib.request import pathname2url
@@ -104,10 +105,11 @@ class GuiMainMenu(QMenuBar):
def _openUserManualFile(self):
"""Open the documentation in PDF format.
"""
- if self.mainConf.pdfDocs is None:
- return False
- QDesktopServices.openUrl(QUrl(urljoin("file:", pathname2url(self.mainConf.pdfDocs))))
- return True
+ if isinstance(self.mainConf.pdfDocs, Path):
+ QDesktopServices.openUrl(
+ QUrl(urljoin("file:", pathname2url(str(self.mainConf.pdfDocs))))
+ )
+ return
##
# Menu Builders
@@ -881,7 +883,7 @@ class GuiMainMenu(QMenuBar):
self.helpMenu.addAction(self.aHelpDocs)
# Help > User Manual (PDF)
- if self.mainConf.pdfDocs is not None:
+ if isinstance(self.mainConf.pdfDocs, Path):
self.aPdfDocs = QAction(self.tr("User Manual (PDF)"), self)
self.aPdfDocs.setShortcut("Shift+F1")
self.aPdfDocs.triggered.connect(self._openUserManualFile)
diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py
index fc8a7f49..061c8b5c 100644
--- a/novelwriter/gui/theme.py
+++ b/novelwriter/gui/theme.py
@@ -24,12 +24,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import logging
import novelwriter
from math import ceil
-from pathlib import Path
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import qApp
@@ -120,9 +118,8 @@ class GuiTheme:
self._availThemes = {}
self._availSyntax = {}
- self._listConf(self._availSyntax, os.path.join(self.mainConf.assetPath, "syntax"))
- self._listConf(self._availThemes, os.path.join(self.mainConf.assetPath, "themes"))
-
+ self._listConf(self._availSyntax, self.mainConf.getAssetPath("syntax"))
+ self._listConf(self._availThemes, self.mainConf.getAssetPath("themes"))
self._listConf(self._availSyntax, self.mainConf.getDataPath("syntax"))
self._listConf(self._availThemes, self.mainConf.getDataPath("themes"))
@@ -380,7 +377,6 @@ class GuiTheme:
def _listConf(self, targetDict, checkDir):
"""Scan for theme config files and populate the dictionary.
"""
- checkDir = Path(checkDir)
if not checkDir.is_dir():
return False
@@ -476,7 +472,7 @@ class GuiIcons:
self._confName = "icons.conf"
# Icon Theme Path
- self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
+ self._iconPath = self.mainConf.getAssetPath("icons")
# Icon Theme Meta
self.themeName = ""
@@ -499,12 +495,12 @@ class GuiIcons:
update functions for the classes where they're used.
"""
self._themeMap = {}
- themePath = os.path.join(self.mainConf.assetPath, "icons", iconTheme)
- if not os.path.isdir(themePath):
+ themePath = self._iconPath / iconTheme
+ if not themePath.is_dir():
logger.warning("No icons loaded for '%s'", iconTheme)
return False
- themeConf = os.path.join(themePath, self._confName)
+ themeConf = themePath / self._confName
logger.info("Loading icon theme '%s'", iconTheme)
# Config File
@@ -535,8 +531,8 @@ class GuiIcons:
if iconName not in self.ICON_KEYS:
logger.error("Unknown icon name '%s' in config file", iconName)
else:
- iconPath = os.path.join(themePath, iconFile)
- if os.path.isfile(iconPath):
+ iconPath = themePath / iconFile
+ if iconPath.is_file():
self._themeMap[iconName] = iconPath
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
else:
@@ -572,18 +568,16 @@ class GuiIcons:
if decoKey in self._themeMap:
imgPath = self._themeMap[decoKey]
elif decoKey in self.IMAGE_MAP:
- imgPath = os.path.join(
- self.mainConf.assetPath, "images", self.IMAGE_MAP[decoKey]
- )
+ imgPath = self.mainConf.getAssetPath("images") / self.IMAGE_MAP[decoKey]
else:
logger.error("Decoration with name '%s' does not exist", decoKey)
return QPixmap()
- if not os.path.isfile(imgPath):
+ if not imgPath.is_file():
logger.error("Asset not found: %s", imgPath)
return QPixmap()
- theDeco = QPixmap(imgPath)
+ theDeco = QPixmap(str(imgPath))
if pxW is not None and pxH is not None:
return theDeco.scaled(pxW, pxH, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
elif pxW is None and pxH is not None:
@@ -667,15 +661,14 @@ class GuiIcons:
# If we just want the app icons, return right away
if iconKey == "novelwriter":
- return QIcon(os.path.join(self._iconPath, "novelwriter.svg"))
+ return QIcon(str(self._iconPath / "novelwriter.svg"))
elif iconKey == "proj_nwx":
- return QIcon(os.path.join(self._iconPath, "x-novelwriter-project.svg"))
+ return QIcon(str(self._iconPath / "x-novelwriter-project.svg"))
# Otherwise, we load from the theme folder
if iconKey in self._themeMap:
- relPath = os.path.relpath(self._themeMap[iconKey], self._iconPath)
- logger.debug("Loading: %s", relPath)
- return QIcon(self._themeMap[iconKey])
+ logger.debug("Loading: %s", self._themeMap[iconKey].name)
+ return QIcon(str(self._themeMap[iconKey]))
# If we didn't find one, give up and return an empty icon
logger.warning("Did not load an icon for '%s'", iconKey)
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index 98471d8d..ae918d9a 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -29,6 +29,7 @@ import novelwriter
from enum import Enum
from time import time
+from pathlib import Path
from datetime import datetime
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
@@ -95,7 +96,11 @@ class GuiMain(QMainWindow):
# Prepare Main Window
self.resize(*self.mainConf.getWinSize())
self._updateWindowTitle()
- self.setWindowIcon(QIcon(self.mainConf.appIcon))
+
+ nwIcon = self.mainConf.getAssetPath("icons") / "novelwriter.svg"
+ self.nwIcon = QIcon(str(nwIcon)) if nwIcon.is_file() else QIcon()
+ self.setWindowIcon(self.nwIcon)
+ qApp.setWindowIcon(self.nwIcon)
# Build the GUI
# =============
@@ -1362,7 +1367,7 @@ class GuiMain(QMainWindow):
# Help
self.addAction(self.mainMenu.aHelpDocs)
- if self.mainConf.pdfDocs is not None:
+ if isinstance(self.mainConf.pdfDocs, Path):
self.addAction(self.mainMenu.aPdfDocs)
return True
diff --git a/novelwriter/tools/lipsum.py b/novelwriter/tools/lipsum.py
index 976732dd..002f7769 100644
--- a/novelwriter/tools/lipsum.py
+++ b/novelwriter/tools/lipsum.py
@@ -23,7 +23,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import random
import logging
import novelwriter
@@ -120,7 +119,7 @@ class GuiLipsum(QDialog):
def _doInsert(self):
"""Load the text and insert it in the open document.
"""
- lipsumFile = os.path.join(self.mainConf.assetPath, "text", "lipsum.txt")
+ lipsumFile = self.mainConf.getAssetPath("text") / "lipsum.txt"
lipsumText = readTextFile(lipsumFile).splitlines()
if self.randSwitch.isChecked():
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index 42aa9b14..c0f5fe79 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -159,14 +159,14 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
assert tstConf._confPath == tmpDir
assert tstConf._dataPath == tmpDir
- appRoot = tstConf.appRoot
+ appRoot = tstConf._appRoot
mp.setattr("os.path.isfile", lambda *a: True)
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
assert tstConf._confPath == tmpDir
assert tstConf._dataPath == tmpDir
- assert tstConf.appRoot == os.path.dirname(appRoot)
- assert tstConf.appPath == os.path.dirname(appRoot)
+ assert tstConf._appRoot == os.path.dirname(appRoot)
+ assert tstConf._appPath == os.path.dirname(appRoot)
assert tstConf.loadConfig() is True
assert tstConf.saveConfig() is True
@@ -199,7 +199,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
i18nDir = os.path.join(fncDir, "i18n")
os.mkdir(i18nDir)
os.mkdir(os.path.join(i18nDir, "stuff"))
- tstConf.nwLangPath = i18nDir
+ tstConf._nwLangPath = i18nDir
copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(i18nDir, "nw_en_GB.qm"))
writeFile(os.path.join(i18nDir, "nw_en_GB.ts"), "")
diff --git a/tests/test_core/test_core_coretools.py b/tests/test_core/test_core_coretools.py
index 4fc6c9ec..5398e4f7 100644
--- a/tests/test_core/test_core_coretools.py
+++ b/tests/test_core/test_core_coretools.py
@@ -372,7 +372,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncDir, outDir, refDir, mockGUI, mockR
@pytest.mark.core
-def testCoreTools_NewSample(fncDir, tmpConf, mockGUI, tmpDir):
+def testCoreTools_NewSample(monkeypatch, fncPath, tmpConf, tmpPath, mockGUI):
"""Check that we can create a new project can be created from the
provided sample project via a zip file.
"""
@@ -380,7 +380,7 @@ def testCoreTools_NewSample(fncDir, tmpConf, mockGUI, tmpDir):
"projName": "Test Sample",
"projTitle": "Test Novel",
"projAuthors": "Jane Doe\nJohn Doh\n",
- "projPath": fncDir,
+ "projPath": fncPath,
"popSample": True,
"popMinimal": False,
"popCustom": False,
@@ -392,9 +392,11 @@ def testCoreTools_NewSample(fncDir, tmpConf, mockGUI, tmpDir):
assert projBuild.buildProject({"popSample": True}) is False
# Force the lookup path for assets to our temp folder
- srcSample = os.path.abspath(os.path.join(tmpConf.appRoot, "sample"))
- dstSample = os.path.join(tmpDir, "sample.zip")
- tmpConf.assetPath = tmpDir
+ srcSample = tmpConf._appRoot / "sample"
+ dstSample = tmpPath / "sample.zip"
+ monkeypatch.setattr(
+ "novelwriter.config.Config.getAssetPath", lambda *a: tmpPath / "sample.zip"
+ )
# Cannot extract when the zip does not exist
assert projBuild.buildProject(projData) is False
diff --git a/tests/test_dialogs/test_dlg_about.py b/tests/test_dialogs/test_dlg_about.py
index 63de9039..b4357051 100644
--- a/tests/test_dialogs/test_dlg_about.py
+++ b/tests/test_dialogs/test_dlg_about.py
@@ -21,6 +21,8 @@ along with this program. If not, see .
import pytest
+from pathlib import Path
+
from tools import getGuiItem
from PyQt5.QtWidgets import QAction, QMessageBox
@@ -29,7 +31,7 @@ from novelwriter.dialogs.about import GuiAbout
@pytest.mark.gui
-def testDlgAbout_NWDialog(qtbot, nwGUI):
+def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
"""Test the novelWriter about dialogs.
"""
# NW About
@@ -45,13 +47,12 @@ def testDlgAbout_NWDialog(qtbot, nwGUI):
assert msgAbout.pageNotes.document().characterCount() > 100
assert msgAbout.pageLicense.document().characterCount() > 100
- msgAbout.mainConf.assetPath = "whatever"
-
- msgAbout._fillNotesPage()
- assert msgAbout.pageNotes.toPlainText() == "Error loading release notes text ..."
-
- msgAbout._fillLicensePage()
- assert msgAbout.pageLicense.toPlainText() == "Error loading licence text ..."
+ with monkeypatch.context() as mp:
+ mp.setattr("novelwriter.config.Config.getAssetPath", lambda *a: Path("whatever"))
+ msgAbout._fillNotesPage()
+ assert msgAbout.pageNotes.toPlainText() == "Error loading release notes text ..."
+ msgAbout._fillLicensePage()
+ assert msgAbout.pageLicense.toPlainText() == "Error loading licence text ..."
msgAbout.showReleaseNotes()
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py
index cd16ff86..df0a1b15 100644
--- a/tests/test_gui/test_gui_theme.py
+++ b/tests/test_gui/test_gui_theme.py
@@ -19,10 +19,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import shutil
import pytest
+from pathlib import Path
from configparser import ConfigParser
from mock import causeOSError
@@ -38,7 +38,7 @@ from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui
-def testGuiTheme_Main(qtbot, nwGUI, fncDir):
+def testGuiTheme_Main(qtbot, nwGUI, fncPath):
"""Test the theme class init.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
@@ -75,15 +75,15 @@ def testGuiTheme_Main(qtbot, nwGUI, fncDir):
# Scan for Themes
# ===============
- assert mainTheme._listConf({}, "not_a_path") is False
+ assert mainTheme._listConf({}, Path("not_a_path")) is False
- themeOne = os.path.join(fncDir, "themes", "themeone.conf")
- themeTwo = os.path.join(fncDir, "themes", "themetwo.conf")
+ themeOne = fncPath / "themes" / "themeone.conf"
+ themeTwo = fncPath / "themes" / "themetwo.conf"
writeFile(themeOne, "# Stuff")
writeFile(themeTwo, "# Stuff")
result = {}
- assert mainTheme._listConf(result, os.path.join(fncDir, "themes")) is True
+ assert mainTheme._listConf(result, fncPath / "themes") is True
assert result["themeone"] == themeOne
assert result["themetwo"] == themeTwo
@@ -123,7 +123,7 @@ def testGuiTheme_Main(qtbot, nwGUI, fncDir):
@pytest.mark.gui
-def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
+def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
"""Test the theme part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
@@ -132,15 +132,8 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
# List Themes
# ===========
- shutil.copy(
- os.path.join(mainConf.assetPath, "themes", "default_dark.conf"),
- os.path.join(fncDir, "themes")
- )
- shutil.copy(
- os.path.join(mainConf.assetPath, "themes", "default.conf"),
- os.path.join(fncDir, "themes")
- )
- writeFile(os.path.join(fncDir, "themes", "default.qss"), "/* Stuff */")
+ shutil.copy(mainConf.getAssetPath("themes") / "default_dark.conf", fncPath / "themes")
+ shutil.copy(mainConf.getAssetPath("themes") / "default.conf", fncPath / "themes")
# Block the reading of the files
with monkeypatch.context() as mp:
@@ -197,7 +190,7 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui
-def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
+def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
"""Test the syntax part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
@@ -206,14 +199,8 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
# List Themes
# ===========
- shutil.copy(
- os.path.join(mainConf.assetPath, "syntax", "default_dark.conf"),
- os.path.join(fncDir, "syntax")
- )
- shutil.copy(
- os.path.join(mainConf.assetPath, "syntax", "default_light.conf"),
- os.path.join(fncDir, "syntax")
- )
+ shutil.copy(mainConf.getAssetPath("syntax") / "default_dark.conf", fncPath / "syntax")
+ shutil.copy(mainConf.getAssetPath("syntax") / "default_light.conf", fncPath / "syntax")
# Block the reading of the files
with monkeypatch.context() as mp:
@@ -270,11 +257,10 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui
-def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
+def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncPath):
"""Test the icon cache class.
"""
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
- mainConf: Config = nwGUI.mainConf
# Load Theme
# ==========
@@ -288,10 +274,11 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert iconCache.loadTheme("typicons_dark") is False
# Load a broken theme file
- iconsDir = os.path.join(fncDir, "icons")
- os.mkdir(iconsDir)
- os.mkdir(os.path.join(iconsDir, "testicons"))
- writeFile(os.path.join(iconsDir, "testicons", "icons.conf"), (
+ iconsDir = fncPath / "icons"
+ testIcons = iconsDir / "testicons"
+ iconsDir.mkdir()
+ testIcons.mkdir()
+ writeFile(testIcons / "icons.conf", (
"[Main]\n"
"name = Test Icons\n"
"\n"
@@ -300,15 +287,15 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
"stuff = stuff.svg\n"
))
- assetPath = mainConf.assetPath
- mainConf.assetPath = fncDir
+ iconPath = iconCache._iconPath
+ iconCache._iconPath = fncPath / "icons"
caplog.clear()
assert iconCache.loadTheme("testicons") is True
assert "Unknown icon name 'stuff' in config file" in caplog.text
assert "Icon file 'add.svg' not in theme folder" in caplog.text
- mainConf.assetPath = assetPath
+ iconCache._iconPath = iconPath
# Load working theme file
assert iconCache.loadTheme("typicons_dark") is True
@@ -327,7 +314,7 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
# Fail finding the file
with monkeypatch.context() as mp:
- mp.setattr("os.path.isfile", lambda *a: False)
+ mp.setattr("pathlib.Path.is_file", lambda *a: False)
qPix = iconCache.loadDecoration("wiz-back")
assert qPix.isNull() is True