Commit 3eeaea14 authored by Pierre-Antoine Rouby's avatar Pierre-Antoine Rouby
Browse files

View: Tools: Add PamhyrTableModel and switch Network tables on it.

Showing with 244 additions and 140 deletions
+244 -140
......@@ -25,6 +25,7 @@ from Model.Network.Graph import Graph
from View.ASubWindow import ASubWindow
from View.Network.GraphWidget import GraphWidget
from View.Network.UndoCommand import *
from View.Tools.PamhyrTable import PamhyrTableModel
from PyQt5.QtCore import (
Qt, QRect, QVariant, QAbstractTableModel, pyqtSlot, pyqtSignal,
......@@ -69,151 +70,105 @@ class ComboBoxDelegate(QItemDelegate):
def currentItemChanged(self):
self.commitData.emit(self.sender())
class TrueFalseComboBoxDelegate(QItemDelegate):
def __init__(self, parent=None):
super(TrueFalseComboBoxDelegate, self).__init__(parent)
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
self.editor.addItems(["true", "false"])
self.editor.setCurrentText("true" if index.data(Qt.DisplayRole) else "false")
return self.editor
def setEditorData(self, editor, index):
value = str(index.data(Qt.DisplayRole))
self.editor.currentTextChanged.connect(self.currentItemChanged)
def setModelData(self, editor, model, index):
value = str(editor.currentText()) == "true"
model.setData(index, value)
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 GraphTableModel(QAbstractTableModel):
def __init__(self, headers=[], graph=None, undo = None, rows_type="nodes"):
super(QAbstractTableModel, self).__init__()
self.headers = headers
self.graph = graph
self._type = rows_type
self._undo = undo
if self._type == "nodes":
self.rows = graph.nodes()
elif self._type == "edges":
self.rows = graph.edges()
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self.headers[index.column()] != "type":
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self.rows)
def columnCount(self, parent):
return len(self.headers)
class NodeTableModel(PamhyrTableModel):
def _setup_lst(self):
self._lst = self._data.nodes()
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
if self.headers[index.column()] == "type":
node = self.rows[index.row()]
if self._headers[index.column()] == "type":
node = self._lst[index.row()]
ret = "internal"
if not self.graph.is_enable_node(node):
if not self._data.is_enable_node(node):
ret = "disable"
elif self.graph.is_upstream_node(node):
elif self._data.is_upstream_node(node):
ret = "upstream"
elif self.graph.is_downstream_node(node):
elif self._data.is_downstream_node(node):
ret = "downstream"
return ret
return self.rows[index.row()][self.headers[index.column()]]
def headerData(self, section, orientation, role):
if (role == Qt.ItemDataRole.DisplayRole and
orientation == Qt.Orientation.Horizontal):
return self.headers[section].capitalize()
return QVariant()
return self._lst[index.row()][self._headers[index.column()]]
@pyqtSlot()
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
try:
if (self.headers[index.column()] == "node1" or
self.headers[index.column()] == "node2"):
node = self.graph.node(value)
self._undo.push(
SetNodeCommand(
self.graph,
self.rows[index.row()],
self.headers[index.column()],
node
)
)
# elif self.headers[index.column()] == "enable":
# self._undo.push(
# EnableEdgeCommand(
# self.rows[index.row()], value
# )
# )
else:
self._undo.push(
SetCommand(
self.rows[index.row()],
self.headers[index.column()],
value
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
if not index.isValid():
return False
if role == Qt.EditRole:
try:
self._undo.push(
SetCommand(
self._lst[index.row()],
self._headers[index.column()],
value
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index, [Qt.DisplayRole])
self.layoutChanged.emit()
return True
self.dataChanged.emit(index, index)
else:
return False
def update(self):
if self._type == "nodes":
self.rows = self.graph.nodes()
elif self._type == "edges":
self.rows = self.graph.edges()
self._lst = self._data.nodes()
self.layoutChanged.emit()
def reverse_edge(self, index):
if self._type == "edges":
tmp = self.rows[index.row()].node1
self.rows[index.row()].node1 = self.rows[index.row()].node2
self.rows[index.row()].node2 = tmp
class EdgeTableModel(PamhyrTableModel):
def _setup_lst(self):
self._lst = self._data.edges()
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
return self._lst[index.row()][self._headers[index.column()]]
@pyqtSlot()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
if role != Qt.EditRole:
return QVariant()
try:
if (self._headers[index.column()] == "node1" or
self._headers[index.column()] == "node2"):
node = self.graph.node(value)
self._undo.push(
SetNodeCommand(
self._data,
self._lst[index.row()],
self._headers[index.column()],
node
)
)
else:
self._undo.push(
SetCommand(
self._lst[index.row()],
self._headers[index.column()],
value
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index, [Qt.DisplayRole])
self.layoutChanged.emit()
return True
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
self.dataChanged.emit(index, index)
def redo(self):
self._undo.redo()
def update(self):
self._lst = self._data.edges()
self.layoutChanged.emit()
......@@ -37,8 +37,9 @@ from Model.River import RiverNode, RiverReach, River
from View.ASubWindow import ASubMainWindow
from View.Network.GraphWidget import GraphWidget
from View.Network.UndoCommand import *
from View.Network.translate import *
from View.Network.Table import (
GraphTableModel, ComboBoxDelegate, TrueFalseComboBoxDelegate,
ComboBoxDelegate, NodeTableModel, EdgeTableModel,
)
class NetworkWindow(ASubMainWindow):
......@@ -70,42 +71,38 @@ class NetworkWindow(ASubMainWindow):
def setup_table(self):
# Nodes table
self._nodes_model = GraphTableModel(
headers = ["name", "type"],
graph = self._graph,
rows_type = "nodes",
table = self.find(QTableView, "tableView_nodes")
self._nodes_model = NodeTableModel(
table_view = table,
table_headers = table_headers_node,
editable_headers = ["name"],
data = self._graph,
undo = self._undo_stack,
)
table = self.find(QTableView, "tableView_nodes")
table.setModel(self._nodes_model)
#table.resizeColumnsToContents()
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# Edges table
self._reachs_model = GraphTableModel(
headers = ["name", # "enable",
"node1", "node2"],
graph = self._graph,
rows_type = "edges",
undo = self._undo_stack,
)
table = self.find(QTableView, "tableView_reachs")
self.delegate_combobox = ComboBoxDelegate(
graph = self._graph,
parent = self,
)
self.delegate_true_false_combobox = TrueFalseComboBoxDelegate(
parent = self,
)
table = self.find(QTableView, "tableView_reachs")
self._reachs_model = EdgeTableModel(
table_view = table,
table_headers = table_headers_edge,
editable_headers = ["name", "node1", "node2"],
delegates = {
"node1": self.delegate_combobox,
"node2": self.delegate_combobox,
},
data = self._graph,
undo = self._undo_stack,
)
table.setModel(self._reachs_model)
# table.setItemDelegateForColumn(1, self.delegate_true_false_combobox)
table.setItemDelegateForColumn(1, self.delegate_combobox)
table.setItemDelegateForColumn(2, self.delegate_combobox)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
#table.resizeColumnsToContents()
......
# PamhyrTable.py -- Pamhyr abstract table model
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
from Model.Except import NotImplementedMethodeError
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, QStyledItemDelegate,
)
logger = logging.getLogger()
class PamhyrTextDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(PamhyrTextDelegate, self).__init__(parent)
def createEditor(self, parent, option, index):
index.model().data(index, Qt.DisplayRole)
return QLineEdit(parent)
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.DisplayRole)
editor.setText(str(value))
def setModelData(self, editor, model, index):
model.setData(index, editor.text())
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class PamhyrTableModel(QAbstractTableModel):
def _setup_delegates(self):
if self._table_view is None:
return
for h in self._headers:
if h in self._delegates:
self._table_view.setItemDelegateForColumn(
self._headers.index(h), self._delegates[h]
)
else:
self._table_view.setItemDelegateForColumn(
self._headers.index(h), PamhyrTextDelegate(
parent=self
)
)
def __init__(self,
table_view=None,
table_headers={},
editable_headers=[],
delegates = {},
data=None,
undo=None):
super(PamhyrTableModel, self).__init__()
self._table_view = table_view
self._table_headers = table_headers
self._headers = list(table_headers.keys())
self._editable_headers = editable_headers
self._delegates = delegates
self._data = data
self._undo = undo
self._lst = []
self._setup_delegates()
self._setup_lst()
def _setup_lst(self):
self._lst = self.data
def flags(self, index):
column = index.column()
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self._headers[column] in self._editable_headers:
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._lst)
def columnCount(self, parent):
return len(self._headers)
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return self._table_headers[self._headers[section]]
return QVariant()
def data(self, index, role):
raise NotImplementedMethodeError(self, self.data)
def setData(self, index, value, role=Qt.EditRole):
raise NotImplementedMethodeError(self, self.setData)
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
def add(self, row, parent=QModelIndex()):
raise NotImplementedMethodeError(self, self.add)
def delete(self, rows, parent=QModelIndex()):
raise NotImplementedMethodeError(self, self.delete)
def sort(self, _reverse, parent=QModelIndex()):
raise NotImplementedMethodeError(self, self.sort)
def move_up(self, row, parent=QModelIndex()):
raise NotImplementedMethodeError(self, self.move_up)
def move_down(self, index, parent=QModelIndex()):
raise NotImplementedMethodeError(self, self.move_down)
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