Merge branch 'split_reach' into dev_dylan

mesh_tab
Dylan Jeannin 2026-08-11 11:17:16 +02:00
commit cdbcb2b539
40 changed files with 905 additions and 93 deletions

View File

@ -277,3 +277,10 @@ class BasicHS(SQLSubModel):
def convert(self, new_type):
return new_type(id=self.id, name=self.name, status=self._status)
def cloned(self):
new = type(self)(name=self._name, status=self._status)
new._enabled = self._enabled
new._data = [value.cloned() for value in self._data]
new.modified()
return new

View File

@ -208,3 +208,11 @@ class BHSValue(SQLSubModel):
def value(self, value):
self._value = self._type(value)
self.modified()
def cloned(self):
return BHSValue(
name=self._name,
type=self._type,
value=self._value,
status=self._status
)

View File

@ -427,6 +427,18 @@ class HydraulicStructure(SQLSubModel):
def basic_structures(self):
return self.lst.copy()
def cloned_for(self, input_reach, input_section,
output_reach, output_section):
new = HydraulicStructure(name=self._name, status=self._status)
new._enabled = self._enabled
new._input_reach = input_reach
new._input_section = input_section
new._output_reach = output_reach
new._output_section = output_section
new._data = [structure.cloned() for structure in self.lst]
new.modified()
return new
def basic_structure(self, index: int):
if len(self._data) == 0:
return None

View File

@ -61,6 +61,71 @@ class HydraulicStructureList(PamhyrModelList):
self.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
profiles = reach.reach.profiles
split_index = profiles.index(profile)
sections1 = dict(zip(
profiles[:split_index + 1],
reach1.reach.profiles
))
sections2 = dict(zip(
profiles[split_index:],
reach2.reach.profiles
))
def split_endpoint(endpoint_reach, section):
if endpoint_reach is not reach:
return endpoint_reach, section
if section not in profiles:
return endpoint_reach, section
index = profiles.index(section)
if index < split_index:
return reach1, sections1[section]
return reach2, sections2[section]
structures = [
structure
for structure in self.lst
if (structure.input_reach is reach or
structure.output_reach is reach)
]
for structure in structures:
input_reach, input_section = split_endpoint(
structure.input_reach,
structure.input_section
)
output_reach, output_section = split_endpoint(
structure.output_reach,
structure.output_section
)
self._lst.append(structure.cloned_for(
input_reach,
input_section,
output_reach,
output_section
))
if len(structures) != 0:
self.modified()
@property
def enabled_reaches_list(self):
return [
structure
for structure in self.lst
if (structure.input_reach is None or
(not structure.input_reach.is_deleted() and
structure.input_reach.is_enable()))
]
def get_for_enabled_reaches(self, index):
structures = self.enabled_reaches_list
if not 0 <= index < len(structures):
return None
return structures[index]
def __copy__(self):
new = HydraulicStructureList()

View File

@ -256,6 +256,17 @@ class Data(SQLSubModel):
)
return new
def cloned_for(self, reach, section):
return Data(
name=self._name,
comment=self._comment,
section=section,
discharge=self._discharge,
height=self._height,
reach=reach,
status=self._status,
)
@property
def name(self):
return self._name
@ -505,6 +516,20 @@ class InitialConditions(SQLSubModel):
for data in self._data:
data.set_as_deleted()
def _splited_for(self, reach, sections):
new = InitialConditions(reach=reach, status=self._status)
new._data = [
data.cloned_for(reach, sections[data["section"]])
for data in self.data
if data["section"] in sections
]
if len(new._data) != 0:
new.modified()
return new
def generate_growing_constant_depth(self, height: float,
compute_discharge: bool):
profiles = self._reach.reach.profiles.copy()

View File

@ -88,3 +88,28 @@ class InitialConditionsDict(PamhyrModelDict):
new = InitialConditions(reach=reach, status=self._status)
self.set(reach, new)
return new
def split_reach(self, reach, profile, reach1, reach2):
if reach not in self._dict:
return
profiles = reach.reach.profiles
split_index = profiles.index(profile)
sections1 = dict(zip(
profiles[:split_index + 1],
reach1.reach.profiles
))
sections2 = dict(zip(
profiles[split_index:],
reach2.reach.profiles
))
self.set(
reach1,
self._dict[reach]._splited_for(reach1, sections1)
)
self.set(
reach2,
self._dict[reach]._splited_for(reach2, sections2)
)

View File

@ -359,3 +359,60 @@ class InitialConditionsAdisTS(SQLSubModel):
x.set_as_not_deleted()
self.modified()
def split_reach(self, reach, profile, reach1, reach2):
split_rk = profile.rk
reach_rks = reach.reach.get_rk()
if len(reach_rks) == 0:
return
lower_reach_rk = min(reach_rks)
upper_reach_rk = max(reach_rks)
parts = [
(reach1.id, lower_reach_rk, split_rk),
(reach2.id, split_rk, upper_reach_rk),
]
specifications = [
specification
for specification in self._data
if (not specification.is_deleted() and
specification.reach == reach.id)
]
for specification in specifications:
begin = specification.start_rk
end = specification.end_rk
if begin is None or end is None:
continue
lower, upper = sorted((begin, end))
reverse = begin > end
for new_reach, part_lower, part_upper in parts:
clipped_lower = max(lower, part_lower)
clipped_upper = min(upper, part_upper)
if clipped_lower > clipped_upper:
continue
start_rk = clipped_upper if reverse else clipped_lower
end_rk = clipped_lower if reverse else clipped_upper
self._data.append(specification.cloned_for(
new_reach, start_rk, end_rk
))
if len(specifications) != 0:
self.modified()
def get_specs_for_enabled_reaches(self, reaches):
enabled_reaches = {
reach.id
for reach in reaches
if not reach.is_deleted() and reach.is_enable()
}
return [
specification
for specification in self._data
if (not specification.is_deleted() and
(specification.reach in (None, -1) or
specification.reach in enabled_reaches))
]

View File

@ -62,6 +62,12 @@ class InitialConditionsAdisTSList(PamhyrModelList):
self._status.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
for initial_condition in self.lst:
initial_condition.split_reach(
reach, profile, reach1, reach2
)
@property
def Initial_Conditions_List(self):
return self.lst

View File

@ -31,10 +31,12 @@ class ICAdisTSSpec(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1, name: str = "",
status=None, owner_scenario=None):
super(ICAdisTSSpec, self).__init__()
self._status = status
status=None, owner_scenario=-1):
super(ICAdisTSSpec, self).__init__(
id=id,
status=status,
owner_scenario=owner_scenario
)
self._name_section = name
self._reach = None
@ -234,7 +236,7 @@ class ICAdisTSSpec(SQLSubModel):
@name.setter
def name(self, name):
self._name_section = name
self._status.modified()
self.modified()
@property
def reach(self):
@ -243,7 +245,7 @@ class ICAdisTSSpec(SQLSubModel):
@reach.setter
def reach(self, reach):
self._reach = reach
self._status.modified()
self.modified()
@property
def start_rk(self):
@ -252,7 +254,7 @@ class ICAdisTSSpec(SQLSubModel):
@start_rk.setter
def start_rk(self, start_rk):
self._start_rk = start_rk
self._status.modified()
self.modified()
@property
def end_rk(self):
@ -261,7 +263,7 @@ class ICAdisTSSpec(SQLSubModel):
@end_rk.setter
def end_rk(self, end_rk):
self._end_rk = end_rk
self._status.modified()
self.modified()
@property
def concentration(self):
@ -270,7 +272,7 @@ class ICAdisTSSpec(SQLSubModel):
@concentration.setter
def concentration(self, concentration):
self._concentration = concentration
self._status.modified()
self.modified()
@property
def eg(self):
@ -279,7 +281,7 @@ class ICAdisTSSpec(SQLSubModel):
@eg.setter
def eg(self, eg):
self._eg = eg
self._status.modified()
self.modified()
@property
def em(self):
@ -288,7 +290,7 @@ class ICAdisTSSpec(SQLSubModel):
@em.setter
def em(self, em):
self._em = em
self._status.modified()
self.modified()
@property
def ed(self):
@ -297,7 +299,7 @@ class ICAdisTSSpec(SQLSubModel):
@ed.setter
def ed(self, ed):
self._ed = ed
self._status.modified()
self.modified()
@property
def rate(self):
@ -306,7 +308,7 @@ class ICAdisTSSpec(SQLSubModel):
@rate.setter
def rate(self, rate):
self._rate = rate
self._status.modified()
self.modified()
@property
def enabled(self):
@ -315,4 +317,18 @@ class ICAdisTSSpec(SQLSubModel):
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self._status.modified()
self.modified()
def cloned_for(self, reach, start_rk, end_rk):
new = ICAdisTSSpec(name=self._name_section, status=self._status)
new._reach = reach
new._start_rk = start_rk
new._end_rk = end_rk
new._concentration = self._concentration
new._eg = self._eg
new._em = self._em
new._ed = self._ed
new._rate = self._rate
new._enabled = self._enabled
new.modified()
return new

View File

@ -227,3 +227,42 @@ class InitialConditionsTemperature(SQLSubModel):
x.set_as_not_deleted()
self.modified()
def split_reach(self, reach, profile, reach1, reach2):
parts = []
for new_reach in (reach1, reach2):
rks = new_reach.reach.get_rk()
if rks:
parts.append((new_reach.id, min(rks), max(rks)))
specifications = [
specification
for specification in self._data
if (not specification.is_deleted() and
specification.reach == reach.id)
]
clones = []
for specification in specifications:
begin = specification.start_rk
end = specification.end_rk
if begin is None or end is None:
continue
lower, upper = sorted((begin, end))
reverse = begin > end
for new_reach, part_lower, part_upper in parts:
clipped_lower = max(lower, part_lower)
clipped_upper = min(upper, part_upper)
if clipped_lower > clipped_upper:
continue
start_rk = clipped_upper if reverse else clipped_lower
end_rk = clipped_lower if reverse else clipped_upper
clones.append(specification.cloned_for(
new_reach, start_rk, end_rk
))
if clones:
self._data.extend(clones)
self.modified()

View File

@ -62,6 +62,12 @@ class InitialConditionsTemperatureList(PamhyrModelList):
self._status.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
for initial_condition in self.lst:
initial_condition.split_reach(
reach, profile, reach1, reach2
)
@property
def Initial_Conditions_List(self):
return self.lst

View File

@ -200,3 +200,15 @@ class ICTemperatureSpec(SQLSubModel):
def temperature(self, temperature):
self._temperature = temperature
self._status.modified()
def cloned_for(self, reach, start_rk, end_rk):
new = ICTemperatureSpec(
name=self._name_section,
status=self._status
)
new._reach = reach
new._start_rk = start_rk
new._end_rk = end_rk
new._temperature = self._temperature
new.modified()
return new

View File

@ -196,6 +196,13 @@ class Data(SQLSubModel):
def __setitem__(self, key, value):
self._data[key] = self._types[key](value)
def cloned(self):
return Data(
self[0], self[1],
types=self._types,
status=self._status
)
class LateralContribution(SQLSubModel):
_sub_classes = [Data]
@ -511,6 +518,15 @@ class LateralContribution(SQLSubModel):
def has_reach(self):
return self._reach is not None
def cloned_for(self, reach, begin_section, end_section):
new = type(self)(name=self._name, status=self._status)
new._reach = reach
new._begin_section = begin_section
new._end_section = end_section
new._data = [data.cloned() for data in self.data]
new.modified()
return new
@property
def begin_rk(self):
if self._begin_section is None:

View File

@ -71,6 +71,89 @@ class LateralContributionList(PamhyrModelListWithTab):
self._status.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
profiles = reach.reach.profiles
split_index = profiles.index(profile)
parts = [
(
reach1,
0,
split_index,
dict(zip(
profiles[:split_index + 1],
reach1.reach.profiles
))
),
(
reach2,
split_index,
len(profiles) - 1,
dict(zip(
profiles[split_index:],
reach2.reach.profiles
))
),
]
for tab in self._tabs:
contributions = [
contribution
for contribution in self.get_tab(tab)
if contribution.reach is reach
]
for contribution in contributions:
if (contribution.begin_section not in profiles or
contribution.end_section not in profiles):
continue
begin = profiles.index(contribution.begin_section)
end = profiles.index(contribution.end_section)
lower, upper = sorted((begin, end))
reverse = begin > end
clones = []
for new_reach, part_lower, part_upper, sections in parts:
clipped_lower = max(lower, part_lower)
clipped_upper = min(upper, part_upper)
if clipped_lower > clipped_upper:
continue
begin_index = (
clipped_upper if reverse else clipped_lower
)
end_index = (
clipped_lower if reverse else clipped_upper
)
clones.append(contribution.cloned_for(
new_reach,
sections[profiles[begin_index]],
sections[profiles[end_index]]
))
if len(clones) == 0:
continue
self._tabs[tab].extend(clones)
self._status.modified()
def get_tab_for_enabled_reaches(self, tab):
return [
contribution
for contribution in self.get_tab(tab)
if (contribution.reach is None or
(not contribution.reach.is_deleted() and
contribution.reach.is_enable()))
]
def get_for_enabled_reaches(self, tab, index):
contributions = self.get_tab_for_enabled_reaches(tab)
if not 0 <= index < len(contributions):
return None
return contributions[index]
def __copy__(self):
new = LateralContributionList()

View File

@ -192,6 +192,13 @@ class Data(SQLSubModel):
def __setitem__(self, key, value):
self._data[key] = self._types[key](value)
def cloned(self):
return Data(
self[0], self[1],
types=self._types,
status=self._status
)
class LateralContributionAdisTS(SQLSubModel):
_sub_classes = [Data]
@ -477,6 +484,18 @@ class LateralContributionAdisTS(SQLSubModel):
self._end_rk = end_rk
self.modified()
def cloned_for(self, reach, begin_rk, end_rk):
new = LateralContributionAdisTS(
pollutant=self._pollutant,
status=self._status
)
new._reach = reach
new._begin_rk = begin_rk
new._end_rk = end_rk
new._data = [data.cloned() for data in self.data]
new.modified()
return new
@property
def _default_0(self):
return self._types[0](0)

View File

@ -68,6 +68,46 @@ class LateralContributionsAdisTSList(PamhyrModelList):
self._status.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
parts = []
for new_reach in (reach1, reach2):
rks = new_reach.reach.get_rk()
if len(rks) == 0:
continue
parts.append((new_reach.id, min(rks), max(rks)))
contributions = [
contribution
for contribution in self.lst
if contribution.reach == reach.id
]
clones = []
for contribution in contributions:
begin = contribution.begin_rk
end = contribution.end_rk
if begin is None or end is None:
continue
lower, upper = sorted((begin, end))
reverse = begin > end
for new_reach, part_lower, part_upper in parts:
clipped_lower = max(lower, part_lower)
clipped_upper = min(upper, part_upper)
if clipped_lower > clipped_upper:
continue
begin_rk = clipped_upper if reverse else clipped_lower
end_rk = clipped_lower if reverse else clipped_upper
clones.append(contribution.cloned_for(
new_reach, begin_rk, end_rk
))
if len(clones) != 0:
self._lst.extend(clones)
self._status.modified()
@property
def Lat_Cont_List(self):
return self.lst

View File

@ -979,4 +979,26 @@ Last export at: @date."""
self._add_edge(r1)
self._add_edge(r2)
self._initial_conditions.split_reach(
reach, profile, r1, r2
)
self._lateral_contribution.split_reach(
reach, profile, r1, r2
)
self._hydraulic_structures.split_reach(
reach, profile, r1, r2
)
self._InitialConditionsAdisTS.split_reach(
reach, profile, r1, r2
)
self._LateralContributionsAdisTS.split_reach(
reach, profile, r1, r2
)
self._InitialConditionsTemperature.split_reach(
reach, profile, r1, r2
)
self._WeatherParameters.split_reach(
reach, profile, r1, r2
)
return r1, r2

View File

@ -153,6 +153,13 @@ class Data(SQLSubModel):
def __setitem__(self, key, value):
self._data[key] = self._types[key](value)
def cloned(self):
return Data(
self[0], self[1],
types=self._types,
status=self._status
)
class WeatherParametersDefault(SQLSubModel):
def __init__(self, type: str, value: float = 0.0,
@ -580,6 +587,15 @@ class WeatherParameters(SQLSubModel):
self._end_section = section
self.modified()
def cloned_for(self, reach, begin_section, end_section):
new = type(self)(name=self._name, status=self._status)
new._reach = reach
new._begin_section = begin_section
new._end_section = end_section
new._data = [data.cloned() for data in self.data]
new.modified()
return new
@property
def header(self):
return self._header.copy()

View File

@ -161,6 +161,64 @@ class WeatherParametersList(PamhyrModelList):
self._status.modified()
return n
def split_reach(self, reach, profile, reach1, reach2):
profiles = reach.reach.profiles
split_index = profiles.index(profile)
parts = [
(
reach1,
0,
split_index,
dict(zip(
profiles[:split_index + 1],
reach1.reach.profiles
))
),
(
reach2,
split_index,
len(profiles) - 1,
dict(zip(
profiles[split_index:],
reach2.reach.profiles
))
),
]
weather_parameters = [
weather_parameter
for weather_parameter in self.lst
if weather_parameter.reach is reach
]
clones = []
for weather_parameter in weather_parameters:
if (weather_parameter.begin_section not in profiles or
weather_parameter.end_section not in profiles):
continue
begin = profiles.index(weather_parameter.begin_section)
end = profiles.index(weather_parameter.end_section)
lower, upper = sorted((begin, end))
reverse = begin > end
for new_reach, part_lower, part_upper, sections in parts:
clipped_lower = max(lower, part_lower)
clipped_upper = min(upper, part_upper)
if clipped_lower > clipped_upper:
continue
begin_index = clipped_upper if reverse else clipped_lower
end_index = clipped_lower if reverse else clipped_upper
clones.append(weather_parameter.cloned_for(
new_reach,
sections[profiles[begin_index]],
sections[profiles[end_index]]
))
if clones:
self._lst.extend(clones)
self._status.modified()
@property
def Weather_Parameters_List(self):
return self.lst

View File

@ -23,6 +23,8 @@ import tempfile
from Model.Status import StudyStatus
from Model.Study import Study
from Model.River import River
from Model.LateralContribution.LateralContributionTypes import LateralContrib
from Model.HydraulicStructures.Basic.Types import DischargeWeir
class StudyTestCase(unittest.TestCase):
@ -287,3 +289,89 @@ class RiverTestCase(unittest.TestCase):
edges = river.edges()
self.assertEqual(edges[0], e0)
self.assertEqual(edges[1], e1)
def test_split_reach_splits_initial_conditions(self):
status = StudyStatus()
river = River(status=status)
node1 = river.add_node()
node2 = river.add_node(x=40.0)
reach = river.add_edge(node1, node2)
profiles = []
for index in range(5):
profile = reach.reach.insert(index)
profile.rk = index * 10.0
profile.insert(0)
profiles.append(profile)
initial_conditions = river.initial_conditions.get(reach)
for index, profile in enumerate(profiles):
data = initial_conditions.new_from_data(
profile.rk,
discharge=100.0 + index,
elevation=10.0 + index,
)
initial_conditions.insert(index, data)
reach1, reach2 = river._split_reach(reach, profiles[2])
conditions1 = river.initial_conditions.get(reach1).data
conditions2 = river.initial_conditions.get(reach2).data
self.assertEqual(
[data["discharge"] for data in conditions1],
[100.0, 101.0, 102.0]
)
self.assertEqual(
[data["discharge"] for data in conditions2],
[102.0, 103.0, 104.0]
)
self.assertEqual(
[data["section"] for data in conditions1],
reach1.reach.profiles
)
self.assertEqual(
[data["section"] for data in conditions2],
reach2.reach.profiles
)
self.assertTrue(all(
data._reach is reach1 for data in conditions1
))
self.assertTrue(all(
data._reach is reach2 for data in conditions2
))
def test_split_reach_keeps_partial_initial_conditions_in_scope(self):
status = StudyStatus()
river = River(status=status)
node1 = river.add_node()
node2 = river.add_node(x=40.0)
reach = river.add_edge(node1, node2)
profiles = []
for index in range(5):
profile = reach.reach.insert(index)
profile.rk = index * 10.0
profile.insert(0)
profiles.append(profile)
initial_conditions = river.initial_conditions.get(reach)
for index in (1, 3):
data = initial_conditions.new_from_data(
profiles[index].rk,
discharge=100.0 + index,
elevation=10.0 + index,
)
initial_conditions.insert(len(initial_conditions.data), data)
reach1, reach2 = river._split_reach(reach, profiles[2])
self.assertEqual(
[data["discharge"]
for data in river.initial_conditions.get(reach1).data],
[101.0]
)
self.assertEqual(
[data["discharge"]
for data in river.initial_conditions.get(reach2).data],
[103.0]
)

View File

@ -549,7 +549,7 @@ class AdisTSwc(AdisTS):
for LC in POL_LC:
reach = next((
edge for edge in study.river.enable_edges()
if edge.id == LC.edge
if edge.id == LC.reach
), None)
if reach is None:
continue

View File

@ -935,7 +935,8 @@ class AdisTTwc(AdisTT):
for weather_parameter in study.river.weather_parameters.lst:
config = self._weather_files.get(weather_parameter.type)
if (config is None or weather_parameter.reach is None
or weather_parameter.reach.is_deleted()):
or weather_parameter.reach.is_deleted()
or not weather_parameter.reach.is_enable()):
continue
_, extension = config
exported = self._export_weather_file(

View File

@ -78,7 +78,7 @@ class ComboBoxDelegate(QItemDelegate):
else:
val = list(
map(
lambda n: n.name, self._data.edges()
lambda n: n.name, self._data.enable_edges()
)
)

View File

@ -80,7 +80,7 @@ class ComboBoxDelegate(QItemDelegate):
else:
val = list(
map(
lambda n: n.name, self._data.edges()
lambda n: n.name, self._data.enable_edges()
)
)

View File

@ -61,7 +61,7 @@ class ComboBoxDelegate(QItemDelegate):
val = []
if self._mode == "rk":
reach = self._data.hydraulic_structures\
.get(index.row())\
.get_for_enabled_reaches(index.row())\
.input_reach
if reach is not None:
val = list(
@ -73,7 +73,7 @@ class ComboBoxDelegate(QItemDelegate):
else:
val = list(
map(
lambda n: n.name, self._data.edges()
lambda n: n.name, self._data.enable_edges()
)
)
@ -94,7 +94,7 @@ class ComboBoxDelegate(QItemDelegate):
if self._mode == "rk":
reach = self._data.hydraulic_structures\
.get(index.row())\
.get_for_enabled_reaches(index.row())\
.input_reach
profiles = list(
filter(
@ -128,20 +128,22 @@ class TableModel(PamhyrTableModel):
self._lst = self._data._hydraulic_structures
def get_true_data_row(self, row):
hs = self._lst.get(row)
hs = self._lst.get_for_enabled_reaches(row)
if hs is None:
return len(self._lst.lst)
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == hs,
enumerate(self._lst._lst)
enumerate(self._lst.lst)
)
), 0
)
def rowCount(self, parent):
return len(self._lst)
return len(self._lst.enabled_reaches_list)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
@ -149,16 +151,17 @@ class TableModel(PamhyrTableModel):
row = index.row()
column = index.column()
structure = self._lst.get_for_enabled_reaches(row)
if self._headers[column] == "name":
return self._lst.get(row).name
return structure.name
elif self._headers[column] == "reach":
n = self._lst.get(row).input_reach
n = structure.input_reach
if n is None:
return self._trad['not_associated']
return n.name
elif self._headers[column] == "rk":
n = self._lst.get(row).input_section
n = structure.input_section
if n is None:
return self._trad['not_associated']
return n.display_name()
@ -174,6 +177,7 @@ class TableModel(PamhyrTableModel):
row = index.row()
column = index.column()
row = self.get_true_data_row(row)
na = self._trad['not_associated']
try:

View File

@ -220,7 +220,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
def add(self):
rows = self.index_selected_rows()
if len(self._hs_lst) == 0 or len(rows) == 0:
if len(self._hs_lst.enabled_reaches_list) == 0 or len(rows) == 0:
self._table.add(0)
else:
self._table.add(rows[0])
@ -247,7 +247,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
def edit(self):
rows = self.index_selected_rows()
for row in rows:
data = self._hs_lst.get(row)
data = self._hs_lst.get_for_enabled_reaches(row)
if self.sub_window_exists(
BasicHydraulicStructuresWindow,
@ -269,7 +269,8 @@ class HydraulicStructuresWindow(PamhyrWindow):
self._checkbox.setChecked(True)
else:
self._checkbox.setEnabled(True)
self._checkbox.setChecked(self._hs_lst.get(row).enabled)
structure = self._hs_lst.get_for_enabled_reaches(row)
self._checkbox.setChecked(structure.enabled)
def _set_structure_state(self):
rows = self.index_selected_rows()
@ -288,16 +289,18 @@ class HydraulicStructuresWindow(PamhyrWindow):
def _update_clear_plot(self):
rows = self.index_selected_rows()
if len(rows) == 0 or len(self._hs_lst) == 0:
if (len(rows) == 0 or
len(self._hs_lst.enabled_reaches_list) == 0):
self._update_clear_all()
return
reach = self._hs_lst.get(rows[0]).input_reach
structure = self._hs_lst.get_for_enabled_reaches(rows[0])
reach = structure.input_reach
if reach is not None:
self.plot_rkc.set_reach(reach)
self.plot_ac.set_reach(reach)
profile = self._hs_lst.get(rows[0]).input_section
profile = structure.input_section
if profile is not None:
profiles = reach.reach\
.get_profiles_from_rk(

View File

@ -55,17 +55,27 @@ class ComboBoxDelegate(QItemDelegate):
self._trad = trad
self._ic_spec_lst = ic_spec_lst
def _specifications(self):
if hasattr(
self._ic_spec_lst,
"get_specs_for_enabled_reaches"
):
return self._ic_spec_lst.get_specs_for_enabled_reaches(
self._data.edges()
)
return self._ic_spec_lst
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
val = []
if self._mode == "rk":
reach_id = self._ic_spec_lst[index.row()].reach
reach_id = self._specifications()[index.row()].reach
reach = next(filter(lambda edge: edge.id == reach_id,
self._data.edges()), None)
if reach_id is not None:
if reach is not None:
val = list(
map(
lambda rk: str(rk), reach.reach.get_rk()
@ -74,7 +84,7 @@ class ComboBoxDelegate(QItemDelegate):
else:
val = list(
map(
lambda n: n.name, self._data.edges()
lambda n: n.name, self._data.enable_edges()
)
)
@ -117,11 +127,8 @@ class InitialConditionTableModel(PamhyrTableModel):
self._data = data
def _setup_lst(self):
self._lst = list(
filter(
lambda ica: ica._deleted is False,
self._data._data
)
self._lst = self._data.get_specs_for_enabled_reaches(
self._river.edges()
)
def rowCount(self, parent):

View File

@ -152,14 +152,14 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self._delegate_reach = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
ic_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0],
parent=self,
mode="reaches"
)
self._delegate_rk = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
ic_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0],
parent=self,
mode="rk"
)
@ -308,7 +308,10 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
def add(self):
rows = self.index_selected_rows()
if len(self._data[0]._data) == 0 or len(rows) == 0:
visible = self._data[0].get_specs_for_enabled_reaches(
self._study.river.edges()
)
if len(visible) == 0 or len(rows) == 0:
self._table_spec.add(0)
else:
self._table_spec.add(rows[0])

View File

@ -75,7 +75,7 @@ class ComboBoxDelegate(QItemDelegate):
else:
val = list(
map(
lambda n: n.name, self._data.edges()
lambda n: n.name, self._data.enable_edges()
)
)
@ -140,13 +140,30 @@ class InitialConditionTableModel(PamhyrTableModel):
self._data = data
def _setup_lst(self):
enabled_reach_ids = {
reach.id
for reach in self._river.enable_edges()
}
self._lst = list(
filter(
lambda ica: ica._deleted is False,
lambda ica: (
ica._deleted is False and
(
ica.reach in (None, -1) or
ica.reach in enabled_reach_ids
)
),
self._data._data
)
)
def refresh(self):
self.beginResetModel()
try:
self._setup_lst()
finally:
self.endResetModel()
def rowCount(self, parent):
return len(self._lst)
@ -221,20 +238,31 @@ class InitialConditionTableModel(PamhyrTableModel):
)
)
else:
new_value = value
if self._headers[column] == "reach":
reach = self._river.edge(value)
rks = reach.reach.get_rk()
if not rks:
return False
new_value = (reach.id, min(rks))
self._undo.push(
SetCommandSpec(
self._lst, row, self._headers[column],
(self._river.edge(value).id
if self._headers[column] == "reach"
else value
)
new_value
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
if self._headers[column] == "reach":
rk_index = self.index(
row, self._headers.index("rk")
)
self.dataChanged.emit(index, rk_index)
else:
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):

View File

@ -73,6 +73,8 @@ class SetCommandSpec(QUndoCommand):
self._old = self._data[self._row].name
elif self._column == "reach":
self._old = self._data[self._row].reach
self._old_start_rk = self._data[self._row].start_rk
self._old_end_rk = self._data[self._row].end_rk
elif self._column == "rk":
self._old = self._data[self._row].start_rk
elif self._column == "temperature":
@ -84,13 +86,19 @@ class SetCommandSpec(QUndoCommand):
elif column == "reach":
_type = int
self._new = _type(new_value)
if column == "reach":
self._new = _type(new_value[0])
self._new_rk = float(new_value[1])
else:
self._new = _type(new_value)
def undo(self):
if self._column == "name":
self._data[self._row].name = self._old
elif self._column == "reach":
self._data[self._row].reach = self._old
self._data[self._row].start_rk = self._old_start_rk
self._data[self._row].end_rk = self._old_end_rk
elif self._column == "rk":
self._data[self._row].start_rk = self._old
self._data[self._row].end_rk = self._old
@ -102,6 +110,8 @@ class SetCommandSpec(QUndoCommand):
self._data[self._row].name = self._new
elif self._column == "reach":
self._data[self._row].reach = self._new
self._data[self._row].start_rk = self._new_rk
self._data[self._row].end_rk = self._new_rk
elif self._column == "rk":
self._data[self._row].start_rk = self._new
self._data[self._row].end_rk = self._new

View File

@ -309,3 +309,10 @@ class InitialConditionsTemperatureWindow(PamhyrWindow):
if len(rows) == 0:
return
self._table_spec.delete(rows)
def _propagated_update(self, key=Modules(0)):
if Modules.NETWORK not in key:
return
self._table_spec.refresh()
self.table_spec.clearSelection()

View File

@ -103,7 +103,10 @@ class ComboBoxDelegate(QItemDelegate):
else:
self.editor.addItems(
[self._trad['not_associated']] +
self._data.edges_names()
[
reach.name
for reach in self._data.enable_edges()
]
)
self.editor.setCurrentText(index.data(Qt.DisplayRole))
@ -154,7 +157,9 @@ class TableModel(PamhyrTableModel):
self._long_types = self._trad.get_dict("long_types")
def get_true_data_row(self, row):
lc = self._lst.get(self._tab, row)
lc = self._lst.get_for_enabled_reaches(self._tab, row)
if lc is None:
return len(self._lst.get_tab(self._tab))
return next(
map(
@ -167,7 +172,7 @@ class TableModel(PamhyrTableModel):
)
def rowCount(self, parent):
return self._lst.len(self._tab)
return len(self._lst.get_tab_for_enabled_reaches(self._tab))
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
@ -175,21 +180,22 @@ class TableModel(PamhyrTableModel):
row = index.row()
column = index.column()
contribution = self._lst.get_for_enabled_reaches(self._tab, row)
if self._headers[column] == "name":
return self._lst.get(self._tab, row).name
return contribution.name
elif self._headers[column] == "type":
t = self._lst.get(self._tab, row).lctype
t = contribution.lctype
return self._long_types[t]
elif self._headers[column] == "edge":
n = self._lst.get(self._tab, row).reach
n = contribution.reach
if n is None:
return self._trad['not_associated']
return n.name
elif self._headers[column] == "begin_rk":
return str(self._lst.get(self._tab, row).begin_rk)
return str(contribution.begin_rk)
elif self._headers[column] == "end_rk":
return str(self._lst.get(self._tab, row).end_rk)
return str(contribution.end_rk)
return QVariant()
@ -202,6 +208,8 @@ class TableModel(PamhyrTableModel):
row = index.row()
column = index.column()
contribution = self._lst.get_for_enabled_reaches(self._tab, row)
row = self.get_true_data_row(row)
try:
if self._headers[column] == "name":
@ -225,7 +233,7 @@ class TableModel(PamhyrTableModel):
)
)
elif self._headers[column] == "begin_rk":
_edge = self._lst.get(self._tab, row).reach
_edge = contribution.reach
_begin_rk = next(
p for p in _edge.reach.profiles
if p.pamhyr_id == value
@ -236,7 +244,7 @@ class TableModel(PamhyrTableModel):
)
)
elif self._headers[column] == "end_rk":
_edge = self._lst.get(self._tab, row).reach
_edge = contribution.reach
_end_rk = next(
p for p in _edge.reach.profiles
if p.pamhyr_id == value

View File

@ -213,11 +213,11 @@ class LateralContributionWindow(PamhyrWindow):
if len(rows) > 0:
edge = self._study.river\
.lateral_contribution\
.get(tab, rows[0])\
.get_for_enabled_reaches(tab, rows[0])\
.reach
if edge:
data = edge.reach
lc = self._lcs.get(tab, rows[0])
lc = self._lcs.get_for_enabled_reaches(tab, rows[0])
highlight = (lc.begin_rk, lc.end_rk)
for delegate in self._delegate_rk:
@ -236,7 +236,8 @@ class LateralContributionWindow(PamhyrWindow):
def add(self):
tab = self.current_tab()
rows = self.index_selected_rows()
if self._lcs.len(tab) == 0 or len(rows) == 0:
visible = self._lcs.get_tab_for_enabled_reaches(tab)
if len(visible) == 0 or len(rows) == 0:
self._table[tab].add(0)
else:
self._table[tab].add(rows[0])
@ -291,7 +292,7 @@ class LateralContributionWindow(PamhyrWindow):
tab = self.current_tab()
rows = self.index_selected_rows()
for row in rows:
data = self._lcs.get(tab, row)
data = self._lcs.get_for_enabled_reaches(tab, row)
if self.sub_window_exists(
EditLateralContributionWindow,

View File

@ -81,7 +81,10 @@ class ComboBoxDelegate(QItemDelegate):
else:
self.editor.addItems(
[self._trad['not_associated']] +
self._data.edges_names()
[
reach.name
for reach in self._data.enable_edges()
]
)
self.editor.setCurrentText(index.data(Qt.DisplayRole))
@ -119,6 +122,11 @@ class TableModel(PamhyrTableModel):
self._setup_lst()
def _setup_lst(self):
enabled_reach_ids = {
reach.id
for reach in self._data.enable_edges()
}
if self._lcs_list is not None:
self._lcs_pol_list = [
lcs for lcs in self._lcs_list._lst
@ -127,7 +135,13 @@ class TableModel(PamhyrTableModel):
self._lst = list(
filter(
lambda x: x._deleted is False,
lambda x: (
x._deleted is False and
(
x.reach in (None, -1) or
x.reach in enabled_reach_ids
)
),
self._lcs_pol_list
)
)
@ -137,6 +151,18 @@ class TableModel(PamhyrTableModel):
def rowCount(self, parent):
return len(self._lst)
def get(self, row):
if 0 <= row < len(self._lst):
return self._lst[row]
return None
def refresh(self):
self.beginResetModel()
try:
self._setup_lst()
finally:
self.endResetModel()
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
@ -170,11 +196,18 @@ class TableModel(PamhyrTableModel):
try:
if self._headers[column] == "reach":
reach = self._data.reach(value)
rks = reach.reach.get_rk()
if len(rks) == 0:
return False
self._undo.push(
SetReachCommand(
self._lcs_list, self._lst,
row,
self._data.reach(value).id
reach.id,
min(rks),
max(rks)
)
)
elif self._headers[column] == "begin_rk":
@ -193,7 +226,13 @@ class TableModel(PamhyrTableModel):
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
if self._headers[column] == "reach":
end_index = self.index(
row, self._headers.index("end_rk")
)
self.dataChanged.emit(index, end_index)
else:
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):

View File

@ -64,20 +64,30 @@ class SetEndCommand(QUndoCommand):
class SetReachCommand(QUndoCommand):
def __init__(self, lcs, lcs_lst, index, reach):
def __init__(self, lcs, lcs_lst, index, reach, begin_rk, end_rk):
QUndoCommand.__init__(self)
self._lcs = lcs
self._lcs_lst = lcs_lst
self._index = index
self._old = self._lcs_lst[self._index].reach
self._old_begin_rk = self._lcs_lst[self._index].begin_rk
self._old_end_rk = self._lcs_lst[self._index].end_rk
self._new = reach
self._new_begin_rk = begin_rk
self._new_end_rk = end_rk
def undo(self):
self._lcs_lst[self._index].reach = self._old
contribution = self._lcs_lst[self._index]
contribution.reach = self._old
contribution.begin_rk = self._old_begin_rk
contribution.end_rk = self._old_end_rk
def redo(self):
self._lcs_lst[self._index].reach = self._new
contribution = self._lcs_lst[self._index]
contribution.reach = self._new
contribution.begin_rk = self._new_begin_rk
contribution.end_rk = self._new_end_rk
class AddCommand(QUndoCommand):

View File

@ -20,6 +20,7 @@ import logging
from tools import trace, timer
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
@ -187,12 +188,13 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
data = None
highlight = None
tab = "liquid"
if len(rows) > 0:
reach_id = self._study.river\
.lateral_contributions_adists.lst[rows[0]]\
.reach
contribution = self._table.get(rows[0])
reach_id = (
contribution.reach
if contribution is not None
else None
)
if reach_id:
reach = next(
@ -201,8 +203,10 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
self._study.river.reachs()))
data = reach.reach
lc = self._lcs.lst[rows[0]]
highlight = (lc.begin_rk, lc.end_rk)
highlight = (
contribution.begin_rk,
contribution.end_rk
)
for delegate in self._delegate_rk:
delegate.data = reach
@ -254,13 +258,23 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
self._table.redo()
self._set_current_reach()
def _propagated_update(self, key=Modules(0)):
if Modules.NETWORK not in key:
return
self._table.refresh()
self.find(QTableView, "tableView").clearSelection()
self._set_current_reach()
def edit(self):
rows = self.index_selected_rows()
if not rows:
return
for row in rows:
data = self._lcs.lst[row]
data = self._table.get(row)
if data is None:
continue
if self.sub_window_exists(
EditLateralContributionAdisTSWindow,

View File

@ -1526,6 +1526,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
sl.show()
def open_reach_sediment_layers(self):
if self._study is None:
logger.debug(
"No study open for reach sediment layers edition..."
)
return
if not self._study.river.has_current_reach():
self.msg_select_reach()
return
reach = self._study.river.current_reach().reach
if self.sub_window_exists(
@ -2307,11 +2317,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
logger.debug("No study open for sql debuging...")
return
# todo : gérer le cas où le dossier a un espace dans le nom
# (ne veut pas ouvrir sqlitebrowser)
file = self._study.filename
_ = subprocess.Popen(
f"sqlitebrowser {file}",
shell=True
["sqlitebrowser", file]
)

View File

@ -87,7 +87,10 @@ class ComboBoxDelegate(QItemDelegate):
else:
self.editor.addItems(
[self._trad['not_associated']] +
self._data.edges_names()
[
reach.name
for reach in self._data.enable_edges()
]
)
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
@ -144,8 +147,28 @@ class WeatherParametersTableModel(PamhyrTableModel):
if self._type == "ND" or self._data is None:
self._lst = []
return
enabled_reach_ids = {
reach.id
for reach in self._river.enable_edges()
}
self._lst = self._data.lst
self._lst = list(filter(lambda wp: wp.type == self._type, self._lst))
self._lst = list(filter(
lambda wp: (
wp.type == self._type and
(
wp.reach is None or
wp.reach.id in enabled_reach_ids
)
),
self._lst
))
def refresh(self):
self.beginResetModel()
try:
self._setup_lst()
finally:
self.endResetModel()
def update_tab_spec(self, type="ND", enabled=True):
if enabled:
@ -239,16 +262,25 @@ class WeatherParametersTableModel(PamhyrTableModel):
)
)
elif self._headers[column] == "reach":
edge = self._river.edge(value)
if not edge.reach.profiles:
return False
self._undo.push(
SetEdgeCommand(
self._data, global_row, self._river.edge(value)
self._data, global_row, edge
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
if self._headers[column] == "reach":
end_index = self.index(
row, self._headers.index("end_rk")
)
self.dataChanged.emit(index, end_index)
else:
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):

View File

@ -125,14 +125,26 @@ class SetEdgeCommand(QUndoCommand):
self._wps = wps
self._index = index
self._old = self._wps.get(self._index).reach
weather_parameter = self._wps.get(self._index)
self._old = weather_parameter.reach
self._old_begin = weather_parameter.begin_section
self._old_end = weather_parameter.end_section
self._new = edge
profiles = edge.reach.profiles
self._new_begin = min(profiles, key=lambda profile: profile.rk)
self._new_end = max(profiles, key=lambda profile: profile.rk)
def undo(self):
self._wps.get(self._index).reach = self._old
weather_parameter = self._wps.get(self._index)
weather_parameter.reach = self._old
weather_parameter.begin_section = self._old_begin
weather_parameter.end_section = self._old_end
def redo(self):
self._wps.get(self._index).reach = self._new
weather_parameter = self._wps.get(self._index)
weather_parameter.reach = self._new
weather_parameter.begin_section = self._new_begin
weather_parameter.end_section = self._new_end
class AddCommand(QUndoCommand):

View File

@ -439,3 +439,10 @@ class WeatherParametersWindow(PamhyrWindow):
parent=self
)
win.show()
def _propagated_update(self, key=Modules(0)):
if Modules.NETWORK not in key:
return
self._table_spec.refresh()
self.table_spec.clearSelection()