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: