From 47b57172ebb3b833c437d493a34b5c868a26a5da Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 9 Nov 2022 18:21:40 +0100
Subject: [PATCH] Change lastPath to a Path object
---
novelwriter/config.py | 49 +++++++++++----------
novelwriter/guimain.py | 4 +-
novelwriter/tools/build.py | 9 +---
novelwriter/tools/projwizard.py | 7 +--
novelwriter/tools/writingstats.py | 11 +----
tests/conftest.py | 10 ++---
tests/test_base/test_base_config.py | 3 +-
tests/test_dialogs/test_dlg_preferences.py | 4 +-
tests/test_dialogs/test_dlg_projdetails.py | 1 -
tests/test_gui/test_gui_outline.py | 1 -
tests/test_tools/test_tools_build.py | 12 ++---
tests/test_tools/test_tools_projwizard.py | 2 -
tests/test_tools/test_tools_writingstats.py | 3 --
13 files changed, 45 insertions(+), 71 deletions(-)
diff --git a/novelwriter/config.py b/novelwriter/config.py
index ef342f4d..5b4cfebd 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -64,26 +64,20 @@ 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
- if hasattr(sys, "_MEIPASS"):
- self._appPath = Path(sys._MEIPASS).absolute()
- else:
- self._appPath = Path(__file__).parent.absolute()
-
+ 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
+ self.cmdOpen = None # Path from command line for project to be opened on launch
# Localisation Info
self._qLocal = QLocale.system()
@@ -91,6 +85,10 @@ class Config:
self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
self._nwLangPath = str(self._appPath / "assets" / "i18n")
+ # PDF Manual
+ pdfDocs = self._appPath / "assets" / "manual.pdf"
+ self.pdfDocs = pdfDocs if pdfDocs.is_file() else None
+
# User Settings
# =============
@@ -277,6 +275,13 @@ class Config:
return self._appPath / "assets" / target
return self._appPath / "assets"
+ def getLastPath(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()
+
##
# Config Actions
##
@@ -298,9 +303,8 @@ class Config:
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("~")
- logger.debug("Last path: %s", self.lastPath)
+ logger.debug("Last Path: %s", self._lastPath)
+ logger.debug("PDF Manual: %s", self.pdfDocs)
# If the config and data folders don't not exist, create them
# This assumes that the os config and data folders exist
@@ -329,12 +333,6 @@ class Config:
if not self.spellLanguage:
self.spellLanguage = "en"
- # Look for a PDF version of the manual
- 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")
return True
@@ -495,7 +493,7 @@ class Config:
# Path
cnfSec = "Path"
- self.lastPath = theConf.rdStr(cnfSec, "lastpath", self.lastPath)
+ self._lastPath = Path(theConf.rdStr(cnfSec, "lastpath", self._lastPath))
# Check Certain Values for None
self.spellLanguage = self._checkNone(self.spellLanguage)
@@ -605,7 +603,7 @@ class Config:
}
theConf["Path"] = {
- "lastpath": str(self.lastPath),
+ "lastpath": str(self._lastPath),
}
# Write config file
@@ -698,10 +696,13 @@ class Config:
def setLastPath(self, lastPath):
"""Set the last used path (by the user).
"""
- if lastPath is None or lastPath == "":
- self.lastPath = ""
- else:
- self.lastPath = os.path.dirname(lastPath)
+ if isinstance(lastPath, str):
+ lastPath = Path(lastPath)
+ if isinstance(lastPath, Path):
+ if lastPath.is_file():
+ self._lastPath = lastPath.parent
+ elif lastPath.is_dir():
+ self._lastPath = lastPath
return True
def setWinSize(self, newWidth, newHeight):
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index ae918d9a..90141c03 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -698,7 +698,7 @@ class GuiMain(QMainWindow):
logger.error("No project open")
return False
- lastPath = self.mainConf.lastPath
+ lastPath = self.mainConf.getLastPath()
extFilter = [
self.tr("Text files ({0})").format("*.txt"),
self.tr("Markdown files ({0})").format("*.md"),
@@ -706,7 +706,7 @@ class GuiMain(QMainWindow):
self.tr("All files ({0})").format("*"),
]
loadFile, _ = QFileDialog.getOpenFileName(
- self, self.tr("Import File"), lastPath, filter=";;".join(extFilter)
+ self, self.tr("Import File"), str(lastPath), filter=";;".join(extFilter)
)
if not loadFile:
return False
diff --git a/novelwriter/tools/build.py b/novelwriter/tools/build.py
index 78089abf..198ae15e 100644
--- a/novelwriter/tools/build.py
+++ b/novelwriter/tools/build.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 json
import logging
import novelwriter
@@ -891,13 +890,9 @@ class GuiBuildNovel(QDialog):
cleanName = makeFileNameSafe(self.theProject.data.name)
fileName = "%s.%s" % (cleanName, fileExt)
- saveDir = self.mainConf.lastPath
- if not os.path.isdir(saveDir):
- saveDir = os.path.expanduser("~")
-
- savePath = os.path.join(saveDir, fileName)
+ savePath = self.mainConf.getLastPath() / fileName
savePath, _ = QFileDialog.getSaveFileName(
- self, self.tr("Save Document As"), savePath
+ self, self.tr("Save Document As"), str(savePath)
)
if not savePath:
return False
diff --git a/novelwriter/tools/projwizard.py b/novelwriter/tools/projwizard.py
index 94cab911..bcc88484 100644
--- a/novelwriter/tools/projwizard.py
+++ b/novelwriter/tools/projwizard.py
@@ -236,12 +236,9 @@ class ProjWizardFolderPage(QWizardPage):
def _doBrowse(self):
"""Select a project folder.
"""
- lastPath = self.mainConf.lastPath
- if not os.path.isdir(lastPath):
- lastPath = ""
-
+ lastPath = self.mainConf.getLastPath()
projDir = QFileDialog.getExistingDirectory(
- self, self.tr("Select Project Folder"), lastPath, options=QFileDialog.ShowDirsOnly
+ self, self.tr("Select Project Folder"), str(lastPath), options=QFileDialog.ShowDirsOnly
)
if projDir:
projName = self.field("projName")
diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py
index 7015c992..efa3a6ca 100644
--- a/novelwriter/tools/writingstats.py
+++ b/novelwriter/tools/writingstats.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 json
import logging
import novelwriter
@@ -363,15 +362,9 @@ class GuiWritingStats(QDialog):
return False
# Generate the file name
- saveDir = self.mainConf.lastPath
- if not os.path.isdir(saveDir):
- saveDir = os.path.expanduser("~")
-
- fileName = "sessionStats.%s" % fileExt
- savePath = os.path.join(saveDir, fileName)
-
+ savePath = self.mainConf.getLastPath() / f"sessionStats.{fileExt}"
savePath, _ = QFileDialog.getSaveFileName(
- self, self.tr("Save Data As"), savePath, "%s (*.%s)" % (textFmt, fileExt)
+ self, self.tr("Save Data As"), str(savePath), "%s (*.%s)" % (textFmt, fileExt)
)
if not savePath:
return False
diff --git a/tests/conftest.py b/tests/conftest.py
index 93a024c8..696d416e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -166,7 +166,7 @@ def tmpConf(tmpPath):
confFile.unlink()
theConf = Config()
theConf.initConfig(tmpPath, tmpPath)
- theConf.setLastPath("")
+ theConf.setLastPath(tmpPath)
theConf.guiLang = "en_GB"
return theConf
@@ -180,7 +180,7 @@ def fncConf(fncPath):
confFile.unlink()
theConf = Config()
theConf.initConfig(fncPath, fncPath)
- theConf.setLastPath("")
+ theConf.setLastPath(fncPath)
theConf.guiLang = "en_GB"
return theConf
@@ -196,7 +196,7 @@ def mockGUI(monkeypatch, tmpConf):
@pytest.fixture(scope="function")
-def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
+def nwGUI(qtbot, monkeypatch, fncPath, fncConf):
"""Create an instance of the novelWriter GUI.
"""
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
@@ -205,12 +205,12 @@ def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
- nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
+ nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.wait(20)
- nwGUI.mainConf.lastPath = fncDir
+ nwGUI.mainConf.setLastPath(fncPath)
yield nwGUI
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index c0f5fe79..36f0eb79 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -444,7 +444,8 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
assert tmpConf.confChanged is False
copyfile(confFile, testFile)
- assert cmpFiles(testFile, compFile, ignoreStart=("timestamp", "lastnotes", "guilang"))
+ ignore = ("timestamp", "lastnotes", "guilang", "lastpath")
+ assert cmpFiles(testFile, compFile, ignoreStart=ignore)
# END Test testBaseConfig_SettersGetters
diff --git a/tests/test_dialogs/test_dlg_preferences.py b/tests/test_dialogs/test_dlg_preferences.py
index 55d3c056..fbf9af9b 100644
--- a/tests/test_dialogs/test_dlg_preferences.py
+++ b/tests/test_dialogs/test_dlg_preferences.py
@@ -22,6 +22,7 @@ along with this program. If not, see .
import pytest
from shutil import copyfile
+
from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import Qt
@@ -215,7 +216,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
nwPrefs._doClose()
assert theConf.confChanged
- theConf.lastPath = ""
assert nwGUI.mainConf.saveConfig()
projFile = fncPath / "novelwriter.conf"
@@ -225,7 +225,7 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
ignTuple = (
"timestamp", "guifont", "lastnotes", "guilang", "geometry",
"preferences", "projcols", "mainpane", "docpane", "viewpane",
- "outlinepane", "textfont", "textsize"
+ "outlinepane", "textfont", "textsize", "lastpath"
)
assert cmpFiles(testFile, compFile, ignoreStart=ignTuple)
diff --git a/tests/test_dialogs/test_dlg_projdetails.py b/tests/test_dialogs/test_dlg_projdetails.py
index c7d86670..e8d6053c 100644
--- a/tests/test_dialogs/test_dlg_projdetails.py
+++ b/tests/test_dialogs/test_dlg_projdetails.py
@@ -38,7 +38,6 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
qtbot.wait(100)
# Open the Writing Stats dialog
- nwGUI.mainConf.lastPath = ""
nwGUI.mainMenu.aProjectDetails.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectDetails") is not None, timeout=1000)
diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py
index 91467683..77d9e739 100644
--- a/tests/test_gui/test_gui_outline.py
+++ b/tests/test_gui/test_gui_outline.py
@@ -156,7 +156,6 @@ def testGuiOutline_Content(qtbot, nwGUI, nwLipsum):
"""Test the outline view.
"""
assert nwGUI.openProject(nwLipsum)
- nwGUI.mainConf.lastPath = nwLipsum
nwGUI.rebuildIndex()
nwGUI._changeView(nwView.OUTLINE)
diff --git a/tests/test_tools/test_tools_build.py b/tests/test_tools/test_tools_build.py
index e0088a1c..5cb93229 100644
--- a/tests/test_tools/test_tools_build.py
+++ b/tests/test_tools/test_tools_build.py
@@ -23,6 +23,8 @@ import pytest
import os
from shutil import copyfile
+from pathlib import Path
+
from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import Qt
@@ -61,21 +63,13 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Invalid file format
assert not nwBuild._saveDocument(-1)
- # Non-existent path
- with monkeypatch.context() as mp:
- mp.setattr("os.path.expanduser", lambda *a, **k: nwLipsum)
- assert nwGUI.mainConf.lastPath != nwLipsum
- nwGUI.mainConf.lastPath = "no_such_path"
- assert nwBuild._saveDocument(nwBuild.FMT_NWD)
- assert nwGUI.mainConf.lastPath == nwLipsum
-
# No path selected
with monkeypatch.context() as mp:
mp.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: ("", ""))
assert not nwBuild._saveDocument(nwBuild.FMT_NWD)
# Default Settings
- nwGUI.mainConf.lastPath = nwLipsum
+ nwGUI.mainConf._lastPath = Path(nwLipsum)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
assert nwBuild._saveDocument(nwBuild.FMT_NWD)
diff --git a/tests/test_tools/test_tools_projwizard.py b/tests/test_tools/test_tools_projwizard.py
index 5d598e6d..ed9d8a58 100644
--- a/tests/test_tools/test_tools_projwizard.py
+++ b/tests/test_tools/test_tools_projwizard.py
@@ -67,7 +67,6 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, fncProj):
# Test the Wizard Launching
# =========================
- nwGUI.mainConf.lastPath = " "
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
result = nwGUI.showNewProjectDialog()
@@ -102,7 +101,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
"""
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
- nwGUI.mainConf.lastPath = " "
nwWiz = GuiProjectWizard(nwGUI)
nwWiz.show()
qtbot.addWidget(nwWiz)
diff --git a/tests/test_tools/test_tools_writingstats.py b/tests/test_tools/test_tools_writingstats.py
index a4ba1963..60f59750 100644
--- a/tests/test_tools/test_tools_writingstats.py
+++ b/tests/test_tools/test_tools_writingstats.py
@@ -44,7 +44,6 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS)
# Open the Writing Stats dialog
- nwGUI.mainConf.lastPath = ""
nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiWritingStats") is not None, timeout=1000)
@@ -135,8 +134,6 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(100)
- assert nwGUI.mainConf.lastPath == fncDir
-
# Check the exported files
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: