diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd8178d486ebd843149fe4a82ea639f659495178..61a4ab6ffc2839ead886b7c591bfe3dfb34cf098 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -122,7 +122,7 @@ test-pep8: - pip3 install -r ./requirements.txt - pip3 install -U -r ./requirements.txt - pip3 install pycodestyle - - pycodestyle ./src + - pycodestyle --exclude="*_to_*.py" ./src allow_failure: true ######### diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index ecc4a559f9c3a2a5ca8d04850f849c4a2b9e2e58..d3be3cf5980379be21df9ac4201d955279f4919c 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -24,6 +24,7 @@ from Model.Tools.PamhyrDB import SQLSubModel logger = logging.getLogger() + class Friction(SQLSubModel): def __init__(self, name: str = "", status=None): super(Friction, self).__init__() diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 495fbac10c737209f33138ee7f2a05b8acd359be..34146bc1e51b83d910b2df6396a69200599eaa39 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -437,42 +437,57 @@ class ProfileXYZ(Profile, SQLSubModel): return abs(rg.dist(rd)) def get_water_limits(self, z): -#============================================================================== -# détermination des points limites RG et RD pour un niveau d'eau donné -# -# irg et ird sont les premiers indices en partant des rives gauche et -# droite qui correspondent à des points sous la surface de l'eau -# ptX et ptY sont les points interpolés où le plan d'eau intersecte le profil -# known_level est le niveau d'eau pour lequel on a obtenu irg, ird, ptX et ptY -#============================================================================== + # ==================================================================== + # détermination des points limites RG et RD pour un niveau + # d'eau donné + # + # irg et ird sont les premiers indices en partant des rives + # gauche et droite qui correspondent à des points sous la + # surface de l'eau ptX et ptY sont les points interpolés où + # le plan d'eau intersecte le profil known_level est le + # niveau d'eau pour lequel on a obtenu irg, ird, ptX et ptY + # ==================================================================== + # initialisation - irg = -1 ; ird = -1 + irg = -1 + ird = -1 + for i in range(self.number_points): if self.point(i).z <= z: irg = i + for i in reversed(range(self.number_points)): if self.point(i).z <= z: ird = i + # interpolation des points ptX et ptY - if (irg < self.number_points-1): - x=np.interp(z, - [self.point(irg).z,self.point(irg+1).z], - [self.point(irg).x,self.point(irg+1).x]) - y=np.interp(z, - [self.point(irg).z,self.point(irg+1).z], - [self.point(irg).y,self.point(irg+1).y]) - ptX=PointXYZ(x,y,z) + if (irg < self.number_points - 1): + x = np.interp( + z, + [self.point(irg).z, self.point(irg + 1).z], + [self.point(irg).x, self.point(irg + 1).x] + ) + y = np.interp( + z, + [self.point(irg).z, self.point(irg + 1).z], + [self.point(irg).y, self.point(irg + 1).y] + ) + ptX = PointXYZ(x, y, z) else: ptX = self.point(0) if (ird > 0): - x=np.interp(z, - [self.point(ird-1).z,self.point(ird).z], - [self.point(ird-1).x,self.point(ird).x]) - y=np.interp(z, - [self.point(ird).z,self.point(ird-1).z], - [self.point(ird).y,self.point(ird-1).y]) - ptY=PointXYZ(x,y,z) + x = np.interp( + z, + [self.point(ird-1).z, self.point(ird).z], + [self.point(ird-1).x, self.point(ird).x] + ) + y = np.interp( + z, + [self.point(ird).z, self.point(ird - 1).z], + [self.point(ird).y, self.point(ird - 1).y] + ) + ptY = PointXYZ(x, y, z) else: - ptY = self.point(self.number_points-1) + ptY = self.point(self.number_points - 1) - return ptX,ptY + return ptX, ptY diff --git a/src/Model/HydraulicStructures/Basic/HydraulicStructures.py b/src/Model/HydraulicStructures/Basic/HydraulicStructures.py index 691cf60c96afbdc9747ec1e8f4baab05517e40e4..e624d2ef0ca8dc653306527967e34a8f34068ede 100644 --- a/src/Model/HydraulicStructures/Basic/HydraulicStructures.py +++ b/src/Model/HydraulicStructures/Basic/HydraulicStructures.py @@ -29,6 +29,7 @@ from Model.HydraulicStructures.Basic.Value import ( logger = logging.getLogger() + class BasicHS(SQLSubModel): _sub_classes = [ BHSValue, @@ -142,7 +143,7 @@ class BasicHS(SQLSubModel): data['bhs_id'] = self.id execute( - "DELETE FROM hydraulic_structures_basic_value "+ + "DELETE FROM hydraulic_structures_basic_value " + f"WHERE bhs = {bhs_id}" ) diff --git a/src/Model/HydraulicStructures/Basic/Types.py b/src/Model/HydraulicStructures/Basic/Types.py index a976aaa37f7335a5a7e38ad49aab5c3518d5b904..98ded30bbaaf141c4dc0815afea3bfcea4cc11a5 100644 --- a/src/Model/HydraulicStructures/Basic/Types.py +++ b/src/Model/HydraulicStructures/Basic/Types.py @@ -16,18 +16,22 @@ # -*- coding: utf-8 -*- -from Model.Except import NotImplementedMethodeError - from Model.HydraulicStructures.Basic.HydraulicStructures import ( BasicHS ) + from Model.HydraulicStructures.Basic.Value import ( BHSValue ) + class NotDefined(BasicHS): - def __init__(self, id: int = -1, name: str = "", status=None): - super(NotDefined, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None): + super(NotDefined, self).__init__( + id=id, name=name, + status=status + ) self._type = "ND" self._data = [ diff --git a/src/Model/HydraulicStructures/Basic/Value.py b/src/Model/HydraulicStructures/Basic/Value.py index dccd41e545b5bfa3616cf0100a0a2aa44b4c8953..ebf4744eda3199dbe89b1e4baa993d528a9e9fab 100644 --- a/src/Model/HydraulicStructures/Basic/Value.py +++ b/src/Model/HydraulicStructures/Basic/Value.py @@ -18,11 +18,12 @@ from Model.Tools.PamhyrDB import SQLSubModel + class BHSValue(SQLSubModel): _sub_classes = [] _id_cnt = 0 - def __init__(self, name: str = "", type = float, value = 0.0, + def __init__(self, name: str = "", type=float, value=0.0, status=None): super(BHSValue, self).__init__() @@ -117,7 +118,7 @@ class BHSValue(SQLSubModel): "hydraulic_structures_basic_value(name, type, value, bhs) " + "VALUES (" + f"'{self._db_format(self._name)}', " + - f"'{self._db_format(self._type_to_str(self._type))}', "+ + f"'{self._db_format(self._type_to_str(self._type))}', " + f"'{self._db_format(self._value)}', " + f"{bhs_id}" + ")" diff --git a/src/Model/HydraulicStructures/HydraulicStructures.py b/src/Model/HydraulicStructures/HydraulicStructures.py index 6d1a4734b869cc29206fec4e20cb68cc19d5e509..3cb68f97cdd44b84dac2fe46f0a491cdc8830cc5 100644 --- a/src/Model/HydraulicStructures/HydraulicStructures.py +++ b/src/Model/HydraulicStructures/HydraulicStructures.py @@ -30,6 +30,7 @@ from Model.HydraulicStructures.Basic.Types import ( logger = logging.getLogger() + class HydraulicStructure(SQLSubModel): _sub_classes = [ BasicHS, @@ -55,7 +56,10 @@ class HydraulicStructure(SQLSubModel): self._enabled = True self._data = [] - HydraulicStructure._id_cnt = max(HydraulicStructure._id_cnt + 1, self.id) + HydraulicStructure._id_cnt = max( + HydraulicStructure._id_cnt + 1, + self.id + ) @classmethod def _db_create(cls, execute): diff --git a/src/Model/HydraulicStructures/HydraulicStructuresList.py b/src/Model/HydraulicStructures/HydraulicStructuresList.py index 0e1039fda7b4d96d28bd5b2388cdd97ee1c43f7c..549d80745b13099575cc4145d0a1c5a45bb0e773 100644 --- a/src/Model/HydraulicStructures/HydraulicStructuresList.py +++ b/src/Model/HydraulicStructures/HydraulicStructuresList.py @@ -22,6 +22,7 @@ from tools import trace, timer from Model.Tools.PamhyrList import PamhyrModelList from Model.HydraulicStructures.HydraulicStructures import HydraulicStructure + class HydraulicStructureList(PamhyrModelList): _sub_classes = [ HydraulicStructure, diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index 439445fa3887ad85087d59f7efd19a88576d2a95..a71f3133d83d87b9a10c21589c5c031109f5ebe4 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -376,7 +376,7 @@ class InitialConditions(SQLSubModel): * (abs(incline) ** (0.5))) ) - elevation= max( + elevation = max( profile.z_min() + height, previous_elevation ) @@ -422,7 +422,7 @@ class InitialConditions(SQLSubModel): ((width * 0.8) * strickler * (abs(incline) ** (0.5))) ) ** (0.6) - elevation= max( + elevation = max( profile.z_min() + height, previous_elevation ) diff --git a/src/Model/Network/Graph.py b/src/Model/Network/Graph.py index e3616d0e55ab12705458682768f4ffc8c630d17c..7836a89c9e17d3812580d883f4ae6020a9517d6f 100644 --- a/src/Model/Network/Graph.py +++ b/src/Model/Network/Graph.py @@ -213,7 +213,7 @@ class Graph(object): def is_enable_edge(self, edge): return edge._enable - #def get_edge_id(self, reach): + # def get_edge_id(self, reach): # for i, e in enumerate(self.enable_edges): # if e.id == reach.id: # return i diff --git a/src/Model/Reservoir/Reservoir.py b/src/Model/Reservoir/Reservoir.py index 97a6311318e23e56d52c931f5272ad037ad376d2..ebf7e24c883445ac085244dbd22e98c3d7825555 100644 --- a/src/Model/Reservoir/Reservoir.py +++ b/src/Model/Reservoir/Reservoir.py @@ -94,7 +94,11 @@ class Reservoir(SQLSubModel): new_reservoir._node = None if node_id != -1: - new_reservoir._node = next(filter(lambda n: n.id == node_id, data["nodes"])) + new_reservoir._node = next( + filter( + lambda n: n.id == node_id, data["nodes"] + ) + ) new_data = [] table = execute( diff --git a/src/Model/River.py b/src/Model/River.py index 2ba5a54f4105813ee8f92eeebd08064de915ad41..99cf8d2116be0757151a08a9e6b142e0b7a9fde7 100644 --- a/src/Model/River.py +++ b/src/Model/River.py @@ -242,7 +242,9 @@ class River(Graph, SQLSubModel): self._parameters = {} self._sediment_layers = SedimentLayerList(status=self._status) self._reservoir = ReservoirList(status=self._status) - self._hydraulic_structures = HydraulicStructureList(status=self._status) + self._hydraulic_structures = HydraulicStructureList( + status=self._status + ) @classmethod def _db_create(cls, execute): diff --git a/src/Model/Tools/PamhyrDict.py b/src/Model/Tools/PamhyrDict.py index c8cab70a1b0572c4092aa729aee5487e70a612b8..31bc12a6bc9df306df64b719ed977153371830f8 100644 --- a/src/Model/Tools/PamhyrDict.py +++ b/src/Model/Tools/PamhyrDict.py @@ -67,7 +67,7 @@ class PamhyrModelDict(SQLSubModel): if key in self._dict: v = self._dict[key] - if type(v) == types.GeneratorType: + if type(v) is types.GeneratorType: return list(v) return v diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index 5dc5920754696588fc2a08ebae3474abaf771286..7326d6742af289440219fcca4bd90983e70cfc28 100644 --- a/src/Solver/Mage.py +++ b/src/Solver/Mage.py @@ -453,9 +453,20 @@ class Mage(CommandLineSolver): for hs in hydraulic_structures: if hs.reach.is_enable: reach_id = study.river.get_edge_id(hs.reach) - params = [p.value for p in hs.basic_hydraulic_structure.param] - param_str = ' '.join([f'{p.value:>10.3f}' for p in hs.basic_hydraulic_structure.param]) - f.write(f"{hs.basic_hydraulic_structure.type} {reach_id} {hs.kp:>12.3f} {params} {hs.name}\n") + params = [ + p.value for p in hs.basic_hydraulic_structure.param + ] + param_str = ' '.join( + [ + f'{p.value:>10.3f}' + for p in hs.basic_hydraulic_structure.param + ] + ) + f.write( + f"{hs.basic_hydraulic_structure.type} " + + f"{reach_id} {hs.kp:>12.3f} {params} " + + "{hs.name}\n" + ) return files @@ -465,7 +476,11 @@ class Mage(CommandLineSolver): qlog.put("Export REP file") # Write header - with mage_file_open(os.path.join(repertory, f"{name}.REP"), "w+") as f: + with mage_file_open( + os.path.join( + repertory, f"{name}.REP" + ), "w+" + ) as f: f.write("confirmation=non\n") for file in files: @@ -832,12 +847,13 @@ class Mage8(Mage): # Set data for profile RI reach.set(ri, timestamp, key, d) if key == "Z": - profile = study.river.current_reach().reach.profile(ri) - ptX,ptY = profile.get_water_limits(d) + profile = study.river\ + .current_reach()\ + .reach.profile(ri) + ptX, ptY = profile.get_water_limits(d) reach.set(ri, timestamp, "ptX", ptX) reach.set(ri, timestamp, "ptY", ptY) - endline() end = newline().size <= 0 diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index ccec087baf3dd644adbb199619b2a4e94fcadd01..4bdbb5ae72ab145418a4e73fe20d5a6b7d0842b6 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -160,7 +160,7 @@ class EditBoundaryConditionWindow(PamhyrWindow): table_headers=headers, editable_headers=self._data.header, delegates={ - #"time": self._delegate_time, + # "time": self._delegate_time, }, data=self._data, undo=self._undo_stack, diff --git a/src/View/BoundaryCondition/Table.py b/src/View/BoundaryCondition/Table.py index 024dc2889f44a8374ea8c0604d0ccc1b06dc460f..aad78fc35c80eb3ee65c7c519759594152f01e2f 100644 --- a/src/View/BoundaryCondition/Table.py +++ b/src/View/BoundaryCondition/Table.py @@ -115,13 +115,13 @@ class ComboBoxDelegate(QItemDelegate): class TableModel(PamhyrTableModel): - def __init__(self, trad = None, **kwargs): + def __init__(self, trad=None, **kwargs): self._trad = trad self._long_types = {} if self._trad is not None: self._long_types = self._trad.get_dict("long_types") - super(TableModel, self).__init__(trad = trad, **kwargs) + super(TableModel, self).__init__(trad=trad, **kwargs) def _setup_lst(self): self._lst = self._data.boundary_condition diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Table.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Table.py index 0522af555679571f817f2bad2e2fc925da874ba4..983a5ad0b296d0a9a4c02aadae31238135b696bd 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Table.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Table.py @@ -18,3 +18,160 @@ import logging import traceback + +from tools import trace, timer + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, + QCoreApplication, QModelIndex, pyqtSlot, + QRect, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QPushButton, QLineEdit, + QFileDialog, QTableView, QAbstractItemView, + QUndoStack, QShortcut, QAction, QItemDelegate, + QComboBox, +) + +from View.Tools.PamhyrTable import PamhyrTableModel + +from View.HydraulicStructures.BasicHydraulicStructures.UndoCommand import ( + SetNameCommand, SetTypeCommand, + AddCommand, DelCommand, +) +from Model.HydraulicStructures.Basic.Types import BHS_types + +logger = logging.getLogger() + +_translate = QCoreApplication.translate + + +class ComboBoxDelegate(QItemDelegate): + def __init__(self, data=None, trad=None, parent=None): + super(ComboBoxDelegate, self).__init__(parent) + + self._data = data + self._trad = trad + + self._long_types = {} + if self._trad is not None: + self._long_types = self._trad.get_dict("long_types") + + def createEditor(self, parent, option, index): + self.editor = QComboBox(parent) + + + lst = list( + map( + lambda k: self._long_types[k], BHS_types.keys() + ) + ) + self.editor.addItems( + lst + ) + + self.editor.setCurrentText(index.data(Qt.DisplayRole)) + return self.editor + + def setEditorData(self, editor, index): + value = index.data(Qt.DisplayRole) + self.editor.currentTextChanged.connect(self.currentItemChanged) + + def setModelData(self, editor, model, index): + text = str(editor.currentText()) + model.setData(index, text) + editor.close() + editor.deleteLater() + + def updateEditorGeometry(self, editor, option, index): + r = QRect(option.rect) + if self.editor.windowFlags() & Qt.Popup: + if editor.parent() is not None: + r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft())) + editor.setGeometry(r) + + @pyqtSlot() + def currentItemChanged(self): + self.commitData.emit(self.sender()) + + +class TableModel(PamhyrTableModel): + def _setup_lst(self): + self._lst = self._data.basic_structures + + def rowCount(self, parent): + return len(self._lst) + + def data(self, index, role): + if role != Qt.ItemDataRole.DisplayRole: + return QVariant() + + row = index.row() + column = index.column() + + if self._headers[column] == "name": + return self._lst[row].name + elif self._headers[column] == "type": + n = self._lst[row].type + + return QVariant() + + def setData(self, index, value, role=Qt.EditRole): + if not index.isValid() or role != Qt.EditRole: + return False + + row = index.row() + column = index.column() + + try: + if self._headers[column] == "name": + self._undo.push( + SetNameCommand( + self._data, row, value + ) + ) + elif self._headers[column] == "type": + self._undo.push( + SetTypeCommand( + self._data, row, type + ) + ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) + + self.dataChanged.emit(index, index) + return True + + def add(self, row, parent=QModelIndex()): + self.beginInsertRows(parent, row, row - 1) + + self._undo.push( + AddCommand( + self._data, row + ) + ) + + self.endInsertRows() + self.layoutChanged.emit() + + def delete(self, rows, parent=QModelIndex()): + self.beginRemoveRows(parent, rows[0], rows[-1]) + + self._undo.push( + DelCommand( + self._data, rows + ) + ) + + self.endRemoveRows() + self.layoutChanged.emit() + + def undo(self): + self._undo.undo() + self.layoutChanged.emit() + + def redo(self): + self._undo.redo() + self.layoutChanged.emit() diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py index f2d74d02529c6ac24646c5f6e3219826385e7ee3..b267fc422b93438be8aa44797f85c86198ae7ef5 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py @@ -21,3 +21,17 @@ from PyQt5.QtCore import QCoreApplication from View.Tools.PamhyrTranslate import PamhyrTranslate _translate = QCoreApplication.translate + + +class BasicHydraulicStructuresTranslate(PamhyrTranslate): + def __init__(self): + super(BasicHydraulicStructuresTranslate, self).__init__() + + self._sub_dict["long_types"] = { + "ND": _translate("BasicHydraulicStructures", "Not defined"), + } + + self._sub_dict["table_headers"] = { + "name": _translate("BasicHydraulicStructures", "Name"), + "type": _translate("BasicHydraulicStructures", "Type"), + } diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/UndoCommand.py b/src/View/HydraulicStructures/BasicHydraulicStructures/UndoCommand.py index ca18efaa83e126490b58714fbe6cf34775dfb5e9..2d7e40676fc4a9a9b48d69edabcc8af4bf6c9be4 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/UndoCommand.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/UndoCommand.py @@ -15,3 +15,104 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # -*- coding: utf-8 -*- + +from copy import deepcopy +from tools import trace, timer + +from PyQt5.QtWidgets import ( + QMessageBox, QUndoCommand, QUndoStack, +) + + +class SetNameCommand(QUndoCommand): + def __init__(self, hs, index, new_value): + QUndoCommand.__init__(self) + + self._hs = hs + self._index = index + self._old = self._hs.basic_structure(self._index).name + self._new = str(new_value) + + def undo(self): + self._hs.basic_structure(self._index).name = self._old + + def redo(self): + self._hs.basic_structure(self._index).name = self._new + + +class SetTypeCommand(QUndoCommand): + def __init__(self, hs, index, reach): + QUndoCommand.__init__(self) + + self._hs = hs + self._index = index + self._type = _type + self._old = self._hs.basic_structure(self._index) + self._new = self._hs.basic_structure(self._index)\ + .convert(self._type) + + def undo(self): + self._hs.basic_structure(self._index).convert(self._old) + + def redo(self): + self._hs.basic_structure(self._index).convert(self._new) + + +class AddCommand(QUndoCommand): + def __init__(self, hs, index): + QUndoCommand.__init__(self) + + self._hs = hs + + self._index = index + self._new = None + + def undo(self): + self._hs.delete_i([self._index]) + + def redo(self): + if self._new is None: + self._new = self._hs.add(self._index) + else: + self._hs.insert(self._index, self._new) + + +class DelCommand(QUndoCommand): + def __init__(self, hs, rows): + QUndoCommand.__init__(self) + + self._hs = hs + + self._rows = rows + + self._bhs = [] + for row in rows: + self._bhs.append((row, self._hs.basic_structure(row))) + + def undo(self): + for row, el in self._bhs: + self._hs.insert(row, el) + + def redo(self): + self._hs.delete_i(self._rows) + + +class PasteCommand(QUndoCommand): + def __init__(self, hs, row, h_s): + QUndoCommand.__init__(self) + + self._hs = hs + + self._row = row + self._bhs = deepcopy(h_s) + self._bhs.reverse() + + def undo(self): + self._hs.delete_i( + self._tab, + range(self._row, self._row + len(self._bhs)) + ) + + def redo(self): + for r in self._bhs: + self._hs.insert(self._row, r) diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py index 43e78cefde37ebc935e71267888843bc9eda55d8..a555c2bf3001390b91e6a510222f84d447f409b3 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py @@ -15,3 +15,207 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # -*- coding: utf-8 -*- + +import logging + +from tools import timer, trace + +from View.Tools.PamhyrWindow import PamhyrWindow + +from PyQt5 import QtCore +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, QCoreApplication, + pyqtSlot, pyqtSignal, QItemSelectionModel, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QPushButton, QLineEdit, + QFileDialog, QTableView, QAbstractItemView, + QUndoStack, QShortcut, QAction, QItemDelegate, + QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox +) + +from View.Tools.Plot.PamhyrCanvas import MplCanvas +from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar + +from View.HydraulicStructures.PlotAC import PlotAC + +from View.HydraulicStructures.BasicHydraulicStructures.Table import ( + TableModel, ComboBoxDelegate +) + +from View.Network.GraphWidget import GraphWidget +from View.HydraulicStructures.BasicHydraulicStructures.Translate import BasicHydraulicStructuresTranslate + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + + +class BasicHydraulicStructuresWindow(PamhyrWindow): + _pamhyr_ui = "BasicHydraulicStructures" + _pamhyr_name = "Basic Hydraulic Structures" + + def __init__(self, data=None, study=None, config=None, parent=None): + name = self._pamhyr_name + " - " + study.name + + super(BasicHydraulicStructuresWindow, self).__init__( + title=name, + study=study, + config=config, + trad=BasicHydraulicStructuresTranslate(), + parent=parent + ) + + self._hash_data.append(data) + + self._hs = data + + self.setup_table() + self.setup_checkbox() + self.setup_plot() + self.setup_connections() + + def setup_table(self): + self._table = None + + self._delegate_type = ComboBoxDelegate( + trad=self._trad, + parent=self + ) + + table = self.find(QTableView, f"tableView") + self._table = TableModel( + table_view=table, + table_headers=self._trad.get_dict("table_headers"), + editable_headers=["name", "type"], + delegates={ + "type": self._delegate_type, + }, + trad=self._trad, + data=self._hs, + undo=self._undo_stack, + ) + + selectionModel = table.selectionModel() + index = table.model().index(0, 0) + + selectionModel.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + table.scrollTo(index) + + def setup_checkbox(self): + self._checkbox = self.find(QCheckBox, f"checkBox") + self._set_checkbox_state() + + def setup_plot(self): + self.canvas = MplCanvas(width=5, height=4, dpi=100) + self.canvas.setObjectName("canvas") + self.toolbar = PamhyrPlotToolbar( + self.canvas, self + ) + self.plot_layout = self.find(QVBoxLayout, "verticalLayout") + self.plot_layout.addWidget(self.toolbar) + self.plot_layout.addWidget(self.canvas) + + self.plot_ac = PlotAC( + canvas=self.canvas, + river=self._study.river, + reach=None, + profile=None, + toolbar=self.toolbar + ) + self.plot_ac.draw() + + def setup_connections(self): + self.find(QAction, "action_add").triggered.connect(self.add) + self.find(QAction, "action_delete").triggered.connect(self.delete) + self._checkbox.stateChanged.connect(self._set_basic_structure_state) + + table = self.find(QTableView, "tableView") + table.selectionModel()\ + .selectionChanged\ + .connect(self.update) + + def index_selected(self): + table = self.find(QTableView, "tableView") + r = table.selectionModel()\ + .selectedRows() + if len(r)>0: + return r[0] + else: + return None + + def index_selected_row(self): + table = self.find(QTableView, "tableView") + r = table.selectionModel()\ + .selectedRows() + if len(r)>0: + return r[0].row() + else: + return None + + def index_selected_rows(self): + table = self.find(QTableView, "tableView") + return list( + # Delete duplicate + set( + map( + lambda i: i.row(), + table.selectedIndexes() + ) + ) + ) + + def add(self): + rows = self.index_selected_rows() + if len(self._hs) == 0 or len(rows) == 0: + self._table.add(0) + else: + self._table.add(rows[0]) + + def delete(self): + rows = self.index_selected_rows() + if len(rows) == 0: + return + + self._table.delete(rows) + + def _copy(self): + logger.info("TODO: copy") + + def _paste(self): + logger.info("TODO: paste") + + def _undo(self): + self._table.undo() + + def _redo(self): + self._table.redo() + + def _set_checkbox_state(self): + row = self.index_selected_row() + if row is None: + self._checkbox.setEnabled(False) + self._checkbox.setChecked(True) + else: + self._checkbox.setEnabled(True) + self._checkbox.setChecked(self._hs.basic_structure(row).enabled) + + def _set_basic_structure_state(self): + row = self.index_selected_row() + if row is None: + self._checkbox.setEnabled(False) + else: + self._hs.basic_structure(row).enabled = self._checkbox.isChecked() + + def update(self): + self._set_checkbox_state() + self._update_clear_plot() + + def _update_clear_plot(self): + rows = self.index_selected_rows() diff --git a/src/View/HydraulicStructures/PlotAC.py b/src/View/HydraulicStructures/PlotAC.py index 355842031e32a018bc1ddf7707c1d0b675a383ce..028d65730f8fb626b50c7e32b8a0fbcd16a0a2f7 100644 --- a/src/View/HydraulicStructures/PlotAC.py +++ b/src/View/HydraulicStructures/PlotAC.py @@ -126,5 +126,5 @@ class PlotAC(PamhyrPlot): def clear(self): if self.line_kp is not None: - self.line_kp.set_data([],[]) + self.line_kp.set_data([], []) self.canvas.figure.canvas.draw_idle() diff --git a/src/View/HydraulicStructures/PlotKPC.py b/src/View/HydraulicStructures/PlotKPC.py index 15a5a1a40f03569710bc3c8d63697912082b9051..d3070c88b2b1e96a7090a853fa36897dc3ff7141 100644 --- a/src/View/HydraulicStructures/PlotKPC.py +++ b/src/View/HydraulicStructures/PlotKPC.py @@ -110,7 +110,6 @@ class PlotKPC(PamhyrPlot): color='red', lw=1. ) - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() if self.toolbar is not None: @@ -144,16 +143,15 @@ class PlotKPC(PamhyrPlot): def clear(self): if self.profile is not None: - self.profile.set_data([],[]) + self.profile.set_data([], []) if self.line_kp_zmin_zmax is not None: self.line_kp_zmin_zmax.remove() self.line_kp_zmin_zmax = None if self.line_kp_zmin is not None: - self.line_kp_zmin.set_data([],[]) + self.line_kp_zmin.set_data([], []) self.canvas.figure.canvas.draw_idle() def clear_profile(self): if self.profile is not None: - self.profile.set_data([],[]) + self.profile.set_data([], []) self.canvas.figure.canvas.draw_idle() - diff --git a/src/View/HydraulicStructures/Table.py b/src/View/HydraulicStructures/Table.py index e61d9094adef9f9ac96a867be590b34cb92e7e77..58e086848c2f5d0fc62acfdb11ed0ec584b6653e 100644 --- a/src/View/HydraulicStructures/Table.py +++ b/src/View/HydraulicStructures/Table.py @@ -59,7 +59,9 @@ class ComboBoxDelegate(QItemDelegate): val = [] if self._mode == "kp": - reach = self._data.hydraulic_structures.get(index.row()).input_reach + reach = self._data.hydraulic_structures\ + .get(index.row())\ + .input_reach if reach is not None: val = list( map( @@ -138,6 +140,7 @@ class TableModel(PamhyrTableModel): row = index.row() column = index.column() + na = _translate("Hydraulic structure", "Not associated") try: if self._headers[column] == "name": @@ -147,12 +150,18 @@ class TableModel(PamhyrTableModel): ) ) elif self._headers[column] == "reach": + if value == na: + value = None + self._undo.push( SetReachCommand( self._lst, row, self._data.edge(value) ) ) elif self._headers[column] == "kp": + if value == na: + value = None + self._undo.push( SetKpCommand( self._lst, row, value diff --git a/src/View/HydraulicStructures/UndoCommand.py b/src/View/HydraulicStructures/UndoCommand.py index 75e2886b972eda03713a56297a99c52e5277c4d0..826cd268a4370d32ebef7b8fd47f3397a2294c09 100644 --- a/src/View/HydraulicStructures/UndoCommand.py +++ b/src/View/HydraulicStructures/UndoCommand.py @@ -99,6 +99,7 @@ class SetEnabledCommand(QUndoCommand): logger.info(f"Undo {self._old} -> {self._new}") self._h_s_lst.get(self._index).enabled = self._new + class AddCommand(QUndoCommand): def __init__(self, h_s_lst, index): QUndoCommand.__init__(self) diff --git a/src/View/HydraulicStructures/Window.py b/src/View/HydraulicStructures/Window.py index ce90eafd57613331c37719aed4ee1d3e8bc0de8f..7c139dd3906f016ab859a6a8852b4e65cb39b63b 100644 --- a/src/View/HydraulicStructures/Window.py +++ b/src/View/HydraulicStructures/Window.py @@ -84,13 +84,13 @@ class HydraulicStructuresWindow(PamhyrWindow): trad=self._trad, data=self._study.river, parent=self, - mode = "reaches" + mode="reaches" ) self._delegate_kp = ComboBoxDelegate( trad=self._trad, data=self._study.river, parent=self, - mode = "kp" + mode="kp" ) table = self.find(QTableView, f"tableView") @@ -177,7 +177,7 @@ class HydraulicStructuresWindow(PamhyrWindow): table = self.find(QTableView, "tableView") r = table.selectionModel().selectedRows() - if len(r)>0: + if len(r) > 0: return r[0] else: return None @@ -186,7 +186,7 @@ class HydraulicStructuresWindow(PamhyrWindow): table = self.find(QTableView, "tableView") r = table.selectionModel().selectedRows() - if len(r)>0: + if len(r) > 0: return r[0].row() else: return None @@ -233,7 +233,8 @@ class HydraulicStructuresWindow(PamhyrWindow): rows = self.index_selected_rows() for row in rows: data = self._hs_lst.get(row) - + print(row) + print(data) if self.sub_window_exists( BasicHydraulicStructuresWindow, data=[self._study, None, data] @@ -266,37 +267,43 @@ class HydraulicStructuresWindow(PamhyrWindow): def update(self): self._set_checkbox_state() + self._update_clear_plot() + def _update_clear_plot(self): rows = self.index_selected_rows() - if len(rows) > 0 and len(self._hs_lst) > 0: - reach = self._hs_lst.get(rows[0]).input_reach - else: - reach=None - self.plot_kpc.clear() - self.plot_ac.clear() + if len(rows) == 0 or len(self._hs_lst) == 0: + self._update_clear_all() return - profile_kp = self._hs_lst.get(rows[0]).input_kp - if profile_kp is None or profile_kp == "Not associated": - profile = None - self.plot_ac.clear() - self.plot_kpc.clear_profile() - else: - profile = reach.reach.get_profiles_from_kp(float(profile_kp)) - - if reach is not None and reach != "Not associated": + reach = self._hs_lst.get(rows[0]).input_reach + if reach is not None: self.plot_kpc.set_reach(reach) self.plot_ac.set_reach(reach) + + profile_kp = self._hs_lst.get(rows[0]).input_kp + if profile_kp is not None: + profiles = reach.reach\ + .get_profiles_from_kp( + float(profile_kp) + ) + + if profiles is not None: + profile = profiles[0] + + self.plot_kpc.set_profile(profile) + self.plot_ac.set_profile(profile) + else: + self._update_clear_profile() + else: + self._update_clear_profile() else: - self.plot_kpc.clear() - self.plot_ac.clear() - return + self._update_clear_all() + def _update_clear_all(self): + self.plot_kpc.clear() + self.plot_ac.clear() - if profile is not None: - self.plot_kpc.set_profile(profile[0]) - self.plot_ac.set_profile(profile[0]) - else: - self.plot_ac.clear() - self.plot_kpc.clear_profile() + def _update_clear_profile(self): + self.plot_ac.clear() + self.plot_kpc.clear_profile() diff --git a/src/View/LateralContribution/Edit/Window.py b/src/View/LateralContribution/Edit/Window.py index 745fd15e143e13320ab67ed85df45dfb7dfe1436..897703a469c147761234d2ff7d2003775d95d342 100644 --- a/src/View/LateralContribution/Edit/Window.py +++ b/src/View/LateralContribution/Edit/Window.py @@ -101,7 +101,7 @@ class EditLateralContributionWindow(PamhyrWindow): table_headers=headers, editable_headers=self._data.header, delegates={ - #"time": self._delegate_time, + # "time": self._delegate_time, }, data=self._data, undo=self._undo_stack, diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index a9d98062dbab2b1e1aa5091477bb00b6af6cf0c5..89df56f38c4f315ab16667a667182313ddfeedde 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -195,7 +195,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): "action_menu_edit_geometry": self.open_geometry, "action_menu_boundary_conditions": self.open_boundary_cond, "action_menu_edit_reservoirs": self.open_reservoir, - "action_menu_edit_hydraulic_structures": self.open_hydraulic_structures, + "action_menu_edit_hydraulic_structures": + self.open_hydraulic_structures, "action_menu_initial_conditions": self.open_initial_conditions, "action_menu_edit_friction": self.open_frictions, "action_menu_edit_lateral_contribution": self.open_lateral_contrib, @@ -282,8 +283,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Nothing """ self.update_enable_action() - # Maximise window - #self.showMaximized() + # self.showMaximized() def set_debug_lvl(self, debug=True): if debug: @@ -638,7 +638,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): ): return - hydraulic_structures = HydraulicStructuresWindow(study=self._study, parent=self) + hydraulic_structures = HydraulicStructuresWindow( + study=self._study, + parent=self + ) hydraulic_structures.show() def open_lateral_contrib(self): @@ -648,7 +651,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): ): return - lateral = LateralContributionWindow(study=self._study, parent=self) + lateral = LateralContributionWindow( + study=self._study, + parent=self + ) lateral.show() def open_stricklers(self): diff --git a/src/View/Reservoir/Edit/Plot.py b/src/View/Reservoir/Edit/Plot.py index 8f8f49b858228c34d3b143ec17bea797afa9f146..8c75a19971d85a4284fd4b234c4f76063721b636 100644 --- a/src/View/Reservoir/Edit/Plot.py +++ b/src/View/Reservoir/Edit/Plot.py @@ -69,7 +69,7 @@ class Plot(PamhyrPlot): ) # Plot label - #header = self.data.header + # header = self.data.header self.canvas.axes.set_xlabel( self._table_headers["z"], color='black', fontsize=10 ) diff --git a/src/View/Reservoir/Edit/Window.py b/src/View/Reservoir/Edit/Window.py index 34724bea63ab44e4e37aec5557fd02a32a2bd9f5..865ae6c89e1eb109bbf570a3afe9c937aa2c329e 100644 --- a/src/View/Reservoir/Edit/Window.py +++ b/src/View/Reservoir/Edit/Window.py @@ -88,18 +88,13 @@ class EditReservoirWindow(PamhyrWindow): def setup_table(self): headers = {} table_headers = self._trad.get_dict("table_headers") - #for h in self._data.header: - #headers[h] = table_headers[h] table = self.find(QTableView, "tableView") self._table = TableModel( table_view=table, table_headers=table_headers, editable_headers=table_headers, - #editable_headers=self._data.header, - delegates={ - #"time": self._delegate_time, - }, + delegates={}, data=self._data, undo=self._undo_stack, opt_data=self._study.time_system @@ -181,7 +176,7 @@ class EditReservoirWindow(PamhyrWindow): rows = self.index_selected_rows() table = [] - #table.append(self._data.header) + # table.append(self._data.header) table.append(self._trad.get_dict("table_headers")) data = self._data.data diff --git a/src/View/Results/PlotH.py b/src/View/Results/PlotH.py index 5709efb4fa903d8616100decea36f2f62acf9e7e..3494cfa5ea542896f22d539a24aa06f051649539 100644 --- a/src/View/Results/PlotH.py +++ b/src/View/Results/PlotH.py @@ -175,12 +175,12 @@ class PlotH(PamhyrPlot): def set_timestamp(self, timestamp): self._current_timestamp = timestamp - #self.update() + # self.update() def update(self): reach = self.results.river.reach(self._current_reach_id) profile = reach.profile(self._current_profile_id) x = self.ts y = profile.get_key("Q") - self._line.set_data(x,y) + self._line.set_data(x, y) self.canvas.figure.canvas.draw_idle() diff --git a/src/View/Results/PlotKPC.py b/src/View/Results/PlotKPC.py index 93f91c9f4282527c8fb360d7f0566ba465a62f89..34ed91f9b28c1a4730dcb0b379579aa08ecbb22d 100644 --- a/src/View/Results/PlotKPC.py +++ b/src/View/Results/PlotKPC.py @@ -105,8 +105,14 @@ class PlotKPC(PamhyrPlot): ) self.profile, = self.canvas.axes.plot( - [kp[self._current_profile_id], kp[self._current_profile_id]], - [z_max[self._current_profile_id],z_min[self._current_profile_id]], + [ + kp[self._current_profile_id], + kp[self._current_profile_id] + ], + [ + z_max[self._current_profile_id], + z_min[self._current_profile_id] + ], color='red', lw=1. ) @@ -137,7 +143,13 @@ class PlotKPC(PamhyrPlot): z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() self.profile.set_data( - [kp[self._current_profile_id], kp[self._current_profile_id]], - [z_max[self._current_profile_id],z_min[self._current_profile_id]] + [ + kp[self._current_profile_id], + kp[self._current_profile_id] + ], + [ + z_max[self._current_profile_id], + z_min[self._current_profile_id] + ] ) self.canvas.figure.canvas.draw_idle() diff --git a/src/View/Results/PlotXY.py b/src/View/Results/PlotXY.py index 559fd9a6c05b4797a2cd59ffc110166416edd309..3d0e22810c68749e59835a45a45c1b42f581a76d 100644 --- a/src/View/Results/PlotXY.py +++ b/src/View/Results/PlotXY.py @@ -136,10 +136,14 @@ class PlotXY(PamhyrPlot): poly_x = [0] poly_y = [0] - self.fill = self.canvas.axes.fill(poly_x, poly_y, color='skyblue', alpha=0.7) + self.fill = self.canvas.axes.fill( + poly_x, poly_y, + color='skyblue', + alpha=0.7 + ) - #self.canvas.axes.autoscale_view(True, True, True) - #self.canvas.axes.autoscale() + # self.canvas.axes.autoscale_view(True, True, True) + # self.canvas.axes.autoscale() self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() if self.toolbar is not None: @@ -166,7 +170,7 @@ class PlotXY(PamhyrPlot): # Current profile profile = reach.profile(self._current_profile_id).geometry - self.plot_selected.set_data(profile.x(),profile.y()) + self.plot_selected.set_data(profile.x(), profile.y()) self.plot_selected.set_visible(True) self.canvas.draw_idle() @@ -196,12 +200,12 @@ class PlotXY(PamhyrPlot): poly_r_x.append(ptY.x) poly_r_y.append(ptY.y) - #self.canvas.axes.plot( - #x, y, lw=1., - #color='b', - #markersize=1, - #marker='o' - #) + # self.canvas.axes.plot( + # x, y, lw=1., + # color='b', + # markersize=1, + # marker='o' + # ) poly_x = poly_l_x + list(reversed(poly_r_x)) poly_y = poly_l_y + list(reversed(poly_r_y)) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 9933f18989582b3ce73db51c0c27decface8d42a..68a38b34a6fc5c872a59b30c6a52986254a82a82 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -125,9 +125,13 @@ class ResultsWindow(PamhyrWindow): self._slider_time.setValue(len(self._timestamps) - 1) self._icon_start = QIcon() - self._icon_start.addPixmap(QPixmap('./src/View/ui/ressources/media-playback-start.png')) + self._icon_start.addPixmap( + QPixmap('./src/View/ui/ressources/media-playback-start.png') + ) self._icon_pause = QIcon() - self._icon_pause.addPixmap(QPixmap('./src/View/ui/ressources/media-playback-pause.png')) + self._icon_pause.addPixmap( + QPixmap('./src/View/ui/ressources/media-playback-pause.png') + ) self._button_play = self.find(QPushButton, f"playButton") self._button_play.setIcon(self._icon_start) self._button_back = self.find(QPushButton, f"backButton") diff --git a/src/tools.py b/src/tools.py index b6f78857a90de16178ce21d680047c6015e70b99..1ad8c382ec46c63e5881573f43eafe014bfbc30c 100644 --- a/src/tools.py +++ b/src/tools.py @@ -67,6 +67,7 @@ def logger_color_reset(): return f"{Style.RESET_ALL}" return "" + def logger_exception(exception): logger.error( f"[{Fore.RED}ERROR{Style.RESET_ALL}] " + diff --git a/tests.sh b/tests.sh index 06c8fd846e45c5ffa156fde542901a4387b12e23..ff235027a698f756112e4ffbdf28d8a90505a863 100755 --- a/tests.sh +++ b/tests.sh @@ -16,7 +16,7 @@ cd .. echo " PEP8" -pycodestyle ./src +pycodestyle --exclude="*_to_*.py" ./src if [ $? -eq 0 ] then