Merge: Merge branch 'sediment' with sediment export and result for mage.

This commit is contained in:
Pierre-Antoine Rouby committed 2023-08-22 11:44:04 +02:00
commit 7429952168
57 files changed
+3994 -75

No files matched your search

@@ -83,7 +83,8 @@ class BoundaryCondition(SQLSubModel):
def _get_ctor_from_type(cls, t): def _get_ctor_from_type(cls, t):
from Model.BoundaryCondition.BoundaryConditionTypes import ( from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, PonctualContribution, NotDefined, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge TimeOverZ, TimeOverDischarge, ZOverDischarge,
Solid,
) )
res = NotDefined res = NotDefined
@@ -95,6 +96,8 @@ class BoundaryCondition(SQLSubModel):
res = TimeOverDischarge res = TimeOverDischarge
elif t == "ZD": elif t == "ZD":
res = ZOverDischarge res = ZOverDischarge
elif t == "SL":
res = Solid
return res return res
@classmethod @classmethod
@@ -26,7 +26,8 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
from Model.BoundaryCondition.BoundaryConditionTypes import ( from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, NotDefined,
PonctualContribution, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge TimeOverZ, TimeOverDischarge, ZOverDischarge,
Solid,
) )
class BoundaryConditionList(SQLSubModel): class BoundaryConditionList(SQLSubModel):
@@ -83,3 +83,16 @@ class ZOverDischarge(BoundaryCondition):
@property @property
def _default_0(self): def _default_0(self):
return 0.0 return 0.0
class Solid(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
super(Solid, self).__init__(id=id, name=name, status=status)
self._type = "SL"
self._header = ["time", "solid"]
self._types = [TimeOverDischarge.time_convert, float]
@classmethod
def compatibility(cls):
return ["solid"]
+18 -1
View File
@@ -19,12 +19,29 @@
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
class Point(object): class Point(object):
def __init__(self, name:str = "", status = None): def __init__(self, name:str = "", profile=None, status = None):
super(Point, self).__init__() super(Point, self).__init__()
self._status = status self._status = status
self._name = name self._name = name
self._profile = profile
self._sl = None
@property
def sl(self):
if self._sl is None:
return self._profile.sl
return self._sl
@sl.setter
def sl(self, sl):
if sl == self._profile.sl:
self._sl = None
self._sl = sl
@property @property
def name(self): def name(self):
+35 -8
View File
@@ -26,8 +26,8 @@ class PointXYZ(Point, SQLSubModel):
_sub_classes = [] _sub_classes = []
def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0, def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0,
name:str = "", status = None): name:str = "", profile = None, status = None):
super(PointXYZ, self).__init__(name=name, status=status) super(PointXYZ, self).__init__(name=name, profile=profile, status=status)
self._x = float(x) self._x = float(x)
self._y = float(y) self._y = float(y)
@@ -44,7 +44,9 @@ class PointXYZ(Point, SQLSubModel):
y INTEGER NOT NULL, y INTEGER NOT NULL,
z INTEGER NOT NULL, z INTEGER NOT NULL,
profile INTEGER NOT NULL, profile INTEGER NOT NULL,
FOREIGN KEY(profile) REFERENCES profileXYZ(id) sl INTEGER,
FOREIGN KEY(profile) REFERENCES profileXYZ(id),
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
) )
""") """)
@@ -53,6 +55,17 @@ class PointXYZ(Point, SQLSubModel):
@classmethod @classmethod
def _sql_update(cls, execute, version): def _sql_update(cls, execute, version):
cls._update_submodel(execute, version) cls._update_submodel(execute, version)
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
execute(
"""
ALTER TABLE geometry_pointXYZ
ADD COLUMN sl INTEGER
REFERENCES sedimentary_layer(id)
"""
)
return True return True
@classmethod @classmethod
@@ -63,9 +76,9 @@ class PointXYZ(Point, SQLSubModel):
profile = data["profile"] profile = data["profile"]
table = execute( table = execute(
"SELECT ind, name, x, y, z " + "SELECT ind, name, x, y, z, sl " +
"FROM geometry_pointXYZ " + "FROM geometry_pointXYZ " +
f"WHERE profile = {profile}" f"WHERE profile = {profile.id}"
) )
# Create points list # Create points list
@@ -79,13 +92,25 @@ class PointXYZ(Point, SQLSubModel):
x = row[2] x = row[2]
y = row[3] y = row[3]
z = row[4] z = row[4]
sl = row[5]
new = cls( new = cls(
name = name, name = name,
x = x, y = y, z = z, x = x, y = y, z = z,
profile = profile,
status = status status = status
) )
if sl == -1 or sl == None:
new._sl = None
else:
new._sl = next(
filter(
lambda s: s.id == sl,
data["sediment_layers_list"].sediment_layers
)
)
points[ind] = new points[ind] = new
return points return points
@@ -94,13 +119,15 @@ class PointXYZ(Point, SQLSubModel):
profile = data["profile"] profile = data["profile"]
ind = data["ind"] ind = data["ind"]
sl = self._sl.id if self._sl is not None else -1
sql = ( sql = (
"INSERT OR REPLACE INTO " + "INSERT OR REPLACE INTO " +
"geometry_pointXYZ(ind, name, x, y, z, profile) "+ "geometry_pointXYZ(ind, name, x, y, z, profile, sl) "+
"VALUES (" + "VALUES (" +
f"{ind}, '{self._sql_format(self._name)}', " + f"{ind}, '{self._sql_format(self._name)}', " +
f"{self.x}, {self.y}, {self.z}, " + f"{self.x}, {self.y}, {self.z}, " +
f"{profile}" + f"{profile.id}, {sl}" +
")" ")"
) )
execute(sql) execute(sql)
@@ -117,7 +144,7 @@ class PointXYZ(Point, SQLSubModel):
*data *data
) )
else: else:
valid_header = {'name', 'x', 'y', 'z'} valid_header = {'name', 'x', 'y', 'z', 'profile'}
d = {} d = {}
for i, v in enumerate(data): for i, v in enumerate(data):
h = header[i].strip().lower().split(' ')[0] h = header[i].strip().lower().split(' ')[0]
+50
View File
@@ -50,6 +50,7 @@ class Profile(object):
self._kp = float(kp) self._kp = float(kp)
self._name = str(name) self._name = str(name)
self._reach = reach self._reach = reach
self._sl = None
self._points: List[Point] = [] self._points: List[Point] = []
@@ -63,6 +64,9 @@ class Profile(object):
def points(self): def points(self):
return self._points.copy() return self._points.copy()
def point(self, index):
return self._points[index]
@property @property
def reach(self): def reach(self):
return self._reach return self._reach
@@ -136,6 +140,19 @@ class Profile(object):
self._name = value.strip() self._name = value.strip()
self._status.modified() self._status.modified()
@property
def sl(self):
"""
Returns:
Profile sediment layers.
"""
return self._sl
@sl.setter
def sl(self, value):
self._sl = value
self._status.modified()
@property @property
def profile_type(self): def profile_type(self):
""" """
@@ -274,6 +291,39 @@ class Profile(object):
) )
self._status.modified() self._status.modified()
# Sediment Layers
def get_sl(self):
"""Get sediment layer height of points
Get sediment layer of points (without spesific point sl)
Returns:
List of sediment layers height
"""
res = []
psl = [point.sl for point in self.points]
# Compute max number of layers
sl_max = 0
for sl in psl:
n = 0 if sl is None else len(sl)
sl_max = max(n, sl_max)
# Create list of height for each sl and each layer
for i in range(0, sl_max):
cur = []
# Compute new layer line for each sl
for sl in psl:
if sl is not None and i < len(sl):
cur.append(sl.get(i).height)
else:
cur.append(0)
# Add layer line to result
res.append(cur)
return res
# Abstract method, must be implemented for in non abstract class # Abstract method, must be implemented for in non abstract class
def get_station(self): def get_station(self):
raise NotImplementedMethodeError(self, self.get_station) raise NotImplementedMethodeError(self, self.get_station)
+37 -11
View File
@@ -76,7 +76,9 @@ class ProfileXYZ(Profile, SQLSubModel):
num INTEGER NOT NULL, num INTEGER NOT NULL,
code1 INTEGER NOT NULL, code1 INTEGER NOT NULL,
code2 INTEGER NOT NULL, code2 INTEGER NOT NULL,
FOREIGN KEY(reach) REFERENCES river_reach(id) sl INTEGER,
FOREIGN KEY(reach) REFERENCES river_reach(id),
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
) )
""") """)
@@ -84,6 +86,17 @@ class ProfileXYZ(Profile, SQLSubModel):
@classmethod @classmethod
def _sql_update(cls, execute, version): def _sql_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
execute(
"""
ALTER TABLE geometry_profileXYZ
ADD COLUMN sl INTEGER
REFERENCES sedimentary_layer(id)
"""
)
return cls._update_submodel(execute, version) return cls._update_submodel(execute, version)
@classmethod @classmethod
@@ -93,7 +106,7 @@ class ProfileXYZ(Profile, SQLSubModel):
reach = data["reach"] reach = data["reach"]
table = execute( table = execute(
"SELECT id, ind, name, kp, num, code1, code2 " + "SELECT id, ind, name, kp, num, code1, code2, sl " +
"FROM geometry_profileXYZ " + "FROM geometry_profileXYZ " +
f"WHERE reach = {reach}" f"WHERE reach = {reach}"
) )
@@ -109,6 +122,7 @@ class ProfileXYZ(Profile, SQLSubModel):
num = row[5] num = row[5]
code1 = row[5] code1 = row[5]
code2 = row[6] code2 = row[6]
sl = row[7]
new = cls( new = cls(
id=id, num = num, id=id, num = num,
@@ -118,7 +132,17 @@ class ProfileXYZ(Profile, SQLSubModel):
status = status status = status
) )
data["profile"] = id if sl == -1 or sl == None:
new._sl = None
else:
new._sl = next(
filter(
lambda s: s.id == sl,
data["sediment_layers_list"].sediment_layers
)
)
data["profile"] = new
new._points = PointXYZ._sql_load(execute, data) new._points = PointXYZ._sql_load(execute, data)
profiles[ind] = new profiles[ind] = new
@@ -129,18 +153,20 @@ class ProfileXYZ(Profile, SQLSubModel):
ok = True ok = True
ind = data["ind"] ind = data["ind"]
sl = self._sl.id if self._sl is not None else -1
sql = ( sql = (
"INSERT OR REPLACE INTO " + "INSERT OR REPLACE INTO " +
"geometry_profileXYZ(id, ind, name, reach, kp, num, code1, code2) "+ "geometry_profileXYZ(id, ind, name, reach, kp, num, code1, code2, sl) "+
"VALUES (" + "VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " + f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"{self.reach.id}, {self.kp}, {self.num}, " + f"{self.reach.id}, {self.kp}, {self.num}, " +
f"{self.code1}, {self.code1}" + f"{self.code1}, {self.code1}, {sl}" +
")" ")"
) )
execute(sql) execute(sql)
data["profile"] = self.id data["profile"] = self
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}") execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
ind = 0 ind = 0
@@ -178,11 +204,11 @@ class ProfileXYZ(Profile, SQLSubModel):
try: try:
if len(header) == 0: if len(header) == 0:
point = PointXYZ( point = PointXYZ(
*data, status=self._status *data, profile=self, status=self._status
) )
else: else:
valid_header = {'name', 'x', 'y', 'z'} valid_header = {'name', 'x', 'y', 'z'}
d = {"status": self._status} d = {"status": self._status, "profile": self}
for i, v in enumerate(data): for i, v in enumerate(data):
h = header[i].strip().lower().split(' ')[0] h = header[i].strip().lower().split(' ')[0]
if h in valid_header: if h in valid_header:
@@ -235,7 +261,7 @@ class ProfileXYZ(Profile, SQLSubModel):
Nothing. Nothing.
""" """
for point in list_points: for point in list_points:
pt = PointXYZ(*point, status=self._status) pt = PointXYZ(*point, profile=self, status=self._status)
self._points.append(pt) self._points.append(pt)
self._status.modified() self._status.modified()
@@ -259,7 +285,7 @@ class ProfileXYZ(Profile, SQLSubModel):
Returns: Returns:
Nothing. Nothing.
""" """
point_xyz = PointXYZ(0., 0., 0., status=self._status) point_xyz = PointXYZ(0., 0., 0., profile=self, status=self._status)
self._points.append(point_xyz) self._points.append(point_xyz)
self._status.modified() self._status.modified()
@@ -272,7 +298,7 @@ class ProfileXYZ(Profile, SQLSubModel):
Returns: Returns:
The new point. The new point.
""" """
point = PointXYZ(0., 0., 0., status=self._status) point = PointXYZ(0., 0., 0., profile=self, status=self._status)
self._points.insert(index, point) self._points.insert(index, point)
self._status.modified() self._status.modified()
return point return point
+33
View File
@@ -276,6 +276,39 @@ class Reach(SQLSubModel):
return max(self.get_kp()) return max(self.get_kp())
# Sediment Layers
def get_sl(self):
"""Get sediment layer height of profile
Get sediment layer of profile (without spesific point sl)
Returns:
List of sediment layers height
"""
res = []
psl = [profile.sl for profile in self.profiles]
# Compute max number of layers
sl_max = 0
for sl in psl:
n = 0 if sl is None else len(sl)
sl_max = max(n, sl_max)
# Create list of height for each sl and each layer
for i in range(0, sl_max):
cur = []
# Compute new layer line for each sl
for sl in psl:
if sl is not None and i < len(sl):
cur.append(sl.get(i).height)
else:
cur.append(0)
# Add layer line to result
res.append(cur)
return res
# Guidelines # Guidelines
@timer @timer
+3
View File
@@ -98,6 +98,9 @@ class River(object):
def reach(self, id): def reach(self, id):
return self._reachs[id] return self._reachs[id]
def has_reach(self, id):
return 0 <= id < len(self._reachs)
def add(self, reach_id): def add(self, reach_id):
reachs = self._study.river.enable_edges() reachs = self._study.river.enable_edges()
+31
View File
@@ -16,6 +16,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from tools import flatten
from Model.DB import SQLSubModel from Model.DB import SQLSubModel
from Model.Network.Node import Node from Model.Network.Node import Node
@@ -31,6 +33,7 @@ from Model.InitialConditions.InitialConditionsDict import InitialConditionsDict
from Model.Stricklers.StricklersList import StricklersList from Model.Stricklers.StricklersList import StricklersList
from Model.Friction.FrictionList import FrictionList from Model.Friction.FrictionList import FrictionList
from Model.SolverParameters.SolverParametersList import SolverParametersList from Model.SolverParameters.SolverParametersList import SolverParametersList
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
from Solver.Solvers import solver_type_list from Solver.Solvers import solver_type_list
@@ -208,6 +211,7 @@ class River(Graph, SQLSubModel):
InitialConditionsDict, InitialConditionsDict,
StricklersList, StricklersList,
SolverParametersList, SolverParametersList,
SedimentLayerList,
] ]
def __init__(self, status=None): def __init__(self, status=None):
@@ -223,6 +227,7 @@ class River(Graph, SQLSubModel):
self._initial_conditions = InitialConditionsDict(status=self._status) self._initial_conditions = InitialConditionsDict(status=self._status)
self._stricklers = StricklersList(status=self._status) self._stricklers = StricklersList(status=self._status)
self._parameters = {} self._parameters = {}
self._sediment_layers = SedimentLayerList(status=self._status)
@classmethod @classmethod
def _sql_create(cls, execute): def _sql_create(cls, execute):
@@ -246,6 +251,14 @@ class River(Graph, SQLSubModel):
) )
data["stricklers"] = new._stricklers data["stricklers"] = new._stricklers
# Initial conditions
new._sediment_layers = SedimentLayerList._sql_load(
execute,
data
)
data["sediment_layers_list"] = new._sediment_layers
# Network # Network
new._nodes = RiverNode._sql_load( new._nodes = RiverNode._sql_load(
execute, execute,
@@ -289,6 +302,7 @@ class River(Graph, SQLSubModel):
objs.append(self._boundary_condition) objs.append(self._boundary_condition)
objs.append(self._initial_conditions) objs.append(self._initial_conditions)
objs.append(self._lateral_contribution) objs.append(self._lateral_contribution)
objs.append(self._sediment_layers)
objs.append(self._stricklers) objs.append(self._stricklers)
for solver in self._parameters: for solver in self._parameters:
@@ -317,6 +331,10 @@ class River(Graph, SQLSubModel):
def initial_conditions(self): def initial_conditions(self):
return self._initial_conditions return self._initial_conditions
@property
def sediment_layers(self):
return self._sediment_layers
@property @property
def stricklers(self): def stricklers(self):
return self._stricklers return self._stricklers
@@ -366,3 +384,16 @@ class River(Graph, SQLSubModel):
def set_current_reach(self, reach): def set_current_reach(self, reach):
self._current_reach = reach self._current_reach = reach
def has_sediment(self):
has = len(self._sediment_layers) != 0
has &= any(
filter(
lambda p: p.sl != None,
flatten(
map(lambda e: e.reach.profiles, self.edges())
)
)
)
return has
+339
View File
@@ -0,0 +1,339 @@
# -*- coding: utf-8 -*-
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Except import NotImplementedMethodeError
class Layer(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self,
id:int = -1, name:str = "",
type = "",
height = 0.0, d50 = 0.0, sigma = 0.0,
critical_constraint = 0.0,
sl = None, status = None):
super(Layer, self).__init__()
self._status = status
self._name = name
self._type = type
self._height = height
self._d50 = d50
self._sigma = sigma
self._critical_constraint = critical_constraint
if id == -1:
self.id = Layer._id_cnt
else:
self.id = id
Layer._id_cnt = max(id, Layer._id_cnt+1)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def type(self):
return self._type
@type.setter
def type(self, type):
self._type = type
@property
def height(self):
return self._height
@height.setter
def height(self, height):
self._height = float(height)
@property
def d50(self):
return self._d50
@d50.setter
def d50(self, d50):
self._d50 = float(d50)
@property
def sigma(self):
return self._sigma
@sigma.setter
def sigma(self, sigma):
self._sigma = float(sigma)
@property
def critical_constraint(self):
return self._critical_constraint
@critical_constraint.setter
def critical_constraint(self, critical_constraint):
self._critical_constraint = float(critical_constraint)
@classmethod
def _sql_create(cls, execute):
execute("""
CREATE TABLE sedimentary_layer_layer(
id INTEGER NOT NULL PRIMARY KEY,
ind INTEGER NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL,
height REAL NOT NULL,
d50 REAL NOT NULL,
sigma REAL NOT NULL,
critical_constraint REAL NOT NULL,
sl INTEGER,
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
)
""")
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
cls._sql_create(execute)
return True
@classmethod
def _sql_load(cls, execute, data = None):
new = []
sl = data["sl"]
table = execute(
"SELECT id, ind, name, type, height, d50, sigma, critical_constraint " +
"FROM sedimentary_layer_layer " +
f"WHERE sl = {sl}"
)
for _ in table:
new.append(None)
for row in table:
ind = row[1]
layer = cls(
id = row[0], name = row[2],
type = row[3], height = row[4],
d50 = row[5], sigma = row[6],
critical_constraint = row[7],
sl = sl, status = data['status']
)
new[ind] = layer
return new
def _sql_save(self, execute, data = None):
ind = data["ind"]
sl = data["sl"]
sql = (
"INSERT INTO " +
"sedimentary_layer_layer(id, ind, name, type, height, d50, sigma, critical_constraint, sl) "+
"VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', {self._height}, " +
f"{self._d50}, {self._sigma}, {self._critical_constraint}, " +
f"{sl.id}" +
")"
)
execute(sql)
return True
class SedimentLayer(SQLSubModel):
_sub_classes = [Layer]
_id_cnt = 0
def __init__(self, id:int = -1,
name:str = "", comment:str = "",
status = None):
super(SedimentLayer, self).__init__()
self._status = status
self._name = name
self._comment = comment
self._layers = []
if id == -1:
self.id = SedimentLayer._id_cnt
else:
self.id = id
SedimentLayer._id_cnt = max(id, SedimentLayer._id_cnt+1)
def __str__(self):
s = f"{self.name} ({len(self)})"
if self.comment != "":
s += f" - {self.comment}"
return s
def __len__(self):
return len(self._layers)
@property
def layers(self):
return self._layers.copy()
def height(self):
return list(
map(lambda l: l.height, self._layers)
)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
def names(self):
return list(
map(lambda l: l.name, self._layers)
)
@property
def comment(self):
return self._comment
@comment.setter
def comment(self, comment):
self._comment = comment
@classmethod
def _sql_create(cls, execute):
execute("""
CREATE TABLE sedimentary_layer(
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
comment TEXT NOT NULL
)
""")
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
cls._sql_create(execute)
return True
@classmethod
def _sql_load(cls, execute, data = None):
new = []
table = execute(
"SELECT id, name, comment " +
"FROM sedimentary_layer "
)
for row in table:
sl = cls(
id = row[0],
name = row[1],
comment = row[2],
status = data['status']
)
data["sl"] = sl.id
sl._layers = Layer._sql_load(execute, data)
new.append(sl)
return new
def _sql_save(self, execute, data = None):
if data is None:
data = {}
sql = (
"INSERT INTO sedimentary_layer (id, name, comment) " +
f"VALUES ({self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._comment)}')"
)
execute(sql)
data["sl"] = self
ind = 0
for l in self._layers:
data["ind"] = ind
l._sql_save(execute, data)
ind += 1
return True
def get(self, index):
return self._layers[index]
def set(self, index, new):
self._layers[index] = new
self._status.modified()
def insert(self, index, new):
self._layers.insert(index, new)
self._status.modified()
def new(self, index):
n = Layer(sl=self, status = self._status)
self.insert(index, n)
self._status.modified()
return n
def delete(self, els):
for el in els:
self._layers.remove(el)
self._status.modified()
def delete_i(self, indexes):
els = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._layers)
)
)
)
self.delete(els)
def move_up(self, index):
if index >= 0:
next = index - 1
l = self._layers
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, index):
if index + 1 < len(self._layers):
prev = index + 1
l = self._layers
l[index], l[prev] = l[prev], l[index]
self._status.modified()
@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.SedimentLayer.SedimentLayer import SedimentLayer
class SedimentLayerList(SQLSubModel):
_sub_classes = [SedimentLayer]
def __init__(self, status = None):
super(SedimentLayerList, self).__init__()
self._status = status
self._sl = []
def __len__(self):
return len(self._sl)
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data["status"])
new._sl = SedimentLayer._sql_load(execute, data)
return new
def _sql_save(self, execute, data = None):
ok = True
# Delete previous data
execute("DELETE FROM sedimentary_layer")
execute("DELETE FROM sedimentary_layer_layer")
for sl in self._sl:
ok &= sl._sql_save(execute, data)
return ok
@property
def sediment_layers(self):
return self._sl.copy()
def get(self, index):
return self._sl[index]
def set(self, index, new):
self._sl[index] = new
self._status.modified()
def insert(self, index, new):
self._sl.insert(index, new)
self._status.modified()
def new(self, index):
n = SedimentLayer(status = self._status)
self.insert(index, n)
self._status.modified()
return n
def delete(self, els):
for el in els:
self._sl.remove(el)
self._status.modified()
def delete_i(self, indexes):
els = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._sl)
)
)
)
self.delete(els)
def move_up(self, index):
if index < len(self._sl):
next = index - 1
l = self._sl
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
l = self._sl
l[index], l[prev] = l[prev], l[index]
self._status.modified()
@@ -93,6 +93,13 @@ class SolverParametersList(SQLSubModel):
@classmethod @classmethod
def _sql_update(cls, execute, version): def _sql_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 3:
execute(f"UPDATE solver_parameter SET name='mage_implicitation' WHERE name='mage_implication'")
execute(f"UPDATE solver_parameter SET name='mage_iteration_type' WHERE name='mage_iter_type'")
return cls._update_submodel(execute, version) return cls._update_submodel(execute, version)
@classmethod @classmethod
@@ -160,6 +167,17 @@ class SolverParametersList(SQLSubModel):
def get(self, index): def get(self, index):
return self._parameters[index] return self._parameters[index]
def get_by_key(self, key):
try:
return next(
filter(
lambda p: p["name"] == key,
self._parameters
)
)["value"]
except:
return None
def set(self, index, new): def set(self, index, new):
self._parameters[index] = new self._parameters[index] = new
self._status.modified() self._status.modified()
+1 -1
View File
@@ -40,7 +40,7 @@ class Study(SQLModel):
def __init__(self, filename = None, init_new = True): def __init__(self, filename = None, init_new = True):
# Metadata # Metadata
self._version = "0.0.1" self._version = "0.0.3"
self.creation_date = datetime.now() self.creation_date = datetime.now()
self.last_modification_date = datetime.now() self.last_modification_date = datetime.now()
self.last_save_date = datetime.now() self.last_save_date = datetime.now()
+44 -21
View File
@@ -86,6 +86,7 @@ class AbstractSolver(object):
("all_init_time", "000:00:00:00"), ("all_init_time", "000:00:00:00"),
("all_final_time", "999:99:00:00"), ("all_final_time", "999:99:00:00"),
("all_timestep", "300.0"), ("all_timestep", "300.0"),
("all_command_line_arguments", ""),
] ]
return lst return lst
@@ -153,6 +154,17 @@ class AbstractSolver(object):
def export(self, study, repertory, qlog = None): def export(self, study, repertory, qlog = None):
raise NotImplementedMethodeError(self, self.export) raise NotImplementedMethodeError(self, self.export)
def cmd_args(self, study):
"""Return solver command line arguments list
Returns:
Command line arguments list
"""
params = study.river.get_params(self.type)
args = params.get_by_key("all_command_line_arguments")
return args.split(" ")
def input_param(self): def input_param(self):
"""Return input command line parameter(s) """Return input command line parameter(s)
@@ -190,7 +202,7 @@ class AbstractSolver(object):
) )
) )
def _format_command(self, cmd, path = ""): def _format_command(self, study, cmd, path = ""):
"""Format command line """Format command line
Args: Args:
@@ -206,6 +218,7 @@ class AbstractSolver(object):
cmd = cmd.replace("@path", path.replace(" ", "\ ")) cmd = cmd.replace("@path", path.replace(" ", "\ "))
cmd = cmd.replace("@input", self.input_param()) cmd = cmd.replace("@input", self.input_param())
cmd = cmd.replace("@dir", self._process.workingDirectory()) cmd = cmd.replace("@dir", self._process.workingDirectory())
cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
logger.debug(f"! {cmd}") logger.debug(f"! {cmd}")
@@ -213,23 +226,33 @@ class AbstractSolver(object):
# Command line executable path is between " char # Command line executable path is between " char
cmd = cmd.split("\"") cmd = cmd.split("\"")
exe = cmd[1].replace("\ ", " ") exe = cmd[1].replace("\ ", " ")
args = "\"".join(cmd[2:]).split(" ")[1:] args = list(
filter(
lambda s: s != "",
"\"".join(cmd[2:]).split(" ")[1:]
)
)
else: else:
# We suppose the command line executable path as no space char # We suppose the command line executable path as no space char
cmd = cmd.replace("\ ", "&_&").split(" ") cmd = cmd.replace("\ ", "&_&").split(" ")
exe = cmd[0].replace("&_&", " ") exe = cmd[0].replace("&_&", " ")
args = list(map(lambda s: s.replace("&_&", "\ "), cmd[1:])) args = list(
filter(
lambda s: s != "",
map(lambda s: s.replace("&_&", "\ "), cmd[1:])
)
)
logger.info(f"! {exe} {args}") logger.info(f"! {exe} {args}")
return exe, args return exe, args
def run_input_data_fomater(self): def run_input_data_fomater(self, study):
if self._cmd_input == "": if self._cmd_input == "":
self._run_next() self._run_next(study)
return True return True
cmd = self._cmd_input cmd = self._cmd_input
exe, args = self._format_command(cmd, self._path_input) exe, args = self._format_command(study, cmd, self._path_input)
if not os.path.exists(exe): if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists" error = f"[ERROR] Path {exe} do not exists"
@@ -242,13 +265,13 @@ class AbstractSolver(object):
return True return True
def run_solver(self): def run_solver(self, study):
if self._cmd_solver == "": if self._cmd_solver == "":
self._run_next() self._run_next(study)
return True return True
cmd = self._cmd_solver cmd = self._cmd_solver
exe, args = self._format_command(cmd, self._path_solver) exe, args = self._format_command(study, cmd, self._path_solver)
if not os.path.exists(exe): if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists" error = f"[ERROR] Path {exe} do not exists"
@@ -262,13 +285,13 @@ class AbstractSolver(object):
self._status = STATUS.RUNNING self._status = STATUS.RUNNING
return True return True
def run_output_data_fomater(self): def run_output_data_fomater(self, study):
if self._cmd_output == "": if self._cmd_output == "":
self._run_next() self._run_next(study)
return True return True
cmd = self._cmd_output cmd = self._cmd_output
exe, args = self._format_command(cmd, self._path_output) exe, args = self._format_command(study, cmd, self._path_output)
if not os.path.exists(exe): if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists" error = f"[ERROR] Path {exe} do not exists"
@@ -287,29 +310,29 @@ class AbstractSolver(object):
for x in s.split('\n'): for x in s.split('\n'):
self._output.put(x) self._output.put(x)
def _run_next(self): def _run_next(self, study):
self._step += 1 self._step += 1
if self._step < len(self._runs): if self._step < len(self._runs):
res = self._runs[self._step]() res = self._runs[self._step](study)
if res is not True: if res is not True:
self._output.put(res) self._output.put(res)
else: else:
self._status = STATUS.STOPED self._status = STATUS.STOPED
def _finished(self, exit_code, exit_status): def _finished(self, study, exit_code, exit_status):
if self._output is not None: if self._output is not None:
self._output.put(exit_code) self._output.put(exit_code)
self._run_next() self._run_next(study)
def run(self, process = None, output_queue = None): def run(self, study, process = None, output_queue = None):
if process is not None: if process is not None:
self._process = process self._process = process
if output_queue is not None: if output_queue is not None:
self._output = output_queue self._output = output_queue
self._process.readyRead.connect(self._data_ready) self._process.readyRead.connect(self._data_ready)
self._process.finished.connect(self._finished) self._process.finished.connect(lambda c, s: self._finished(study, c, s))
self._runs = [ self._runs = [
self.run_input_data_fomater, self.run_input_data_fomater,
@@ -318,7 +341,7 @@ class AbstractSolver(object):
] ]
self._step = 0 self._step = 0
# Run first step # Run first step
res = self._runs[0]() res = self._runs[0](study)
if res is not True: if res is not True:
self._output.put(res) self._output.put(res)
@@ -330,14 +353,14 @@ class AbstractSolver(object):
self._status = STATUS.STOPED self._status = STATUS.STOPED
return True return True
def start(self, process = None): def start(self, study, process = None):
if _signal: if _signal:
if self._status == STATUS.PAUSED: if self._status == STATUS.PAUSED:
os.kill(self._process.pid(), SIGCONT) os.kill(self._process.pid(), SIGCONT)
self._status = STATUS.RUNNING self._status = STATUS.RUNNING
return True return True
self.run(process) self.run(study, process)
return True return True
def pause(self): def pause(self):
+246 -10
View File
@@ -61,11 +61,11 @@ class Mage(AbstractSolver):
("mage_timestep_tra", "3600"), ("mage_timestep_tra", "3600"),
("mage_timestep_bin", "0"), ("mage_timestep_bin", "0"),
# ("mage_timestep_melissa", "0"), # ("mage_timestep_melissa", "0"),
("mage_implication", "0.70"), ("mage_implicitation", "0.70"),
("mage_continuity_discretization", "S"), ("mage_continuity_discretization", "S"),
("mage_qsj_discretization", "B"), ("mage_qsj_discretization", "B"),
("mage_stop_criterion_iterations", "R"), ("mage_stop_criterion_iterations", "R"),
("mage_iter_type", "0"), ("mage_iteration_type", "0"),
("mage_smooth_coef", "0"), ("mage_smooth_coef", "0"),
("mage_cfl_max", "-1."), ("mage_cfl_max", "-1."),
("mage_min_height", "0.1"), ("mage_min_height", "0.1"),
@@ -97,6 +97,13 @@ class Mage(AbstractSolver):
# Export # # Export #
########## ##########
def cmd_args(self, study):
l = super(Mage, self).cmd_args(study)
l.append("-r")
return l
def input_param(self): def input_param(self):
return "0.REP" return "0.REP"
@@ -127,23 +134,47 @@ class Mage(AbstractSolver):
with mage_file_open(os.path.join(repertory, f"{name}.ST"), "w+") as f: with mage_file_open(os.path.join(repertory, f"{name}.ST"), "w+") as f:
files.append(f"{name}.ST") files.append(f"{name}.ST")
cnt_num = 1
for profile in edge.reach.profiles: for profile in edge.reach.profiles:
num = f"{profile.num:>6}" num = f"{cnt_num:>6}"
c1 = f"{profile.code1:>6}" c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}" c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}" t = f"{len(profile.points):>6}"
kp = f"{profile.kp:>13.4f}" kp = f"{profile.kp:>13.4f}"
name = profile.name pname = profile.name
if profile.name == "":
pname = f"p{profile.id:>3}".replace(" ", "p")
name = f"{pname}"
f.write(f"{num}{c1}{c2}{t}{kp} {name}\n") sediment = ""
if profile.sl is not None:
if not any(filter(lambda f: ".GRA" in f, files)):
files.append("0.GRA")
nl = len(profile.sl)
sediment = f" {nl:>3}"
for l in profile.sl.layers:
sediment += f" {l.height:>10} {l.d50:>10} {l.sigma:>10} {l.critical_constraint:>10}"
f.write(f"{num}{c1}{c2}{t}{kp} {name} {sediment}\n")
cnt_num += 1
for point in profile.points: for point in profile.points:
x = f"{point.x:>13.4f}" x = f"{point.x:>13.4f}"
y = f"{point.y:>13.4f}" y = f"{point.y:>13.4f}"
z = f"{point.z:>13.4f}" z = f"{point.z:>13.4f}"
n = point.name n = f"{point.name:<3}"
f.write(f"{x}{y}{z} {n}\n") sediment = ""
prev = point.z
if point.sl is not None:
nl = len(point.sl)
sediment = f"{nl:>3}"
for l in point.sl.layers:
prev = round(prev - l.height, 5)
sediment += f" {prev:>10} {l.d50:>10} {l.sigma:>10} {l.critical_constraint:>10}"
f.write(f"{x}{y}{z} {n} {sediment}\n")
f.write(f" 999.9990 999.9990 999.9990\n") f.write(f" 999.9990 999.9990 999.9990\n")
@@ -170,7 +201,7 @@ class Mage(AbstractSolver):
f.write(f"*{header[0]:>9}|{header[1]:>10}\n") f.write(f"*{header[0]:>9}|{header[1]:>10}\n")
for d in bound.data: for d in bound.data:
f.write(f"{d[0]:10.3f}{d[1]:10.3f}\n") f.write(f"{d[0]:10}{d[1]:10}\n")
return files return files
@@ -182,6 +213,7 @@ class Mage(AbstractSolver):
AVA = [] AVA = []
HYD = [] HYD = []
LIM = [] LIM = []
QSO = []
for tab in ["liquid", "solid", "suspenssion"]: for tab in ["liquid", "solid", "suspenssion"]:
for bound in lst.get_tab(tab): for bound in lst.get_tab(tab):
@@ -191,10 +223,13 @@ class Mage(AbstractSolver):
HYD.append(bound) HYD.append(bound)
elif bound.bctype == "TZ": elif bound.bctype == "TZ":
LIM.append(bound) LIM.append(bound)
elif bound.bctype == "SL":
QSO.append(bound)
files = files + self._export_BC("AVA", AVA, repertory, qlog) files = files + self._export_BC("AVA", AVA, repertory, qlog)
files = files + self._export_BC("HYD", HYD, repertory, qlog) files = files + self._export_BC("HYD", HYD, repertory, qlog)
files = files + self._export_BC("LIM", LIM, repertory, qlog) files = files + self._export_BC("LIM", LIM, repertory, qlog)
files = files + self._export_QSO(QSO, repertory, qlog)
return files return files
@@ -332,12 +367,20 @@ class Mage(AbstractSolver):
for file in files: for file in files:
EXT = file.split('.')[1] EXT = file.split('.')[1]
if EXT not in ["ST", "GRA"]:
f.write(f"{EXT} {file}\n") f.write(f"{EXT} {file}\n")
f.write("* OUTPUT\n") f.write("* OUTPUT\n")
f.write(f"TRA 0.TRA\n") f.write(f"TRA 0.TRA\n")
f.write(f"BIN 0.BIN\n") f.write(f"BIN 0.BIN\n")
for file in files:
EXT = file.split('.')[1]
if EXT in ["GRA"]:
f.write(f"{EXT} {file}\n")
@timer @timer
def export(self, study, repertory, qlog = None): def export(self, study, repertory, qlog = None):
self._export_ST(study, repertory, qlog) self._export_ST(study, repertory, qlog)
@@ -404,6 +447,14 @@ class Mage8(Mage):
# Export # # Export #
########## ##########
def cmd_args(self, study):
l = super(Mage8, self).cmd_args(study)
if study.river.has_sediment():
l.append("-c=3")
return l
@timer @timer
def _export_PAR(self, study, repertory, qlog = None): def _export_PAR(self, study, repertory, qlog = None):
files = [] files = []
@@ -423,7 +474,6 @@ class Mage8(Mage):
f.write(f"{name} {value}\n") f.write(f"{name} {value}\n")
return files return files
@timer @timer
@@ -459,11 +509,37 @@ class Mage8(Mage):
return files return files
@timer
def _export_QSO(self, bounds, repertory, qlog):
files = []
if len(bounds) == 0:
return files
if qlog is not None:
qlog.put(f"Export QSO file")
with mage_file_open(os.path.join(repertory, f"0.QSO"), "w+") as f:
files.append(f"0.QSO")
for bound in bounds:
name = f"{bound.node.id:3}".replace(" ", "x")
f.write(f"* {bound.node.name} ({name}) {bound.bctype}\n")
f.write(f"${name}\n")
header = bound.header
f.write(f"*{header[0]:>9}|{header[1]:>10}\n")
for d in bound.data:
f.write(f"{d[0]:10.3f}{d[1]:10.3f}\n")
return files
@timer @timer
def export(self, study, repertory, qlog = None): def export(self, study, repertory, qlog = None):
files = [] files = []
self._export_ST(study, repertory, qlog) files = self._export_ST(study, repertory, qlog)
files = files + self._export_PAR(study, repertory, qlog) files = files + self._export_PAR(study, repertory, qlog)
files = files + self._export_NET(study, repertory, qlog) files = files + self._export_NET(study, repertory, qlog)
files = files + self._export_bound_cond(study, repertory, qlog) files = files + self._export_bound_cond(study, repertory, qlog)
@@ -477,6 +553,7 @@ class Mage8(Mage):
# RESULTS # # RESULTS #
########### ###########
@timer
def read_bin(self, study, repertory, results, qlog = None): def read_bin(self, study, repertory, results, qlog = None):
logger.info(f"read_bin: Start ...") logger.info(f"read_bin: Start ...")
@@ -597,3 +674,162 @@ class Mage8(Mage):
logger.debug(reachs[0].profiles[0]._data) logger.debug(reachs[0].profiles[0]._data)
results.set("timestamps", ts) results.set("timestamps", ts)
logger.info(f"read_bin: ... end with {len(ts)} timestamp read") logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
@timer
def read_gra(self, study, repertory, results, qlog = None):
if not study.river.has_sediment():
return
logger.info(f"read_gra: Start ...")
with mage_file_open(os.path.join(repertory, f"0.GRA"), "r") as f:
newline = lambda: np.fromfile(f, dtype=np.int32, count=1)
endline = lambda: np.fromfile(f, dtype=np.int32, count=1)
read_int = lambda size: np.fromfile(f, dtype=np.int32, count=size)
read_float = lambda size: np.fromfile(f, dtype=np.float32, count=size)
read_float64 = lambda size: np.fromfile(f, dtype=np.float64, count=size)
# Meta data (1st line)
newline()
data = read_int(3)
nb_reach = data[0]
nb_profile = data[1]
mage_version = data[2]
logger.debug(f"read_gra: nb_reach = {nb_reach}")
logger.debug(f"read_gra: nb_profile = {nb_profile}")
logger.debug(f"read_gra: mage_version = {mage_version}")
if mage_version <= 80:
msg = (
"Read GRA files: " +
f"Possible incompatible mage version '{mage_version}', " +
"please check your solver configuration..."
)
logger.warning(msg)
if qlog is not None:
qlog.put("[WARNING] " + msg)
results.set("gra_solver_version", f"Mage8 ({mage_version})")
results.set("gra_nb_reach", f"{nb_reach}")
results.set("gra_nb_profile", f"{nb_profile}")
endline()
# Reach information (2nd line)
newline()
reachs = []
iprofiles = {}
reach_offset = {}
data = read_int(2*nb_reach)
for i in range(nb_reach):
# Get results reach to reach list
r = results.river.reach(i)
reachs.append(r)
# ID of first and last reach profiles
i1 = data[2*i] - 1
i2 = data[2*i+1] - 1
# Add profile id correspondance to reach
key = (i1, i2)
iprofiles[key] = r
# Profile ID offset
reach_offset[r] = i1
logger.debug(f"read_gra: iprofiles = {iprofiles}")
endline()
# X (3rd line)
newline()
_ = read_float(nb_profile)
endline()
# npts (4th line)
newline()
_ = read_int(nb_profile)
endline()
# Data
ip_to_r = lambda i: iprofiles[
next(
filter(
lambda k: k[0] <= i <= k[1],
iprofiles
)
)
]
ip_to_ri = lambda r, i: i - reach_offset[r]
ts = set()
ind = 0
end = False
newline()
while not end:
n = read_int(1)[0]
timestamp = read_float64(1)[0]
logger.debug(f"read_gra: timestamp = {timestamp} sec")
ts.add(timestamp)
endline()
for i in range(n):
newline()
nsl = read_int(1)[0]
endline()
# Get current profile id
reach = ip_to_r(i)
ri = ip_to_ri(reach, i)
if nsl > 1:
logger.warning(
"read_gra: " +
"Multiple sediment layers for one profile " +
"is not implemented yet..."
)
for j in range(nsl):
newline()
nl = read_int(1)[0]
endline()
sl = []
for k in range(nl):
newline()
data = read_float64(3)
endline()
h = data[0]
d50 = data[1]
sigma = data[2]
sl.append((h, d50, sigma))
reach.set(ri, timestamp, "sl", sl)
ind += 1
end = newline().size <= 0
logger.debug(reachs[0].profiles[0]._data)
results.set("timestamps", ts)
logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
@timer
def results(self, study, repertory, qlog = None):
results = super(Mage8, self).results(study, repertory, qlog)
self.read_gra(study, repertory, results, qlog)
return results
+2 -1
View File
@@ -26,5 +26,6 @@ table_headers = {
"time": _translate("BoundaryCondition", "Time"), "time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"), "date": _translate("BoundaryCondition", "Date"),
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"), "discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
"z": _translate("BoundaryCondition", "Z (m)") "z": _translate("BoundaryCondition", "Z (m)"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
} }
+5 -2
View File
@@ -20,7 +20,8 @@ from PyQt5.QtCore import QCoreApplication
from Model.BoundaryCondition.BoundaryConditionTypes import ( from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, PonctualContribution, NotDefined, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge TimeOverZ, TimeOverDischarge, ZOverDischarge,
Solid
) )
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
@@ -31,6 +32,7 @@ long_types = {
"TZ": _translate("BoundaryCondition", "Time over Z"), "TZ": _translate("BoundaryCondition", "Time over Z"),
"TD": _translate("BoundaryCondition", "Time over Discharge"), "TD": _translate("BoundaryCondition", "Time over Discharge"),
"ZD": _translate("BoundaryCondition", "Z over Discharge"), "ZD": _translate("BoundaryCondition", "Z over Discharge"),
"SL": _translate("BoundaryCondition", "Solid"),
} }
table_headers = { table_headers = {
@@ -44,5 +46,6 @@ BC_types = {
"PC": PonctualContribution, "PC": PonctualContribution,
"TZ": TimeOverZ, "TZ": TimeOverZ,
"TD": TimeOverDischarge, "TD": TimeOverDischarge,
"ZD": ZOverDischarge "ZD": ZOverDischarge,
"SL": Solid,
} }
+3 -3
View File
@@ -49,7 +49,7 @@ from View.Frictions.Table import (
) )
from View.Plot.MplCanvas import MplCanvas from View.Plot.MplCanvas import MplCanvas
from View.Geometry.PlotKPC import PlotKPC from View.Geometry.PlotKPZ import PlotKPZ
from View.Frictions.PlotStricklers import PlotStricklers from View.Frictions.PlotStricklers import PlotStricklers
from View.Frictions.translate import * from View.Frictions.translate import *
@@ -128,7 +128,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
self.plot_layout = self.find(QVBoxLayout, "verticalLayout") self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.canvas) self.plot_layout.addWidget(self.canvas)
self.plot = PlotKPC( self.plot = PlotKPZ(
canvas = self.canvas, canvas = self.canvas,
data = self._reach.reach, data = self._reach.reach,
toolbar = None, toolbar = None,
@@ -193,7 +193,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
sec = self._frictions.get(rows[0]) sec = self._frictions.get(rows[0])
highlight = (sec.begin_kp, sec.end_kp) highlight = (sec.begin_kp, sec.end_kp)
self.plot = PlotKPC( self.plot = PlotKPZ(
canvas = self.canvas, canvas = self.canvas,
data = reach, data = reach,
toolbar = None, toolbar = None,
@@ -29,10 +29,10 @@ _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class PlotKPC(APlot): class PlotKPZ(APlot):
def __init__(self, canvas=None, data=None, toolbar=None, def __init__(self, canvas=None, data=None, toolbar=None,
display_current=True): display_current=True):
super(PlotKPC, self).__init__( super(PlotKPZ, self).__init__(
canvas=canvas, canvas=canvas,
data=data, data=data,
toolbar=toolbar toolbar=toolbar
@@ -64,7 +64,7 @@ class PlotKPC(APlot):
color='green', fontsize=12 color='green', fontsize=12
) )
self.canvas.axes.set_ylabel( self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Cote (m)"), _translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12 color='green', fontsize=12
) )
@@ -234,7 +234,7 @@ class PlotKPC(APlot):
kp, z_complete[i] kp, z_complete[i]
) )
except Exception as e: except Exception as e:
logger.warning(f"Failed to update graphic KPC: {e}") logger.warning(f"Failed to update graphic KPZ: {e}")
self.canvas.axes.autoscale_view(True, True, True) self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.canvas.draw_idle() self.canvas.figure.canvas.draw_idle()
+2 -2
View File
@@ -38,7 +38,7 @@ from PyQt5.QtWidgets import (
) )
from View.Geometry.PlotXY import PlotXY from View.Geometry.PlotXY import PlotXY
from View.Geometry.PlotKPC import PlotKPC from View.Geometry.PlotKPZ import PlotKPZ
from View.Geometry.PlotAC import PlotAC from View.Geometry.PlotAC import PlotAC
from View.ASubWindow import ASubMainWindow, WindowToolKit from View.ASubWindow import ASubMainWindow, WindowToolKit
@@ -230,7 +230,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
def plot_kpc(self): def plot_kpc(self):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
self._plot_kpc = PlotKPC( self._plot_kpc = PlotKPZ(
canvas = self.ui.canvas_2, canvas = self.ui.canvas_2,
data = self._reach, data = self._reach,
toolbar = self.ui.toolbar_2 toolbar = self.ui.toolbar_2
+39 -1
View File
@@ -18,6 +18,7 @@
import os import os
import logging import logging
import subprocess
from queue import Queue from queue import Queue
from PyQt5 import QtGui from PyQt5 import QtGui
@@ -49,6 +50,8 @@ from View.LateralContribution.Window import LateralContributionWindow
from View.InitialConditions.Window import InitialConditionsWindow from View.InitialConditions.Window import InitialConditionsWindow
from View.Stricklers.Window import StricklersWindow from View.Stricklers.Window import StricklersWindow
from View.Frictions.Window import FrictionsWindow from View.Frictions.Window import FrictionsWindow
from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.SolverParameters.Window import SolverParametersWindow from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
from View.CheckList.Window import CheckListWindow from View.CheckList.Window import CheckListWindow
@@ -87,7 +90,8 @@ define_model_action = [
"action_menu_edit_network", "action_menu_edit_geometry", "action_menu_edit_network", "action_menu_edit_geometry",
"action_menu_boundary_conditions", "action_menu_initial_conditions", "action_menu_boundary_conditions", "action_menu_initial_conditions",
"action_menu_edit_friction", "action_menu_edit_lateral_contribution", "action_menu_edit_friction", "action_menu_edit_lateral_contribution",
"action_menu_run_solver", "action_menu_run_solver", "action_menu_sediment_layers",
"action_menu_edit_reach_sediment_layers"
] ]
action = ( action = (
@@ -170,6 +174,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_edit_friction": self.open_frictions, "action_menu_edit_friction": self.open_frictions,
"action_menu_edit_lateral_contribution": self.open_lateral_contrib, "action_menu_edit_lateral_contribution": self.open_lateral_contrib,
"action_menu_run_solver": self.run_solver, "action_menu_run_solver": self.run_solver,
"action_menu_sediment_layers": self.open_sediment_layers,
"action_menu_edit_reach_sediment_layers": self.open_reach_sediment_layers,
## Help ## Help
"action_menu_about": self.open_about, "action_menu_about": self.open_about,
# ToolBar action # ToolBar action
@@ -258,15 +264,22 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.debug_action.setStatusTip("Debug") self.debug_action.setStatusTip("Debug")
self.debug_action.triggered.connect(self.open_debug) self.debug_action.triggered.connect(self.open_debug)
self.debug_sqlite_action = QAction("Debug SQLite", self)
self.debug_sqlite_action.setStatusTip("Open SQLite debuging tool (sqlitebrowser)")
self.debug_sqlite_action.triggered.connect(self.open_sqlite)
if self.conf.debug: if self.conf.debug:
menu.addAction(self.debug_action) menu.addAction(self.debug_action)
menu.addAction(self.debug_sqlite_action)
self.set_debug_lvl(debug = True) self.set_debug_lvl(debug = True)
else: else:
if self.conf.debug: if self.conf.debug:
menu.addAction(self.debug_action) menu.addAction(self.debug_action)
menu.addAction(self.debug_sqlite_action)
self.set_debug_lvl(debug = True) self.set_debug_lvl(debug = True)
else: else:
menu.removeAction(self.debug_action) menu.removeAction(self.debug_action)
menu.removeAction(self.debug_sqlite_action)
self.set_debug_lvl(debug = False) self.set_debug_lvl(debug = False)
######### #########
@@ -617,6 +630,20 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
else: else:
params.activateWindow() params.activateWindow()
def open_sediment_layers(self):
sl = SedimentLayersWindow(
study = self.model,
parent = self
)
sl.show()
def open_reach_sediment_layers(self):
sl = ReachSedimentLayersWindow(
study = self.model,
parent = self
)
sl.show()
def run_solver(self): def run_solver(self):
if self.model is None: if self.model is None:
return return
@@ -683,6 +710,17 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
repl.show() repl.show()
def open_sqlite(self):
if self.model is None:
logger.debug("No study open for sql debuging...")
return
file = self.model.filename
_ = subprocess.Popen(
f"sqlitebrowser {file}",
shell=True
)
# TODO: Delete me ! # TODO: Delete me !
############### ###############
# DUMMY STUFF # # DUMMY STUFF #
+48 -4
View File
@@ -19,6 +19,7 @@
import logging import logging
from functools import reduce from functools import reduce
from datetime import datetime
from tools import timer, trace from tools import timer, trace
from View.Plot.APlot import APlot from View.Plot.APlot import APlot
@@ -34,14 +35,14 @@ logger = logging.getLogger()
class PlotH(APlot): class PlotH(APlot):
def __init__(self, canvas=None, results=None, def __init__(self, canvas=None, results=None,
reach_id=0, profile_id=0, reach_id=0, profile_id=0,
toolbar=None, display_current=True): toolbar=None):
super(PlotH, self).__init__( super(PlotH, self).__init__(
canvas=canvas, canvas=canvas,
data=results, data=results,
toolbar=toolbar toolbar=toolbar
) )
self.display_current = display_current self._mode = "time"
self._current_timestamp = max(results.get("timestamps")) self._current_timestamp = max(results.get("timestamps"))
self._current_reach_id = reach_id self._current_reach_id = reach_id
@@ -99,6 +100,10 @@ class PlotH(APlot):
) )
return return
self.canvas.axes.set_ylim(
[min(min(y),0), max(y)]
)
self._line = [ self._line = [
self.canvas.axes.plot( self.canvas.axes.plot(
x, y, lw=1., x, y, lw=1.,
@@ -107,8 +112,47 @@ class PlotH(APlot):
) )
] ]
self.canvas.axes.autoscale_view(True, True, True) # Custom time display
self.canvas.axes.autoscale() nb = len(x)
mod = int(nb / 5)
mod = mod if mod > 0 else nb
fx = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] % mod == 0,
enumerate(x)
)
)
)
if self._mode == "time":
t0 = datetime.fromtimestamp(0)
xt = list(
map(
lambda v: (
str(
datetime.fromtimestamp(v) - t0
).split(",")[0]\
.replace("days", _translate("Results", "days"))\
.replace("day", _translate("Results", "day"))
),
fx
)
)
else:
xt = list(
map(
lambda v: str(datetime.fromtimestamp(v).date()),
fx
)
)
self.canvas.axes.set_xticks(ticks=fx, labels=xt, rotation=45)
# self.canvas.axes.autoscale_view(True, True, True)
# self.canvas.axes.autoscale()
self.canvas.figure.tight_layout() self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle() self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None: if self.toolbar is not None:
+130
View File
@@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class PlotSedProfile(APlot):
def __init__(self, canvas=None, study=None, results=None,
reach_id=0, profile_id=0,
toolbar=None):
super(PlotSedProfile, self).__init__(
canvas=canvas,
data=study,
toolbar=toolbar
)
self._results = results
self._current_timestamp = max(results.get("timestamps"))
self._current_reach_id = reach_id
self._current_profile_id = profile_id
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if self.data is None:
return
reach = self._results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id)
if profile.geometry.number_points == 0:
return
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
color='green', fontsize=12
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12
)
x = profile.geometry.get_station()
z = profile.geometry.z()
psl = list(
map(
lambda sl: sl[0],
profile.get_ts_key(self._current_timestamp, "sl")
)
)
sl = []
for i in range(len(psl)):
cur = []
for p in range(profile.geometry.number_points):
cur.append(psl[i])
sl.append(cur)
# logger.info(sl)
self.canvas.axes.set_xlim(
left = min(x), right = max(x)
)
# Compute sediment layer in function to point z
z_sl = reduce(
lambda acc, v: acc + [
list(
map(lambda x, y: y - x, v, acc[-1])
)
],
sl,
[z]
)
self.line_kp_sl = []
for i, zsl in enumerate(reversed(z_sl)):
self.line_kp_sl.append(None)
self.line_kp_sl[i], = self.canvas.axes.plot(
x, zsl,
linestyle="solid" if i == len(z_sl) - 1 else "--",
lw=1.8,
color='grey' if i == len(z_sl) - 1 else None
)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self._init = False
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
if ind is None:
logger.info("TODO: Update")
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.canvas.draw_idle()
def set_reach(self, reach_id):
self._current_reach_id = reach_id
self._current_profile_id = 0
self.draw()
def set_profile(self, profile_id):
self._current_profile_id = profile_id
self.draw()
def set_timestamp(self, timestamp):
self._current_timestamp = timestamp
self.draw()
+134
View File
@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class PlotSedReach(APlot):
def __init__(self, canvas=None, study=None, results=None,
reach_id=0, profile_id=0,
toolbar=None):
super(PlotSedReach, self).__init__(
canvas=canvas,
data=study,
toolbar=toolbar
)
self._results = results
self._current_timestamp = max(results.get("timestamps"))
self._current_reach_id = reach_id
self._current_profile_id = profile_id
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if self.data is None:
return
reach = self._results.river.reach(self._current_reach_id)
if reach.geometry.number_profiles == 0:
return
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Kp (m)"),
color='green', fontsize=12
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12
)
kp = reach.geometry.get_kp()
z_min = reach.geometry.get_z_min()
z_max = reach.geometry.get_z_max()
psl = list(
map(
# Get SL list for profile p
lambda p: p.get_ts_key(self._current_timestamp, "sl"),
reach.profiles
)
)
max_sl = reduce(
lambda acc, sl: max(acc, len(sl)),
psl,
0
)
sl = []
for i in range(max_sl):
cur = []
for csl in psl:
if i < len(csl):
cur.append(csl[i][0])
else:
cur.append(0)
sl.append(cur)
# logger.info(f"sl = {sl}")
self.canvas.axes.set_xlim(
left = min(kp), right = max(kp)
)
# Compute sediment layer in function to profile z_min
z_sl = reduce(
lambda acc, v: acc + [
list(
map(lambda x, y: y - x, v, acc[-1])
)
],
sl,
[z_min]
)
self.line_kp_sl = []
for i, z in enumerate(reversed(z_sl)):
self.line_kp_sl.append(None)
self.line_kp_sl[i], = self.canvas.axes.plot(
kp, z,
linestyle="solid" if i == len(z_sl) - 1 else "--",
lw=1.8,
color='grey' if i == len(z_sl) - 1 else None
)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self._init = False
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
def set_reach(self, reach_id):
self._current_reach_id = reach_id
self._current_profile_id = 0
self.draw()
def set_profile(self, profile_id):
self._current_profile_id = profile_id
self.draw()
def set_timestamp(self, timestamp):
self._current_timestamp = timestamp
self.draw()
+50
View File
@@ -46,6 +46,8 @@ from View.Results.PlotXY import PlotXY
from View.Results.PlotAC import PlotAC from View.Results.PlotAC import PlotAC
from View.Results.PlotKPC import PlotKPC from View.Results.PlotKPC import PlotKPC
from View.Results.PlotH import PlotH from View.Results.PlotH import PlotH
from View.Results.PlotSedReach import PlotSedReach
from View.Results.PlotSedProfile import PlotSedProfile
from View.Results.Table import TableModel from View.Results.Table import TableModel
from View.Results.translate import * from View.Results.translate import *
@@ -180,6 +182,38 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
) )
self.plot_h.draw() self.plot_h.draw()
self.canvas_5 = MplCanvas(width=5, height=4, dpi=100)
self.canvas_5.setObjectName("canvas_5")
self.plot_layout_5 = self.find(QVBoxLayout, "verticalLayout_sed_reach")
self.plot_layout_5.addWidget(self.canvas_5)
if self._study.river.has_sediment():
self.plot_sed_reach = PlotSedReach(
canvas = self.canvas_5,
results = self._results,
study = self._study,
reach_id = 0,
profile_id = 0,
toolbar = None
)
self.plot_sed_reach.draw()
self.canvas_6 = MplCanvas(width=5, height=4, dpi=100)
self.canvas_6.setObjectName("canvas_6")
self.plot_layout_6 = self.find(QVBoxLayout, "verticalLayout_sed_profile")
self.plot_layout_6.addWidget(self.canvas_6)
if self._study.river.has_sediment():
self.plot_sed_profile = PlotSedProfile(
canvas = self.canvas_6,
results = self._results,
study = self._study,
reach_id = 0,
profile_id = 0,
toolbar = None
)
self.plot_sed_profile.draw()
def setup_connections(self): def setup_connections(self):
self.undo_sc.activated.connect(self.undo) self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo) self.redo_sc.activated.connect(self.redo)
@@ -210,22 +244,38 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
self.plot_ac.set_reach(reach_id) self.plot_ac.set_reach(reach_id)
self.plot_kpc.set_reach(reach_id) self.plot_kpc.set_reach(reach_id)
self.plot_h.set_reach(reach_id) self.plot_h.set_reach(reach_id)
if self._study.river.has_sediment():
self.plot_sed_reach.set_reach(reach_id)
self.plot_sed_profile.set_reach(reach_id)
if profile_id is not None: if profile_id is not None:
self.plot_xy.set_profile(profile_id) self.plot_xy.set_profile(profile_id)
self.plot_ac.set_profile(profile_id) self.plot_ac.set_profile(profile_id)
self.plot_kpc.set_profile(profile_id) self.plot_kpc.set_profile(profile_id)
self.plot_h.set_profile(profile_id) self.plot_h.set_profile(profile_id)
if self._study.river.has_sediment():
self.plot_sed_reach.set_profile(profile_id)
self.plot_sed_profile.set_profile(profile_id)
if timestamp is not None: if timestamp is not None:
self.plot_xy.set_timestamp(timestamp) self.plot_xy.set_timestamp(timestamp)
self.plot_ac.set_timestamp(timestamp) self.plot_ac.set_timestamp(timestamp)
self.plot_kpc.set_timestamp(timestamp) self.plot_kpc.set_timestamp(timestamp)
self.plot_h.set_timestamp(timestamp) self.plot_h.set_timestamp(timestamp)
if self._study.river.has_sediment():
self.plot_sed_reach.set_timestamp(timestamp)
self.plot_sed_profile.set_timestamp(timestamp)
self.plot_xy.draw() self.plot_xy.draw()
self.plot_ac.draw() self.plot_ac.draw()
self.plot_kpc.draw() self.plot_kpc.draw()
self.plot_h.draw() self.plot_h.draw()
if self._study.river.has_sediment():
self.plot_sed_reach.draw()
self.plot_sed_profile.draw()
def _set_current_reach(self): def _set_current_reach(self):
table = self.find(QTableView, f"tableView_reach") table = self.find(QTableView, f"tableView_reach")
+2 -1
View File
@@ -147,6 +147,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
self._log(f" *** Run solver {self._solver.name}", color="blue") self._log(f" *** Run solver {self._solver.name}", color="blue")
self._solver.run( self._solver.run(
study,
process = self._process, process = self._process,
output_queue = self._output output_queue = self._output
) )
@@ -220,7 +221,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
self._log(" *** Start", color="blue") self._log(" *** Start", color="blue")
self._results = None self._results = None
self._solver.start(self._process) self._solver.start(self._study, process = self._process)
self.find(QAction, "action_start").setEnabled(False) self.find(QAction, "action_start").setEnabled(False)
if _signal: if _signal:
+105
View File
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None, toolbar=None,
display_current=True):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
self._display_current = display_current
self.line_kp_zmin = None
self.line_kp_sl = []
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
# self.canvas.axes.set_xlabel(
# _translate("MainWindow_reach", "X (m)"),
# color='green', fontsize=12
# )
self.canvas.axes.axes.get_xaxis().set_visible(False)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12
)
if self.data is None:
return
x = [0,1]
z = [0,0]
sl = self.data.height()
names = ["bottom"] + self.data.names()
self.canvas.axes.set_xlim(
left = min(x), right = max(x)
)
lsl = []
for i in range(len(sl)):
cur = []
for _ in x:
cur.append(sl[i])
lsl.append(cur)
# Compute sediment layer in function to point z
z_sl = reduce(
lambda acc, v: acc + [
list(
map(lambda x, y: y - x, v, acc[-1])
)
],
lsl,
[z]
)
for i, zsl in enumerate(reversed(z_sl)):
self.line_kp_sl.append(None)
self.line_kp_sl[i], = self.canvas.axes.plot(
x, zsl,
label=names[-(i+1)],
linestyle="solid" if i == len(names) - 1 else "--",
lw=1.8,
color='grey' if i == len(names) - 1 else None
)
self.canvas.axes.text(x[0] + 0.01, zsl[0] + 0.01, f'{names[-(i+1)]}')
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
if ind is None:
logger.info("TODO: Update")
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.canvas.draw_idle()
+183
View File
@@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
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.SedimentLayers.Edit.UndoCommand import *
from View.SedimentLayers.Edit.translate import *
_translate = QCoreApplication.translate
class TableModel(QAbstractTableModel):
def __init__(self, study=None, sl=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._study = study
self._undo = undo
self._sl = sl
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._sl)
def columnCount(self, parent):
return len(self._headers)
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._sl.get(row).name
elif self._headers[column] == "type":
return self._sl.get(row).type
elif self._headers[column] == "height":
return self._sl.get(row).height
elif self._headers[column] == "d50":
return self._sl.get(row).d50
elif self._headers[column] == "sigma":
return self._sl.get(row).sigma
elif self._headers[column] == "critical_constraint":
return self._sl.get(row).critical_constraint
return QVariant()
def headerData(self, friction, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[friction]]
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()
if self._headers[column] == "name":
self._undo.push(
SetNameCommand(
self._sl, row, value
)
)
if self._headers[column] == "type":
self._undo.push(
SetTypeCommand(
self._sl, row, value
)
)
if self._headers[column] == "height":
self._undo.push(
SetHeightCommand(
self._sl, row, value
)
)
if self._headers[column] == "d50":
self._undo.push(
SetD50Command(
self._sl, row, value
)
)
if self._headers[column] == "sigma":
self._undo.push(
SetSigmaCommand(
self._sl, row, value
)
)
if self._headers[column] == "critical_constraint":
self._undo.push(
SetCriticalConstraintCommand(
self._sl, row, value
)
)
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._sl, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._sl, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def move_up(self, row, parent=QModelIndex()):
if row <= 0:
return
target = row + 2
self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo.push(
MoveCommand(
self._sl, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_down(self, row, parent=QModelIndex()):
if row + 1 >= len(self._sl):
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo.push(
MoveCommand(
self._sl, "down", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
+157
View File
@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.SedimentLayer.SedimentLayer import SedimentLayer
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
class SetNameCommand(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).name
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).name = self._old
def redo(self):
self._sediment_layers.get(self._index).name = self._new
class SetTypeCommand(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).type
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).type = self._old
def redo(self):
self._sediment_layers.get(self._index).type = self._new
class SetHeightCommand(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).height
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).height = self._old
def redo(self):
self._sediment_layers.get(self._index).height = self._new
class SetD50Command(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).d50
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).d50 = self._old
def redo(self):
self._sediment_layers.get(self._index).d50 = self._new
class SetSigmaCommand(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).sigma
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).sigma = self._old
def redo(self):
self._sediment_layers.get(self._index).sigma = self._new
class SetCriticalConstraintCommand(QUndoCommand):
def __init__(self, sediment_layers, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._old = self._sediment_layers.get(self._index).critical_constraint
self._new = new_value
def undo(self):
self._sediment_layers.get(self._index).critical_constraint = self._old
def redo(self):
self._sediment_layers.get(self._index).critical_constraint = self._new
class AddCommand(QUndoCommand):
def __init__(self, sediment_layers, index):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._index = index
self._new = None
def undo(self):
self._sediment_layers.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._sediment_layers.new(self._index)
else:
self._sediment_layers.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, sediment_layers, rows):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._rows = rows
self._sl_pos = []
for row in rows:
self._sl_pos.append((row, self._sediment_layers.get(row)))
self._sl_pos.sort()
def undo(self):
for row, el in self._sl_pos:
self._sediment_layers.insert(row, el)
def redo(self):
self._sediment_layers.delete_i(self._rows)
class MoveCommand(QUndoCommand):
def __init__(self, sediment_layers, up, i):
QUndoCommand.__init__(self)
self._sediment_layers = sediment_layers
self._up = up == "up"
self._i = i
def undo(self):
if self._up:
self._sediment_layers.move_up(self._i)
else:
self._sediment_layers.move_down(self._i)
def redo(self):
if self._up:
self._sediment_layers.move_up(self._i)
else:
self._sediment_layers.move_down(self._i)
+170
View File
@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
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, QVBoxLayout, QHeaderView, QTabWidget,
)
from View.Plot.MplCanvas import MplCanvas
from View.SedimentLayers.Edit.UndoCommand import *
from View.SedimentLayers.Edit.Table import *
from View.SedimentLayers.Edit.Plot import Plot
from View.SedimentLayers.Edit.translate import *
_translate = QCoreApplication.translate
logger = logging.getLogger()
class EditSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Edit Sediment Layers",
study=None, sl=None, parent=None):
self._study = study
self._sl = sl
self.setup_title(title)
super(EditSedimentLayersWindow, self).__init__(
name=self._title, ui="EditSedimentLayers", parent=parent
)
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
name = self._sl.name
if name == "":
name = _translate("SedimentLayers", "(no name)")
self._title = (
title + " - " + self._study.name + " - " + name
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
self._table = {}
table = self.find(QTableView, f"tableView")
self._table = TableModel(
study = self._study,
sl = self._sl,
undo = self._undo_stack,
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.canvas)
self._set_plot()
def _set_plot(self):
self.plot = Plot(
canvas = self.canvas,
data = self._sl,
toolbar = None,
display_current = False
)
self.plot.draw()
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_move_up").triggered.connect(self.move_up)
self.find(QAction, "action_move_down").triggered.connect(self.move_down)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.dataChanged.connect(self._set_plot)
self._table.layoutChanged.connect(self._set_plot)
def index_selected_rows(self):
table = self.find(QTableView, f"tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._sl) == 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 move_up(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table.move_up(rows[0])
def move_down(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table.move_down(rows[0])
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()
+14
View File
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"name": _translate("SedimentLayers", "Name"),
"type": _translate("SedimentLayers", "Type"),
"height": _translate("Sedimentlayers", "Height"),
"d50": _translate("Sedimentlayers", "D50"),
"sigma": _translate("Sedimentlayers", "Sigma"),
"critical_constraint": _translate("Sedimentlayers", "Critical constraint"),
}
+102
View File
@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None, toolbar=None,
display_current=True):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
self._display_current = display_current
self.line_kp_zmin = None
self.line_kp_sl = []
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if self.data is None:
return
if self.data.number_profiles == 0:
return
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Kp (m)"),
color='green', fontsize=12
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12
)
kp = self.data.get_kp()
sl = self.data.get_sl()
z_min = self.data.get_z_min()
z_max = self.data.get_z_max()
self.canvas.axes.set_xlim(
left = min(kp), right = max(kp)
)
# Compute sediment layer in function to profile z_min
z_sl = reduce(
lambda acc, v: acc + [
list(
map(lambda x, y: y - x, v, acc[-1])
)
],
sl,
[z_min]
)
for i, z in enumerate(reversed(z_sl)):
self.line_kp_sl.append(None)
self.line_kp_sl[i], = self.canvas.axes.plot(
kp, z,
linestyle="solid" if i == len(z_sl) - 1 else "--",
lw=1.8,
color='grey' if i == len(z_sl) - 1 else None
)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
if ind is None:
kp = self.data.get_kp()
z_min = self.data.get_z_min()
z_max = self.data.get_z_max()
self.line_kp_zmin.set_data(kp, z_min)
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.canvas.draw_idle()
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None, toolbar=None,
display_current=True):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
self._display_current = display_current
self.line_kp_zmin = None
self.line_kp_sl = []
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if self.data is None:
return
if self.data.number_points == 0:
return
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
color='green', fontsize=12
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=12
)
x = self.data.get_station()
z = self.data.z()
sl = self.data.get_sl()
self.canvas.axes.set_xlim(
left = min(x), right = max(x)
)
# Compute sediment layer in function to point z
z_sl = reduce(
lambda acc, v: acc + [
list(
map(lambda x, y: y - x, v, acc[-1])
)
],
sl,
[z]
)
for i, zsl in enumerate(reversed(z_sl)):
self.line_kp_sl.append(None)
self.line_kp_sl[i], = self.canvas.axes.plot(
x, zsl,
linestyle="solid" if i == len(z_sl) - 1 else "--",
lw=1.8,
color='grey' if i == len(z_sl) - 1 else None
)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
if ind is None:
logger.info("TODO: Update")
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.canvas.draw_idle()
@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
import logging
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.SedimentLayers.Reach.Profile.UndoCommand import *
from View.SedimentLayers.Reach.Profile.translate import *
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ComboBoxDelegate(QItemDelegate):
def __init__(self, study=None, parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._study = study
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
self.editor.addItems(
# [_translate("SedimentLayers", "Not defined")] +
list(
map(
lambda sl: str(sl),
self._study.river.sediment_layers.sediment_layers
)
)
)
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 and 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(QAbstractTableModel):
def __init__(self, study=None, profile=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._study = study
self._undo = undo
self._profile = profile
def flags(self, index):
column = index.column()
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self._headers[column] == "sl":
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return self._profile.number_points
def columnCount(self, parent):
return len(self._headers)
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._profile.point(row).name
elif self._headers[column] == "sl":
value = self._profile.point(row).sl
if value == None:
text = _translate("SedimentLayers", "Not defined")
return text
return str(value)
elif self._headers[column] == "x":
return self._profile.point(row).x
elif self._headers[column] == "y":
return self._profile.point(row).y
elif self._headers[column] == "z":
return self._profile.point(row).z
return QVariant()
def headerData(self, friction, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[friction]]
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()
if self._headers[column] == "sl":
new = None
if value != _translate("SedimentLayers", "Not defined"):
new = next(
filter(
lambda sl: str(sl) == value,
self._study.river.sediment_layers.sediment_layers
)
)
self._undo.push(
SetSLCommand(
self._profile, row, new
)
)
self.dataChanged.emit(index, index)
return True
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import logging
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.Geometry.Reach import Reach
from Model.Geometry.Profile import Profile
from Model.SedimentLayer.SedimentLayer import SedimentLayer
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
logger = logging.getLogger()
class SetSLCommand(QUndoCommand):
def __init__(self, profile, index, new_value):
QUndoCommand.__init__(self)
self._profile = profile
self._index = index
self._old = self._profile.point(self._index).sl
self._new = new_value
def undo(self):
self._profile.point(self._index).sl = self._old
def redo(self):
self._profile.point(self._index).sl = self._new
@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
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, QVBoxLayout, QHeaderView, QTabWidget,
)
from View.SedimentLayers.Reach.Profile.UndoCommand import *
from View.SedimentLayers.Reach.Profile.Table import *
from View.SedimentLayers.Reach.Profile.Plot import Plot
from View.Plot.MplCanvas import MplCanvas
from View.SedimentLayers.Reach.Profile.translate import *
from View.SedimentLayers.Window import SedimentLayersWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ProfileSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Profile sediment layers", study=None, profile=None, parent=None):
self._study = study
self._sediment_layers = self._study.river.sediment_layers
self._profile = profile
self._reach = self._study.river.current_reach().reach
self.setup_title(title)
super(ProfileSedimentLayersWindow, self).__init__(
name=self._title, ui="ProfileSedimentLayers", parent=parent
)
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
rname = self._reach.name
if rname == "":
rname = _translate("SedimentLayers", "(no name)")
pname = self._profile.name
if pname == "":
pname = _translate(
"SedimentLayers",
"(no name - @kp)").replace("@kp", str(self._profile.kp)
)
self._title = (
title + " - "
+ self._study.name + " - "
+ rname + " - " + pname
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
study = self._study,
profile = self._profile,
undo = self._undo_stack,
)
table.setModel(self._table)
self._delegate_stricklers = ComboBoxDelegate(
study = self._study,
parent=self
)
table.setItemDelegateForColumn(
list(table_headers).index("sl"),
self._delegate_stricklers
)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.canvas)
self._update_plot()
def _update_plot(self):
self.plot = Plot(
canvas = self.canvas,
data = self._profile,
toolbar = None,
display_current = False
)
self.plot.draw()
def setup_connections(self):
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.layoutChanged\
.connect(self._update_plot)
self._table.dataChanged\
.connect(self._update_plot)
def index_selected_rows(self):
table = self.find(QTableView, f"tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
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()
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"x": _translate("SedimentLayers", "X (m)"),
"y": _translate("SedimentLayers", "Y (m)"),
"z": _translate("SedimentLazers", "Z (m)"),
"name": _translate("SedimentLayers", "Name"),
"sl": _translate("SedimentLayers", "Sediment layers"),
}
+61
View File
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QComboBox, QUndoStack, QShortcut,
QDoubleSpinBox,
)
from View.SedimentLayers.Reach.translate import *
_translate = QCoreApplication.translate
class SLDialog(ASubWindow, ListedSubWindow):
def __init__(self, title="SL", study=None, parent=None):
self._study = study
super(SLDialog, self).__init__(
name=title, ui="SLDialog", parent=parent
)
self.setup_combobox()
self.value = None
def setup_combobox(self):
self.combobox_add_items(
"comboBox",
[_translate("SedimentLayers", "Not defined")] +
list(
map(
lambda sl: str(sl),
self._study.river.sediment_layers.sediment_layers
)
)
)
@property
def sl(self):
if self.value == _translate("SedimentLayers", "Not defined"):
return None
return next(
filter(
lambda sl: str(sl) == self.value,
self._study.river.sediment_layers.sediment_layers
)
)
def accept(self):
self.value = self.get_combobox_text("comboBox")
super().accept()
+159
View File
@@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
import logging
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.SedimentLayers.Reach.UndoCommand import *
from View.SedimentLayers.Reach.translate import *
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ComboBoxDelegate(QItemDelegate):
def __init__(self, study=None, parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._study = study
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
self.editor.addItems(
[_translate("SedimentLayers", "Not defined")] +
list(
map(
lambda sl: str(sl),
self._study.river.sediment_layers.sediment_layers
)
)
)
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 and 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(QAbstractTableModel):
def __init__(self, study=None, reach=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._study = study
self._undo = undo
self._reach = reach
def flags(self, index):
column = index.column()
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self._headers[column] == "sl":
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return self._reach.number_profiles
def columnCount(self, parent):
return len(self._headers)
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._reach.profile(row).name
if self._headers[column] == "kp":
return self._reach.profile(row).kp
if self._headers[column] == "sl":
value = self._reach.profile(row).sl
if value == None:
text = _translate("SedimentLayers", "Not defined")
return text
return str(value)
return QVariant()
def headerData(self, friction, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[friction]]
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()
if self._headers[column] == "sl":
new = None
if value != _translate("SedimentLayers", "Not defined"):
new = next(
filter(
lambda sl: str(sl) == value,
self._study.river.sediment_layers.sediment_layers
)
)
self._undo.push(
SetSLCommand(
self._reach, row, new
)
)
self.dataChanged.emit(index, index)
return True
def apply_sl_each_profile(self, sl):
self._undo.push(
ApplySLCommand(
self._reach, sl
)
)
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
import logging
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.Geometry.Reach import Reach
from Model.Geometry.Profile import Profile
from Model.SedimentLayer.SedimentLayer import SedimentLayer
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
logger = logging.getLogger()
class SetSLCommand(QUndoCommand):
def __init__(self, reach, index, new_value):
QUndoCommand.__init__(self)
self._reach = reach
self._index = index
self._old = self._reach.profile(self._index).sl
self._new = new_value
def undo(self):
self._reach.profile(self._index).sl = self._old
def redo(self):
self._reach.profile(self._index).sl = self._new
class ApplySLCommand(QUndoCommand):
def __init__(self, reach, new_value):
QUndoCommand.__init__(self)
self._reach = reach
self._old = []
for profile in self._reach.profiles:
self._old.append(profile.sl)
self._new = new_value
def undo(self):
for i, profile in enumerate(self._reach.profiles):
profile.sl = self._old[i]
def redo(self):
for profile in self._reach.profiles:
profile.sl = self._new
+190
View File
@@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
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, QVBoxLayout, QHeaderView, QTabWidget,
)
from View.SedimentLayers.Reach.UndoCommand import *
from View.SedimentLayers.Reach.Table import *
from View.SedimentLayers.Reach.Plot import Plot
from View.SedimentLayers.Reach.SLDialog import SLDialog
from View.Plot.MplCanvas import MplCanvas
from View.SedimentLayers.Reach.translate import *
from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Profile.Window import ProfileSedimentLayersWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ReachSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Reach sediment layers", study=None, parent=None):
self._study = study
self._sediment_layers = self._study.river.sediment_layers
self._reach = self._study.river.current_reach().reach
self.setup_title(title)
super(ReachSedimentLayersWindow, self).__init__(
name=self._title, ui="ReachSedimentLayers", parent=parent
)
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
self._title = (
title + " - " + self._study.name + " - " + self._reach.name
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
study = self._study,
reach = self._reach,
undo = self._undo_stack,
)
table.setModel(self._table)
self._delegate_stricklers = ComboBoxDelegate(
study = self._study,
parent=self
)
table.setItemDelegateForColumn(
list(table_headers).index("sl"),
self._delegate_stricklers
)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout, "verticalLayout_2")
self.plot_layout.addWidget(self.canvas)
self._update_plot()
def _update_plot(self):
self.plot = Plot(
canvas = self.canvas,
data = self._reach,
toolbar = None,
display_current = False
)
self.plot.draw()
def setup_connections(self):
self.find(QAction, "action_edit").triggered.connect(self.edit_profile)
self.find(QPushButton, "pushButton_edit")\
.clicked\
.connect(self.edit_sl)
self.find(QPushButton, "pushButton_apply")\
.clicked\
.connect(self.apply_sl_each_profile)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.layoutChanged\
.connect(self._update_plot)
self._table.dataChanged\
.connect(self._update_plot)
def index_selected_rows(self):
table = self.find(QTableView, f"tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def copy(self):
logger.info("TODO: copy")
def paste(self):
logger.info("TODO: paste")
def undo(self):
self._table.undo()
self._update_plot()
def redo(self):
self._table.redo()
self._update_plot()
def apply_sl_each_profile(self):
slw = SLDialog(
study = self._study,
parent = self
)
if slw.exec():
sl = slw.sl
self._table.apply_sl_each_profile(sl)
self._update_plot()
def edit_profile(self):
rows = self.index_selected_rows()
for row in rows:
slw = ProfileSedimentLayersWindow(
study = self._study,
profile = self._reach.profile(row),
parent = self
)
slw.show()
def edit_sl(self):
slw = SedimentLayersWindow(
study = self._study,
parent = self
)
slw.show()
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"name": _translate("SedimentLayers", "Name"),
"kp": _translate("SedimentLayers", "KP (m)"),
"sl": _translate("SedimentLayers", "Sediment layers"),
}
+151
View File
@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
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.SedimentLayers.UndoCommand import *
from View.SedimentLayers.translate import *
_translate = QCoreApplication.translate
class TableModel(QAbstractTableModel):
def __init__(self, study=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._study = study
self._undo = undo
self._sl = self._study.river.sediment_layers
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._sl)
def columnCount(self, parent):
return len(self._headers)
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._sl.get(row).name
elif self._headers[column] == "comment":
return self._sl.get(row).comment
return QVariant()
def headerData(self, friction, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[friction]]
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()
if self._headers[column] == "name":
self._undo.push(
SetNameCommand(
self._sl, row, value
)
)
if self._headers[column] == "comment":
self._undo.push(
SetCommentCommand(
self._sl, row, value
)
)
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._sl, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._sl, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def move_up(self, row, parent=QModelIndex()):
if row <= 0:
return
target = row + 2
self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo_stack.push(
MoveCommand(
self._sl, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._sl):
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
MoveCommand(
self._sl, "down", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
+97
View File
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.SedimentLayer.SedimentLayer import SedimentLayer
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
class SetNameCommand(QUndoCommand):
def __init__(self, sediment_layers_list, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers_list = sediment_layers_list
self._index = index
self._old = self._sediment_layers_list.get(self._index).name
self._new = new_value
def undo(self):
self._sediment_layers_list.get(self._index).name = self._old
def redo(self):
self._sediment_layers_list.get(self._index).name = self._new
class SetCommentCommand(QUndoCommand):
def __init__(self, sediment_layers_list, index, new_value):
QUndoCommand.__init__(self)
self._sediment_layers_list = sediment_layers_list
self._index = index
self._old = self._sediment_layers_list.get(self._index).comment
self._new = new_value
def undo(self):
self._sediment_layers_list.get(self._index).comment = self._old
def redo(self):
self._sediment_layers_list.get(self._index).comment = self._new
class AddCommand(QUndoCommand):
def __init__(self, sediment_layers_list, index):
QUndoCommand.__init__(self)
self._sediment_layers_list = sediment_layers_list
self._index = index
self._new = None
def undo(self):
self._sediment_layers_list.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._sediment_layers_list.new(self._index)
else:
self._sediment_layers_list.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, sediment_layers_list, rows):
QUndoCommand.__init__(self)
self._sediment_layers_list = sediment_layers_list
self._rows = rows
self._sl_pos = []
for row in rows:
self._sl_pos.append((row, self._sediment_layers_list.get(row)))
self._sl_pos.sort()
def undo(self):
for row, el in self._sl_pos:
self._sediment_layers_list.insert(row, el)
def redo(self):
self._sediment_layers_list.delete_i(self._rows)
class MoveCommand(QUndoCommand):
def __init__(self, sediment_layers_list, up, i):
QUndoCommand.__init__(self)
self._sediment_layers_list = sediment_layers_list
self._up = up == "up"
self._i = i
def undo(self):
if self._up:
self._sediment_layers_list.move_up(self._i)
else:
self._sediment_layers_list.move_down(self._i)
def redo(self):
if self._up:
self._sediment_layers_list.move_up(self._i)
else:
self._sediment_layers_list.move_down(self._i)
+170
View File
@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
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, QVBoxLayout, QHeaderView, QTabWidget,
)
from View.SedimentLayers.UndoCommand import *
from View.SedimentLayers.Table import *
from View.SedimentLayers.Edit.Plot import Plot
from View.Plot.MplCanvas import MplCanvas
from View.SedimentLayers.translate import *
from View.SedimentLayers.Edit.Window import EditSedimentLayersWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class SedimentLayersWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="SedimentLayersList", study=None, parent=None):
self._study = study
self._sediment_layers = self._study.river.sediment_layers
self.setup_title(title)
super(SedimentLayersWindow, self).__init__(
name=self._title, ui="SedimentLayersList", parent=parent
)
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
self._title = (
title + " - " + self._study.name
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
study = self._study,
undo = self._undo_stack,
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.canvas)
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit_sediment_layers)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
table = self.find(QTableView, f"tableView")
table.selectionModel()\
.selectionChanged\
.connect(self._set_current_sl)
self._table.dataChanged\
.connect(self._set_current_sl)
def _set_current_sl(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self.plot = Plot(
canvas = self.canvas,
data = self._sediment_layers.get(rows[0]),
toolbar = None,
display_current = False
)
self.plot.draw()
def index_selected_rows(self):
table = self.find(QTableView, f"tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._sediment_layers) == 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 edit_sediment_layers(self):
rows = self.index_selected_rows()
for row in rows:
slw = EditSedimentLayersWindow(
study = self._study,
sl = self._sediment_layers.get(row),
parent = self
)
slw.show()
+10
View File
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"name": _translate("SedimentLayers", "Name"),
"comment": _translate("SedimentLayers", "Comment"),
}
+3 -2
View File
@@ -55,15 +55,16 @@ def init():
"all_init_time": _translate("SolverParameters", "Initial time (jj:hh:mm:ss)"), "all_init_time": _translate("SolverParameters", "Initial time (jj:hh:mm:ss)"),
"all_final_time": _translate("SolverParameters", "Final time (jj:hh:mm:ss)"), "all_final_time": _translate("SolverParameters", "Final time (jj:hh:mm:ss)"),
"all_timestep": _translate("SolverParameters", "Timestep (second)"), "all_timestep": _translate("SolverParameters", "Timestep (second)"),
"all_command_line_arguments": _translate("SolverParameters", "Command line arguments"),
# Mage specific parameters # Mage specific parameters
"mage_min_timestep": _translate("SolverParameters", "Minimum timestep (second)"), "mage_min_timestep": _translate("SolverParameters", "Minimum timestep (second)"),
"mage_timestep_tra": _translate("SolverParameters", "Time step of writing on .TRA"), "mage_timestep_tra": _translate("SolverParameters", "Time step of writing on .TRA"),
"mage_timestep_bin": _translate("SolverParameters", "Time step of writing on .BIN"), "mage_timestep_bin": _translate("SolverParameters", "Time step of writing on .BIN"),
"mage_implication": _translate("SolverParameters", "Implicitation parameter"), "mage_implicitation": _translate("SolverParameters", "Implicitation parameter"),
"mage_continuity_discretization": _translate("SolverParameters", "Continuity discretization type (S/L)"), "mage_continuity_discretization": _translate("SolverParameters", "Continuity discretization type (S/L)"),
"mage_qsj_discretization": _translate("SolverParameters", "QSJ discretization (A/B)"), "mage_qsj_discretization": _translate("SolverParameters", "QSJ discretization (A/B)"),
"mage_stop_criterion_iterations": _translate("SolverParameters", "Stop criterion iterations (G/A/R)"), "mage_stop_criterion_iterations": _translate("SolverParameters", "Stop criterion iterations (G/A/R)"),
"mage_iter_type": _translate("SolverParameters", "Iteration type"), "mage_iteration_type": _translate("SolverParameters", "Iteration type"),
"mage_smooth_coef": _translate("SolverParameters", "Smoothing coefficient"), "mage_smooth_coef": _translate("SolverParameters", "Smoothing coefficient"),
"mage_cfl_max": _translate("SolverParameters", "Maximun accepted number of CFL"), "mage_cfl_max": _translate("SolverParameters", "Maximun accepted number of CFL"),
"mage_min_height": _translate("SolverParameters", "Minimum water height (meter)"), "mage_min_height": _translate("SolverParameters", "Minimum water height (meter)"),
+114
View File
@@ -0,0 +1,114 @@
<?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>960</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableView" name="tableView"/>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</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_del"/>
<addaction name="action_move_up"/>
<addaction name="action_move_down"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add sediment layer</string>
</property>
<property name="toolTip">
<string>Add a new sediment layer</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="action_del">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
</property>
<property name="text">
<string>Delete sediment layer</string>
</property>
<property name="toolTip">
<string>Delete selected sediment layer(s)</string>
</property>
<property name="shortcut">
<string>Ctrl+D</string>
</property>
</action>
<action name="action_move_up">
<property name="icon">
<iconset>
<normaloff>ressources/up.png</normaloff>ressources/up.png</iconset>
</property>
<property name="text">
<string>Move up</string>
</property>
<property name="toolTip">
<string>Move up</string>
</property>
</action>
<action name="action_move_down">
<property name="icon">
<iconset>
<normaloff>ressources/down.png</normaloff>ressources/down.png</iconset>
</property>
<property name="text">
<string>Move down</string>
</property>
<property name="toolTip">
<string>Move down</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
+18
View File
@@ -165,10 +165,18 @@
<addaction name="action_menu_help_mage"/> <addaction name="action_menu_help_mage"/>
<addaction name="action_menu_about"/> <addaction name="action_menu_about"/>
</widget> </widget>
<widget class="QMenu" name="menuSediment">
<property name="title">
<string>&amp;Sediment</string>
</property>
<addaction name="action_menu_sediment_layers"/>
<addaction name="action_menu_edit_reach_sediment_layers"/>
</widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menu_network"/> <addaction name="menu_network"/>
<addaction name="menu_geometry"/> <addaction name="menu_geometry"/>
<addaction name="menu_Hydraulics"/> <addaction name="menu_Hydraulics"/>
<addaction name="menuSediment"/>
<addaction name="menu_run"/> <addaction name="menu_run"/>
<addaction name="menu_plot"/> <addaction name="menu_plot"/>
<addaction name="menu_cartography"/> <addaction name="menu_cartography"/>
@@ -878,6 +886,16 @@
<string>Define initial conditions</string> <string>Define initial conditions</string>
</property> </property>
</action> </action>
<action name="action_menu_sediment_layers">
<property name="text">
<string>Sediment layers</string>
</property>
</action>
<action name="action_menu_edit_reach_sediment_layers">
<property name="text">
<string>Edit reach sediment layers</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>
+95
View File
@@ -0,0 +1,95 @@
<?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>960</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableView" name="tableView"/>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</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_sediment_layers"/>
<addaction name="action_delete_sediment_layers"/>
<addaction name="action_edit_sediment_layers"/>
</widget>
<action name="action_add_sediment_layers">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add sediment layers</string>
</property>
<property name="toolTip">
<string>Add specific sediment layers on selected point(s)</string>
</property>
</action>
<action name="action_delete_sediment_layers">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
</property>
<property name="text">
<string>Delete sediment layers</string>
</property>
<property name="toolTip">
<string>Delete specific sediment layers of selected point(s)</string>
</property>
</action>
<action name="action_edit_sediment_layers">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit sediment layers</string>
</property>
<property name="toolTip">
<string>Edit sediment layers list</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
+89
View File
@@ -0,0 +1,89 @@
<?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>960</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item>
<widget class="QPushButton" name="pushButton_edit">
<property name="text">
<string>Edit sediment layers list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_apply">
<property name="text">
<string>Apply sediment layers on all reach</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</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_edit"/>
</widget>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit profile</string>
</property>
<property name="toolTip">
<string>Edit profile sediment layer</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
+27 -1
View File
@@ -30,7 +30,7 @@
<widget class="QTableView" name="tableView_reach"/> <widget class="QTableView" name="tableView_reach"/>
<widget class="QTableView" name="tableView_profile"/> <widget class="QTableView" name="tableView_profile"/>
</widget> </widget>
<widget class="QWidget" name=""> <widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
@@ -75,6 +75,32 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_3">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>Sediment</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QSplitter" name="splitter_5">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget_4">
<layout class="QVBoxLayout" name="verticalLayout_sed_reach"/>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_5">
<layout class="QVBoxLayout" name="verticalLayout_sed_profile"/>
</widget>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>194</width>
<height>76</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QComboBox" name="comboBox"/>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+95
View File
@@ -0,0 +1,95 @@
<?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>960</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableView" name="tableView"/>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</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_del"/>
<addaction name="action_edit"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add sediment layer</string>
</property>
</action>
<action name="action_del">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
</property>
<property name="text">
<string>Delete sediment layer</string>
</property>
<property name="toolTip">
<string>Delete sediment layer</string>
</property>
</action>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit sediment layer</string>
</property>
<property name="toolTip">
<string>Edit sediment layer</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
+3
View File
@@ -28,6 +28,9 @@
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="maximum">
<number>999999</number>
</property>
</widget> </widget>
</item> </item>
<item> <item>
Binary file not shown.