From 92bf8fbfe4dd94828e7bb1ff6ebf43c2369e5aa3 Mon Sep 17 00:00:00 2001 From: sebastian-swob Date: Tue, 8 Oct 2024 17:07:19 +0200 Subject: [PATCH 1/3] Added open recent functionality --- trnsysGUI/UserSettings.py | 16 ++++++++++++ trnsysGUI/diagram/Editor.py | 11 ++++----- trnsysGUI/gui.py | 1 + trnsysGUI/mainWindow.py | 26 ++++++++++++++++++++ trnsysGUI/project.py | 49 +++++++++++++++++++++++-------------- 5 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 trnsysGUI/UserSettings.py diff --git a/trnsysGUI/UserSettings.py b/trnsysGUI/UserSettings.py new file mode 100644 index 00000000..2cf9e04a --- /dev/null +++ b/trnsysGUI/UserSettings.py @@ -0,0 +1,16 @@ +from PyQt5.QtCore import QSettings +import pathlib as _pl + +class UserSettings: + + _SETTINGS = QSettings("SPF", "pytrnsys") + + @staticmethod + def setRecentProjectJsonPath(jsonFilePath: _pl.Path) -> None: + jsonFilePath = _pl.Path(jsonFilePath) + if jsonFilePath.exists(): + UserSettings._SETTINGS.setValue("recentProject", jsonFilePath) + + @staticmethod + def getRecentProjectJsonPath() -> _pl.Path: + return UserSettings._SETTINGS.value("recentProject") diff --git a/trnsysGUI/diagram/Editor.py b/trnsysGUI/diagram/Editor.py index caebaa37..991b2445 100644 --- a/trnsysGUI/diagram/Editor.py +++ b/trnsysGUI/diagram/Editor.py @@ -60,7 +60,6 @@ from . import _sizes from . import fileSystemTreeView as _fst - class Editor(_qtw.QWidget, _ip.HasInternalPipingsProvider): def __init__(self, parent, projectFolder, jsonPath, loadValue, logger): super().__init__(parent) @@ -73,6 +72,7 @@ def __init__(self, parent, projectFolder, jsonPath, loadValue, logger): self.projectFolder = projectFolder self.diagramName = os.path.split(self.projectFolder)[-1] + ".json" + self.diagramPath = os.path.join(self.projectFolder, self.diagramName) self.saveAsPath = _pl.Path() self.idGen = IdGenerator() @@ -809,9 +809,8 @@ def save(self, showWarning=True): """ self.diagramName = os.path.split(self.projectFolder)[-1] + ".json" - diagramPath = os.path.join(self.projectFolder, self.diagramName) - if os.path.isfile(diagramPath) and showWarning: + if os.path.isfile(self.diagramPath) and showWarning: qmb = _qtw.QMessageBox(self) qmb.setText("Warning: " + "This diagram name exists already. Do you want to overwrite or cancel?") qmb.setStandardButtons(_qtw.QMessageBox.Save | _qtw.QMessageBox.Cancel) @@ -823,13 +822,13 @@ def save(self, showWarning=True): return self.logger.info("Overwriting") - self.encodeDiagram(diagramPath) + self.encodeDiagram(self.diagramPath) - self.encodeDiagram(diagramPath) + self.encodeDiagram(self.diagramPath) if showWarning: msgb = _qtw.QMessageBox() msgb.setWindowTitle("Saved successfully") - msgb.setText("Saved diagram at " + diagramPath) + msgb.setText("Saved diagram at " + self.diagramPath) msgb.setStandardButtons(_qtw.QMessageBox.Ok) msgb.setDefaultButton(_qtw.QMessageBox.Ok) msgb.exec() diff --git a/trnsysGUI/gui.py b/trnsysGUI/gui.py index 8a376e9a..e9c33ba5 100644 --- a/trnsysGUI/gui.py +++ b/trnsysGUI/gui.py @@ -26,6 +26,7 @@ def main(): app = _qtw.QApplication(_sys.argv) app.setApplicationName("Diagram Creator") + app.setOrganizationName("pytrnsys") if errorMessage: _werrors.showMessageBox(errorMessage, title="Missing requirements") diff --git a/trnsysGUI/mainWindow.py b/trnsysGUI/mainWindow.py index 1b9e77c6..3d871a63 100644 --- a/trnsysGUI/mainWindow.py +++ b/trnsysGUI/mainWindow.py @@ -25,6 +25,7 @@ from trnsysGUI.MassFlowVisualizer import MassFlowVisualizer from trnsysGUI.ProcessMain import ProcessMain from trnsysGUI.RunMain import RunMain +from trnsysGUI.UserSettings import UserSettings from trnsysGUI.common import cancelled as _ccl from trnsysGUI.diagram import Editor as _de from trnsysGUI.storageTank.widget import StorageTank @@ -130,6 +131,10 @@ def __init__(self, logger, project: _prj.Project, parent=None): fileMenuOpenAction.setShortcut("Ctrl+o") self.fileMenu.addAction(fileMenuOpenAction) + fileMenuOpenRecentAction = _qtw.QAction("Open Recent", self) + fileMenuOpenRecentAction.triggered.connect(self.openRecentFile) + self.fileMenu.addAction(fileMenuOpenRecentAction) + fileMenuSaveAction = _qtw.QAction("Save", self) fileMenuSaveAction.triggered.connect(self.saveDia) fileMenuSaveAction.setShortcut("Ctrl+s") @@ -256,6 +261,7 @@ def newDia(self): createProjectMaybeCancelled = _prj.getCreateProject(startingDirectoryPath) if _ccl.isCancelled(createProjectMaybeCancelled): return + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) createProject = _ccl.value(createProjectMaybeCancelled) self._resetEditor(createProject) @@ -466,6 +472,7 @@ def openFile(self): maybeCancelled = _prj.getLoadOrMigrateProject() if _ccl.isCancelled(maybeCancelled): return + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) project = _ccl.value(maybeCancelled) self._resetEditor(project) @@ -473,6 +480,22 @@ def openFile(self): if isinstance(project, _prj.MigrateProject): self.editor.save() + def openRecentFile(self): + qmb = _qtw.QMessageBox() + qmb.setText("Are you sure you want to open another project? Unsaved progress on the current one will be lost.") + qmb.setStandardButtons(_qtw.QMessageBox.Yes | _qtw.QMessageBox.Cancel) + qmb.setDefaultButton(_qtw.QMessageBox.Cancel) + ret = qmb.exec() + + if ret == _qtw.QMessageBox.Cancel: + return + maybeCancelled = _prj.loadRecentProject() + if _ccl.isCancelled(maybeCancelled): + return + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) + project = _ccl.value(maybeCancelled) + self._resetEditor(project) + def _resetEditor(self, project): wasRunning = self.editor and self.editor.isRunning() @@ -577,11 +600,14 @@ def closeEvent(self, e): if ret == _qtw.QMessageBox.Cancel: e.ignore() elif ret == _qtw.QMessageBox.Close: + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) e.accept() elif ret == _qtw.QMessageBox.Save: self.editor.save() + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) e.accept() else: + UserSettings.setRecentProjectJsonPath(self.editor.diagramPath) e.accept() def ensureSettingsExist(self): diff --git a/trnsysGUI/project.py b/trnsysGUI/project.py index f0c4159b..00349401 100644 --- a/trnsysGUI/project.py +++ b/trnsysGUI/project.py @@ -16,27 +16,23 @@ import PyQt5.QtWidgets as _qtw import trnsysGUI.common.cancelled as _ccl - +from trnsysGUI.UserSettings import UserSettings @_dc.dataclass class CreateProject: jsonFilePath: _pl.Path - @_dc.dataclass class LoadProject: jsonFilePath: _pl.Path - @_dc.dataclass class MigrateProject: oldJsonFilePath: _pl.Path newProjectFolderPath: _pl.Path - Project = CreateProject | LoadProject | MigrateProject - def getProject() -> _ccl.MaybeCancelled[Project]: createOpenMaybeCancelled = _askUserWhetherToCreateNewProjectOrOpenExisting() @@ -52,21 +48,21 @@ def getProject() -> _ccl.MaybeCancelled[Project]: return _ccl.CANCELLED - class _CreateNewOrOpenExisting(_enum.Enum): CREATE_NEW = _enum.auto() OPEN_EXISTING = _enum.auto() - + OPEN_RECENT = _enum.auto() def _askUserWhetherToCreateNewProjectOrOpenExisting() -> _ccl.MaybeCancelled[_CreateNewOrOpenExisting]: messageBox = _qtw.QMessageBox() messageBox.setWindowTitle("Start a new or open an existing project") messageBox.setText("Do you want to start a new project or open an existing one?") - + recentButton = _qtw.QPushButton("Recent") createButton = _qtw.QPushButton("New") openButton = _qtw.QPushButton("Open") messageBox.addButton(createButton, _qtw.QMessageBox.YesRole) messageBox.addButton(openButton, _qtw.QMessageBox.NoRole) + messageBox.addButton(recentButton, _qtw.QMessageBox.NoRole) messageBox.addButton(_qtw.QMessageBox.Cancel) messageBox.setFocus() messageBox.exec() @@ -83,6 +79,9 @@ def _askUserWhetherToCreateNewProjectOrOpenExisting() -> _ccl.MaybeCancelled[_Cr if clickedButton is openButton: return _CreateNewOrOpenExisting.OPEN_EXISTING + if clickedButton is recentButton: + return _CreateNewOrOpenExisting.OPEN_RECENT + raise AssertionError("Unknown button was clicked.") @@ -93,6 +92,9 @@ def _getProjectInternal(createOrOpenExisting: "_CreateNewOrOpenExisting") -> _cc if createOrOpenExisting == _CreateNewOrOpenExisting.CREATE_NEW: return getCreateProject() + if createOrOpenExisting == _CreateNewOrOpenExisting.OPEN_RECENT: + return loadRecentProject() + raise AssertionError(f"Unknown value for enum {_CreateNewOrOpenExisting}: {createOrOpenExisting}") @@ -128,32 +130,44 @@ def getExistingEmptyDirectory( messageBox.setText(errorMessage) messageBox.exec() - def _isEmptyDirectory(path: _pl.Path) -> bool: if not path.is_dir(): return False - containedFilesAndDirectories = list(path.iterdir()) - isDirectoryEmpty = len(containedFilesAndDirectories) == 0 - return isDirectoryEmpty - def getLoadOrMigrateProject() -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: projectFolderPathString, _ = _qtw.QFileDialog.getOpenFileName(caption="Open diagram", filter="*.json") if not projectFolderPathString: return _ccl.CANCELLED jsonFilePath = _pl.Path(projectFolderPathString) - projectFolderPath = jsonFilePath.parent + return checkIfProjectEnviromentIsValid(projectFolderPath, jsonFilePath) + +def loadRecentProject()-> _ccl.MaybeCancelled[LoadProject]: + messageBox = _qtw.QMessageBox() + messageBox.setStandardButtons(_qtw.QMessageBox.Ok) + try: + recentProjectJsonPath = _pl.Path(UserSettings.getRecentProjectJsonPath()) + if not recentProjectJsonPath.exists(): + messageBox.setText("Recent project has moved or was deleted") + result = messageBox.exec() + if result == _qtw.QMessageBox.Ok: + return _ccl.CANCELLED + else: + return checkIfProjectEnviromentIsValid(recentProjectJsonPath.parent, recentProjectJsonPath) + except: + messageBox.setText("No recent project available") + result = messageBox.exec() + if result == _qtw.QMessageBox.Ok: + return _ccl.CANCELLED +def checkIfProjectEnviromentIsValid(projectFolderPath, jsonFilePath) -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: containingFolderIsCalledSameAsJsonFile = projectFolderPath.name == jsonFilePath.stem ddckFolder = projectFolderPath / "ddck" - if not containingFolderIsCalledSameAsJsonFile or not ddckFolder.is_dir(): oldJsonFilePath = jsonFilePath - messageBox = _qtw.QMessageBox() messageBox.setText( "The json you are opening does not have a proper project folder environment. " @@ -164,12 +178,9 @@ def getLoadOrMigrateProject() -> _ccl.MaybeCancelled[LoadProject | MigrateProjec result = messageBox.exec() if result == _qtw.QMessageBox.Cancel: return _ccl.CANCELLED - maybeCancelled = getExistingEmptyDirectory(startingDirectoryPath=projectFolderPath.parent) if _ccl.isCancelled(maybeCancelled): return _ccl.CANCELLED newProjectFolderPath = _ccl.value(maybeCancelled) - return MigrateProject(oldJsonFilePath, newProjectFolderPath) - return LoadProject(jsonFilePath) From 410f01fe9bfe5f0563978827b15bf9bc3fb9fc52 Mon Sep 17 00:00:00 2001 From: sebastian-swob Date: Tue, 15 Oct 2024 09:54:48 +0200 Subject: [PATCH 2/3] fixed pylint, mypy and reformatted with black --- trnsysGUI/diagram/Editor.py | 1 + trnsysGUI/mainWindow.py | 2 +- trnsysGUI/project.py | 22 +++++++++++++++---- .../{UserSettings.py => userSettings.py} | 3 ++- 4 files changed, 22 insertions(+), 6 deletions(-) rename trnsysGUI/{UserSettings.py => userSettings.py} (87%) diff --git a/trnsysGUI/diagram/Editor.py b/trnsysGUI/diagram/Editor.py index 991b2445..dff1348f 100644 --- a/trnsysGUI/diagram/Editor.py +++ b/trnsysGUI/diagram/Editor.py @@ -60,6 +60,7 @@ from . import _sizes from . import fileSystemTreeView as _fst + class Editor(_qtw.QWidget, _ip.HasInternalPipingsProvider): def __init__(self, parent, projectFolder, jsonPath, loadValue, logger): super().__init__(parent) diff --git a/trnsysGUI/mainWindow.py b/trnsysGUI/mainWindow.py index 3d871a63..79033cea 100644 --- a/trnsysGUI/mainWindow.py +++ b/trnsysGUI/mainWindow.py @@ -25,7 +25,7 @@ from trnsysGUI.MassFlowVisualizer import MassFlowVisualizer from trnsysGUI.ProcessMain import ProcessMain from trnsysGUI.RunMain import RunMain -from trnsysGUI.UserSettings import UserSettings +from trnsysGUI.userSettings import UserSettings from trnsysGUI.common import cancelled as _ccl from trnsysGUI.diagram import Editor as _de from trnsysGUI.storageTank.widget import StorageTank diff --git a/trnsysGUI/project.py b/trnsysGUI/project.py index 00349401..f11ac32c 100644 --- a/trnsysGUI/project.py +++ b/trnsysGUI/project.py @@ -16,23 +16,28 @@ import PyQt5.QtWidgets as _qtw import trnsysGUI.common.cancelled as _ccl -from trnsysGUI.UserSettings import UserSettings +from trnsysGUI.userSettings import UserSettings + @_dc.dataclass class CreateProject: jsonFilePath: _pl.Path + @_dc.dataclass class LoadProject: jsonFilePath: _pl.Path + @_dc.dataclass class MigrateProject: oldJsonFilePath: _pl.Path newProjectFolderPath: _pl.Path + Project = CreateProject | LoadProject | MigrateProject + def getProject() -> _ccl.MaybeCancelled[Project]: createOpenMaybeCancelled = _askUserWhetherToCreateNewProjectOrOpenExisting() @@ -48,11 +53,13 @@ def getProject() -> _ccl.MaybeCancelled[Project]: return _ccl.CANCELLED + class _CreateNewOrOpenExisting(_enum.Enum): CREATE_NEW = _enum.auto() OPEN_EXISTING = _enum.auto() OPEN_RECENT = _enum.auto() + def _askUserWhetherToCreateNewProjectOrOpenExisting() -> _ccl.MaybeCancelled[_CreateNewOrOpenExisting]: messageBox = _qtw.QMessageBox() messageBox.setWindowTitle("Start a new or open an existing project") @@ -130,6 +137,7 @@ def getExistingEmptyDirectory( messageBox.setText(errorMessage) messageBox.exec() + def _isEmptyDirectory(path: _pl.Path) -> bool: if not path.is_dir(): return False @@ -137,6 +145,7 @@ def _isEmptyDirectory(path: _pl.Path) -> bool: isDirectoryEmpty = len(containedFilesAndDirectories) == 0 return isDirectoryEmpty + def getLoadOrMigrateProject() -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: projectFolderPathString, _ = _qtw.QFileDialog.getOpenFileName(caption="Open diagram", filter="*.json") if not projectFolderPathString: @@ -145,7 +154,8 @@ def getLoadOrMigrateProject() -> _ccl.MaybeCancelled[LoadProject | MigrateProjec projectFolderPath = jsonFilePath.parent return checkIfProjectEnviromentIsValid(projectFolderPath, jsonFilePath) -def loadRecentProject()-> _ccl.MaybeCancelled[LoadProject]: + +def loadRecentProject() -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: messageBox = _qtw.QMessageBox() messageBox.setStandardButtons(_qtw.QMessageBox.Ok) try: @@ -157,13 +167,17 @@ def loadRecentProject()-> _ccl.MaybeCancelled[LoadProject]: return _ccl.CANCELLED else: return checkIfProjectEnviromentIsValid(recentProjectJsonPath.parent, recentProjectJsonPath) - except: + except TypeError: messageBox.setText("No recent project available") result = messageBox.exec() if result == _qtw.QMessageBox.Ok: return _ccl.CANCELLED + return _ccl.CANCELLED + -def checkIfProjectEnviromentIsValid(projectFolderPath, jsonFilePath) -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: +def checkIfProjectEnviromentIsValid( + projectFolderPath, jsonFilePath +) -> _ccl.MaybeCancelled[LoadProject | MigrateProject]: containingFolderIsCalledSameAsJsonFile = projectFolderPath.name == jsonFilePath.stem ddckFolder = projectFolderPath / "ddck" if not containingFolderIsCalledSameAsJsonFile or not ddckFolder.is_dir(): diff --git a/trnsysGUI/UserSettings.py b/trnsysGUI/userSettings.py similarity index 87% rename from trnsysGUI/UserSettings.py rename to trnsysGUI/userSettings.py index 2cf9e04a..e1f94d85 100644 --- a/trnsysGUI/UserSettings.py +++ b/trnsysGUI/userSettings.py @@ -1,5 +1,6 @@ -from PyQt5.QtCore import QSettings import pathlib as _pl +from PyQt5.QtCore import QSettings # pylint: disable=invalid-name + class UserSettings: From cbf9c8c8f2a1607190aa2c8ef77ddf96052561c6 Mon Sep 17 00:00:00 2001 From: sebastian-swob Date: Tue, 15 Oct 2024 14:06:37 +0200 Subject: [PATCH 3/3] removed cause not needed --- trnsysGUI/gui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/trnsysGUI/gui.py b/trnsysGUI/gui.py index e9c33ba5..8a376e9a 100644 --- a/trnsysGUI/gui.py +++ b/trnsysGUI/gui.py @@ -26,7 +26,6 @@ def main(): app = _qtw.QApplication(_sys.argv) app.setApplicationName("Diagram Creator") - app.setOrganizationName("pytrnsys") if errorMessage: _werrors.showMessageBox(errorMessage, title="Missing requirements")