LC: Add code base (don't working).

mesh
Pierre-Antoine Rouby 2023-05-12 14:45:07 +02:00
parent 41fa0f403b
commit 115d84d436
14 changed files with 1959 additions and 1 deletions

View File

@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
from tools import trace, timer, old_pamhyr_date_to_timestamp
from Model.Except import NotImplementedMethodeError
class LateralContribution(object):
def __init__(self, name:str = ""):
super(LateralContribution, self).__init__()
self._name = name
self._type = ""
self._edge = None
self._data = []
self._header = []
self._types = [float, float]
def __len__(self):
return len(self._data)
@classmethod
def compatibility(cls):
return ["liquid", "solid", "suspenssion"]
@classmethod
def time_convert(cls, data):
if type(data) == str and data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
return int(data)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def bctype(self):
return self._type
@property
def edge(self):
return self._edge
@edge.setter
def edge(self, edge):
self._edge = edge
def has_edge(self):
return self._edge is not None
@property
def header(self):
return self._header.copy()
@property
def data(self):
return self._data.copy()
def get_type_column(self, column):
if 0 <= column < 2:
return self._types[column]
return None
@property
def _default_0(self):
return self._types[0](0)
@property
def _default_1(self):
return self._types[1](0.0)
def is_define(self):
return self._data is not None
def new_from_data(self, header, data):
new_0 = self._default_0
new_1 = self._default_1
if len(header) != 0:
for i in [0,1]:
for j in range(len(header)):
if self._header[i] == header[j]:
if i == 0:
new_0 = self._types[i](data[j].replace(",", "."))
else:
new_1 = self._types[i](data[j].replace(",", "."))
else:
new_0 = self._types[0](data[0].replace(",", "."))
new_1 = self._types[1](data[1].replace(",", "."))
return (new_0, new_1)
def add(self, index:int):
value = (self._default_0, self._default_1)
self._data.insert(index, value)
return value
def insert(self, index:int, value):
self._data.insert(index, value)
def delete_i(self, indexes):
self._data = list(
map(
lambda e: e[1],
filter(
lambda e: e[0] not in indexes,
enumerate(self.data)
)
)
)
def delete(self, els):
self._data = list(
filter(
lambda e: e not in els,
self.data
)
)
def sort(self, _reverse=False, key=None):
if key is None:
self._data.sort(reverse=_reverse)
else:
self._data.sort(reverse=_reverse, key=key)
def get_i(self, index):
return self.data[index]
def get_range(self, _range):
l = []
for r in _range:
l.append(r)
return l
def _set_i_c_v(self, index, column, value):
v = list(self._data[index])
v[column] = self._types[column](value)
self._data[index] = tuple(v)
def set_i_0(self, index:int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index:int, value):
self._set_i_c_v(index, 1, value)
@timer
def convert(self, cls):
new = cls(name = self.name)
for i, _ in self.data:
new.add(i)
for i in [0,1]:
for j in [0,1]:
if self._header[i] == new.header[j]:
for ind, v in self.data:
new._set_i_c_v(ind, j, v[i])
return new
def move_up(self, index):
if index < len(self):
next = index - 1
d = self._data
d[index], d[next] = d[next], d[index]
def move_down(self, index):
if index >= 0:
prev = index + 1
d = self._data
d[index], d[prev] = d[prev], d[index]

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
from Model.Except import NotImplementedMethodeError
from Model.LateralContribution.LateralContributionTypes import (
NotDefined,
PonctualContribution,
TimeOverZ, TimeOverDebit, ZOverDebit
)
class LateralContributionList(object):
def __init__(self):
super(LateralContributionList, self).__init__()
self._tabs = {
"liquid" : [],
"solid" : [],
"suspenssion" : []
}
def len(self, lst):
return len(self._tabs[lst])
def get_tab(self, lst):
return self._tabs[lst].copy()
def get(self, lst, row):
return self._tabs[lst][row]
def set(self, lst, row, new):
self._tabs[lst][row] = new
def new(self, lst, index):
n = NotDefined()
self._tabs[lst].insert(index, n)
return n
def insert(self, lst, index, new):
self._tabs[lst].insert(index, new)
def delete(self, lst, bcs):
for bc in bcs:
self._tabs[lst].remove(bc)
def delete_i(self, lst, indexes):
bcs = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._tabs[lst])
)
)
)
self.delete(lst, bcs)
def sort(self, lst, reverse=False, key=None):
self._tabs[lst].sort(reverse=reverse, key=key)
def move_up(self, lst, index):
if index < len(self._tabs[lst]):
next = index - 1
l = self._tabs[lst]
l[index], l[next] = l[next], l[index]
def move_down(self, lst, index):
if index >= 0:
prev = index + 1
l = self._tabs[lst]
l[index], l[prev] = l[prev], l[index]
def __copy__(self):
new = LateralContributionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].copy()
return new
def __deepcopy__(self):
new = LateralContributionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].deepcopy()
return new
def copy(self):
return copy(self)

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
from Model.Except import NotImplementedMethodeError
from Model.LateralContribution.LateralContribution import LateralContribution
class NotDefined(LateralContribution):
def __init__(self, name:str = ""):
super(NotDefined, self).__init__(name=name)
self._type = "ND"
self._header = ["x", "y"]
@property
def _default_0(self):
return 0.0
class PonctualContribution(LateralContribution):
def __init__(self, name:str = ""):
super(PonctualContribution, self).__init__(name=name)
self._type = "PC"
self._header = ["time", "debit"]
self._types = [PonctualContribution.time_convert, float]
@classmethod
def compatibility(cls):
return ["liquid"]
class TimeOverZ(LateralContribution):
def __init__(self, name:str = ""):
super(TimeOverZ, self).__init__(name=name)
self._type = "TZ"
self._header = ["time", "z"]
self._types = [TimeOverZ.time_convert, float]
@classmethod
def compatibility(cls):
return ["liquid"]
class TimeOverDebit(LateralContribution):
def __init__(self, name:str = ""):
super(TimeOverDebit, self).__init__(name=name)
self._type = "TD"
self._header = ["time", "debit"]
self._types = [TimeOverDebit.time_convert, float]
@classmethod
def compatibility(cls):
return ["liquid"]
class ZOverDebit(LateralContribution):
def __init__(self, name:str = ""):
super(ZOverDebit, self).__init__(name=name)
self._type = "ZD"
self._header = ["z", "debit"]
self._types = [float, float]
@classmethod
def compatibility(cls):
return ["liquid"]
@property
def _default_0(self):
return 0.0

View File

@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from tools import timer, trace
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
from View.LateralContribution.Edit.translate import *
_translate = QCoreApplication.translate
class Plot(APlot):
def __init__(self, canvas=None, data=None,
mode = "time", toolbar=None):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
self._mode = mode
def custom_ticks(self):
t0 = datetime.fromtimestamp(0)
nb = len(self.data.data)
mod = int(nb / 5)
fx = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] % mod == 0,
enumerate(self.data.data)
)
)
)
xx = list(map(lambda v: v[0], fx))
if self._mode == "time":
xt = list(
map(
lambda v: str(
datetime.fromtimestamp(v[0]) - t0
).split(",")[0]\
.replace("days", _translate("LateralContribution", "days"))\
.replace("day", _translate("LateralContribution", "day")),
fx
)
)
else:
xt = list(
map(
lambda v: str(datetime.fromtimestamp(v[0]).date()),
fx
)
)
self.canvas.axes.set_xticks(ticks=xx, labels=xt, rotation=45)
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if len(self.data) == 0:
self._init = False
return
# Plot data
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
self._line, = self.canvas.axes.plot(
x, y,
color='r', lw=1.,
markersize=5, marker='+',
picker=30,
)
self.custom_ticks()
# Plot label
header = self.data.header
self.canvas.axes.set_xlabel(
table_headers[header[0]], color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
table_headers[header[1]], color='black', fontsize=10
)
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
#self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if self._init == False:
self.draw()
return
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
self._line.set_data(x, y)
self.custom_ticks()
self.canvas.axes.relim()
self.canvas.axes.autoscale()
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()

View File

@ -0,0 +1,302 @@
# -*- coding: utf-8 -*-
from datetime import date, time, datetime, timedelta
from tools import trace, timer
from View.ASubWindow import ASubMainWindow, AWidget
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect, QTime, QDateTime,
)
from PyQt5.QtWidgets import (
QTableView, QAbstractItemView, QSpinBox,
QTimeEdit, QDateTimeEdit, QItemDelegate,
)
from Model.LateralContribution.LateralContributionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDebit, ZOverDebit
)
from View.LateralContribution.Edit.UndoCommand import (
SetDataCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
from View.LateralContribution.Edit.translate import *
_translate = QCoreApplication.translate
class ExtendedTimeEdit(AWidget):
def __init__(self, parent=None):
super(ExtendedTimeEdit, self).__init__(
ui="extendedTimeEdit",
parent=parent
)
self.parent = parent
self.spinBox_days = self.find(QSpinBox, "spinBox_days")
self.timeEdit = self.find(QTimeEdit, "timeEdit")
def set_time(self, time):
days = 0
stime = time
# if ',' in time, time format is 'DD days, HH:MM:SS',
# otherelse is 'HH:MM:SS'
if "," in time:
s = time.strip().split(" ")
days = int(s[0])
stime = s[-1]
qtime = QTime.fromString(
stime,
"h:mm:ss"
)
self.spinBox_days.setValue(days)
self.timeEdit.setTime(qtime)
def get_time(self):
days = self.spinBox_days.value()
time = self.timeEdit.time().toPyTime()
secs = (
(time.hour * 3600) +
(time.minute * 60) +
time.second
)
return timedelta(days=days, seconds=secs)
class ExtendedDateTimeEdit(AWidget):
def __init__(self, parent=None):
super(ExtendedDateTimeEdit, self).__init__(
ui="extendedDateTimeEdit",
parent=parent
)
self.parent = parent
self.dateTimeEdit = self.find(QDateTimeEdit, "dateTimeEdit")
def set_time(self, time):
qdatetime = QDateTime.fromString(
time,
"yyyy-MM-dd hh:mm:ss"
)
self.dateTimeEdit.setDateTime(qdatetime)
def get_time(self):
time = self.dateTimeEdit.dateTime().toPyDateTime()
return time
class ExTimeDelegate(QItemDelegate):
def __init__(self, data=None, mode="time", parent=None):
super(ExTimeDelegate, self).__init__(parent)
self._data = data
self._mode = mode
def createEditor(self, parent, option, index):
if self._mode == "time":
self.editor = ExtendedTimeEdit(parent=parent)
else:
self.editor = ExtendedDateTimeEdit(parent=parent)
value = index.data(Qt.DisplayRole)
self.editor.set_time(value)
print(value)
return self.editor
def setModelData(self, editor, model, index):
time = editor.get_time()
if self._mode == "time":
model.setData(index, int(time.total_seconds()))
else:
print(time.timestamp())
model.setData(index, int(time.timestamp()))
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, data=None, mode="time", undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = data.header
self._data = data
self._mode = mode
self._undo = undo
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self._headers)
def data(self, index, role):
if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
row = index.row()
column = index.column()
value = QVariant()
if 0 <= column < 2:
v = self._data.get_i(row)[column]
if self._data.get_type_column(column) == float:
value = f"{v:.4f}"
elif self._data.header[column] == "time":
if self._mode == "time":
t0 = datetime.fromtimestamp(0)
t = datetime.fromtimestamp(v)
value = str(t - t0)
else:
value = str(datetime.fromtimestamp(v))
else:
value = f"{v}"
return value
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[section]]
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()
self._undo.push(
SetDataCommand(
self._data, row, column, 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._data, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._data, rows
)
)
self.endRemoveRows()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
self._undo.push(
SortCommand(
self._data, _reverse
)
)
self.layoutAboutToBeChanged.emit()
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._data, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._data):
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
MoveCommand(
self._data, "down", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def paste(self, row, header, data):
if len(data) == 0:
return
self.layoutAboutToBeChanged.emit()
self._undo.push(
PasteCommand(
self._data, row,
list(
map(
lambda d: self._data.new_from_data(header, d),
data
)
)
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()

View File

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.LateralContribution.LateralContribution import LateralContribution
class SetDataCommand(QUndoCommand):
def __init__(self, data, index, column, new_value):
QUndoCommand.__init__(self)
self._data = data
self._index = index
self._column = column
self._old = self._data.get_i(self._index)[self._column]
self._new = new_value
def undo(self):
self._data._set_i_c_v(self._index, self._column, self._old)
def redo(self):
self._data._set_i_c_v(self._index, self._column, self._new)
class AddCommand(QUndoCommand):
def __init__(self, data, index):
QUndoCommand.__init__(self)
self._data = data
self._index = index
self._new = None
def undo(self):
self._data.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._data.add(self._index)
else:
self._data.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, data, rows):
QUndoCommand.__init__(self)
self._data = data
self._rows = rows
self._bc = []
for row in rows:
self._bc.append((row, self._data.get_i(row)))
self._bc.sort()
def undo(self):
for row, el in self._bc:
self._data.insert(row, el)
def redo(self):
self._data.delete_i(self._rows)
class SortCommand(QUndoCommand):
def __init__(self, data, _reverse):
QUndoCommand.__init__(self)
self._data = data
self._reverse = _reverse
self._old = self._data.data
self._indexes = None
def undo(self):
ll = self._data.data
self._data.sort(
key=lambda x: self._indexes[ll.index(x)]
)
def redo(self):
self._data.sort(
_reverse=self._reverse,
key=lambda x: x[0]
)
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._data.data
)
)
self._old = None
class MoveCommand(QUndoCommand):
def __init__(self, data, up, i):
QUndoCommand.__init__(self)
self._data = data
self._up = up == "up"
self._i = i
def undo(self):
if self._up:
self._data.move_up(self._i)
else:
self._data.move_down(self._i)
def redo(self):
if self._up:
self._data.move_up(self._i)
else:
self._data.move_down(self._i)
class PasteCommand(QUndoCommand):
def __init__(self, data, row, bcs):
QUndoCommand.__init__(self)
self._data = data
self._row = row
self._bcs = bcs
self._bcs.reverse()
def undo(self):
self._data.delete(self._bcs)
def redo(self):
for bc in self._bcs:
self._data.insert(self._row, bc)
class DuplicateCommand(QUndoCommand):
def __init__(self, data, rows, bc):
QUndoCommand.__init__(self)
self._data = data
self._rows = rows
self._bc = deepcopy(bc)
self._bc.reverse()
def undo(self):
self._data.delete(self._bc)
def redo(self):
for bc in self._bcs:
self._data.insert(self._rows[0], bc)

View File

@ -0,0 +1,205 @@
# -*- coding: utf-8 -*-
from tools import timer, trace
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, QCoreApplication,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QHeaderView,
)
from View.Plot.MplCanvas import MplCanvas
from View.LateralContribution.translate import long_types
from View.LateralContribution.Edit.Table import TableModel, ExTimeDelegate
from View.LateralContribution.Edit.Plot import Plot
_translate = QCoreApplication.translate
class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Edit boundary condition",
data=None, study=None, parent=None):
self._data = data
self._study = study
self._title = title
self.compute_title()
super(EditLateralContributionWindow, self).__init__(
name=self._title, ui="EditLateralContributions", parent=parent
)
self.ui.setWindowTitle(self._title)
self.setup_sc()
self.setup_table()
self.setup_plot()
self.setup_connections()
def compute_title(self):
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("LateralContribution", "Not associate"))
self._title = (
_translate("Edit boundary condition", self._title) +
f" - {self._study.name} " +
f" - {self._data.name} " +
f"({long_types[self._data.bctype]} - {node_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, "tableView")
self._table = TableModel(
data = self._data,
undo = self._undo_stack,
mode = self._study.time_system
)
if self._data.header[0] == "time":
self._delegate_time = ExTimeDelegate(
data = self._data,
mode = self._study.time_system,
parent = self
)
table.setItemDelegateForColumn(
0, self._delegate_time
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_plot(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.verticalLayout.addWidget(self.canvas)
self.plot = Plot(
canvas = self.canvas,
data = self._data,
mode = self._study.time_system,
)
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_sort").triggered.connect(self.sort)
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.update)
def update(self):
self.plot.update()
def index_selected_row(self):
table = self.find(QTableView, "tableView")
return table.selectionModel()\
.selectedRows()[0]\
.row()
def index_selected_rows(self):
table = self.find(QTableView, "tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._data) == 0 or len(rows) == 0:
self._table.add(0)
else:
self._table.add(rows[0])
self.plot.update()
def delete(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table.delete(rows)
self.plot.update()
def sort(self):
self._table.sort(False)
self.plot.update()
def move_up(self):
row = self.index_selected_row()
self._table.move_up(row)
self.plot.update()
def move_down(self):
row = self.index_selected_row()
self._table.move_down(row)
self.plot.update()
def copy(self):
rows = self.index_selected_rows()
table = []
table.append(self._data.header)
data = self._data.data
for row in rows:
table.append(list(data[row]))
self.copyTableIntoClipboard(table)
def paste(self):
header, data = self.parseClipboardTable()
if len(data) == 0:
return
row = 0
rows = self.index_selected_rows()
if len(rows) != 0:
row = rows[0]
self._table.paste(row, header, data)
self.plot.update()
def undo(self):
self._table.undo()
self.plot.update()
def redo(self):
self._table.redo()
self.plot.update()

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"x": _translate("LateralContribution", "X"),
"y": _translate("LateralContribution", "Y"),
"time": _translate("LateralContribution", "Time"),
"debit": _translate("LateralContribution", "Debit"),
"z": _translate("LateralContribution", "Z (m)")
}

View File

@ -0,0 +1,239 @@
# -*- 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.LateralContribution.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand,
)
from Model.LateralContribution.LateralContributionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDebit, ZOverDebit
)
from View.LateralContribution.translate import *
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, mode="type", tab="", parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._tab = tab
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
if self._mode == "type":
lst = list(
map(
lambda k: long_types[k],
filter(
lambda k: self._tab in BC_types[k].compatibility(),
BC_types.keys()
)
)
)
self.editor.addItems(
lst
)
else:
self.editor.addItems(
[_translate("LateralContribution", "Not associate")] +
self._data.nodes_names()
)
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, data=None, undo=None, tab=""):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._data = data
self._undo = undo
self._tab = tab
self._lcs = self._data.boundary_condition
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return self._lcs.len(self._tab)
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._lcs.get(self._tab, row).name
elif self._headers[column] == "type":
t = self._lcs.get(self._tab, row).bctype
return long_types[t]
elif self._headers[column] == "node":
n = self._lcs.get(self._tab, row).node
if n is None:
return _translate("LateralContribution", "Not associate")
return n.name
return QVariant()
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[section]]
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._lcs, self._tab,row, value
)
)
elif self._headers[column] == "type":
key = next(k for k, v in long_types.items() if v == value)
self._undo.push(
SetTypeCommand(
self._lcs, self._tab,row, BC_types[key]
)
)
elif self._headers[column] == "node":
self._undo.push(
SetNodeCommand(
self._lcs, self._tab,row, self._data.node(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._lcs, self._tab,row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._lcs, self._tab,rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
self._undo.push(
SortCommand(
self._lcs, self._tab,False
)
)
self.layoutAboutToBeChanged.emit()
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._lcs, self._tab,"up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._lcs):
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
MoveCommand(
self._lcs, self._tab,"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()

View File

@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.LateralContribution.LateralContribution import LateralContribution
from Model.LateralContribution.LateralContributionList import LateralContributionList
class SetNameCommand(QUndoCommand):
def __init__(self, lcs, tab, index, new_value):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._index = index
self._old = self._lcs.get(self._tab, self._index).name
self._new = new_value
def undo(self):
self._lcs.get(self._tab, self._index).name = self._old
def redo(self):
self._lcs.get(self._tab, self._index).name = self._new
class SetNodeCommand(QUndoCommand):
def __init__(self, lcs, tab, index, node):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._index = index
self._old = self._lcs.get(self._tab, self._index).node
self._new = node
def undo(self):
self._lcs.get(self._tab, self._index).node = self._old
def redo(self):
self._lcs.get(self._tab, self._index).node = self._new
class SetTypeCommand(QUndoCommand):
def __init__(self, lcs, tab, index, _type):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._index = index
self._type = _type
self._old = self._lcs.get(self._tab, self._index)
self._new = self._lcs.get(self._tab, self._index)\
.convert(self._type)
def undo(self):
self._lcs.set(self._tab, self._index, self._old)
def redo(self):
self._lcs.set(self._tab, self._index, self._new)
class AddCommand(QUndoCommand):
def __init__(self, lcs, tab, index):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._index = index
self._new = None
def undo(self):
self._lcs.delete_i(self._tab, [self._index])
def redo(self):
if self._new is None:
self._new = self._lcs.new(self._tab, self._index)
else:
self._lcs.insert(self._tab, self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, lcs, tab, rows):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._rows = rows
self._bc = []
for row in rows:
self._bc.append((row, self._lcs.get(self._tab, row)))
self._bc.sort()
def undo(self):
for row, el in self._bc:
self._lcs.insert(self._tab, row, el)
def redo(self):
self._lcs.delete_i(self._tab, self._rows)
class SortCommand(QUndoCommand):
def __init__(self, lcs, tab, _reverse):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._reverse = _reverse
self._old = self._lcs.get_tab(self._tab)
self._indexes = None
def undo(self):
ll = self._lcs.get_tab(self._tab)
self._lcs.sort(
self._tab,
key=lambda x: self._indexes[ll.index(x)]
)
def redo(self):
self._lcs.sort(
self._tab,
reverse=self._reverse,
key=lambda x: x.name
)
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._lcs.get_tab(self._tab)
)
)
self._old = None
class MoveCommand(QUndoCommand):
def __init__(self, lcs, tab, up, i):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._up = up == "up"
self._i = i
def undo(self):
if self._up:
self._lcs.move_up(self._tab, self._i)
else:
self._lcs.move_down(self._tab, self._i)
def redo(self):
if self._up:
self._lcs.move_up(self._tab, self._i)
else:
self._lcs.move_down(self._tab, self._i)
class PasteCommand(QUndoCommand):
def __init__(self, lcs, tab, row, bc):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._row = row
self._bc = deepcopy(bc)
self._bc.reverse()
def undo(self):
self._lcs.delete(self._tab, self._bc)
def redo(self):
for bc in self._bc:
self._lcs.insert(self._tab, self._row, bc)
class DuplicateCommand(QUndoCommand):
def __init__(self, lcs, tab, rows, bc):
QUndoCommand.__init__(self)
self._lcs = lcs
self._tab = tab
self._rows = rows
self._bc = deepcopy(bc)
self._bc.reverse()
def undo(self):
self._lcs.delete(self._tab, self._bc)
def redo(self):
for bc in self._lcs:
self._lcs.insert(self._tab, self._rows[0], bc)

View File

@ -0,0 +1,209 @@
# -*- coding: utf-8 -*-
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.LateralContribution.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand,
)
from Model.LateralContribution.LateralContributionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDebit, ZOverDebit
)
from View.LateralContribution.Table import (
TableModel, ComboBoxDelegate
)
from View.Network.GraphWidget import GraphWidget
from View.LateralContribution.translate import *
from View.LateralContribution.Edit.Window import EditLateralContributionWindow
_translate = QCoreApplication.translate
class LateralContributionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Lateral contribution", study=None, parent=None):
title = title + " - " + study.name
super(LateralContributionWindow, self).__init__(
name=title, ui="LateralContributions", parent=parent
)
self._study = study
self._lcs = self._study.river.lateral_contribution
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(title)
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 = {}
for t in ["liquid", "solid", "suspenssion"]:
table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel(
data = self._study.river,
undo = self._undo_stack,
tab = t,
)
table.setModel(self._table[t])
self._delegate_type = ComboBoxDelegate(
data = self._study.river,
mode = "type",
tab = t,
parent=self
)
self._delegate_node = ComboBoxDelegate(
data = self._study.river,
mode = "node",
tab = t,
parent=self
)
table.setItemDelegateForColumn(
1, self._delegate_type
)
table.setItemDelegateForColumn(
2, self._delegate_node
)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
self.graph_widget = GraphWidget(
self._study.river,
min_size=None, size=(200,200),
only_display=True,
parent=self
)
self.graph_layout = self.find(QVBoxLayout, "verticalLayout")
self.graph_layout.addWidget(self.graph_widget)
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)
self.find(QAction, "action_sort").triggered.connect(self.sort)
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)
def current_tab(self):
return self.find(QTabWidget, "tabWidget")\
.currentWidget()\
.objectName()\
.replace("tab_", "")
def index_selected_row(self):
tab = self.current_tab()
table = self.find(QTableView, f"tableView_{tab}")
return table.selectionModel()\
.selectedRows()[0]\
.row()
def index_selected_rows(self):
tab = self.current_tab()
table = self.find(QTableView, f"tableView_{tab}")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
tab = self.current_tab()
rows = self.index_selected_rows()
if self._lcs.len(tab) == 0 or len(rows) == 0:
self._table[tab].add(0)
else:
self._table[tab].add(rows[0])
def delete(self):
tab = self.current_tab()
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table[tab].delete(rows)
def sort(self):
tab = self.current_tab()
self._table[tab].sort(False)
def move_up(self):
tab = self.current_tab()
row = self.index_selected_row()
self._table[tab].move_up(row)
def move_down(self):
tab = self.current_tab()
row = self.index_selected_row()
self._table[tab].move_down(row)
def copy(self):
print("TODO")
def paste(self):
print("TODO")
def undo(self):
tab = self.current_tab()
self._table[tab].undo()
def redo(self):
tab = self.current_tab()
self._table[tab].redo()
def edit(self):
tab = self.current_tab()
rows = self.index_selected_rows()
for row in rows:
win = EditLateralContributionWindow(
data=self._lcs.get(tab, row),
study=self._study,
parent=self
)
win.show()

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from Model.LateralContribution.LateralContributionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDebit, ZOverDebit
)
_translate = QCoreApplication.translate
long_types = {
"ND": _translate("LateralContribution", "Not defined"),
"PC": _translate("LateralContribution", "Ponctual contribution"),
"TZ": _translate("LateralContribution", "Time over Z"),
"TD": _translate("LateralContribution", "Time over Debit"),
"ZD": _translate("LateralContribution", "Z over Debit"),
}
table_headers = {
"name": _translate("LateralContribution", "Name"),
"type": _translate("LateralContribution", "Type"),
"node": _translate("LateralContribution", "Node")
}
BC_types = {
"ND": NotDefined,
"PC": PonctualContribution,
"TZ": TimeOverZ,
"TD": TimeOverDebit,
"ZD": ZOverDebit
}

View File

@ -23,6 +23,7 @@ from View.Main.AboutWindow import AboutWindow
from View.Network.NetworkWindow import NetworkWindow
from View.Geometry.GeometryWindow import GeometryWindow
from View.BoundaryCondition.BoundaryConditionWindow import BoundaryConditionWindow
from View.LateralContribution.Window import LateralContributionWindow
from Model.Study import Study
@ -116,7 +117,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_toolBar_mesh": lambda: self.open_dummy("Mesh"),
"action_toolBar_run_meshing_tool": lambda: self.open_dummy("Lancement mailleur externe"),
"action_toolBar_boundary_cond": self.open_boundary_cond,
"action_toolBar_lateral_contrib": lambda: self.open_dummy("Apport Lateraux"),
"action_toolBar_lateral_contrib": self.open_lateral_contrib,
"action_toolBar_spills": lambda: self.open_dummy("Deversement"),
"action_toolBar_sections": lambda: self.open_dummy("Tronçons"),
"action_toolBar_frictions": lambda: self.open_dummy("Frottements"),
@ -320,6 +321,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.bound = BoundaryConditionWindow(study = self.model, parent=self)
self.bound.show()
def open_lateral_contrib(self):
self.lateral = LateralContributionWindow(study = self.model, parent=self)
self.lateral.show()
# TODO: Delete me !
###############

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</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="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_liquid">
<attribute name="title">
<string>Liquid</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QTableView" name="tableView_liquid"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_solid">
<attribute name="title">
<string>Solid</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QTableView" name="tableView_solid"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_suspenssion">
<attribute name="title">
<string>Suspenssion</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QTableView" name="tableView_suspenssion"/>
</item>
</layout>
</widget>
</widget>
<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>800</width>
<height>22</height>
</rect>
</property>
</widget>
<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"/>
<addaction name="action_sort"/>
</widget>
<action name="action_add">
<property name="checkable">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add</string>
</property>
<property name="toolTip">
<string>Add a new boundary condition or lateral contribution</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</string>
</property>
<property name="toolTip">
<string>Delete current selected rows</string>
</property>
<property name="shortcut">
<string>Ctrl+D</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</string>
</property>
<property name="toolTip">
<string>Edit boundary condition or lateral contribution</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
<action name="action_sort">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-sort-ascending.png</normaloff>ressources/gtk-sort-ascending.png</iconset>
</property>
<property name="text">
<string>Sort</string>
</property>
<property name="toolTip">
<string>Sort boundary condition by name</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>