mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Merge: Merge branch 'sediment' with sediment export and result for mage.
This commit is contained in:
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):
|
||||
from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||
NotDefined, PonctualContribution,
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge,
|
||||
Solid,
|
||||
)
|
||||
|
||||
res = NotDefined
|
||||
@@ -95,6 +96,8 @@ class BoundaryCondition(SQLSubModel):
|
||||
res = TimeOverDischarge
|
||||
elif t == "ZD":
|
||||
res = ZOverDischarge
|
||||
elif t == "SL":
|
||||
res = Solid
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -26,7 +26,8 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
||||
from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||
NotDefined,
|
||||
PonctualContribution,
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge,
|
||||
Solid,
|
||||
)
|
||||
|
||||
class BoundaryConditionList(SQLSubModel):
|
||||
|
||||
@@ -83,3 +83,16 @@ class ZOverDischarge(BoundaryCondition):
|
||||
@property
|
||||
def _default_0(self):
|
||||
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"]
|
||||
@@ -19,12 +19,29 @@
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
class Point(object):
|
||||
def __init__(self, name:str = "", status = None):
|
||||
def __init__(self, name:str = "", profile=None, status = None):
|
||||
super(Point, self).__init__()
|
||||
|
||||
self._status = status
|
||||
|
||||
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
|
||||
def name(self):
|
||||
|
||||
@@ -26,8 +26,8 @@ class PointXYZ(Point, SQLSubModel):
|
||||
_sub_classes = []
|
||||
|
||||
def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0,
|
||||
name:str = "", status = None):
|
||||
super(PointXYZ, self).__init__(name=name, status=status)
|
||||
name:str = "", profile = None, status = None):
|
||||
super(PointXYZ, self).__init__(name=name, profile=profile, status=status)
|
||||
|
||||
self._x = float(x)
|
||||
self._y = float(y)
|
||||
@@ -44,7 +44,9 @@ class PointXYZ(Point, SQLSubModel):
|
||||
y INTEGER NOT NULL,
|
||||
z 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
|
||||
def _sql_update(cls, 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
|
||||
|
||||
@classmethod
|
||||
@@ -63,9 +76,9 @@ class PointXYZ(Point, SQLSubModel):
|
||||
profile = data["profile"]
|
||||
|
||||
table = execute(
|
||||
"SELECT ind, name, x, y, z " +
|
||||
"SELECT ind, name, x, y, z, sl " +
|
||||
"FROM geometry_pointXYZ " +
|
||||
f"WHERE profile = {profile}"
|
||||
f"WHERE profile = {profile.id}"
|
||||
)
|
||||
|
||||
# Create points list
|
||||
@@ -79,13 +92,25 @@ class PointXYZ(Point, SQLSubModel):
|
||||
x = row[2]
|
||||
y = row[3]
|
||||
z = row[4]
|
||||
sl = row[5]
|
||||
|
||||
new = cls(
|
||||
name = name,
|
||||
x = x, y = y, z = z,
|
||||
profile = profile,
|
||||
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
|
||||
|
||||
return points
|
||||
@@ -94,13 +119,15 @@ class PointXYZ(Point, SQLSubModel):
|
||||
profile = data["profile"]
|
||||
ind = data["ind"]
|
||||
|
||||
sl = self._sl.id if self._sl is not None else -1
|
||||
|
||||
sql = (
|
||||
"INSERT OR REPLACE INTO " +
|
||||
"geometry_pointXYZ(ind, name, x, y, z, profile) "+
|
||||
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) "+
|
||||
"VALUES (" +
|
||||
f"{ind}, '{self._sql_format(self._name)}', " +
|
||||
f"{self.x}, {self.y}, {self.z}, " +
|
||||
f"{profile}" +
|
||||
f"{profile.id}, {sl}" +
|
||||
")"
|
||||
)
|
||||
execute(sql)
|
||||
@@ -117,7 +144,7 @@ class PointXYZ(Point, SQLSubModel):
|
||||
*data
|
||||
)
|
||||
else:
|
||||
valid_header = {'name', 'x', 'y', 'z'}
|
||||
valid_header = {'name', 'x', 'y', 'z', 'profile'}
|
||||
d = {}
|
||||
for i, v in enumerate(data):
|
||||
h = header[i].strip().lower().split(' ')[0]
|
||||
|
||||
@@ -50,6 +50,7 @@ class Profile(object):
|
||||
self._kp = float(kp)
|
||||
self._name = str(name)
|
||||
self._reach = reach
|
||||
self._sl = None
|
||||
|
||||
self._points: List[Point] = []
|
||||
|
||||
@@ -63,6 +64,9 @@ class Profile(object):
|
||||
def points(self):
|
||||
return self._points.copy()
|
||||
|
||||
def point(self, index):
|
||||
return self._points[index]
|
||||
|
||||
@property
|
||||
def reach(self):
|
||||
return self._reach
|
||||
@@ -136,6 +140,19 @@ class Profile(object):
|
||||
self._name = value.strip()
|
||||
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
|
||||
def profile_type(self):
|
||||
"""
|
||||
@@ -274,6 +291,39 @@ class Profile(object):
|
||||
)
|
||||
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
|
||||
def get_station(self):
|
||||
raise NotImplementedMethodeError(self, self.get_station)
|
||||
@@ -76,7 +76,9 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
num INTEGER NOT NULL,
|
||||
code1 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
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
@@ -93,7 +106,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
reach = data["reach"]
|
||||
|
||||
table = execute(
|
||||
"SELECT id, ind, name, kp, num, code1, code2 " +
|
||||
"SELECT id, ind, name, kp, num, code1, code2, sl " +
|
||||
"FROM geometry_profileXYZ " +
|
||||
f"WHERE reach = {reach}"
|
||||
)
|
||||
@@ -109,6 +122,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
num = row[5]
|
||||
code1 = row[5]
|
||||
code2 = row[6]
|
||||
sl = row[7]
|
||||
|
||||
new = cls(
|
||||
id=id, num = num,
|
||||
@@ -118,7 +132,17 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
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)
|
||||
|
||||
profiles[ind] = new
|
||||
@@ -129,18 +153,20 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
ok = True
|
||||
ind = data["ind"]
|
||||
|
||||
sl = self._sl.id if self._sl is not None else -1
|
||||
|
||||
sql = (
|
||||
"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 (" +
|
||||
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
|
||||
f"{self.reach.id}, {self.kp}, {self.num}, " +
|
||||
f"{self.code1}, {self.code1}" +
|
||||
f"{self.code1}, {self.code1}, {sl}" +
|
||||
")"
|
||||
)
|
||||
execute(sql)
|
||||
|
||||
data["profile"] = self.id
|
||||
data["profile"] = self
|
||||
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
|
||||
|
||||
ind = 0
|
||||
@@ -178,11 +204,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
try:
|
||||
if len(header) == 0:
|
||||
point = PointXYZ(
|
||||
*data, status=self._status
|
||||
*data, profile=self, status=self._status
|
||||
)
|
||||
else:
|
||||
valid_header = {'name', 'x', 'y', 'z'}
|
||||
d = {"status": self._status}
|
||||
d = {"status": self._status, "profile": self}
|
||||
for i, v in enumerate(data):
|
||||
h = header[i].strip().lower().split(' ')[0]
|
||||
if h in valid_header:
|
||||
@@ -235,7 +261,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
Nothing.
|
||||
"""
|
||||
for point in list_points:
|
||||
pt = PointXYZ(*point, status=self._status)
|
||||
pt = PointXYZ(*point, profile=self, status=self._status)
|
||||
self._points.append(pt)
|
||||
self._status.modified()
|
||||
|
||||
@@ -259,7 +285,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
Returns:
|
||||
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._status.modified()
|
||||
|
||||
@@ -272,7 +298,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
Returns:
|
||||
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._status.modified()
|
||||
return point
|
||||
|
||||
@@ -276,6 +276,39 @@ class Reach(SQLSubModel):
|
||||
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
|
||||
|
||||
@timer
|
||||
|
||||
@@ -98,6 +98,9 @@ class River(object):
|
||||
def reach(self, id):
|
||||
return self._reachs[id]
|
||||
|
||||
def has_reach(self, id):
|
||||
return 0 <= id < len(self._reachs)
|
||||
|
||||
def add(self, reach_id):
|
||||
reachs = self._study.river.enable_edges()
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from tools import flatten
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
|
||||
from Model.Network.Node import Node
|
||||
@@ -31,6 +33,7 @@ from Model.InitialConditions.InitialConditionsDict import InitialConditionsDict
|
||||
from Model.Stricklers.StricklersList import StricklersList
|
||||
from Model.Friction.FrictionList import FrictionList
|
||||
from Model.SolverParameters.SolverParametersList import SolverParametersList
|
||||
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
|
||||
|
||||
from Solver.Solvers import solver_type_list
|
||||
|
||||
@@ -208,6 +211,7 @@ class River(Graph, SQLSubModel):
|
||||
InitialConditionsDict,
|
||||
StricklersList,
|
||||
SolverParametersList,
|
||||
SedimentLayerList,
|
||||
]
|
||||
|
||||
def __init__(self, status=None):
|
||||
@@ -223,6 +227,7 @@ class River(Graph, SQLSubModel):
|
||||
self._initial_conditions = InitialConditionsDict(status=self._status)
|
||||
self._stricklers = StricklersList(status=self._status)
|
||||
self._parameters = {}
|
||||
self._sediment_layers = SedimentLayerList(status=self._status)
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
@@ -246,6 +251,14 @@ class River(Graph, SQLSubModel):
|
||||
)
|
||||
data["stricklers"] = new._stricklers
|
||||
|
||||
|
||||
# Initial conditions
|
||||
new._sediment_layers = SedimentLayerList._sql_load(
|
||||
execute,
|
||||
data
|
||||
)
|
||||
data["sediment_layers_list"] = new._sediment_layers
|
||||
|
||||
# Network
|
||||
new._nodes = RiverNode._sql_load(
|
||||
execute,
|
||||
@@ -289,6 +302,7 @@ class River(Graph, SQLSubModel):
|
||||
objs.append(self._boundary_condition)
|
||||
objs.append(self._initial_conditions)
|
||||
objs.append(self._lateral_contribution)
|
||||
objs.append(self._sediment_layers)
|
||||
objs.append(self._stricklers)
|
||||
|
||||
for solver in self._parameters:
|
||||
@@ -317,6 +331,10 @@ class River(Graph, SQLSubModel):
|
||||
def initial_conditions(self):
|
||||
return self._initial_conditions
|
||||
|
||||
@property
|
||||
def sediment_layers(self):
|
||||
return self._sediment_layers
|
||||
|
||||
@property
|
||||
def stricklers(self):
|
||||
return self._stricklers
|
||||
@@ -366,3 +384,16 @@ class River(Graph, SQLSubModel):
|
||||
|
||||
def set_current_reach(self, 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
|
||||
@@ -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
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
@@ -160,6 +167,17 @@ class SolverParametersList(SQLSubModel):
|
||||
def get(self, 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):
|
||||
self._parameters[index] = new
|
||||
self._status.modified()
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class Study(SQLModel):
|
||||
|
||||
def __init__(self, filename = None, init_new = True):
|
||||
# Metadata
|
||||
self._version = "0.0.1"
|
||||
self._version = "0.0.3"
|
||||
self.creation_date = datetime.now()
|
||||
self.last_modification_date = datetime.now()
|
||||
self.last_save_date = datetime.now()
|
||||
|
||||
+44
-21
@@ -86,6 +86,7 @@ class AbstractSolver(object):
|
||||
("all_init_time", "000:00:00:00"),
|
||||
("all_final_time", "999:99:00:00"),
|
||||
("all_timestep", "300.0"),
|
||||
("all_command_line_arguments", ""),
|
||||
]
|
||||
|
||||
return lst
|
||||
@@ -153,6 +154,17 @@ class AbstractSolver(object):
|
||||
def export(self, study, repertory, qlog = None):
|
||||
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):
|
||||
"""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
|
||||
|
||||
Args:
|
||||
@@ -206,6 +218,7 @@ class AbstractSolver(object):
|
||||
cmd = cmd.replace("@path", path.replace(" ", "\ "))
|
||||
cmd = cmd.replace("@input", self.input_param())
|
||||
cmd = cmd.replace("@dir", self._process.workingDirectory())
|
||||
cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
|
||||
|
||||
logger.debug(f"! {cmd}")
|
||||
|
||||
@@ -213,23 +226,33 @@ class AbstractSolver(object):
|
||||
# Command line executable path is between " char
|
||||
cmd = cmd.split("\"")
|
||||
exe = cmd[1].replace("\ ", " ")
|
||||
args = "\"".join(cmd[2:]).split(" ")[1:]
|
||||
args = list(
|
||||
filter(
|
||||
lambda s: s != "",
|
||||
"\"".join(cmd[2:]).split(" ")[1:]
|
||||
)
|
||||
)
|
||||
else:
|
||||
# We suppose the command line executable path as no space char
|
||||
cmd = cmd.replace("\ ", "&_&").split(" ")
|
||||
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}")
|
||||
return exe, args
|
||||
|
||||
def run_input_data_fomater(self):
|
||||
def run_input_data_fomater(self, study):
|
||||
if self._cmd_input == "":
|
||||
self._run_next()
|
||||
self._run_next(study)
|
||||
return True
|
||||
|
||||
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):
|
||||
error = f"[ERROR] Path {exe} do not exists"
|
||||
@@ -242,13 +265,13 @@ class AbstractSolver(object):
|
||||
|
||||
return True
|
||||
|
||||
def run_solver(self):
|
||||
def run_solver(self, study):
|
||||
if self._cmd_solver == "":
|
||||
self._run_next()
|
||||
self._run_next(study)
|
||||
return True
|
||||
|
||||
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):
|
||||
error = f"[ERROR] Path {exe} do not exists"
|
||||
@@ -262,13 +285,13 @@ class AbstractSolver(object):
|
||||
self._status = STATUS.RUNNING
|
||||
return True
|
||||
|
||||
def run_output_data_fomater(self):
|
||||
def run_output_data_fomater(self, study):
|
||||
if self._cmd_output == "":
|
||||
self._run_next()
|
||||
self._run_next(study)
|
||||
return True
|
||||
|
||||
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):
|
||||
error = f"[ERROR] Path {exe} do not exists"
|
||||
@@ -287,29 +310,29 @@ class AbstractSolver(object):
|
||||
for x in s.split('\n'):
|
||||
self._output.put(x)
|
||||
|
||||
def _run_next(self):
|
||||
def _run_next(self, study):
|
||||
self._step += 1
|
||||
if self._step < len(self._runs):
|
||||
res = self._runs[self._step]()
|
||||
res = self._runs[self._step](study)
|
||||
if res is not True:
|
||||
self._output.put(res)
|
||||
else:
|
||||
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:
|
||||
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:
|
||||
self._process = process
|
||||
if output_queue is not None:
|
||||
self._output = output_queue
|
||||
|
||||
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.run_input_data_fomater,
|
||||
@@ -318,7 +341,7 @@ class AbstractSolver(object):
|
||||
]
|
||||
self._step = 0
|
||||
# Run first step
|
||||
res = self._runs[0]()
|
||||
res = self._runs[0](study)
|
||||
if res is not True:
|
||||
self._output.put(res)
|
||||
|
||||
@@ -330,14 +353,14 @@ class AbstractSolver(object):
|
||||
self._status = STATUS.STOPED
|
||||
return True
|
||||
|
||||
def start(self, process = None):
|
||||
def start(self, study, process = None):
|
||||
if _signal:
|
||||
if self._status == STATUS.PAUSED:
|
||||
os.kill(self._process.pid(), SIGCONT)
|
||||
self._status = STATUS.RUNNING
|
||||
return True
|
||||
|
||||
self.run(process)
|
||||
self.run(study, process)
|
||||
return True
|
||||
|
||||
def pause(self):
|
||||
|
||||
+246
-10
@@ -61,11 +61,11 @@ class Mage(AbstractSolver):
|
||||
("mage_timestep_tra", "3600"),
|
||||
("mage_timestep_bin", "0"),
|
||||
# ("mage_timestep_melissa", "0"),
|
||||
("mage_implication", "0.70"),
|
||||
("mage_implicitation", "0.70"),
|
||||
("mage_continuity_discretization", "S"),
|
||||
("mage_qsj_discretization", "B"),
|
||||
("mage_stop_criterion_iterations", "R"),
|
||||
("mage_iter_type", "0"),
|
||||
("mage_iteration_type", "0"),
|
||||
("mage_smooth_coef", "0"),
|
||||
("mage_cfl_max", "-1."),
|
||||
("mage_min_height", "0.1"),
|
||||
@@ -97,6 +97,13 @@ class Mage(AbstractSolver):
|
||||
# Export #
|
||||
##########
|
||||
|
||||
def cmd_args(self, study):
|
||||
l = super(Mage, self).cmd_args(study)
|
||||
|
||||
l.append("-r")
|
||||
|
||||
return l
|
||||
|
||||
def input_param(self):
|
||||
return "0.REP"
|
||||
|
||||
@@ -127,23 +134,47 @@ class Mage(AbstractSolver):
|
||||
with mage_file_open(os.path.join(repertory, f"{name}.ST"), "w+") as f:
|
||||
files.append(f"{name}.ST")
|
||||
|
||||
cnt_num = 1
|
||||
for profile in edge.reach.profiles:
|
||||
num = f"{profile.num:>6}"
|
||||
num = f"{cnt_num:>6}"
|
||||
c1 = f"{profile.code1:>6}"
|
||||
c2 = f"{profile.code2:>6}"
|
||||
t = f"{len(profile.points):>6}"
|
||||
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:
|
||||
x = f"{point.x:>13.4f}"
|
||||
y = f"{point.y:>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")
|
||||
|
||||
@@ -170,7 +201,7 @@ class Mage(AbstractSolver):
|
||||
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")
|
||||
f.write(f"{d[0]:10}{d[1]:10}\n")
|
||||
|
||||
return files
|
||||
|
||||
@@ -182,6 +213,7 @@ class Mage(AbstractSolver):
|
||||
AVA = []
|
||||
HYD = []
|
||||
LIM = []
|
||||
QSO = []
|
||||
|
||||
for tab in ["liquid", "solid", "suspenssion"]:
|
||||
for bound in lst.get_tab(tab):
|
||||
@@ -191,10 +223,13 @@ class Mage(AbstractSolver):
|
||||
HYD.append(bound)
|
||||
elif bound.bctype == "TZ":
|
||||
LIM.append(bound)
|
||||
elif bound.bctype == "SL":
|
||||
QSO.append(bound)
|
||||
|
||||
files = files + self._export_BC("AVA", AVA, repertory, qlog)
|
||||
files = files + self._export_BC("HYD", HYD, repertory, qlog)
|
||||
files = files + self._export_BC("LIM", LIM, repertory, qlog)
|
||||
files = files + self._export_QSO(QSO, repertory, qlog)
|
||||
|
||||
return files
|
||||
|
||||
@@ -332,12 +367,20 @@ class Mage(AbstractSolver):
|
||||
for file in files:
|
||||
EXT = file.split('.')[1]
|
||||
|
||||
if EXT not in ["ST", "GRA"]:
|
||||
f.write(f"{EXT} {file}\n")
|
||||
|
||||
f.write("* OUTPUT\n")
|
||||
f.write(f"TRA 0.TRA\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
|
||||
def export(self, study, repertory, qlog = None):
|
||||
self._export_ST(study, repertory, qlog)
|
||||
@@ -404,6 +447,14 @@ class Mage8(Mage):
|
||||
# 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
|
||||
def _export_PAR(self, study, repertory, qlog = None):
|
||||
files = []
|
||||
@@ -423,7 +474,6 @@ class Mage8(Mage):
|
||||
|
||||
f.write(f"{name} {value}\n")
|
||||
|
||||
|
||||
return files
|
||||
|
||||
@timer
|
||||
@@ -459,11 +509,37 @@ class Mage8(Mage):
|
||||
|
||||
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
|
||||
def export(self, study, repertory, qlog = None):
|
||||
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_NET(study, repertory, qlog)
|
||||
files = files + self._export_bound_cond(study, repertory, qlog)
|
||||
@@ -477,6 +553,7 @@ class Mage8(Mage):
|
||||
# RESULTS #
|
||||
###########
|
||||
|
||||
@timer
|
||||
def read_bin(self, study, repertory, results, qlog = None):
|
||||
logger.info(f"read_bin: Start ...")
|
||||
|
||||
@@ -597,3 +674,162 @@ class Mage8(Mage):
|
||||
logger.debug(reachs[0].profiles[0]._data)
|
||||
results.set("timestamps", ts)
|
||||
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
|
||||
@@ -26,5 +26,6 @@ table_headers = {
|
||||
"time": _translate("BoundaryCondition", "Time"),
|
||||
"date": _translate("BoundaryCondition", "Date"),
|
||||
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
|
||||
"z": _translate("BoundaryCondition", "Z (m)")
|
||||
"z": _translate("BoundaryCondition", "Z (m)"),
|
||||
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
|
||||
}
|
||||
@@ -20,7 +20,8 @@ from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||
NotDefined, PonctualContribution,
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge,
|
||||
Solid
|
||||
)
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -31,6 +32,7 @@ long_types = {
|
||||
"TZ": _translate("BoundaryCondition", "Time over Z"),
|
||||
"TD": _translate("BoundaryCondition", "Time over Discharge"),
|
||||
"ZD": _translate("BoundaryCondition", "Z over Discharge"),
|
||||
"SL": _translate("BoundaryCondition", "Solid"),
|
||||
}
|
||||
|
||||
table_headers = {
|
||||
@@ -44,5 +46,6 @@ BC_types = {
|
||||
"PC": PonctualContribution,
|
||||
"TZ": TimeOverZ,
|
||||
"TD": TimeOverDischarge,
|
||||
"ZD": ZOverDischarge
|
||||
"ZD": ZOverDischarge,
|
||||
"SL": Solid,
|
||||
}
|
||||
@@ -49,7 +49,7 @@ from View.Frictions.Table import (
|
||||
)
|
||||
|
||||
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.translate import *
|
||||
|
||||
@@ -128,7 +128,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
self.plot_layout.addWidget(self.canvas)
|
||||
|
||||
self.plot = PlotKPC(
|
||||
self.plot = PlotKPZ(
|
||||
canvas = self.canvas,
|
||||
data = self._reach.reach,
|
||||
toolbar = None,
|
||||
@@ -193,7 +193,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
sec = self._frictions.get(rows[0])
|
||||
highlight = (sec.begin_kp, sec.end_kp)
|
||||
|
||||
self.plot = PlotKPC(
|
||||
self.plot = PlotKPZ(
|
||||
canvas = self.canvas,
|
||||
data = reach,
|
||||
toolbar = None,
|
||||
|
||||
@@ -29,10 +29,10 @@ _translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class PlotKPC(APlot):
|
||||
class PlotKPZ(APlot):
|
||||
def __init__(self, canvas=None, data=None, toolbar=None,
|
||||
display_current=True):
|
||||
super(PlotKPC, self).__init__(
|
||||
super(PlotKPZ, self).__init__(
|
||||
canvas=canvas,
|
||||
data=data,
|
||||
toolbar=toolbar
|
||||
@@ -64,7 +64,7 @@ class PlotKPC(APlot):
|
||||
color='green', fontsize=12
|
||||
)
|
||||
self.canvas.axes.set_ylabel(
|
||||
_translate("MainWindow_reach", "Cote (m)"),
|
||||
_translate("MainWindow_reach", "Height (m)"),
|
||||
color='green', fontsize=12
|
||||
)
|
||||
|
||||
@@ -234,7 +234,7 @@ class PlotKPC(APlot):
|
||||
kp, z_complete[i]
|
||||
)
|
||||
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.figure.canvas.draw_idle()
|
||||
@@ -38,7 +38,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
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.ASubWindow import ASubMainWindow, WindowToolKit
|
||||
@@ -230,7 +230,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
|
||||
def plot_kpc(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
|
||||
self._plot_kpc = PlotKPC(
|
||||
self._plot_kpc = PlotKPZ(
|
||||
canvas = self.ui.canvas_2,
|
||||
data = self._reach,
|
||||
toolbar = self.ui.toolbar_2
|
||||
|
||||
+39
-1
@@ -18,6 +18,7 @@
|
||||
|
||||
import os
|
||||
import logging
|
||||
import subprocess
|
||||
from queue import Queue
|
||||
|
||||
from PyQt5 import QtGui
|
||||
@@ -49,6 +50,8 @@ from View.LateralContribution.Window import LateralContributionWindow
|
||||
from View.InitialConditions.Window import InitialConditionsWindow
|
||||
from View.Stricklers.Window import StricklersWindow
|
||||
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.RunSolver.Window import SelectSolverWindow, SolverLogWindow
|
||||
from View.CheckList.Window import CheckListWindow
|
||||
@@ -87,7 +90,8 @@ define_model_action = [
|
||||
"action_menu_edit_network", "action_menu_edit_geometry",
|
||||
"action_menu_boundary_conditions", "action_menu_initial_conditions",
|
||||
"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 = (
|
||||
@@ -170,6 +174,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
"action_menu_edit_friction": self.open_frictions,
|
||||
"action_menu_edit_lateral_contribution": self.open_lateral_contrib,
|
||||
"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
|
||||
"action_menu_about": self.open_about,
|
||||
# ToolBar action
|
||||
@@ -258,15 +264,22 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
self.debug_action.setStatusTip("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:
|
||||
menu.addAction(self.debug_action)
|
||||
menu.addAction(self.debug_sqlite_action)
|
||||
self.set_debug_lvl(debug = True)
|
||||
else:
|
||||
if self.conf.debug:
|
||||
menu.addAction(self.debug_action)
|
||||
menu.addAction(self.debug_sqlite_action)
|
||||
self.set_debug_lvl(debug = True)
|
||||
else:
|
||||
menu.removeAction(self.debug_action)
|
||||
menu.removeAction(self.debug_sqlite_action)
|
||||
self.set_debug_lvl(debug = False)
|
||||
|
||||
#########
|
||||
@@ -617,6 +630,20 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
else:
|
||||
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):
|
||||
if self.model is None:
|
||||
return
|
||||
@@ -683,6 +710,17 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
)
|
||||
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 !
|
||||
###############
|
||||
# DUMMY STUFF #
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import logging
|
||||
|
||||
from functools import reduce
|
||||
from datetime import datetime
|
||||
|
||||
from tools import timer, trace
|
||||
from View.Plot.APlot import APlot
|
||||
@@ -34,14 +35,14 @@ logger = logging.getLogger()
|
||||
class PlotH(APlot):
|
||||
def __init__(self, canvas=None, results=None,
|
||||
reach_id=0, profile_id=0,
|
||||
toolbar=None, display_current=True):
|
||||
toolbar=None):
|
||||
super(PlotH, self).__init__(
|
||||
canvas=canvas,
|
||||
data=results,
|
||||
toolbar=toolbar
|
||||
)
|
||||
|
||||
self.display_current = display_current
|
||||
self._mode = "time"
|
||||
|
||||
self._current_timestamp = max(results.get("timestamps"))
|
||||
self._current_reach_id = reach_id
|
||||
@@ -99,6 +100,10 @@ class PlotH(APlot):
|
||||
)
|
||||
return
|
||||
|
||||
self.canvas.axes.set_ylim(
|
||||
[min(min(y),0), max(y)]
|
||||
)
|
||||
|
||||
self._line = [
|
||||
self.canvas.axes.plot(
|
||||
x, y, lw=1.,
|
||||
@@ -107,8 +112,47 @@ class PlotH(APlot):
|
||||
)
|
||||
]
|
||||
|
||||
self.canvas.axes.autoscale_view(True, True, True)
|
||||
self.canvas.axes.autoscale()
|
||||
# Custom time display
|
||||
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.canvas.draw_idle()
|
||||
if self.toolbar is not None:
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -46,6 +46,8 @@ from View.Results.PlotXY import PlotXY
|
||||
from View.Results.PlotAC import PlotAC
|
||||
from View.Results.PlotKPC import PlotKPC
|
||||
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.translate import *
|
||||
@@ -180,6 +182,38 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
)
|
||||
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):
|
||||
self.undo_sc.activated.connect(self.undo)
|
||||
self.redo_sc.activated.connect(self.redo)
|
||||
@@ -210,22 +244,38 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.plot_ac.set_reach(reach_id)
|
||||
self.plot_kpc.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:
|
||||
self.plot_xy.set_profile(profile_id)
|
||||
self.plot_ac.set_profile(profile_id)
|
||||
self.plot_kpc.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:
|
||||
self.plot_xy.set_timestamp(timestamp)
|
||||
self.plot_ac.set_timestamp(timestamp)
|
||||
self.plot_kpc.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_ac.draw()
|
||||
self.plot_kpc.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):
|
||||
table = self.find(QTableView, f"tableView_reach")
|
||||
|
||||
@@ -147,6 +147,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self._log(f" *** Run solver {self._solver.name}", color="blue")
|
||||
self._solver.run(
|
||||
study,
|
||||
process = self._process,
|
||||
output_queue = self._output
|
||||
)
|
||||
@@ -220,7 +221,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self._log(" *** Start", color="blue")
|
||||
self._results = None
|
||||
self._solver.start(self._process)
|
||||
self._solver.start(self._study, process = self._process)
|
||||
|
||||
self.find(QAction, "action_start").setEnabled(False)
|
||||
if _signal:
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -55,15 +55,16 @@ def init():
|
||||
"all_init_time": _translate("SolverParameters", "Initial time (jj:hh:mm:ss)"),
|
||||
"all_final_time": _translate("SolverParameters", "Final time (jj:hh:mm:ss)"),
|
||||
"all_timestep": _translate("SolverParameters", "Timestep (second)"),
|
||||
"all_command_line_arguments": _translate("SolverParameters", "Command line arguments"),
|
||||
# Mage specific parameters
|
||||
"mage_min_timestep": _translate("SolverParameters", "Minimum timestep (second)"),
|
||||
"mage_timestep_tra": _translate("SolverParameters", "Time step of writing on .TRA"),
|
||||
"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_qsj_discretization": _translate("SolverParameters", "QSJ discretization (A/B)"),
|
||||
"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_cfl_max": _translate("SolverParameters", "Maximun accepted number of CFL"),
|
||||
"mage_min_height": _translate("SolverParameters", "Minimum water height (meter)"),
|
||||
|
||||
@@ -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>
|
||||
@@ -165,10 +165,18 @@
|
||||
<addaction name="action_menu_help_mage"/>
|
||||
<addaction name="action_menu_about"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSediment">
|
||||
<property name="title">
|
||||
<string>&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_network"/>
|
||||
<addaction name="menu_geometry"/>
|
||||
<addaction name="menu_Hydraulics"/>
|
||||
<addaction name="menuSediment"/>
|
||||
<addaction name="menu_run"/>
|
||||
<addaction name="menu_plot"/>
|
||||
<addaction name="menu_cartography"/>
|
||||
@@ -878,6 +886,16 @@
|
||||
<string>Define initial conditions</string>
|
||||
</property>
|
||||
</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>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -30,7 +30,7 @@
|
||||
<widget class="QTableView" name="tableView_reach"/>
|
||||
<widget class="QTableView" name="tableView_profile"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="">
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
@@ -75,6 +75,32 @@
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -28,6 +28,9 @@
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user