Manning: Add parameter to setup manning coef instead of strickler, and switch the value on the view only, but keeps strickler value in the model for the solvers

manning
Dylan Jeannin 2026-07-27 13:22:32 +02:00
parent ce04f5fd1e
commit fefc6ebc4f
12 changed files with 189 additions and 31 deletions

View File

@ -43,6 +43,7 @@ from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QComboBox, QUndoStack, QShortcut, QHeaderView,
QCheckBox,
)
from Modules import Modules
@ -119,11 +120,15 @@ class ConfigureWindow(PamhyrDialog):
editable_headers=["name", "comment", "minor", "medium"],
data=self._stricklers,
undo=self._undo_stack,
display_manning=self._config.display_manning,
)
def setup_data(self):
# Backup
self.set_check_box("checkBox_backup", self._config.backup_enable)
self.set_check_box(
"checkBox_display_manning", self._config.display_manning
)
# self.set_line_edit_text("lineEdit_backup_path",
# self._config.backup_path)
self.set_time_edit("timeEdit_backup_frequence",
@ -167,6 +172,10 @@ class ConfigureWindow(PamhyrDialog):
for button in buttons:
self.find(QPushButton, button).clicked.connect(buttons[button])
self.find(QCheckBox, "checkBox_display_manning").stateChanged.connect(
self.update_stricklers_display
)
def accept(self):
# Solvers
self._config.solvers = self.solver_table_model._data.copy()
@ -178,6 +187,9 @@ class ConfigureWindow(PamhyrDialog):
self._config.backup_frequence = self.get_time_edit(
"timeEdit_backup_frequence"
)
self._config.display_manning = self.get_check_box(
"checkBox_display_manning"
)
# self._config.backup_max = self.get_spin_box("spinBox_backup_max")
# Stricklers
@ -237,6 +249,12 @@ class ConfigureWindow(PamhyrDialog):
# Stricklers
def update_stricklers_display(self):
self._stricklers_table.display_manning = self.get_check_box(
"checkBox_display_manning"
)
self._stricklers_table.layoutChanged.emit()
def index_selected_rows_strikclers(self):
table = self.find(QTableView, f"tableView_stricklers")
return list(

View File

@ -20,6 +20,7 @@ import logging
from tools import timer, flatten
from View.Tools.PamhyrPlot import PamhyrPlot
from View.Tools.CoefficientDisplay import display_coefficient
from PyQt5.QtCore import (
QCoreApplication
@ -32,7 +33,7 @@ _translate = QCoreApplication.translate
class PlotStricklers(PamhyrPlot):
def __init__(self, data=None, canvas=None, trad=None,
toolbar=None, parent=None):
toolbar=None, display_manning=True, parent=None):
super(PlotStricklers, self).__init__(
canvas=canvas,
trad=trad,
@ -42,7 +43,11 @@ class PlotStricklers(PamhyrPlot):
)
self.label_x = self._trad["rk"]
self.label_y = self._trad["strickler_plot"]
self._display_manning = display_manning
self.label_y = (
self._trad["manning_plot"] if display_manning
else self._trad["strickler_plot"]
)
self.line_rk_elevation = [None, None]
@ -113,8 +118,12 @@ class PlotStricklers(PamhyrPlot):
)
)
coef_minor = list(map(lambda s: s.minor, coef))
coef_medium = list(map(lambda s: s.medium, coef))
coef_minor = [
display_coefficient(s.minor, self._display_manning) for s in coef
]
coef_medium = [
display_coefficient(s.medium, self._display_manning) for s in coef
]
self.line_rk_elevation[0] = self.canvas.axes.plot(
rk, coef_minor,

View File

@ -42,6 +42,7 @@ from View.Frictions.UndoCommand import (
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.Tools.CoefficientDisplay import display_strickler
from View.Frictions.translate import *
@ -52,26 +53,25 @@ _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, study=None,
mode="stricklers", trad=None, parent=None):
mode="stricklers", trad=None, display_manning=True,
parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._study = study
self._trad = trad
self._mode = mode
self._display_manning = display_manning
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
if self._mode == "stricklers":
self.editor.addItems(
[self._trad["not_defined"]] +
list(
map(
lambda s: str(s),
self._study.river.stricklers.stricklers
)
)
self.editor.addItem(self._trad["not_defined"], None)
for strickler in self._study.river.stricklers.stricklers:
self.editor.addItem(
display_strickler(strickler, self._display_manning),
strickler
)
elif self._mode == "rk":
self.editor.addItems(
@ -86,8 +86,9 @@ class ComboBoxDelegate(QItemDelegate):
self.editor.currentTextChanged.connect(self.currentItemChanged)
def setModelData(self, editor, model, index):
text = str(editor.currentText())
model.setData(index, text)
value = editor.currentData() if self._mode == "stricklers" else \
str(editor.currentText())
model.setData(index, value)
editor.close()
editor.deleteLater()
@ -104,6 +105,10 @@ class ComboBoxDelegate(QItemDelegate):
class FrictionTableModel(PamhyrTableModel):
def __init__(self, *args, display_manning=False, **kwargs):
self.display_manning = display_manning
super(FrictionTableModel, self).__init__(*args, **kwargs)
def _setup_lst(self):
self._lst = self._data.frictions
self._study = self._opt_data
@ -125,12 +130,12 @@ class FrictionTableModel(PamhyrTableModel):
value = self._lst.get(row).begin_strickler
if value is None:
return self._trad["not_defined"]
return str(value)
return display_strickler(value, self.display_manning)
elif self._headers[column] == "end_strickler":
value = self._lst.get(row).end_strickler
if value is None:
return self._trad["not_defined"]
return str(value)
return display_strickler(value, self.display_manning)
return QVariant()
@ -164,15 +169,19 @@ class FrictionTableModel(PamhyrTableModel):
)
)
elif self._headers[column] == "begin_strickler":
strickler = value if hasattr(value, "minor") else \
self._study.river.strickler(value)
self._undo.push(
SetBeginStricklerCommand(
self._lst, row, self._study.river.strickler(value)
self._lst, row, strickler
)
)
elif self._headers[column] == "end_strickler":
strickler = value if hasattr(value, "minor") else \
self._study.river.strickler(value)
self._undo.push(
SetEndStricklerCommand(
self._lst, row, self._study.river.strickler(value)
self._lst, row, strickler
)
)
except Exception as e:

View File

@ -67,6 +67,9 @@ class FrictionsWindow(PamhyrWindow):
def __init__(self, reach=None, study=None, config=None, parent=None):
trad = FrictionsTranslate()
if config is None and parent is not None:
config = getattr(parent, "conf", None)
if reach is not None:
self._reach = reach
else:
@ -103,6 +106,7 @@ class FrictionsWindow(PamhyrWindow):
study=self._study,
mode="stricklers",
trad=self._trad,
display_manning=getattr(self._config, "display_manning", False),
parent=self
)
self._delegate_rk = ComboBoxDelegate(
@ -122,9 +126,15 @@ class FrictionsWindow(PamhyrWindow):
editable_headers = []
table = self.find(QTableView, f"tableView")
table_headers = self._trad.get_dict("table_headers").copy()
table_headers["begin_strickler"] = (
self._trad["manning"]
if getattr(self._config, "display_manning", False)
else self._trad["strickler"]
)
self._table = FrictionTableModel(
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
table_headers=table_headers,
editable_headers=editable_headers,
delegates={
"begin_rk": self._delegate_rk,
@ -135,7 +145,8 @@ class FrictionsWindow(PamhyrWindow):
data=self._reach,
trad=self._trad,
undo=self._undo_stack,
opt_data=self._study
opt_data=self._study,
display_manning=getattr(self._config, "display_manning", False)
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
@ -167,7 +178,8 @@ class FrictionsWindow(PamhyrWindow):
canvas=self.canvas_2,
data=self._reach,
trad=self._trad,
toolbar=None
toolbar=None,
display_manning=getattr(self._config, "display_manning", False)
)
self.plot_2.draw()
@ -274,13 +286,13 @@ class FrictionsWindow(PamhyrWindow):
def edit_stricklers(self):
if self.sub_window_exists(
StricklersWindow,
data=[self._study, self.parent.conf]
data=[self._study, self._config]
):
return
strick = StricklersWindow(
study=self._study,
config=self.parent.conf,
config=self._config,
parent=self
)
strick.show()

View File

@ -31,9 +31,18 @@ class FrictionsTranslate(MainTranslate):
self._dict["strickler_plot"] = _translate(
"Frictions", "Strickler ($m^{1/3}/s$)"
)
self._dict["manning_plot"] = _translate(
"Frictions", "Manning ($s/m^{1/3}$)"
)
self._dict["stricklers"] = _translate(
"Frictions", "Stricklers"
)
self._dict["strickler"] = _translate(
"Frictions", "Strickler"
)
self._dict["manning"] = _translate(
"Frictions", "Manning"
)
self._dict["Edit frictions"] = _translate(
"Frictions", "Edit frictions"

View File

@ -1427,12 +1427,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if self.sub_window_exists(
FrictionsWindow,
data=[self._study, None, reach]
data=[self._study, self.conf, reach]
):
return
frictions = FrictionsWindow(
study=self._study,
config=self.conf,
parent=self
)
frictions.show()

View File

@ -41,6 +41,9 @@ from View.Stricklers.UndoCommand import (
SetMinorCommand, SetMediumCommand,
AddCommand, DelCommand, SortCommand,
)
from View.Tools.CoefficientDisplay import (
display_coefficient, stored_coefficient
)
logger = logging.getLogger()
@ -48,6 +51,10 @@ _translate = QCoreApplication.translate
class TableModel(PamhyrTableModel):
def __init__(self, *args, display_manning=False, **kwargs):
self.display_manning = display_manning
super(TableModel, self).__init__(*args, **kwargs)
def get_true_data_row(self, row):
el = self._data.get(row)
@ -73,9 +80,13 @@ class TableModel(PamhyrTableModel):
elif self._headers[column] == "comment":
return self._data.get(row).comment
elif self._headers[column] == "minor":
return self._data.get(row).minor
return display_coefficient(
self._data.get(row).minor, self.display_manning
)
elif self._headers[column] == "medium":
return self._data.get(row).medium
return display_coefficient(
self._data.get(row).medium, self.display_manning
)
return QVariant()
@ -106,13 +117,15 @@ class TableModel(PamhyrTableModel):
elif self._headers[column] == "minor":
self._undo.push(
SetMinorCommand(
self._data, row, value
self._data, row,
stored_coefficient(value, self.display_manning)
)
)
elif self._headers[column] == "medium":
self._undo.push(
SetMediumCommand(
self._data, row, value
self._data, row,
stored_coefficient(value, self.display_manning)
)
)
except Exception as e:

View File

@ -37,6 +37,7 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QGroupBox,
)
from Model.Stricklers.Stricklers import Stricklers
@ -56,7 +57,11 @@ class StricklersWindow(PamhyrWindow):
def __init__(self, study=None, config=None, parent=None):
trad = StricklersTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
display_manning = getattr(config, "display_manning", False)
coefficient_name = (
"Mannings" if display_manning else self._pamhyr_name
)
name = trad[coefficient_name] + " - " + study.name
super(StricklersWindow, self).__init__(
title=name,
@ -66,9 +71,23 @@ class StricklersWindow(PamhyrWindow):
parent=parent
)
self.setup_labels()
self.setup_table()
self.setup_connections()
def setup_labels(self):
display_manning = getattr(self._config, "display_manning", False)
study_label = (
self._trad["study_mannings"] if display_manning
else self._trad["study_stricklers"]
)
application_label = (
self._trad["application_mannings"] if display_manning
else self._trad["application_stricklers"]
)
self.find(QGroupBox, "groupBox_2").setTitle(study_label)
self.find(QGroupBox, "groupBox").setTitle(application_label)
def setup_table(self):
self._table = {}
@ -90,6 +109,9 @@ class StricklersWindow(PamhyrWindow):
editable_headers=editable_headers,
data=data,
undo=self._undo_stack,
display_manning=getattr(
self._config, "display_manning", False
),
)
table.setModel(self._table[t])

View File

@ -30,6 +30,21 @@ class StricklersTranslate(MainTranslate):
self._dict["Stricklers"] = _translate(
"Stricklers", "Strickler coefficients"
)
self._dict["Mannings"] = _translate(
"Stricklers", "Manning coefficients"
)
self._dict["study_stricklers"] = _translate(
"Stricklers", "Strickler coefficients of the study"
)
self._dict["study_mannings"] = _translate(
"Stricklers", "Manning coefficients of the study"
)
self._dict["application_stricklers"] = _translate(
"Stricklers", "Strickler coefficients of the application"
)
self._dict["application_mannings"] = _translate(
"Stricklers", "Manning coefficients of the application"
)
self._sub_dict["table_headers"] = {
"name": self._dict["name"],

View File

@ -0,0 +1,30 @@
# CoefficientDisplay.py -- Pamhyr
# Copyright (C) 2023-2026 INRAE
"""View-only conversion helpers for friction coefficients.
Strickler coefficients remain the canonical representation in models,
study files and solver exports.
"""
def display_coefficient(strickler, display_manning=False):
value = float(strickler)
if not display_manning:
return value
return float("inf") if value == 0.0 else 1.0 / value
def stored_coefficient(displayed, display_manning=False):
value = float(displayed)
if not display_manning:
return value
if value == 0.0:
raise ValueError("A Manning coefficient cannot be zero")
return 1.0 / value
def display_strickler(strickler, display_manning=False):
minor = display_coefficient(strickler.minor, display_manning)
medium = display_coefficient(strickler.medium, display_manning)
return f"{strickler.name} ({minor:g}, {medium:g})"

View File

@ -242,6 +242,13 @@
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_display_manning">
<property name="text">
<string>Display Manning coefficients</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="pushButton_stricklers_add">

View File

@ -37,7 +37,7 @@ logger = logging.getLogger()
class Config(SQL):
def __init__(self):
self._version = '0.0.8'
self._version = '0.0.9'
self.filename = Config.filename()
self.set_default_value()
@ -215,6 +215,12 @@ class Config(SQL):
)
""")
if int(release) < 9:
self.execute(
"INSERT OR IGNORE INTO data "
"VALUES ('display_manning', 'False')"
)
self.execute(
f"UPDATE info SET value='{self._version}' " +
"WHERE key='version'"
@ -275,6 +281,11 @@ class Config(SQL):
# self.backup_path = v[0]
v = self.execute("SELECT value FROM data WHERE key='backup_frequence'")
self.backup_frequence = v[0]
# Friction coefficient display
v = self.execute(
"SELECT value FROM data WHERE key='display_manning'"
)
self.display_manning = v[0] == "True"
# v = self.execute("SELECT value FROM data WHERE key='backup_max'")
# self.backup_max = int(v[0])
@ -342,6 +353,7 @@ class Config(SQL):
"backup_enable": self.backup_enable,
# "backup_path": self.backup_path,
"backup_frequence": self.backup_frequence,
"display_manning": self.display_manning,
# "backup_max": self.backup_max,
"editor": self.editor,
"lang": self.lang,
@ -424,6 +436,7 @@ class Config(SQL):
self.backup_enable = True
# self.backup_path = ""
self.backup_frequence = "00:15:00"
self.display_manning = False
# self.backup_max = 10
# Editor