From c6fd041e0c034a1aa0dbd1c2710d2b7239a47c89 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Tue, 25 Apr 2023 13:53:53 +0200
Subject: [PATCH] geometry: Profile: Fix table station display.

---
 src/Model/Geometry/PointXYZ.py                |  8 +--
 src/Model/Geometry/ProfileXYZ.py              | 11 ---
 src/View/Geometry/GeometryWindow.py           |  8 +--
 src/View/Geometry/Profile/ProfileWindow.py    | 71 ++++++++++++++++---
 .../Geometry/Profile/qtableview_profile.py    | 11 +--
 5 files changed, 77 insertions(+), 32 deletions(-)

diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py
index e0c29860..7df8123c 100644
--- a/src/Model/Geometry/PointXYZ.py
+++ b/src/Model/Geometry/PointXYZ.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 from math import dist
-from pandas import isna
+import numpy as np
 
 from Model.Geometry.Point import Point
 
@@ -46,9 +46,9 @@ class PointXYZ(Point):
         Returns:
             True if at least one coordinate is as np.nan
         """
-        return (isna(self.x) or
-                isna(self.y) or
-                isna(self.z))
+        return (np.isnan(self.x) or
+                np.isnan(self.y) or
+                np.isnan(self.z))
 
     def dist(self, p2):
         return PointXYZ.distance(self, p2)
diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py
index fce2bc5c..1118cc00 100644
--- a/src/Model/Geometry/ProfileXYZ.py
+++ b/src/Model/Geometry/ProfileXYZ.py
@@ -40,17 +40,6 @@ class ProfileXYZ(Profile):
                           data=[[p.x, p.y, p.z, p.name] for p in self._points])
         return f"\nProfileXYZ : {self.name}\n{df}"
 
-    @property
-    def header(self):
-        """
-        Returns:
-            Profile header.
-        """
-        return np.array(
-            [self._num, self._code1, self._code2,
-             self.nb_points(), self._kp, self._name]
-        )
-
     def x(self):
         return [point.x for point in self._points]
 
diff --git a/src/View/Geometry/GeometryWindow.py b/src/View/Geometry/GeometryWindow.py
index c3325acc..89f3be26 100644
--- a/src/View/Geometry/GeometryWindow.py
+++ b/src/View/Geometry/GeometryWindow.py
@@ -28,7 +28,7 @@ from View.Geometry.PlotAC import PlotAC
 
 from View.ASubWindow import WindowToolKit
 from View.Geometry.mainwindow_ui_reach import Ui_MainWindow
-from View.Geometry import qtableview_reach
+from View.Geometry.qtableview_reach import *
 from View.Geometry.Profile.ProfileWindow import ProfileWindow
 
 _translate = QCoreApplication.translate
@@ -71,13 +71,13 @@ class GeometryWindow(QMainWindow, WindowToolKit):
         self.paste_sc = QShortcut(QKeySequence.Paste, self)
 
     def setup_model(self):
-        self._tablemodel = qtableview_reach.PandasModelEditable(
+        self._tablemodel = PandasModelEditable(
             headers = self.ui.tableView_header,
             reach = self._reach,
             undo = self._undo_stack
         )
         self.tableView.setModel(self._tablemodel)
-        self.tableView.setItemDelegate(qtableview_reach.Delegate())
+        self.tableView.setItemDelegate(Delegate())
 
     def setup_plots(self):
         if self._reach.number_profiles != 0:
@@ -435,6 +435,6 @@ class GeometryWindow(QMainWindow, WindowToolKit):
                     buf.append(row)
 
                 self._tablemodel = None
-                self._tablemodel = qtableview_reach.PandasModelEditable(buf)
+                self._tablemodel = PandasModelEditable(buf)
                 self.tableView.setModel(self._tablemodel)
                 filename = ''
diff --git a/src/View/Geometry/Profile/ProfileWindow.py b/src/View/Geometry/Profile/ProfileWindow.py
index fb59d7d1..c7f574fa 100644
--- a/src/View/Geometry/Profile/ProfileWindow.py
+++ b/src/View/Geometry/Profile/ProfileWindow.py
@@ -7,9 +7,16 @@ from time import time
 
 from tools import trace, timer
 
-from PyQt5 import QtWidgets, QtGui
-from PyQt5.QtCore import QModelIndex, Qt, QEvent, QCoreApplication
-from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QCheckBox
+from PyQt5.QtGui import (
+    QKeySequence,
+)
+from PyQt5.QtCore import (
+    QModelIndex, Qt, QEvent, QCoreApplication
+)
+from PyQt5.QtWidgets import (
+    QApplication, QMainWindow, QFileDialog, QCheckBox,
+    QUndoStack, QShortcut,
+)
 
 from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow
 
@@ -34,6 +41,7 @@ class ProfileWindow(QMainWindow):
         self._model = None
 
         self.setup_window()
+        self.setup_sc()
         self.setup_model()
         self.setup_connections()
         self.plot()
@@ -64,6 +72,23 @@ class ProfileWindow(QMainWindow):
             f"{name} ({self._profile.kp})"
         )
 
+    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_model(self):
+        self._model = PandasModelEditable(
+            profile = self._profile,
+            undo = self._undo_stack
+        )
+
+        self.ui.tableView.setModel(self._model)
+        self.ui.tableView.setItemDelegate(Delegate())
+
     def setup_connections(self):
         self.ui.btn_sort_asc_x.clicked.connect(self.sort_X_ascending)
         self.ui.btn_sort_desc_x.clicked.connect(self.sort_X_descending)
@@ -80,11 +105,10 @@ class ProfileWindow(QMainWindow):
         # self.ui.btn_go_back.clicked.connect(self.cancel_validate_changes)
         # self.ui.btn_reset.clicked.connect(self.go_back_to_initial_state)
 
-    def setup_model(self):
-        self._model = PandasModelEditable(self._profile)
-
-        self.ui.tableView.setModel(self._model)
-        self.ui.tableView.setItemDelegate(Delegate())
+        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 plot(self):
         self.ui.tableView.model().blockSignals(True)
@@ -169,6 +193,37 @@ class ProfileWindow(QMainWindow):
 
         self.update_plot()
 
+    def copy(self):
+        rows = self.tableView\
+                   .selectionModel()\
+                   .selectedRows()
+
+        self._clipboard = []
+
+        for row in rows:
+            self._clipboard.append(
+                deepcopy(
+                    self._reach.profile(row.row())
+                )
+            )
+
+    def paste(self):
+        row = self.index_selected_row()
+        self._tablemodel.paste(row, self._clipboard)
+        self.select_current_profile()
+
+    def undo(self):
+        self._tablemodel.undo()
+        self.select_current_profile()
+        self.update_plot_xy()
+        self.update_plot_kpc()
+
+    def redo(self):
+        self._tablemodel.redo()
+        self.select_current_profile()
+        self.update_plot_xy()
+        self.update_plot_kpc()
+
     def handleSave(self):
         if self.fileName is None or self.fileName == '':
             self.fileName, self.filters = QFileDialog.getSaveFileName(
diff --git a/src/View/Geometry/Profile/qtableview_profile.py b/src/View/Geometry/Profile/qtableview_profile.py
index 7081ad48..f2a25f26 100644
--- a/src/View/Geometry/Profile/qtableview_profile.py
+++ b/src/View/Geometry/Profile/qtableview_profile.py
@@ -1,7 +1,6 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-import pandas as pd
 from PyQt5.QtGui import QFont
 
 from PyQt5.QtWidgets import QMessageBox
@@ -16,20 +15,20 @@ _translate = QCoreApplication.translate
 
 
 class PandasModelEditable(QAbstractTableModel):
-    def __init__(self, profile: ProfileXYZ, table_header=None, undo=None):
+    def __init__(self, profile: ProfileXYZ, header=None, undo=None):
         QAbstractTableModel.__init__(self)
 
         self._undo_stack = undo
         self._profile = profile
 
-        if table_header is None:
+        if header is None:
             self._header = [
                 "X (m)", "Y (m)", "Z (m)",
                 _translate("MainWindowProfile", "Nom"),
                 _translate("MainWindowProfile", "Abs en travers (m)")
             ]
         else:
-            self._header = table_header
+            self._header = header
 
     def rowCount(self, parent=QModelIndex()):
         return self._profile.number_points
@@ -51,11 +50,13 @@ class PandasModelEditable(QAbstractTableModel):
                 elif index.column() == 3:
                     value = self._profile.point(index.row()).name
                 elif index.column() == 4:
-                    value = self._profile.get_station()
+                    value = self._profile.get_station()[index.row()]
 
                 if 0 <= index.column() < 3:
                     return f"{value:.4f}"
                 elif index.column() == 4:
+                    if np.isnan(value):
+                        return "-"
                     return f"{value:.3f}"
 
                 return f"{value}"
-- 
GitLab