Commit 0512947e authored by Pierre-Antoine Rouby's avatar Pierre-Antoine Rouby
Browse files

IC: Add Model and View base code.

Showing with 800 additions and 4 deletions
+800 -4
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
class Data(object):
def __init__(self, status = None):
super(Data, self).__init()
self._status = status
self._name = ""
self._comment = ""
self._kp = 0.0
self._flow = 0.0
self._cote = 0.0
self._tiran = 0.0
def __getitems__(self, key):
val = None
if key == "name":
val = self._name
elif key == "comment":
val = self._comment
elif key == "kp":
val = self._kp
elif key == "flow":
val = self._flow
elif key == "cote":
val = self._cote
elif key == "tiran":
val = self._tiran
return val
def __setitems__(self, key, value):
if key == "name":
self._name = str(value)
elif key == "comment":
self._comment = str(value)
elif key == "kp":
self._kp = float(value)
elif key == "flow":
self._flow = float(value)
elif key == "cote":
self._cote = float(value)
elif key == "tiran":
self._tiran = float(value)
self._status.modified()
class InitialConditions(object):
def __init__(self, reach = None, status = None):
super(InitialConditions, self).__init__()
self._status = status
self._reach = reach
self._data = []
def __len__(self):
return len(self._data)
@property
def reach(self):
return self._reach
@reach.setter
def reach(self, new):
self._reach = reach
self._status.modified()
@property
def data(self):
return self._data.copy()
def get(self, index):
return self._data[index]
def set(self, index, data):
self._data.insert(index, data)
self._status.modified()
def new(self, index):
n = Data(self._status)
self._data.insert(index, n)
self._status.modified()
def insert(self, index, data):
self._data.insert(index, data)
self._status.modified()
def delete(self, data):
self._data = list(
filter(
lambda x: x in data,
self._data
)
)
self._status.modified()
def delete_i(self, indexes):
data = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._data)
)
)
)
self.delete(data)
def sort(self, reverse=False, key=None):
self._data.sort(reverse=reverse, key=key)
self._status.modified()
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
from Model.InitialConditions.InitialConditions import InitialConditions
class InitialConditionsDict(object):
def __init__(self, status = None):
super(InitialConditionsDict, self).__init__()
self._status = status
self._reach = {}
def __len__(self):
return len(self._reach)
def is_defined(self, reach):
return reach in self._reach
def get(self, reach):
if reach in self._reach:
return self._reach[reach]
new = self.new(reach)
self.set(reach, new)
return new
def set(self, reach, new):
self._reach[reach] = new
self._status.modified()
def new(self, reach):
new = InitialConditions(reach = reach, status = self._status)
self.set(reach, new)
......@@ -9,6 +9,7 @@ from Model.Geometry.Reach import Reach
from Model.BoundaryCondition.BoundaryConditionList import BoundaryConditionList
from Model.LateralContribution.LateralContributionList import LateralContributionList
from Model.InitialConditions.InitialConditionsDict import InitialConditionsDict
from Model.Stricklers.StricklersList import StricklersList
from Model.Section.SectionList import SectionList
......@@ -62,6 +63,7 @@ class River(Graph):
self._current_reach = None
self._boundary_condition = BoundaryConditionList(status=self._status)
self._lateral_contribution = LateralContributionList(status=self._status)
self._initial_conditions = InitialConditionsDict(status=self._status)
self._stricklers = StricklersList(status=self._status)
self._sections = SectionList(status=self._status)
......@@ -73,6 +75,10 @@ class River(Graph):
def lateral_contribution(self):
return self._lateral_contribution
@property
def initial_conditions(self):
return self._initial_conditions
@property
def stricklers(self):
return self._stricklers
......
# -*- 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.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
from View.InitialConditions.translate import *
_translate = QCoreApplication.translate
class TableModel(QAbstractTableModel):
def __init__(self, river=None, reach=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._river = river
self._reach = reach
self._undo = undo
self._ics = self._river.initial_conditions.get(reach)
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._ics)
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] is not None:
return self._ics.get(row)[self._headers[column]]
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] is not None:
self._undo.push(
SetCommand(
self._ics, row, self._headers[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._ics, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._ics, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
self._undo.push(
SortCommand(
self._ics, 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._ics, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._ics):
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
MoveCommand(
self._ics, "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()
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.InitialConditions.InitialConditions import InitialConditions
from Model.InitialConditions.InitialConditionsDict import InitialConditionsDict
class SetCommand(QUndoCommand):
def __init__(self, ics, row, column, new_value):
QUndoCommand.__init__(self)
self._ics = ics
self._row = row
self._old = self._ics.get(self._row)[column]
self._new = new_value
def undo(self):
self._ics.get(self._row)[column] = self._old
def redo(self):
self._ics.get(self._row)[column] = self._new
class AddCommand(QUndoCommand):
def __init__(self, ics, index):
QUndoCommand.__init__(self)
self._ics = ics
self._index = index
self._new = None
def undo(self):
self._ics.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._ics.new(self._index)
else:
self._ics.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, ics, tab, rows):
QUndoCommand.__init__(self)
self._ics = ics
self._rows = rows
self._ic = []
for row in rows:
self._ic.append((row, self._ics.get(row)))
self._ic.sort()
def undo(self):
for row, el in self._ic:
self._ics.insert(row, el)
def redo(self):
self._ics.delete_i(self._rows)
class SortCommand(QUndoCommand):
def __init__(self, ics, tab, _reverse):
QUndoCommand.__init__(self)
self._ics = ics
self._reverse = _reverse
self._old = self._ics.data
self._indexes = None
def undo(self):
ll = self._ics.data
self._ics.sort(
key=lambda x: self._indexes[ll.index(x)]
)
def redo(self):
self._ics.sort(
reverse=self._reverse,
key=lambda x: x.name
)
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._ics.data
)
)
self._old = None
class MoveCommand(QUndoCommand):
def __init__(self, ics, tab, up, i):
QUndoCommand.__init__(self)
self._ics = ics
self._up = up == "up"
self._i = i
def undo(self):
if self._up:
self._ics.move_up(self._i)
else:
self._ics.move_down(self._i)
def redo(self):
if self._up:
self._ics.move_up(self._i)
else:
self._ics.move_down(self._i)
class PasteCommand(QUndoCommand):
def __init__(self, ics, tab, row, ic):
QUndoCommand.__init__(self)
self._ics = ics
self._row = row
self._ic = deepcopy(ic)
self._ic.reverse()
def undo(self):
self._ics.delete(self._ic)
def redo(self):
for ic in self._ic:
self._ics.insert(self._row, ic)
class DuplicateCommand(QUndoCommand):
def __init__(self, ics, tab, rows, ic):
QUndoCommand.__init__(self)
self._ics = ics
self._rows = rows
self._ic = deepcopy(ic)
self._ic.reverse()
def undo(self):
self._ics.delete(self._ic)
def redo(self):
for ic in self._ics:
self._ics.insert(self._rows[0], ic)
# -*- 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.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
from View.InitialConditions.Table import TableModel
from View.Plot.MplCanvas import MplCanvas
from View.InitialConditions.translate import *
_translate = QCoreApplication.translate
class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Initial condition",
study=None, parent=None):
title = title + " - " + study.name
super(InitialConditionsWindow, self).__init__(
name=title, ui="InitialConditions", parent=parent
)
self._study = study
self._reach = study.river.current_reach()
self._ics = self._study.river.initial_conditions.get(self._reach)
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):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
river = self._study.river,
reach = self._reach,
undo = self._undo_stack,
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
print("TODO")
# self.canvas_1 = MplCanvas(width=5, height=4, dpi=100)
# self.canvas_1.setObjectName("canvas_1")
# self.plot_layout_1 = self.find(QVBoxLayout, "verticalLayout_1")
# self.plot_layout_1.addWidget(self.canvas_1)
# self.plot = PlotXY(
# canvas = self.canvas_1,
# data = None,
# toolbar = None,
# )
# self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
# self.canvas_2.setObjectName("canvas_2")
# self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
# self.plot_layout_2.addWidget(self.canvas_2)
# self.plot = PlotXY(
# canvas = self.canvas_2,
# data = None,
# toolbar = None,
# )
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)
def index_selected_row(self):
table = self.find(QTableView, f"tableView")
return table.selectionModel()\
.selectedRows()[0]\
.row()
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 self._ics.len(tab) == 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 sort(self):
self._table.sort(False)
def move_up(self):
row = self.index_selected_row()
self._table.move_up(row)
def move_down(self):
row = self.index_selected_row()
self._table.move_down(row)
def copy(self):
print("TODO")
def paste(self):
print("TODO")
def undo(self):
self._table.undo()
def redo(self):
self._table.redo()
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
table_headers = {
"name": _translate("LateralContribution", "Name"),
"comment": _translate("LateralContribution", "Comment"),
"kp": _translate("LateralContribution", "KP (m)"),
"flow": _translate("LateralContribution", "Flow (m³/s)"),
"cote": _translate("LateralContribution", "Cote (m)"),
"tiran": _translate("LateralContribution", "Tiran (m)")
}
......@@ -24,6 +24,7 @@ from View.Network.Window import NetworkWindow
from View.Geometry.Window import GeometryWindow
from View.BoundaryCondition.Window import BoundaryConditionWindow
from View.LateralContribution.Window import LateralContributionWindow
from View.InitialConditions.Window import InitialConditionsWindow
from View.Stricklers.Window import StricklersWindow
from View.Sections.Window import SectionsWindow
......@@ -49,6 +50,7 @@ define_model_action = [
"action_toolBar_boundary_cond", "action_toolBar_lateral_contrib",
"action_toolBar_spills", "action_toolBar_sections",
"action_toolBar_stricklers", "action_toolBar_building",
"action_toolBar_initial_cond",
]
action = (
......@@ -130,6 +132,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_toolBar_stricklers": self.open_stricklers,
"action_toolBar_sections": self.open_sections,
"action_toolBar_building": lambda: self.open_dummy("Ouvrages"),
"action_toolBar_initial_cond": self.open_initial_conditions,
}
for action in actions:
......@@ -328,28 +331,40 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"into river network window to work on it")
def open_boundary_cond(self):
self.bound = BoundaryConditionWindow(study = self.model, parent=self)
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 = LateralContributionWindow(study = self.model, parent = self)
self.lateral.show()
def open_stricklers(self):
self.strick = StricklersWindow(
study = self.model,
config = self.conf,
parent=self
parent = self
)
self.strick.show()
def open_sections(self):
self.sections = SectionsWindow(
study = self.model,
parent=self
parent = self
)
self.sections.show()
def open_initial_conditions(self):
print("xxx")
if self.model.river.has_current_reach():
print("yyy")
self.initial = InitialConditionsWindow(
study = self.model,
parent = self
)
self.initial.show()
print("zzz")
# TODO: Delete me !
###############
# DUMMY STUFF #
......
<?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>889</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_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_generate_1">
<property name="text">
<string>Generate 1</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_generate_2">
<property name="text">
<string>Generate 2</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_1"/>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>889</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_sort"/>
</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</string>
</property>
<property name="toolTip">
<string>Add new initial condition</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 inital condition</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 inital condition</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
......@@ -302,6 +302,7 @@
<addaction name="separator"/>
<addaction name="action_toolBar_building"/>
<addaction name="separator"/>
<addaction name="action_toolBar_initial_cond"/>
</widget>
<action name="action_menu_new">
<property name="checkable">
......@@ -861,6 +862,11 @@
<string>French</string>
</property>
</action>
<action name="action_toolBar_initial_cond">
<property name="text">
<string>Initial conditions</string>
</property>
</action>
</widget>
<resources/>
<connections>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment