# -*- coding: utf-8 -*- import time from tools import timer, trace from PyQt5.QtGui import ( QKeySequence, QColor ) from PyQt5.QtCore import ( Qt, QAbstractTableModel, QModelIndex, QVariant, pyqtSlot, QCoreApplication, ) from PyQt5.QtWidgets import ( QMessageBox, QUndoCommand, QUndoStack, QStyledItemDelegate, QLineEdit, QAbstractItemView, QComboBox, ) from Model.Geometry import Reach from Model.Geometry.ProfileXYZ import ProfileXYZ from View.Geometry.ReachUndoCommand import * _translate = QCoreApplication.translate class TableEditableModel(QAbstractTableModel): def __init__(self, reach, headers=None, undo=None): QAbstractTableModel.__init__(self) data_list = [] self._undo_stack = undo self._reach = reach # Hack for qtlinguist _ = _translate("Geometry", "Name") _ = _translate("Geometry", "Kp (m)") _ = _translate("Geometry", "Type") if headers is None: self.headers = [ "Name", "Kp (m)", "Type" ] else: self.headers = headers def rowCount(self, parent=QModelIndex()): return self._reach.number_profiles def columnCount(self, parent=QModelIndex()): return len(self.headers) def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return QVariant() if role == Qt.DisplayRole and index.column() == 0: return self._reach.profile(index.row()).name if role == Qt.DisplayRole and index.column() == 1: kp = self._reach.profile(index.row()).kp return f"{kp:.4f}" if role == Qt.DisplayRole and index.column() == 2: return self._reach.profile(index.row()).profile_type if role == Qt.TextAlignmentRole: return Qt.AlignHCenter | Qt.AlignVCenter if role == Qt.ForegroundRole and index.column() == 0: name = self._reach.profile(index.row()).name\ .strip()\ .lower() if (name == "upstream" or name == "up" or name == _translate("Geometry", "upstream")): return QColor("Green") elif (name == "downstream" or name == "down" or name == _translate("Geometry", "downstream")): return QColor("Red") return QVariant() def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: if section < len(self.headers): return _translate("Geometry", self.headers[section]) else: return str(section + 1) return QVariant() def setData(self, index, value, role=Qt.EditRole): row = index.row() column = index.column() if role == Qt.EditRole and index.column() != 2: if index.column() == 0: self._undo_stack.push( SetNameCommand( self._reach, index.row(), self._reach.profile(index.row()).name, value ) ) if index.column() == 1: self._undo_stack.push( SetKPCommand( self._reach, index.row(), self._reach.profile(index.row()).kp, value ) ) self.dataChanged.emit(index, index) self.layoutChanged.emit() 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.ItemIsEnabled | Qt.ItemIsSelectable if index.column() == 2: return flg else: return Qt.ItemIsEditable | flg # @QtCore.pyqtSlot() def insert_row(self, row, parent=QModelIndex()): self.beginInsertRows(parent, row, row - 1) self._undo_stack.push( AddCommand( self._reach, 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._reach, rows ) ) self.endRemoveRows() self.layoutChanged.emit() def sort_profiles(self, _reverse): self.layoutAboutToBeChanged.emit() self._undo_stack.push( SortCommand( self._reach, _reverse ) ) self.layoutAboutToBeChanged.emit() 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._reach, "up", row ) ) self.endMoveRows() self.layoutChanged.emit() def move_row_down(self, row, parent=QModelIndex()): if row > self._reach.number_profiles: return target = row self.beginMoveRows(parent, row + 1, row + 1, parent, target) self._undo_stack.push( MoveCommand( self._reach, "down", row ) ) self.endMoveRows() self.layoutChanged.emit() def paste(self, row, header, data): if row > self._reach.number_profiles: return if len(data) == 0: return self.layoutAboutToBeChanged.emit() self._undo_stack.push( PasteCommand( self._reach, row, list( map( lambda d: ProfileXYZ.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) class Delegate1(QStyledItemDelegate): def __init__(self, owner, choices): super().__init__(owner) self.items = choices def paint(self, painter, option, index): if isinstance(self.parent(), QAbstractItemView): self.parent().openPersistentEditor(index) super(Delegate1, self).paint(painter, option, index) def createEditor(self, parent, option, index): editor = QComboBox(parent) # editor.currentIndexChanged.connect(self.commit_editor) editor.addItems(self.items) return editor def setEditorData(self, editor, index): editor.blockSignals(True) text = index.model().data(index, Qt.DisplayRole) try: i = self.items.index(text) except ValueError: i = 0 editor.setCurrentIndex(i) editor.blockSignals(False) def setModelData(self, editor, model, index): model.setData(index, editor.currentText(), Qt.DisplayRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) @pyqtSlot() def currentIndexChanged(self): self.commitData.emit(self.sender())