From 92eac86c68fcab590efc23639974b7451f710769 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr> Date: Tue, 2 Apr 2024 14:51:17 +0200 Subject: [PATCH] AddFiles, REPLine: Add undo commands. --- src/Model/AdditionalFile/AddFile.py | 24 ++++++++ src/Model/REPLine/REPLine.py | 24 ++++++++ src/View/AdditionalFiles/Edit/Window.py | 20 +++++-- src/View/AdditionalFiles/List.py | 18 ++++-- src/View/AdditionalFiles/UndoCommand.py | 80 +++++++++++++++++++++++++ src/View/AdditionalFiles/Window.py | 17 +++++- src/View/REPLines/Edit/Window.py | 23 +++++-- src/View/REPLines/List.py | 18 ++++-- src/View/REPLines/UndoCommand.py | 80 +++++++++++++++++++++++++ src/View/REPLines/Window.py | 15 ++++- src/View/Tools/PamhyrList.py | 4 +- 11 files changed, 299 insertions(+), 24 deletions(-) create mode 100644 src/View/AdditionalFiles/UndoCommand.py create mode 100644 src/View/REPLines/UndoCommand.py diff --git a/src/Model/AdditionalFile/AddFile.py b/src/Model/AdditionalFile/AddFile.py index 64099205..c6888132 100644 --- a/src/Model/AdditionalFile/AddFile.py +++ b/src/Model/AdditionalFile/AddFile.py @@ -47,6 +47,30 @@ class AddFile(SQLSubModel): AddFile._id_cnt = max(id, AddFile._id_cnt+1) + def __getitem__(self, key): + value = None + + if key == "enabled": + value = self._enabled + elif key == "name": + value = self._name + elif key == "path": + value = self._path + elif key == "text": + value = self._text + + return value + + def __setitem__(self, key, value): + if key == "enabled": + self._enabled = value + elif key == "name": + self._name = value + elif key == "path": + self._path = value + elif key == "text": + self._text = value + @property def enabled(self): return self._enabled diff --git a/src/Model/REPLine/REPLine.py b/src/Model/REPLine/REPLine.py index aea23d8c..17c084b1 100644 --- a/src/Model/REPLine/REPLine.py +++ b/src/Model/REPLine/REPLine.py @@ -47,6 +47,30 @@ class REPLine(SQLSubModel): REPLine._id_cnt = max(id, REPLine._id_cnt+1) + def __getitem__(self, key): + value = None + + if key == "enabled": + value = self._enabled + elif key == "name": + value = self._name + elif key == "line": + value = self._line + elif key == "solvers": + value = self._solvers + + return value + + def __setitem__(self, key, value): + if key == "enabled": + self._enabled = value + elif key == "name": + self._name = value + elif key == "line": + self._line = value + elif key == "solvers": + self._solvers = value + @property def enabled(self): return self._enabled diff --git a/src/View/AdditionalFiles/Edit/Window.py b/src/View/AdditionalFiles/Edit/Window.py index 6db27942..b6c82862 100644 --- a/src/View/AdditionalFiles/Edit/Window.py +++ b/src/View/AdditionalFiles/Edit/Window.py @@ -27,6 +27,9 @@ from PyQt5.QtWidgets import ( ) from View.AdditionalFiles.Translate import AddFileTranslate +from View.AdditionalFiles.UndoCommand import ( + SetCommand +) logger = logging.getLogger() @@ -36,7 +39,7 @@ class EditAddFileWindow(PamhyrWindow): _pamhyr_name = "Edit additional file" def __init__(self, study=None, config=None, add_file=None, - trad=None, parent=None): + trad=None, undo=None, parent=None): name = trad[self._pamhyr_name] + " - " + study.name super(EditAddFileWindow, self).__init__( @@ -50,6 +53,8 @@ class EditAddFileWindow(PamhyrWindow): self._add_file = add_file self._hash_data.append(self._add_file) + self._undo = undo + self.setup_values() self.setup_connection() @@ -71,10 +76,15 @@ class EditAddFileWindow(PamhyrWindow): path = self.get_line_edit_text("lineEdit_path") text = self.get_plaintext_edit_text("plainTextEdit") - self._add_file.enabled = is_enabled - self._add_file.name = name - self._add_file.path = path - self._add_file.text = text + self._undo.push( + SetCommand( + self._add_file, + enabled=is_enabled, + name=name, + path=path, + text=text, + ) + ) self._propagate_update(key=Modules.ADDITIONAL_FILES) self.close() diff --git a/src/View/AdditionalFiles/List.py b/src/View/AdditionalFiles/List.py index 374ce54c..51755733 100644 --- a/src/View/AdditionalFiles/List.py +++ b/src/View/AdditionalFiles/List.py @@ -30,6 +30,9 @@ from PyQt5.QtGui import ( ) from View.Tools.PamhyrList import PamhyrListModel +from View.AdditionalFiles.UndoCommand import ( + AddCommand, DelCommand +) logger = logging.getLogger() @@ -62,10 +65,17 @@ class ListModel(PamhyrListModel): return QVariant() def add(self, row): - self._data.new(row) + self._undo.push( + AddCommand( + self._data, row + ) + ) self.update() - def delete(self, rows): - logger.info(f"add_files: delete {rows}") - self._data.delete_i(rows) + def delete(self, row): + self._undo.push( + DelCommand( + self._data, row + ) + ) self.update() diff --git a/src/View/AdditionalFiles/UndoCommand.py b/src/View/AdditionalFiles/UndoCommand.py new file mode 100644 index 00000000..ba417ff6 --- /dev/null +++ b/src/View/AdditionalFiles/UndoCommand.py @@ -0,0 +1,80 @@ +# UndoCommand.py -- Pamhyr +# Copyright (C) 2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +from tools import trace, timer + +from PyQt5.QtWidgets import ( + QMessageBox, QUndoCommand, QUndoStack, +) + + +class SetCommand(QUndoCommand): + def __init__(self, add_file, **kwargs): + QUndoCommand.__init__(self) + + self._add_file = add_file + self._new = kwargs + self._old = None + + def undo(self): + f = self._add_file + + for key in self._old: + f[key] = self._old[key] + + def redo(self): + f = self._add_file + + if self._old is None: + self._old = {} + for key in self._new: + self._old[key] = f[key] + + for key in self._new: + f[key] = self._new[key] + +class AddCommand(QUndoCommand): + def __init__(self, files, row): + QUndoCommand.__init__(self) + + self._files = files + self._row = row + self._new = None + + def undo(self): + self._files.delete([self._new]) + + def redo(self): + if self._new is None: + self._new = self._files.new(self._row) + else: + self._files.insert(self._row, self._new) + +class DelCommand(QUndoCommand): + def __init__(self, files, row): + QUndoCommand.__init__(self) + + self._files = files + self._row = row + self._old = self._files.get(row) + + def undo(self): + self._files.insert(self._row, self._old) + + def redo(self): + self._files.delete_i([self._row]) diff --git a/src/View/AdditionalFiles/Window.py b/src/View/AdditionalFiles/Window.py index 6cd84203..c6c22522 100644 --- a/src/View/AdditionalFiles/Window.py +++ b/src/View/AdditionalFiles/Window.py @@ -28,7 +28,6 @@ from View.AdditionalFiles.List import ListModel from View.AdditionalFiles.Translate import AddFileTranslate from View.AdditionalFiles.Edit.Window import EditAddFileWindow - class AddFileListWindow(PamhyrWindow): _pamhyr_ui = "AdditionalFileList" _pamhyr_name = "Additional files" @@ -55,6 +54,8 @@ class AddFileListWindow(PamhyrWindow): self._list = ListModel( list_view=lst, data=self._study.river.additional_files, + undo=self._undo_stack, + trad=self._trad, ) def setup_connections(self): @@ -80,7 +81,10 @@ class AddFileListWindow(PamhyrWindow): def delete(self): rows = self.selected_rows() - self._list.delete(rows) + if len(rows) == 0: + return + + self._list.delete(rows[0]) def edit(self): rows = self.selected_rows() @@ -99,6 +103,15 @@ class AddFileListWindow(PamhyrWindow): config=self._config, add_file=add_file, trad=self._trad, + undo=self._undo_stack, parent=self, ) win.show() + + def _undo(self): + self._undo_stack.undo() + self.update() + + def _redo(self): + self._undo_stack.redo() + self.update() diff --git a/src/View/REPLines/Edit/Window.py b/src/View/REPLines/Edit/Window.py index 120a0a29..473ab63d 100644 --- a/src/View/REPLines/Edit/Window.py +++ b/src/View/REPLines/Edit/Window.py @@ -27,6 +27,9 @@ from PyQt5.QtWidgets import ( ) from View.REPLines.Translate import REPLineTranslate +from View.REPLines.UndoCommand import ( + SetCommand +) logger = logging.getLogger() @@ -36,7 +39,7 @@ class EditREPLineWindow(PamhyrDialog): _pamhyr_name = "Edit Mage REP lines" def __init__(self, study=None, config=None, rep_line=None, - trad=None, parent=None): + trad=None, undo=None, parent=None): name = trad[self._pamhyr_name] + " - " + study.name super(EditREPLineWindow, self).__init__( @@ -50,6 +53,8 @@ class EditREPLineWindow(PamhyrDialog): self._rep_line = rep_line self._hash_data.append(self._rep_line) + self._undo = undo + self.setup_values() def setup_values(self): @@ -61,11 +66,17 @@ class EditREPLineWindow(PamhyrDialog): is_enabled = self.get_check_box("checkBox_enabled") name = self.get_line_edit_text("lineEdit_name") line = self.get_line_edit_text("lineEdit_line") - - self._rep_line.enabled = is_enabled - self._rep_line.name = name - self._rep_line.line = line - self._rep_line.solvers = set() + solvers = set() + + self._undo.push( + SetCommand( + self._rep_line, + enabled=is_enabled, + name=name, + line=line, + solvers=solvers, + ) + ) self._propagate_update(key=Modules.ADDITIONAL_FILES) self.close() diff --git a/src/View/REPLines/List.py b/src/View/REPLines/List.py index a591a4b1..b1e1752c 100644 --- a/src/View/REPLines/List.py +++ b/src/View/REPLines/List.py @@ -30,6 +30,9 @@ from PyQt5.QtGui import ( ) from View.Tools.PamhyrList import PamhyrListModel +from View.REPLines.UndoCommand import ( + AddCommand, DelCommand +) logger = logging.getLogger() @@ -62,10 +65,17 @@ class ListModel(PamhyrListModel): return QVariant() def add(self, row): - self._data.new(row) + self._undo.push( + AddCommand( + self._data, row + ) + ) self.update() - def delete(self, rows): - logger.info(f"add_files: delete {rows}") - self._data.delete_i(rows) + def delete(self, row): + self._undo.push( + DelCommand( + self._data, row + ) + ) self.update() diff --git a/src/View/REPLines/UndoCommand.py b/src/View/REPLines/UndoCommand.py new file mode 100644 index 00000000..7618c207 --- /dev/null +++ b/src/View/REPLines/UndoCommand.py @@ -0,0 +1,80 @@ +# UndoCommand.py -- Pamhyr +# Copyright (C) 2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +from tools import trace, timer + +from PyQt5.QtWidgets import ( + QMessageBox, QUndoCommand, QUndoStack, +) + + +class SetCommand(QUndoCommand): + def __init__(self, rep_line, **kwargs): + QUndoCommand.__init__(self) + + self._rep_line = rep_line + self._new = kwargs + self._old = None + + def undo(self): + f = self._rep_line + + for key in self._old: + f[key] = self._old[key] + + def redo(self): + f = self._rep_line + + if self._old is None: + self._old = {} + for key in self._new: + self._old[key] = f[key] + + for key in self._new: + f[key] = self._new[key] + +class AddCommand(QUndoCommand): + def __init__(self, lines, row): + QUndoCommand.__init__(self) + + self._lines = lines + self._row = row + self._new = None + + def undo(self): + self._lines.delete([self._new]) + + def redo(self): + if self._new is None: + self._new = self._lines.new(self._row) + else: + self._lines.insert(self._row, self._new) + +class DelCommand(QUndoCommand): + def __init__(self, lines, row): + QUndoCommand.__init__(self) + + self._lines = lines + self._row = row + self._old = self._lines.get(row) + + def undo(self): + self._lines.insert(self._row, self._old) + + def redo(self): + self._lines.delete_i([self._row]) diff --git a/src/View/REPLines/Window.py b/src/View/REPLines/Window.py index 92cb015b..a7bc4ce0 100644 --- a/src/View/REPLines/Window.py +++ b/src/View/REPLines/Window.py @@ -55,6 +55,7 @@ class REPLineListWindow(PamhyrWindow): self._list = ListModel( list_view=lst, data=self._study.river.rep_lines, + undo=self._undo_stack, ) def setup_connections(self): @@ -80,7 +81,10 @@ class REPLineListWindow(PamhyrWindow): def delete(self): rows = self.selected_rows() - self._list.delete(rows) + if len(rows) < 0: + return + + self._list.delete(rows[0]) def edit(self): rows = self.selected_rows() @@ -99,6 +103,15 @@ class REPLineListWindow(PamhyrWindow): config=self._config, rep_line=rep_line, trad=self._trad, + undo=self._undo_stack, parent=self, ) win.show() + + def _undo(self): + self._undo_stack.undo() + self.update() + + def _redo(self): + self._undo_stack.redo() + self.update() diff --git a/src/View/Tools/PamhyrList.py b/src/View/Tools/PamhyrList.py index 7acc7ee0..709672b0 100644 --- a/src/View/Tools/PamhyrList.py +++ b/src/View/Tools/PamhyrList.py @@ -73,11 +73,11 @@ class PamhyrListModel(QAbstractListModel): def undo(self): self._undo.undo() - self.layoutChanged.emit() + self.update() def redo(self): self._undo.redo() - self.layoutChanged.emit() + self.update() def update(self): self.layoutChanged.emit() -- GitLab