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.Network.NetworkWindow import NetworkWindow
from View.Geometry.GeometryWindow import GeometryWindow from View.Geometry.GeometryWindow import GeometryWindow
from View.BoundaryCondition.BoundaryConditionWindow import BoundaryConditionWindow from View.BoundaryCondition.BoundaryConditionWindow import BoundaryConditionWindow
from View.LateralContribution.Window import LateralContributionWindow
from Model.Study import Study from Model.Study import Study
@ -116,7 +117,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_toolBar_mesh": lambda: self.open_dummy("Mesh"), "action_toolBar_mesh": lambda: self.open_dummy("Mesh"),
"action_toolBar_run_meshing_tool": lambda: self.open_dummy("Lancement mailleur externe"), "action_toolBar_run_meshing_tool": lambda: self.open_dummy("Lancement mailleur externe"),
"action_toolBar_boundary_cond": self.open_boundary_cond, "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_spills": lambda: self.open_dummy("Deversement"),
"action_toolBar_sections": lambda: self.open_dummy("Tronçons"), "action_toolBar_sections": lambda: self.open_dummy("Tronçons"),
"action_toolBar_frictions": lambda: self.open_dummy("Frottements"), "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 = BoundaryConditionWindow(study = self.model, parent=self)
self.bound.show() self.bound.show()
def open_lateral_contrib(self):
self.lateral = LateralContributionWindow(study = self.model, parent=self)
self.lateral.show()
# TODO: Delete me ! # 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>