OutputRK: delete all references to this useless feature

dev_dylan
Dylan Jeannin 2026-07-22 12:54:50 +02:00
parent ae39f33131
commit d027946fb2
15 changed files with 2 additions and 1194 deletions

View File

@ -1,52 +0,0 @@
# Adists.py -- Pamhyr study checkers
# Copyright (C) 2023-2026 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 -*-
import time
import logging
from functools import reduce
from PyQt5.QtCore import QCoreApplication
from Modules import Modules
from Checker.Checker import AbstractModelChecker, STATUS
_translate = QCoreApplication.translate
logger = logging.getLogger()
class AdistsOutputRKChecker(AbstractModelChecker):
def __init__(self):
super(AdistsOutputRKChecker, self).__init__()
self._name = _translate("Checker", "AdisTS output RK checker")
self._description = _translate(
"Checker", "Check output RK"
)
self._modules = Modules.OUTPUT_RK
def run(self, study):
ok = True
nerror = 0
self._summary = "ok"
self._status = STATUS.OK
if not self.basic_check(study):
return False
return ok

View File

@ -1,208 +0,0 @@
# OutputRKAdists.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
import logging
from tools import (
trace, timer,
old_pamhyr_date_to_timestamp,
date_iso_to_timestamp,
date_dmy_to_timestamp,
)
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.Scenario import Scenario
logger = logging.getLogger()
class OutputRKAdists(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1, reach=None,
rk=None, title: str = "", status=None,
owner_scenario=-1):
super(OutputRKAdists, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
self._reach = reach
self._rk = rk
self._title = str(title)
self._enabled = True
@property
def reach(self):
return self._reach
@reach.setter
def reach(self, reach_id):
self._reach = reach_id
self._status.modified()
@property
def rk(self):
return self._rk
@rk.setter
def rk(self, profile_id):
self._rk = profile_id
self._status.modified()
@property
def title(self):
return self._title
@title.setter
def title(self, title):
self._title = title
self._status.modified()
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE output_rk_adists{ext}(
{cls.create_db_add_pamhyr_id()},
deleted BOOLEAN NOT NULL DEFAULT FALSE,
reach INTEGER NOT NULL,
rk REAL NOT NULL,
title TEXT NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(reach) REFERENCES river_reach(id)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "OutputRKAdists"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@classmethod
def _db_update_to_0_2_0(cls, execute, data):
table = "OutputRKAdists"
table_new = "output_rk_adists"
reachs = data['id2pid']['river_reach']
cls.update_db_add_pamhyr_id(execute, table, data)
Scenario.update_db_add_scenario(execute, table)
cls._db_create(execute, ext="_tmp")
execute(
f"INSERT INTO {table_new}_tmp " +
"(pamhyr_id, reach, rk, title, scenario) " +
"SELECT pamhyr_id, reach, rk, title, scenario " +
f"FROM {table}"
)
execute(f"DROP TABLE {table}")
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
@classmethod
def _db_load(cls, execute, data=None):
new = []
status = data['status']
scenario = data["scenario"]
loaded = data['loaded_pid']
# reach = data["reach"]
# profile = data["profile"]
if scenario is None:
return new
table = execute(
"SELECT pamhyr_id, deleted, reach, rk, title, scenario " +
f"FROM output_rk_adists"
)
if table is not None:
for row in table:
it = iter(row)
pid = next(it)
deleted = (next(it) == 1)
id_reach = next(it)
id_rk = next(it)
title = next(it)
owner_scenario = next(it)
new_output = cls(
id=pid, reach=id_reach,
rk=id_rk, title=title,
status=status,
owner_scenario=owner_scenario,
)
if deleted:
new_output.set_as_deleted()
loaded.add(pid)
new.append(new_output)
data["scenario"] = scenario.parent
new += cls._db_load(execute, data)
data["scenario"] = scenario
return new
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"DELETE FROM output_rk_adists " +
f"WHERE pamhyr_id = {self.id} " +
f"AND scenario = {self._status.scenario_id} "
)
execute(
"INSERT INTO " +
"output_rk_adists(pamhyr_id, deleted, " +
"reach, rk, title, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._reach}, {self._rk}, " +
f"'{self._db_format(self._title)}', " +
f"{self._status.scenario_id}" +
")"
)
return True
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self._status.modified()

View File

@ -1,59 +0,0 @@
# OutputRKListAdists.py -- Pamhyr
# Copyright (C) 2024-2025 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 Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
class OutputRKAdistsList(PamhyrModelList):
_sub_classes = [OutputRKAdists]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = OutputRKAdists._db_load(execute, data)
return new
def _db_save(self, execute, data=None):
ok = True
# Delete previous data
execute(
"DELETE FROM output_rk_adists " +
f"WHERE scenario = {self._status.scenario_id}"
)
for sl in self._lst:
ok &= sl._db_save(execute, data)
return ok
@property
def OutputRK_List(self):
return self.lst
def new(self, lst, index):
n = OutputRKAdists(status=self._status)
self._lst.insert(index, n)
self._status.modified()
return n

View File

@ -49,7 +49,6 @@ from Model.REPLine.REPLineList import REPLineList
from Solver.Solvers import solver_type_list
from Model.OutputRKAdists.OutputRKListAdists import OutputRKAdistsList
from Model.Pollutants.PollutantsList import PollutantsList
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSList \
import InitialConditionsAdisTSList
@ -478,7 +477,6 @@ class River(Graph):
HydraulicStructureList,
AddFileList,
REPLineList,
OutputRKAdistsList,
PollutantsList,
InitialConditionsAdisTSList,
BoundaryConditionsAdisTSList,
@ -515,7 +513,6 @@ class River(Graph):
)
self._additional_files = AddFileList(status=self._status)
self._rep_lines = REPLineList(status=self._status)
self._Output_rk_adists = OutputRKAdistsList(status=self._status)
self._Pollutants = PollutantsList(status=self._status)
self._InitialConditionsAdisTS = InitialConditionsAdisTSList(
status=self._status)
@ -630,10 +627,6 @@ class River(Graph):
new._Pollutants = PollutantsList._db_load(execute, data)
new._Output_rk_adists = OutputRKAdistsList._db_load(
execute, data
)
new._InitialConditionsAdisTS = InitialConditionsAdisTSList._db_load(
execute, data)
@ -682,7 +675,6 @@ class River(Graph):
for solver in self._parameters:
objs.append(self._parameters[solver])
objs.append(self._Output_rk_adists)
objs.append(self._Pollutants)
objs.append(self._InitialConditionsAdisTS)
objs.append(self._BoundaryConditionsAdisTS)
@ -766,7 +758,6 @@ class River(Graph):
self._hydraulic_structures,
self._additional_files,
self._rep_lines,
self._Output_rk_adists,
self._Pollutants,
self._InitialConditionsAdisTS,
self._BoundaryConditionsAdisTS,
@ -880,10 +871,6 @@ Last export at: @date."""
def parameters(self):
return self._parameters
@property
def Output_rk_adists(self):
return self._Output_rk_adists
@property
def Pollutants(self):
return self._Pollutants

View File

@ -32,7 +32,6 @@ class Scenario(SQLSubModel):
tables_with_deleted_column = [
# Adists
"output_rk_adists",
"boundary_condition_adists", "boundary_condition_data_adists",
"lateral_contribution_adists", "lateral_contribution_data_adists",
"initial_conditions_adists", "initial_conditions_adists_spec",

View File

@ -55,7 +55,6 @@ class Modules(IterableFlag):
RESERVOIR = auto()
SEDIMENT_LAYER = auto()
ADDITIONAL_FILES = auto()
OUTPUT_RK = auto()
GEOTIFF = auto()
# Results
@ -81,7 +80,6 @@ class Modules(IterableFlag):
cls.ADDITIONAL_FILES,
cls.RESULTS,
cls.WINDOW_LIST,
cls.OUTPUT_RK,
cls.GEOTIFF
]

View File

@ -35,10 +35,6 @@ from Solver.CommandLine import CommandLineSolver
from Model.Results.ResultsAdisTS import Results
from Model.Results.River.River import River, Reach, Profile
from Checker.Adists import (
AdistsOutputRKChecker,
)
from itertools import chain
logger = logging.getLogger()
@ -90,11 +86,7 @@ class AdisTS(CommandLineSolver):
@classmethod
def checkers(cls):
lst = [
AdistsOutputRKChecker(),
]
return lst
return []
##########
# Export #
@ -821,33 +813,8 @@ class AdisTSwc(AdisTS):
if name != "command_line_arguments":
f.write(f"{dict_names[name]} = {value}\n")
outputrks = study.river.Output_rk_adists.OutputRK_List
for outputrk in outputrks:
self._export_outputrk(study, outputrk, f, qlog)
return files
def _export_outputrk(self, study, outputrk, f, qlog, name="0"):
if (outputrk.reach is None
or outputrk.rk is None
or outputrk.title is None):
return
edges = study.river.enable_edges()
id_edges = list(map(lambda x: x.id, edges))
id_reach = outputrk.reach
reach = next((x for x in edges if x.id == id_reach), None)
rk = outputrk.rk
title = outputrk.title
if reach is None:
return
f.write(f"output = {study.river.get_edge_id(reach)+1} {rk} {title}\n")
@timer
def read_bin(self, study, repertory, results, qlog=None, name="0"):

View File

@ -107,7 +107,6 @@ from View.Results.WindowAdisTT import ResultsWindowAdisTT
from View.Results.ReadingResultsDialog import ReadingResultsDialog
from View.Debug.Window import ReplWindow, TimerWindow
from View.OutputRKAdisTS.Window import OutputRKAdisTSWindow
from View.Pollutants.Window import PollutantsWindow
from View.D90AdisTS.Window import D90AdisTSWindow
from View.DIFAdisTS.Window import DIFAdisTSWindow
@ -173,7 +172,7 @@ define_model_action = [
"action_menu_results_last", "action_menu_open_results_from_file",
"action_menu_compare_scenarios_results",
"action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_rep_additional_lines",
"action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif", "action_menu_edit_geotiff",
"action_menu_boundary_conditions_temperature",
@ -301,7 +300,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.select_run_solver_adists,
"action_menu_run_adistt":
self.select_run_solver_adistt,
"action_menu_output_rk": self.open_output_rk_adists,
"action_menu_config": self.open_configure,
"action_menu_new": self.open_new_study,
"action_menu_edit": self.open_edit_study,
@ -1166,19 +1164,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
Pollutants.show()
def open_output_rk_adists(self):
if self.sub_window_exists(
OutputRKAdisTSWindow,
data=[self._study, None]
):
return
Output_RK_AdisTS = OutputRKAdisTSWindow(
study=self._study,
parent=self
)
Output_RK_AdisTS.show()
def open_configure(self):
"""Open configure window

View File

@ -1,229 +0,0 @@
# Table.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
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.OutputRKAdisTS.UndoCommand import (
SetTitleCommand, SetReachCommand, SetRKCommand,
SetEnabledCommand, AddCommand, DelCommand,
)
from functools import reduce
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._trad = trad
self._mode = mode
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
val = []
if self._mode == "rk":
reach_id = self._data.Output_rk_adists \
.get(index.row()) \
.reach
if reach_id is not None:
reach = next(
filter(
lambda edge: edge.id == reach_id, self._data.edges()
)
)
val = list(
map(
lambda rk: str(rk), reach.reach.get_rk()
)
)
else:
val = list(
map(
lambda n: n.name, self._data.edges()
)
)
self.editor.addItems(
[self._trad['not_associated']] +
val
)
self.editor.setCurrentText(str(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._Output_rk_adists
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] == "title":
return self._lst.get(row).title
elif self._headers[column] == "reach":
n = self._lst.get(row).reach
if n is None:
return self._trad['not_associated']
return next(filter(
lambda edge: edge.id == n, self._data.edges())
).name
elif self._headers[column] == "rk":
n = self._lst.get(row).rk
if n is None:
return self._trad['not_associated']
return n
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
na = self._trad['not_associated']
try:
if self._headers[column] == "title":
self._undo.push(
SetTitleCommand(
self._lst, row, value
)
)
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] == "rk":
if value == na:
value = None
self._undo.push(
SetRKCommand(
self._lst, row, value
)
)
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._lst, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._lst, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def enabled(self, row, enabled, parent=QModelIndex()):
self._undo.push(
SetEnabledCommand(
self._lst, row, enabled
)
)
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()

View File

@ -1,40 +0,0 @@
# translate.py -- Pamhyr
# Copyright (C) 2023-2026 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 PyQt5.QtCore import QCoreApplication
from View.Translate import MainTranslate
_translate = QCoreApplication.translate
class OutputRKAdisTSTranslate(MainTranslate):
def __init__(self):
super(OutputRKAdisTSTranslate, self).__init__()
self._dict["Output RK"] = _translate(
"OutputRKAdisTS", "Output RK"
)
self._dict["x"] = _translate("OutputRKAdisTS", "X (m)")
self._sub_dict["table_headers"] = {
"title": self._dict["title"],
"reach": self._dict["reach"],
"rk": self._dict["unit_rk"],
}

View File

@ -1,155 +0,0 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
import logging
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
logger = logging.getLogger()
class SetTitleCommand(QUndoCommand):
def __init__(self, outputrk_lst, index, new_value):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._index = index
self._old = self._outputrk_lst.get(self._index).title
self._new = str(new_value)
def undo(self):
self._outputrk_lst.get(self._index).title = self._old
def redo(self):
self._outputrk_lst.get(self._index).title = self._new
class SetReachCommand(QUndoCommand):
def __init__(self, outputrk_lst, index, reach):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._index = index
self._old = self._outputrk_lst.get(self._index).reach
self._new = reach.id
def undo(self):
i = self._outputrk_lst.get(self._index)
i.reach = self._old
def redo(self):
i = self._outputrk_lst.get(self._index)
i.reach = self._new
class SetRKCommand(QUndoCommand):
def __init__(self, outputrk_lst, index, rk):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._index = index
self._old = self._outputrk_lst.get(self._index).rk
self._new = rk
def undo(self):
self._outputrk_lst.get(self._index).rk = self._old
def redo(self):
self._outputrk_lst.get(self._index).rk = self._new
class SetEnabledCommand(QUndoCommand):
def __init__(self, outputrk_lst, index, enabled):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._index = index
self._old = not enabled
self._new = enabled
def undo(self):
self._outputrk_lst.get(self._index).enabled = self._old
def redo(self):
self._outputrk_lst.get(self._index).enabled = self._new
class AddCommand(QUndoCommand):
def __init__(self, outputrk_lst, index):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._index = index
self._new = None
def undo(self):
self._outputrk_lst.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._outputrk_lst.new(self._outputrk_lst, self._index)
else:
self._outputrk_lst.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, outputrk_lst, rows):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._rows = rows
self._outputrk = []
for row in rows:
self._outputrk.append((row, self._outputrk_lst.get(row)))
self._outputrk.sort()
def undo(self):
for row, el in self._outputrk:
self._outputrk_lst.insert(row, el)
def redo(self):
self._outputrk_lst.delete_i(self._rows)
class PasteCommand(QUndoCommand):
def __init__(self, outputrk_lst, row, outputrk):
QUndoCommand.__init__(self)
self._outputrk_lst = outputrk_lst
self._row = row
self._outputrk = deepcopy(outputrk)
self._outputrk.reverse()
def undo(self):
self._outputrk_lst.delete_i(
self._tab,
range(self._row, self._row + len(self._outputrk))
)
def redo(self):
for r in self._outputrk:
self._outputrk_lst.insert(self._row, r)

View File

@ -1,209 +0,0 @@
# Window.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
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.OutputRKAdisTS.Table import (
TableModel, ComboBoxDelegate,
)
from View.Network.GraphWidget import GraphWidget
from View.OutputRKAdisTS.Translate import OutputRKAdisTSTranslate
logger = logging.getLogger()
class OutputRKAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "OutputRKAdisTS"
_pamhyr_name = "Output RK"
def __init__(self, study=None, config=None, parent=None):
trad = OutputRKAdisTSTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
super(OutputRKAdisTSWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
parent=parent
)
self._outputrk_lst = self._study.river._Output_rk_adists
self.setup_table()
self.setup_checkbox()
self.setup_connections()
self.update()
def setup_table(self):
self._table = None
self._delegate_reach = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
parent=self,
mode="reaches"
)
self._delegate_rk = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
parent=self,
mode="rk"
)
table = self.find(QTableView, f"tableView")
self._table = TableModel(
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=["title", "reach", "rk"],
delegates={
"reach": self._delegate_reach,
"rk": self._delegate_rk,
},
trad=self._trad,
data=self._study.river,
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_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
self._checkbox.clicked.connect(self._set_outputrk_state)
table = self.find(QTableView, "tableView")
table.selectionModel()\
.selectionChanged\
.connect(self.update)
self._table.dataChanged.connect(self.update)
self._table.layoutChanged.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._outputrk_lst) == 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._outputrk_lst.get(row).enabled)
def _set_outputrk_state(self):
rows = self.index_selected_rows()
if len(rows) != 0:
for row in rows:
if row is not None:
self._table.enabled(
row,
self._checkbox.isChecked()
)
def update(self):
self._set_checkbox_state()

View File

@ -225,7 +225,6 @@
<property name="title">
<string>AdisTS</string>
</property>
<addaction name="action_menu_output_rk"/>
<addaction name="action_menu_pollutants"/>
<addaction name="action_menu_d90"/>
<addaction name="action_menu_dif"/>
@ -766,11 +765,6 @@
<string>Edit scenarios tree</string>
</property>
</action>
<action name="action_menu_output_rk">
<property name="text">
<string>Output RK</string>
</property>
</action>
<action name="action_menu_run_adists">
<property name="icon">
<iconset>

View File

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string>Hydraulic structures</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget_3">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTableView" name="tableView">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Enable / Disable Output RK AdisTS</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_add"/>
<addaction name="action_delete"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/add.png</normaloff>ressources/add.png</iconset>
</property>
<property name="text">
<string>Add</string>
</property>
<property name="toolTip">
<string>Add a new point</string>
</property>
</action>
<action name="action_delete">
<property name="icon">
<iconset>
<normaloff>ressources/del.png</normaloff>ressources/del.png</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete points</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -406,16 +406,6 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.</translation>
<source>Check if exists at least one reach exists</source>
<translation>Vérificateur si il exists au moins un bief</translation>
</message>
<message>
<location filename="../Checker/Adists.py" line="37"/>
<source>AdisTS output RK checker</source>
<translation>Vérifie les PK de sortie AdisTS</translation>
</message>
<message>
<location filename="../Checker/Adists.py" line="38"/>
<source>Check output RK</source>
<translation>Vérifie les PK de sortie</translation>
</message>
</context>
<context>
<name>Checklist</name>
@ -2167,7 +2157,6 @@ Some profiles don&apos;t contain any point.</source>
<translation>Éditer les casiers</translation>
</message>
<message>
<location filename="../View/ui/OutputRKAdisTS.ui" line="14"/>
<source>Hydraulic structures</source>
<translation>Ouvrages hydrauliques</translation>
</message>
@ -2386,16 +2375,6 @@ Some profiles don&apos;t contain any point.</source>
<source>Enable / Disable basic hydraulic structure</source>
<translation>Activer/Désactiver l'ouvrage hydraulique élémentaire</translation>
</message>
<message>
<location filename="../View/ui/OutputRKAdisTS.ui" line="108"/>
<source>Add a new point</source>
<translation>Ajouter un nouveau point</translation>
</message>
<message>
<location filename="../View/ui/OutputRKAdisTS.ui" line="120"/>
<source>Delete points</source>
<translation>Supprimer les points</translation>
</message>
<message>
<location filename="../View/ui/HydraulicStructures.ui" line="133"/>
<source>Edit selected hydraulic structure</source>
@ -2906,11 +2885,6 @@ Some profiles don&apos;t contain any point.</source>
<source>AdisTS</source>
<translation>AdisTS</translation>
</message>
<message>
<location filename="../View/ui/MainWindow.ui" line="769"/>
<source>Output RK</source>
<translation>Pk de sortie</translation>
</message>
<message>
<location filename="../View/ui/MainWindow.ui" line="778"/>
<source>Run AdisTS</source>
@ -2936,11 +2910,6 @@ Some profiles don&apos;t contain any point.</source>
<source>Open results AdisTS</source>
<translation>Ouvrir des résultats AdisTS</translation>
</message>
<message>
<location filename="../View/ui/OutputRKAdisTS.ui" line="49"/>
<source>Enable / Disable Output RK AdisTS</source>
<translation>Activer / Désactiver la sortie AdisTS</translation>
</message>
<message>
<location filename="../View/ui/EditBoundaryConditionsAdisTS.ui" line="89"/>
<source>Add a new point in boundary condition or punctual contribution</source>
@ -3388,19 +3357,6 @@ Some profiles don&apos;t contain any point.</source>
<translation>Sélectionner le profile</translation>
</message>
</context>
<context>
<name>OutputRKAdisTS</name>
<message>
<location filename="../View/OutputRKAdisTS/Translate.py" line="30"/>
<source>Output RK</source>
<translation>Pk de sortie</translation>
</message>
<message>
<location filename="../View/OutputRKAdisTS/Translate.py" line="34"/>
<source>X (m)</source>
<translation>X (m)</translation>
</message>
</context>
<context>
<name>Pamhyr</name>
<message>