qtableview_reach.py 7.79 KiB
# -*- coding: utf-8 -*-

import time
from functools import reduce
from itertools import groupby
from operator import itemgetter

import numpy as np
import pandas as pd

from PyQt5 import (
    QtGui, QtWidgets
)
from PyQt5.QtCore import (
    Qt, QAbstractTableModel, QModelIndex,
    QVariant, pyqtSlot, QCoreApplication,
)
from PyQt5.QtWidgets import (
    QMessageBox, QUndoCommand, QUndoStack,
)

from Model.Geometry import Reach
from View.Geometry.ReachUndoCommand import *


_translate = QCoreApplication.translate


class PandasModelEditable(QAbstractTableModel):
    def __init__(self, reach, headers=None):
        QAbstractTableModel.__init__(self)
        data_list = []

        self._undo_stack = QUndoStack()

        self._reach = reach

        if headers is None:
            self.headers = [
                _translate("Geometry", "Name"),
                _translate("Geometry", "Kp (m)"),
                _translate("Geometry", "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 QtGui.QColor("Green")
            elif (name == "downstream" or name == "down" or
                  name == _translate("Geometry", "downstream")):
                return QtGui.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 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_row(self, row, parent=QModelIndex()):
        self.beginRemoveRows(parent, row, row - 1)

        self._undo_stack.push(
            DelCommand(
                self._reach, row
            )
        )

        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()):
        target = row + 2

        if row <= 0:
            return

        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()):
        target = row

        if row > self._reach.number_profiles:
            return

        self.beginMoveRows(parent, row + 1, row + 1, parent, target)

        self._undo_stack.push(
            MoveCommand(
                self._reach, "down", row
            )
        )

        self.endMoveRows()
        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(QtWidgets.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 QtWidgets.QLineEdit(parent)

    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.DisplayRole)  # DisplayRole
        editor.setText(str(value))  # récupère la valeur de la cellule applique la méthode définie dans setData

    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(QtWidgets.QStyledItemDelegate):
    def __init__(self, owner, choices):
        super().__init__(owner)
        self.items = choices

    def paint(self, painter, option, index):
        if isinstance(self.parent(), QtWidgets.QAbstractItemView):
            self.parent().openPersistentEditor(index)
        super(Delegate1, self).paint(painter, option, index)

    def createEditor(self, parent, option, index):
        editor = QtWidgets.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())