# -*- coding: utf-8 -*- import numpy as np from tools import timer, trace from PyQt5.QtGui import ( QFont, QColor ) from PyQt5.QtWidgets import ( QMessageBox, QStyledItemDelegate, QLineEdit ) from PyQt5.QtCore import ( QModelIndex, Qt, QAbstractTableModel, QVariant, QCoreApplication ) from Model.Geometry.PointXYZ import PointXYZ from Model.Geometry.ProfileXYZ import ProfileXYZ from View.Geometry.Profile.UndoCommand import * _translate = QCoreApplication.translate class TableEditableModel(QAbstractTableModel): def __init__(self, profile: ProfileXYZ, header=None, undo=None): QAbstractTableModel.__init__(self) self._undo_stack = undo self._profile = profile if header is None: self._header = [ "X (m)", "Y (m)", "Z (m)", _translate("MainWindowProfile", "Nom"), _translate("MainWindowProfile", "Abs en travers (m)") ] else: self._header = header def rowCount(self, parent=QModelIndex()): return self._profile.number_points def columnCount(self, parent=QModelIndex()): return len(self._header) def data(self, index, role=Qt.DisplayRole): if index.isValid(): if role == Qt.DisplayRole: value = "" if index.column() == 0: value = self._profile.point(index.row()).x elif index.column() == 1: value = self._profile.point(index.row()).y elif index.column() == 2: value = self._profile.point(index.row()).z elif index.column() == 3: value = self._profile.point(index.row()).name elif index.column() == 4: station = self._profile.get_station() if station is None: return "-" else: value = station[index.row()] return f"{value:.3f}" if 0 <= index.column() < 3: return f"{value:.4f}" return f"{value}" if role == Qt.TextAlignmentRole: return Qt.AlignHCenter | Qt.AlignVCenter if index.column() == 2: value = self._profile.point(index.row()).z if role == Qt.ForegroundRole: if value == self._profile.z_min(): return QColor("red") elif value == self._profile.z_max(): return QColor("blue") if role == Qt.ToolTipRole: if value == self._profile.z_min(): return _translate("MainWindowProfile", "La cote du fond", "Z minimale") elif value == self._profile.z_max(): return _translate("MainWindowProfile", "La cote maximale", "Z maximale") if index.column() == 3: value = self._profile.point(index.row()).name if value.strip().upper() in ["RG", "RD"]: if role == Qt.FontRole: font = QFont() font.setBold(True) return font if role == Qt.ForegroundRole: return QColor("darkRed") if role == Qt.ToolTipRole: if value.strip().upper() == "RG": return _translate("MainWindowProfile", "Rive gauche") else: return _translate("MainWindowProfile", "Rive droite") return QVariant() def headerData(self, section, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self._header[section] elif orientation == Qt.Vertical and role == Qt.DisplayRole: return str(section + 1) if role == Qt.ToolTipRole and section == 4: return _translate( "MainWindowProfile", "Abscisse en travers calculée en projétant les points" " \nsur le plan défini par les deux points nommés extrêmes " ) return QVariant() def setData(self, index, value, role=Qt.EditRole): row = index.row() column = index.column() if role == Qt.EditRole: if column == 0: self._undo_stack.push( SetXCommand( self._profile, row, self._profile.point(row).x, value ) ) elif column == 1: self._undo_stack.push( SetYCommand( self._profile, row, self._profile.point(row).y, value ) ) elif column == 2: self._undo_stack.push( SetZCommand( self._profile, row, self._profile.point(row).z, value ) ) elif column == 3: self._undo_stack.push( SetNameCommand( self._profile, row, self._profile.point(row).name, value ) ) self.dataChanged.emit(index, index) return True self.dataChanged.emit(index, index) self.layoutChanged.emit() return False def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() return self.createIndex(row, column, QModelIndex()) def flags(self, index): flg = Qt.ItemIsSelectable if index.column() == 4: return flg return Qt.ItemIsEditable | Qt.ItemIsEnabled | flg def insert_row(self, row, parent=QModelIndex()): self.beginInsertRows(parent, row, row - 1) self._undo_stack.push( AddCommand( self._profile, row ) ) self.endInsertRows() self.layoutChanged.emit() def remove_rows(self, rows, parent=QModelIndex()): self.beginRemoveRows(parent, rows[0], rows[-1]) self._undo_stack.push( DelCommand( self._profile, rows ) ) self.endRemoveRows() self.layoutChanged.emit() def sort(self, column='x', order=Qt.AscendingOrder): self.layoutAboutToBeChanged.emit() reverse = (order != Qt.AscendingOrder) self._undo_stack.push( SortCommand( self._profile, column, reverse ) ) self.layoutChanged.emit() def move_row_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._profile, "up", row ) ) self.endMoveRows() self.layoutChanged.emit() def move_row_down(self, row_to_move, parent=QModelIndex()): if row > self._profile.number_points: return target = row self.beginMoveRows(parent, row + 1, row + 1, parent, target) self._undo_stack.push( MoveCommand( self._profile, "down", row ) ) self.endMoveRows() self.layoutChanged.emit() def paste(self, row, header, data): if row > self._profile.number_points: return if len(data) == 0: return self.layoutAboutToBeChanged.emit() self._undo_stack.push( PasteCommand( self._profile, row, list( map( lambda d: PointXYZ.from_data(header, d), data ) ) ) ) self.layoutAboutToBeChanged.emit() self.layoutChanged.emit() def undo(self): self._undo_stack.undo() self.layoutChanged.emit() def redo(self): self._undo_stack.redo() self.layoutChanged.emit() class Delegate(QStyledItemDelegate): def __init__(self, parent=None, setModelDataEvent=None): super(Delegate, self).__init__(parent) self.setModelDataEvent = setModelDataEvent 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()) if not self.setModelDataEvent is None: self.setModelDataEvent() def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect)