diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index eca5a529e4d82f4bdaae5b048d02aa1f19a7cd77..46decef0188014a79d2571663dbf0a26e1cb3d1d 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -134,6 +134,7 @@ class Reach: def copy(self, index_list: List[int]): self.__list_copied_profiles.clear() index_list = list(set(index_list)) # delete duplicate index + for index in index_list: try: self.__list_copied_profiles.append(deepcopy(self.get_profile_i(index))) diff --git a/src/Model/Geometry/projection_pointXYZ.py b/src/Model/Geometry/projection_pointXYZ.py new file mode 100644 index 0000000000000000000000000000000000000000..439d4f4cd513923fa6c5797d7b3f53a437b3ebd7 --- /dev/null +++ b/src/Model/Geometry/projection_pointXYZ.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import copy + +import numpy as np +import pandas as pd +from time import time + +from Model.Geometry import PointXYZ +from Model.Geometry.ProfileXYZ import ProfileXYZ +from Model.Geometry.vector_1d import Vector1d + + +def update_station(list_header, list_point_xyz): + profile = ProfileXYZ(list_header, list_point_xyz) + if profile.nb_points >= 3: + return get_station(profile) + else: + pass + + +def get_station(profile: ProfileXYZ) -> np.ndarray: + """Projection of the points of the profile on a plane. + + Args: + profile: The profile + + Returns: + Projection of the points of the profile on a plane. + """ + if profile.nb_points >= 3: + first_named_point = None + index_first_named_point = None + last_named_point = None + + for index, point in enumerate(profile.points): + if point.point_is_named(): + index_first_named_point = index + first_named_point = point + break + + for point in profile.points[::-1]: + if point.point_is_named(): + last_named_point = point + break + + station = [] # abscisse en travers + constant = 0.0 # constante pour décaler l'origine de l'ascisse en travers caluculée. + + if (first_named_point is not None) and (last_named_point is not None): + if first_named_point != last_named_point and first_named_point.x != last_named_point.x: + vector = Vector1d(first_named_point, last_named_point) + normalized_direction_vec = vector.normalized_direction_vector() + else: + vector = Vector1d(_first_point_not_nan(profile), _last_point_not_nan(profile)) + normalized_direction_vec = vector.normalized_direction_vector() + + for point in profile.points: + xi = point.x - first_named_point.x + yi = point.y - first_named_point.y + station_i = normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi + station.append(station_i) + + station = np.array(station) + + constant = station[index_first_named_point] # pour placer l'origine au premier point nomme. + elif first_named_point is None: + vector = Vector1d(_first_point_not_nan(profile), _last_point_not_nan(profile)) + normalized_direction_vec = vector.normalized_direction_vector() + + for point in profile.points: + xi = point.x - _first_point_not_nan(profile).x + yi = point.y - _first_point_not_nan(profile).y + station_i = normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi + station.append(station_i) + + station = np.array(station) + index_profile_z_min = np.where(np.array(profile.z) == profile.z_min)[0][0] + constant = station[index_profile_z_min] # pour placer l'origine au fond ie à la cote minimale + # z_min du profil. + station = station - constant + + return station + else: + return np.array(np.nan) + +def _point_is_nan(point: PointXYZ): + return pd.isna(point.x) or pd.isna(point.y) or pd.isna(point.z) + +def _first_point_not_nan(profile: ProfileXYZ): + first_point_not_nan = profile.points[0] + + for point in profile.points: + if not _point_is_nan(point): + first_point_not_nan = point + break + + return first_point_not_nan + +def _last_point_not_nan(profile: ProfileXYZ): + last_point = profile.points[-1] + + for point in profile.points[::-1]: + if not _point_is_nan(point): + last_point = point + break + + return last_point diff --git a/src/Model/Geometry/vector_1d.py b/src/Model/Geometry/vector_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..b82b86b03d5d353b5f165ff66308a53161fa0337 --- /dev/null +++ b/src/Model/Geometry/vector_1d.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import numpy as np +from Model.Geometry.PointXYZ import PointXYZ + + +class Vector1d: + def __init__(self, a: PointXYZ, b: PointXYZ): + self.A = a + self.B = b + + def __repr__(self): + return "vecteur AB = ({}, {}, {})".format( + self.B.x - self.A.x, + self.B.y - self.A.y, + self.B.z - self.A.z + ) + + def vector1d(self): + return np.array([ + self.B.x - self.A.x, + self.B.y - self.A.y, + self.B.z - self.A.z + ]) + + def direction_vector(self): + return np.array([ + self.B.x - self.A.x, + self.B.y - self.A.y, + 0 + ]) + + def norm_vector(self): + return np.linalg.norm(self.vector1d()) + + def norm_direction_vector(self): + return np.linalg.norm(self.direction_vector()) + + def normalized_direction_vector(self): + return self.direction_vector() / self.norm_direction_vector() diff --git a/src/View/ASubWindow.py b/src/View/ASubWindow.py index cbb49ae13782a01efa98c4196496053d181ece4f..6d90eebf0fc056f869722f02882a2077600999ad 100644 --- a/src/View/ASubWindow.py +++ b/src/View/ASubWindow.py @@ -2,6 +2,8 @@ import os +from PyQt5.QtCore import Qt + from PyQt5.QtWidgets import ( QMainWindow, QApplication, QDesktopWidget, QMdiArea, QMdiSubWindow, QDialog, @@ -25,12 +27,12 @@ class ASubWindow(QDialog): self.name = name self.parent = parent self.parent.sub_win_add(name, self) - self.fixed_size() + # self.fixed_size() def fixed_size(self): width = self.frameGeometry().width() height = self.frameGeometry().height() - # self.setFixedSize(width, height) + self.setFixedSize(width, height) def closeEvent(self, event): if not self.parent is None: diff --git a/src/View/Geometry/mainwindow_ui_profile.py b/src/View/Geometry/mainwindow_ui_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..7aeb1bd160f47576ba5e6e00fa5550b5f832debe --- /dev/null +++ b/src/View/Geometry/mainwindow_ui_profile.py @@ -0,0 +1,330 @@ +# -*- coding: utf-8 -*- + +import os.path + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtCore import Qt, QSize +from PyQt5.QtWidgets import ( + QTableView, QAbstractItemView, QHeaderView +) + +from View.Plot import MplCanvas, navigation_toolbar_2qt + +file_path = os.path.abspath(os.path.dirname(__file__)) +icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../../ui/ressources/" + +class Ui_MainWindow(object): + def setupUi(self, MainWindowProfile): + MainWindowProfile.setObjectName("MainWindowProfile") + MainWindowProfile.resize(1089, 699) + self.centralwidget = QtWidgets.QWidget(MainWindowProfile) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.splitter = QtWidgets.QSplitter(self.centralwidget) + self.splitter.setOrientation(Qt.Horizontal) + self.splitter.setObjectName("splitter") + self.widget = QtWidgets.QWidget(self.splitter) + self.widget.setObjectName("widget") + self.verticalLayout_left = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout_left.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_left.setObjectName("verticalLayout_left") + + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + + btn_size = QSize(30, 30) + + self.btn_add = QtWidgets.QPushButton(self.widget) + self.btn_add.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_add.setText("") + self.btn_add.setObjectName("btn_add") + icon_btn_add = QtGui.QIcon() + icon_btn_add.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-add.png")) + self.btn_add.setIcon(icon_btn_add) + self.btn_add.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_add) + + self.btn_delete = QtWidgets.QPushButton(self.widget) + self.btn_delete.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_delete.setText("") + self.btn_delete.setObjectName("btn_delete") + icon_btn_delete = QtGui.QIcon() + icon_btn_delete.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-remove.png")) + self.btn_delete.setIcon(icon_btn_delete) + self.btn_delete.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_delete) + + self.btn_sort_asc_x = QtWidgets.QPushButton(self.widget) + self.btn_sort_asc_x.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_asc_x.setText("") + self.btn_sort_asc_x.setObjectName("btn_sort_asc_x") + icon_btn_sort_asc_x = QtGui.QIcon() + icon_btn_sort_asc_x.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png")) + self.btn_sort_asc_x.setIcon(icon_btn_sort_asc_x) + self.btn_sort_asc_x.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_asc_x) + + self.btn_sort_desc_x = QtWidgets.QPushButton(self.widget) + self.btn_sort_desc_x.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_desc_x.setText("") + self.btn_sort_desc_x.setObjectName("btn_sort_desc_x") + icon_btn_sort_desc_x = QtGui.QIcon() + icon_btn_sort_desc_x.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png")) + self.btn_sort_desc_x.setIcon(icon_btn_sort_desc_x) + self.btn_sort_desc_x.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_desc_x) + + self.btn_sort_asc_y = QtWidgets.QPushButton(self.widget) + self.btn_sort_asc_y.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_asc_y.setText("") + self.btn_sort_asc_y.setObjectName("btn_sort_asc_y") + icon_btn_sort_asc_y = QtGui.QIcon() + icon_btn_sort_asc_y.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png")) + self.btn_sort_asc_y.setIcon(icon_btn_sort_asc_y) + self.btn_sort_asc_y.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_asc_y) + + self.btn_sort_desc_y = QtWidgets.QPushButton(self.widget) + self.btn_sort_desc_y.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_desc_y.setText("") + self.btn_sort_desc_y.setObjectName("btn_sort_desc_y") + icon_btn_sort_desc_y = QtGui.QIcon() + icon_btn_sort_desc_y.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png")) + self.btn_sort_desc_y.setIcon(icon_btn_sort_desc_y) + self.btn_sort_desc_y.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_desc_y) + + self.btn_move_up = QtWidgets.QPushButton(self.widget) + self.btn_move_up.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_move_up.setText("") + self.btn_move_up.setObjectName("btn_move_up") + icon_btn_move_up = QtGui.QIcon() + icon_btn_move_up.addPixmap( + QtGui.QPixmap(f"{icon_path}up.png")) + self.btn_move_up.setIcon(icon_btn_move_up) + self.btn_move_up.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_move_up) + + self.btn_move_down = QtWidgets.QPushButton(self.widget) + self.btn_move_down.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_move_down.setText("") + self.btn_move_down.setObjectName("btn_move_down") + icon_btn_move_down = QtGui.QIcon() + icon_btn_move_down.addPixmap( + QtGui.QPixmap(f"{icon_path}down.png")) + self.btn_move_down.setIcon(icon_btn_move_down) + self.btn_move_down.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_move_down) + + self.btn_export = QtWidgets.QPushButton(self.widget) + self.btn_export.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_export.setText("") + self.btn_export.setObjectName("btn_export") + icon_btn_export = QtGui.QIcon() + icon_btn_export.addPixmap( + QtGui.QPixmap(f"{icon_path}export.png")) + self.btn_export.setIcon(icon_btn_export) + self.btn_export.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_export) + + self.btn_copy = QtWidgets.QPushButton(self.widget) + self.btn_copy.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_copy.setText("") + self.btn_copy.setObjectName("btn_copy") + icon_btn_copy = QtGui.QIcon() + icon_btn_copy.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-copy.png")) + self.btn_copy.setIcon(icon_btn_copy) + self.btn_copy.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_copy) + + self.btn_paste = QtWidgets.QPushButton(self.widget) + self.btn_paste.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_paste.setText("") + self.btn_paste.setObjectName("btn_paste") + icon_btn_paste = QtGui.QIcon() + icon_btn_paste.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-paste.png")) + self.btn_paste.setIcon(icon_btn_paste) + self.btn_paste.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_paste) + + self.btn_check = QtWidgets.QPushButton(self.widget) + self.btn_check.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_check.setText("") + self.btn_check.setObjectName("btn_check") + icon_btn_check = QtGui.QIcon() + icon_btn_check.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-apply.png")) + self.btn_check.setIcon(icon_btn_check) + self.btn_check.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_check) + + self.btn_go_back = QtWidgets.QPushButton(self.widget) + self.btn_go_back.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_go_back.setText("") + self.btn_go_back.setObjectName("btn_go_back") + icon_btn_go_back = QtGui.QIcon() + icon_btn_go_back.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-undo.png")) + self.btn_go_back.setIcon(icon_btn_go_back) + self.btn_go_back.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_go_back) + + self.btn_reset = QtWidgets.QPushButton(self.widget) + self.btn_reset.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_reset.setText("") + self.btn_reset.setObjectName("btn_reset") + icon_btn_reset = QtGui.QIcon() + icon_btn_reset.addPixmap( + QtGui.QPixmap(f"{icon_path}cancel.png")) + self.btn_reset.setIcon(icon_btn_reset) + self.btn_reset.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_reset) + + spacerItem = QtWidgets.QSpacerItem(30, 30, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + + self.verticalLayout_left.addLayout(self.horizontalLayout) + + self.tableView = QTableView(self.widget) + self.tableView.setStyleSheet(" QTableView { border: 1px solid black;\n" + " gridline-color: blue;\n" + " border-radius: 2px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 11.5px;\n" + " font-family: Helvetica\n" + "\n" + " }") + # self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + # self.tableView.setGridStyle(Qt.SolidLine) + self.tableView.setObjectName("tableView") + self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # permet de sélectionner une ligne entière + # self.tableView.verticalHeader().hide() # hide vertical/row headers + self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) + # self.tableView.setSelectionMode(QAbstractItemView.MultiSelection) + # self.tableView.horizontalHeader().setStretchLastSection(True) + # self.tableView.setVisible(False) + self.tableView.resizeColumnsToContents() + self.tableView.resizeRowsToContents() + # self.tableView.setVisible(True) + # self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) # pour que le tableau occupe + # toute la zone + self.tableView.setAlternatingRowColors(True) # colorie une ligne sur 2 + self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # ajuster la largeur du tableau + # self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked) # met en mode édition en cliquant sur une + # ligne https://doc.qt.io/qt-5/qabstractitemview.html#EditTrigger-enum NoEditTriggers, DoubleClicked, + # SelectedClicked, CurrentChanged + self.verticalLayout_left.addWidget(self.tableView) + + self.widget1 = QtWidgets.QWidget(self.splitter) + self.widget1.setObjectName("widget1") + + self.verticalLayout_right = QtWidgets.QVBoxLayout(self.widget1) + self.verticalLayout_right.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_right.setObjectName("verticalLayout_right") + + self.label_graphic_help = QtWidgets.QLabel(self.widget1) + self.label_graphic_help.setMaximumSize(QtCore.QSize(16777215, 25)) + self.label_graphic_help.setBaseSize(QtCore.QSize(0, 30)) + self.label_graphic_help.setStyleSheet( + "QLabel { border: 1px solid blue;\n" + " gridline-color: blue;\n" + " border-radius: 3px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: black;\n" + " font-size: 11px;\n" + " font-family: Helvetica\n" + "}" + ) + self.label_graphic_help.setObjectName("label_graphic_help") + self.verticalLayout_right.addWidget(self.label_graphic_help) + + # self.graphicsView = QtWidgets.QGraphicsView(self.widget1) + # self.graphicsView.setObjectName("graphicsView") + # self.verticalLayout_right.addWidget(self.graphicsView) + + self.my_canvas = MplCanvas.MplCanvas(width=5, height=4, + dpi=100) + self.my_canvas.setObjectName("my_canvas") + + self.my_toolbar = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.my_canvas, self.widget1) + self.my_toolbar.setStyleSheet( + "QToolBar{ border: 1px solid darkGray;\n" + " gridline-color: blue;\n" + " border-radius: 4px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + ) + + self.verticalLayout_right.addWidget(self.my_toolbar) + self.verticalLayout_right.addWidget(self.my_canvas) + self.horizontalLayout_2.addWidget(self.splitter) + + MainWindowProfile.setCentralWidget(self.centralwidget) + + self.menubar = QtWidgets.QMenuBar(MainWindowProfile) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1089, 20)) + self.menubar.setObjectName("menubar") + MainWindowProfile.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindowProfile) + self.statusbar.setObjectName("statusbar") + MainWindowProfile.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindowProfile) + QtCore.QMetaObject.connectSlotsByName(MainWindowProfile) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindowProfile", "MainWindowProfile")) + self.btn_add.setToolTip(_translate("MainWindowProfile", "Insérer un point")) + self.btn_delete.setToolTip( + _translate("MainWindowProfile", "Supprimer le/les point(s) sélectionnés")) + self.btn_sort_asc_x.setToolTip( + _translate("MainWindowProfile", "Trier les points par ordre croissant de X")) + self.btn_sort_desc_x.setToolTip( + _translate("MainWindowProfile", "Trier les points par ordre décroissant de X")) + self.btn_sort_asc_y.setToolTip(_translate("MainWindowProfile", + "Trier les points par ordre croissant de Y" + )) + self.btn_sort_desc_y.setToolTip(_translate("MainWindowProfile", + "Trier les points par ordre décroissant de Y")) + self.btn_move_up.setToolTip(_translate("MainWindowProfile", + "Décaler le point sélectionné vers le haut")) + self.btn_move_down.setToolTip(_translate("MainWindowProfile", + "Décaler le point sélectionné vers le bas")) + self.btn_export.setToolTip(_translate("MainWindowProfile", + "Exporter (dans un fichier) les points du profil au format tabulé")) + self.btn_copy.setToolTip( + _translate("MainWindowProfile", "Copier la sélection au format tabulé")) + self.btn_paste.setToolTip(_translate("MainWindowProfile", + "Coller la sélection depuis le presse-papier au format tabulé")) + self.btn_check.setToolTip(_translate("MainWindowProfile", + "Vérifier la validité de la saisie et garder ou pas les modifications " + "apportées")) + self.btn_go_back.setToolTip(_translate("MainWindowProfile", + "Annuler toutes les modifications depuis la dernière validation")) + self.btn_reset.setToolTip(_translate("MainWindowProfile", + "Annuler toutes les modifications et revenir à l\'état initial")) + self.label_graphic_help.setToolTip(_translate("MainWindowProfile", + "Ligne d'eau : \n \tZ : Cote (m) \n \tA : Aire mouillée (" + "m\u00B2) \n \tp : " + "Périmètre mouillé (m) \n \tL : Largeur au miroir (m)")) + self.label_graphic_help.setText( + _translate("MainWindowProfile", "\'Maj + Clic\' : Ligne d\'eau & \'Ctrl + Clic\' : " + "Sélectionner des points")) diff --git a/src/View/Geometry/mainwindow_ui_reach.py b/src/View/Geometry/mainwindow_ui_reach.py new file mode 100644 index 0000000000000000000000000000000000000000..dfecd2d9620c79ffc498b723ef400e4ec3faf051 --- /dev/null +++ b/src/View/Geometry/mainwindow_ui_reach.py @@ -0,0 +1,425 @@ +# -*- coding: utf-8 -*- + +import sys +import os.path + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtCore import QSize, Qt +from PyQt5.QtWidgets import ( + QAbstractItemView, QHeaderView +) + +from View.Plot import ( + MplCanvas, navigation_toolbar_2qt +) + +file_path = os.path.abspath(os.path.dirname(__file__)) +icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../../ui/resources/" + +class Ui_MainWindow(object): + def setupUi(self, MainWindow_reach): + MainWindow_reach.setObjectName("MainWindow_reach") + MainWindow_reach.resize(1400, 800) + self.centralwidget = QtWidgets.QWidget(MainWindow_reach) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.splitter_3 = QtWidgets.QSplitter(self.centralwidget) + self.splitter_3.setOrientation(Qt.Horizontal) + self.splitter_3.setObjectName("splitter_3") + self.widget = QtWidgets.QWidget(self.splitter_3) + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + + self.label_section_order = QtWidgets.QLabel(self.widget) + self.label_section_order.setLayoutDirection(Qt.LeftToRight) + self.label_section_order.setObjectName("label_section_order") + self.label_section_order.setStyleSheet( + "QLabel{ border: 1px solid darkGray;\n" + " gridline-color: gray;\n" + " border-radius: 1px;\n" + " border-style: solid;\n" + " background-color: ; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + ) + self.verticalLayout.addWidget(self.label_section_order) + btn_size = QSize(30, 30) + self.btn_open = QtWidgets.QPushButton(self.widget) + self.btn_open.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_open.setObjectName("btn_open") + icon_btn_open = QtGui.QIcon() + icon_btn_open.addPixmap( + QtGui.QPixmap(f"{icon_path}open.png")) + self.btn_open.setIcon(icon_btn_open) + self.btn_open.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_open) + + self.btn_add = QtWidgets.QPushButton(self.widget) + self.btn_add.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_add.setObjectName("btn_add") + icon_btn_add = QtGui.QIcon() + icon_btn_add.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk_add.png")) + self.btn_add.setIcon(icon_btn_add) + self.btn_add.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_add) + + self.btn_delete = QtWidgets.QPushButton(self.widget) + self.btn_delete.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_delete.setObjectName("btn_delete") + icon_btn_delete = QtGui.QIcon() + icon_btn_delete.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-remove.png")) + self.btn_delete.setIcon(icon_btn_delete) + self.btn_delete.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_delete) + + self.btn_edit = QtWidgets.QPushButton(self.widget) + self.btn_edit.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_edit.setObjectName("btn_edit") + icon_btn_edit = QtGui.QIcon() + icon_btn_edit.addPixmap( + QtGui.QPixmap(f"{icon_path}gnome-stock-edit.png")) + self.btn_edit.setIcon(icon_btn_edit) + self.btn_edit.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_edit) + + self.btn_copy_selected_profile = QtWidgets.QPushButton(self.widget) + self.btn_copy_selected_profile.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_copy_selected_profile.setObjectName("btn_copy_selected_profile") + icon_btn_copy_selected_profile = QtGui.QIcon() + icon_btn_copy_selected_profile.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-copy.png")) + self.btn_copy_selected_profile.setIcon(icon_btn_copy_selected_profile) + self.btn_copy_selected_profile.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_copy_selected_profile) + + self.btn_paste_selected_profile = QtWidgets.QPushButton(self.widget) + self.btn_paste_selected_profile.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_paste_selected_profile.setObjectName("btn_paste_selected_profile") + icon_btn_paste_selected_profile = QtGui.QIcon() + icon_btn_paste_selected_profile.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-paste.png")) + self.btn_paste_selected_profile.setIcon(icon_btn_paste_selected_profile) + self.btn_paste_selected_profile.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_paste_selected_profile) + + self.btn_duplicate_selected_profile = QtWidgets.QPushButton(self.widget) + self.btn_duplicate_selected_profile.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_duplicate_selected_profile.setObjectName("btn_duplicate_selected_profile") + icon_btn_duplicate_selected_profile = QtGui.QIcon() + icon_btn_duplicate_selected_profile.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-dnd-multiple.png")) + self.btn_duplicate_selected_profile.setIcon(icon_btn_duplicate_selected_profile) + self.btn_duplicate_selected_profile.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_duplicate_selected_profile) + + self.btn_sort_asc = QtWidgets.QPushButton(self.widget) + self.btn_sort_asc.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_asc.setText("") + self.btn_sort_asc.setObjectName("btn_sort_asc") + icon_btn_sort_asc = QtGui.QIcon() + icon_btn_sort_asc.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png")) + self.btn_sort_asc.setIcon(icon_btn_sort_asc) + self.btn_sort_asc.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_asc) + + self.btn_sort_desc = QtWidgets.QPushButton(self.widget) + self.btn_sort_desc.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_sort_desc.setText("") + self.btn_sort_desc.setObjectName("btn_sort_desc") + icon_btn_sort_desc = QtGui.QIcon() + icon_btn_sort_desc.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png")) + self.btn_sort_desc.setIcon(icon_btn_sort_desc) + self.btn_sort_desc.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_sort_desc) + + self.btn_move_up = QtWidgets.QPushButton(self.widget) + self.btn_move_up.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_move_up.setText("") + self.btn_move_up.setObjectName("btn_move_up") + icon_btn_move_up = QtGui.QIcon() + icon_btn_move_up.addPixmap( + QtGui.QPixmap(f"{icon_path}up.png")) + self.btn_move_up.setIcon(icon_btn_move_up) + self.btn_move_up.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_move_up) + + self.btn_move_down = QtWidgets.QPushButton(self.widget) + self.btn_move_down.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_move_down.setText("") + self.btn_move_down.setObjectName("btn_move_down") + icon_btn_move_down = QtGui.QIcon() + icon_btn_move_down.addPixmap( + QtGui.QPixmap(f"{icon_path}down.png")) + self.btn_move_down.setIcon(icon_btn_move_down) + self.btn_move_down.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_move_down) + + self.btn_end_editing = QtWidgets.QPushButton(self.widget) + self.btn_end_editing.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_end_editing.setText("") + self.btn_end_editing.setObjectName("btn_end_editing") + icon_btn_save = QtGui.QIcon() + icon_btn_save.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-apply.png")) + self.btn_end_editing.setIcon(icon_btn_save) + self.btn_end_editing.setIconSize(btn_size) + self.horizontalLayout.addWidget(self.btn_end_editing) + + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.verticalLayout.addLayout(self.horizontalLayout) + self.tableView = QtWidgets.QTableView(self.widget) + self.tableView.setObjectName("tableView") + self.verticalLayout.addWidget(self.tableView) + self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # permet de sélectionner une ligne entière + self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # ajuster la largeur du tableau + self.tableView.setAlternatingRowColors(True) # colorie une ligne sur 2 + self.tableView.verticalHeader().hide() # hide vertical/row headers + self.tableView.setStyleSheet( + " QTableView { border: 1px solid black;\n" + " gridline-color: blue;\n" + " border-radius: 2px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + ) + self.tableView.resizeColumnsToContents() + self.tableView.resizeRowsToContents() + + self.widget1 = QtWidgets.QWidget(self.splitter_3) + self.widget1.setObjectName("widget1") + self.gridLayout = QtWidgets.QGridLayout(self.widget1) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.splitter_2 = QtWidgets.QSplitter(self.widget1) + self.splitter_2.setOrientation(Qt.Vertical) + self.splitter_2.setObjectName("splitter_2") + self.splitter = QtWidgets.QSplitter(self.splitter_2) + self.splitter.setOrientation(Qt.Horizontal) + self.splitter.setObjectName("splitter") + self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.splitter) + self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3") + self.verticalLayout_canvas_1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3) + self.verticalLayout_canvas_1.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_canvas_1.setObjectName("verticalLayout_canvas_1") + self.canvas_1 = MplCanvas.MplCanvas(width=5, height=4, + dpi=100) # QtWidgets.QGraphicsView(self.verticalLayoutWidget_3) + + self.canvas_1.setObjectName("canvas_1") + + self.toolbar_1 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_1, self.centralwidget) + self.toolbar_1.setStyleSheet("QToolBar{ border: 1px solid darkGray;\n" + " gridline-color: blue;\n" + " border-radius: 4px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }") + + self.verticalLayout_canvas_1.addWidget(self.toolbar_1) + + self.verticalLayout_canvas_1.addWidget(self.canvas_1) + self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.splitter) + self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2") + self.verticalLayout_canvas_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2) + self.verticalLayout_canvas_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_canvas_2.setObjectName("verticalLayout_canvas_2") + self.canvas_2 = MplCanvas.MplCanvas(width=5, height=4, + dpi=100) # QtWidgets.QGraphicsView(self.verticalLayoutWidget_2) + # self.canvas_2.setMouseTracking(False) + self.canvas_2.setObjectName("canvas_2") + + self.toolbar_2 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_2, self.centralwidget) + self.toolbar_2.setStyleSheet( + "QToolBar{ border: 1px solid darkGray;\n" + " gridline-color: blue;\n" + " border-radius: 4px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + ) + # self.frame_toolbar.addWidget(self.toolbar) + self.verticalLayout_canvas_2.addWidget(self.toolbar_2) + self.verticalLayout_canvas_2.addWidget(self.canvas_2) + self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter_2) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.verticalLayout_canvas_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayout_canvas_3.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_canvas_3.setObjectName("verticalLayout_canvas_3") + self.canvas_3 = MplCanvas.MplCanvas(width=5, height=4, + dpi=100) # QtWidgets.QGraphicsView(self.verticalLayoutWidget) + self.canvas_3.setObjectName("canvas_3") + + self.toolbar_3 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_3, self.centralwidget) + self.toolbar_3.setStyleSheet( + "QToolBar{ border: 1px solid darkGray;\n" + " gridline-color: blue;\n" + " border-radius: 4px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + ) + # self.frame_toolbar.addWidget(self.toolbar) + self.verticalLayout_canvas_3.addWidget(self.toolbar_3) + # self.verticalLayout_canvas_3.addWidget(self.canvas_3) + # from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas + # import matplotlib.pyplot as plt + # self.figure = plt.figure() + # self.canvas_3 = FigureCanvas(self.figure) + self.verticalLayout_canvas_3.addWidget(self.canvas_3) + + self.gridLayout.addWidget(self.splitter_2, 0, 0, 3, 3) + self.verticalSlider = QtWidgets.QSlider(self.widget1) + self.verticalSlider.setMinimumSize(QtCore.QSize(30, 0)) + self.verticalSlider.setOrientation(Qt.Vertical) + self.verticalSlider.setInvertedAppearance(True) + self.verticalSlider.setInvertedControls(False) + self.verticalSlider.setObjectName("verticalSlider") + self.gridLayout.addWidget(self.verticalSlider, 0, 3, 1, 1) + + self.btn_slider_up = QtWidgets.QPushButton(self.widget1) + self.btn_slider_up.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_slider_up.setObjectName("btn_slider_up") + icon_btn_slider_up = QtGui.QIcon() + icon_btn_slider_up.addPixmap( + QtGui.QPixmap(f"{icon_path}go-up2.png")) + self.btn_slider_up.setIcon(icon_btn_slider_up) + self.btn_slider_up.setIconSize(btn_size) + self.gridLayout.addWidget(self.btn_slider_up, 1, 3, 1, 1) + + self.btn_slider_down = QtWidgets.QPushButton(self.widget1) + self.btn_slider_down.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_slider_down.setObjectName("btn_slider_down") + icon_btn_slider_down = QtGui.QIcon() + icon_btn_slider_down.addPixmap( + QtGui.QPixmap(f"{icon_path}go-down1.png")) + self.btn_slider_down.setIcon(icon_btn_slider_down) + self.btn_slider_down.setIconSize(btn_size) + self.gridLayout.addWidget(self.btn_slider_down, 2, 3, 1, 1) + + self.vertical_slider_label = QtWidgets.QLabel(self.widget1) + # self.vertical_slider_label.setLayoutDirection(Qt.RightToLeft) + # self.vertical_slider_label.setLayoutDirection(Qt.LeftToRight) + self.vertical_slider_label.setObjectName("vertical_slider_label") + self.gridLayout.addWidget(self.vertical_slider_label, 3, 2, 2, 1) + self.vertical_slider_label.setAlignment(Qt.AlignRight) + + self.btn_slider_go_back = QtWidgets.QPushButton(self.widget1) + self.btn_slider_go_back.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_slider_go_back.setObjectName("btn_slider_go_back") + icon_btn_slider_go_back = QtGui.QIcon() + icon_btn_slider_go_back.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-go-back.png")) + self.btn_slider_go_back.setIcon(icon_btn_slider_go_back) + self.btn_slider_go_back.setIconSize(btn_size) + self.gridLayout.addWidget(self.btn_slider_go_back, 5, 0, 1, 1) + self.btn_slider_go_back.setEnabled(False) + + self.btn_slider_go_forward = QtWidgets.QPushButton(self.widget1) + self.btn_slider_go_forward.setMaximumSize(QtCore.QSize(30, 30)) + self.btn_slider_go_forward.setObjectName("btn_slider_go_forward") + icon_btn_slider_go_forward = QtGui.QIcon() + icon_btn_slider_go_forward.addPixmap( + QtGui.QPixmap(f"{icon_path}gtk-go-forward.png")) + self.btn_slider_go_forward.setIcon(icon_btn_slider_go_forward) + self.btn_slider_go_forward.setIconSize(btn_size) + self.gridLayout.addWidget(self.btn_slider_go_forward, 5, 1, 1, 1) + self.btn_slider_go_forward.setEnabled(False) + + self.horizontalSlider = QtWidgets.QSlider(self.widget1) + self.horizontalSlider.setMinimumSize(QtCore.QSize(0, 30)) + self.horizontalSlider.setOrientation(Qt.Horizontal) + self.horizontalSlider.setObjectName("horizontalSlider") + self.gridLayout.addWidget(self.horizontalSlider, 5, 2, 1, 1) + self.horizontalSlider.setEnabled(False) + + self.horizontalLayout_2.addWidget(self.splitter_3) + MainWindow_reach.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow_reach) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1123, 20)) + self.menubar.setObjectName("menubar") + MainWindow_reach.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow_reach) + self.statusbar.setObjectName("statusbar") + MainWindow_reach.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow_reach) + QtCore.QMetaObject.connectSlotsByName(MainWindow_reach) + + def retranslateUi(self, MainWindow_reach): + _translate = QtCore.QCoreApplication.translate + self.name_reach = "" + self.mainwindow_title = _translate("MainWindow_reach", "Jeu de sections du Bief") + MainWindow_reach.setWindowTitle(self.mainwindow_title + f"{self.name_reach}") + self.text_section_order = _translate("MainWindow_reach", "\nOrdre des sections : " + "Amont --> Aval") + self.label_section_order.setText(f"{self.text_section_order}") + self.vertical_slider_value = "" + self.vertical_slider_label.setText(_translate("MainWindow_reach", "Pk = ") + f"{self.vertical_slider_value}") + self.btn_open.setToolTip(_translate("MainWindow_reach", " Importer une géométrie")) + self.btn_add.setToolTip(_translate("MainWindow_reach", " Nouveau profil")) + self.btn_delete.setToolTip(_translate("MainWindow_reach", " Supprimer le profil sélectionné")) + self.btn_edit.setToolTip(_translate("MainWindow_reach", " Éditer le profil sélectionné")) + self.btn_copy_selected_profile.setToolTip(_translate("MainWindow_reach", " Copier le profil sélectionné")) + self.btn_paste_selected_profile.setToolTip(_translate("MainWindow_reach", "Coller le profil en fin de liste (" + "penser à modifier le Pk avant de " + "trier)")) + self.btn_duplicate_selected_profile.setToolTip( + _translate("MainWindow_reach", " Dupliquer la section sélectionnée")) + self.btn_sort_asc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre croissant des Pk")) + self.btn_sort_desc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre décroissant des Pk")) + self.btn_move_up.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le " + "profil sélectionné vers le haut)")) + self.btn_move_down.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le " + "profil sélectionné vers le bas)")) + self.btn_end_editing.setToolTip(_translate("MainWindow_reach", " Terminer l'édition")) + self.tableView_header = [_translate("MainWindow_reach", "Nom"), _translate("MainWindow_reach", "Pk (m)"), + _translate("MainWindow_reach", "Type")] + + self.toolbar_1._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+Z")) + self.toolbar_1._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+Z)")) + + self.toolbar_2._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+E")) + self.toolbar_2._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+E)")) + + self.toolbar_3._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+R")) + self.toolbar_3._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+R)")) + + self.toolbar_1._actions["non_isometric_view"].setShortcut("Alt+S") + self.toolbar_1._actions["non_isometric_view"].setToolTip( + _translate("MainWindow_reach", "Vue globale automatique (Alt+S)")) + + self.toolbar_2._actions["non_isometric_view"].setShortcut("Alt+D") + self.toolbar_2._actions["non_isometric_view"].setToolTip( + _translate("MainWindow_reach", "Vue globale automatique (Alt+D)")) + + self.toolbar_3._actions["non_isometric_view"].setShortcut("Alt+F") + self.toolbar_3._actions["non_isometric_view"].setToolTip( + _translate("MainWindow_reach", "Vue globale automatique (Alt+F)")) diff --git a/src/View/Geometry/qtableview_profile.py b/src/View/Geometry/qtableview_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..7f59e1bf36a9da2f91622a18fe08fa92a4fc3460 --- /dev/null +++ b/src/View/Geometry/qtableview_profile.py @@ -0,0 +1,422 @@ +import numpy as np +import pandas as pd +from PyQt5.QtGui import QFont + +from PyQt5.QtWidgets import QMessageBox +from PyQt5 import QtWidgets, QtGui +from PyQt5.QtCore import QModelIndex, Qt, QAbstractTableModel, QVariant, QCoreApplication +from ProfileXYZ import ProfileXYZ +import projection_pointXYZ + +_translate = QCoreApplication.translate + + +class PandasModelEditable(QAbstractTableModel): + def __init__(self, profile: ProfileXYZ, table_header=None): + QAbstractTableModel.__init__(self) + + if table_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.profile = profile + + data = pd.DataFrame({ + self.header[0]: profile.x, + self.header[1]: profile.y, + self.header[2]: profile.z, + self.header[3]: profile.ld, + self.header[4]: projection_pointXYZ.get_station(profile) + }) + self._data = data + + def rowCount(self, parent=QModelIndex()): + return self._data.shape[0] + + def columnCount(self, parent=QModelIndex()): + return self._data.shape[1] + + def data(self, index, role=Qt.DisplayRole): + value = self._data.iloc[index.row()][index.column()] + if index.isValid(): + if role == Qt.DisplayRole: + if index.column() != 4: + if isinstance(value, float): + return "%.4f" % value + else: + if isinstance(value, float): + return "%.3f" % value + + return str(self._data.iloc[index.row(), index.column()]) + + if role == Qt.TextAlignmentRole: + return Qt.AlignHCenter | Qt.AlignVCenter + + if index.column() == 2: + if role == Qt.ForegroundRole: + if value == min(self._data.iloc[:, index.column()]): + return QtGui.QColor("red") + elif value == max(self._data.iloc[:, index.column()]): + return QtGui.QColor("Blue") + + if role == Qt.ToolTipRole: + if value == min(self._data.iloc[:, index.column()]): + return _translate("MainWindowProfile", "La cote du fond", "Z minimale") + elif value == max(self._data.iloc[:, index.column()]): + return _translate("MainWindowProfile", "La cote maximale", "Z maximale") + + if index.column() == 3: + if value.strip().upper() in ["RG", "RD"]: + if role == Qt.FontRole: + font = QFont() + font.setBold(True) + return font + + if role == Qt.ForegroundRole: + return QtGui.QColor("darkRed") + + if role == Qt.ToolTipRole: + if value.strip().upper() == "RG": + return _translate("MainWindowProfile", "Rive gauche") + else: + return _translate("MainWindowProfile", "Rive droite") + + if index.column() == 4: + if role == Qt.FontRole: + font = QFont() + font.setBold(True) + return font + + if role == Qt.BackgroundRole: + return QtGui.QColor("#ededee") + + return QVariant() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + if orientation == Qt.Horizontal and role == Qt.DisplayRole: + return self.header[section] + + 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 " + ) + + if orientation == Qt.Vertical and role == Qt.DisplayRole: + return self._data.index[section] + 1 + + return None + + def setData(self, index, value, role=Qt.EditRole): + if role == Qt.EditRole: + try: + if index.column() == 3: + self._data.iat[index.row(), index.column()] = str(value) + elif index.column() == 0: + self._data.iat[index.row(), index.column()] = float(value) + elif index.column() == 1: + self._data.iat[index.row(), index.column()] = float(value) + elif index.column() == 2: + self._data.iat[index.row(), index.column()] = float(value) + + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + self.dataChanged.emit(index, index) + except: + print('TODO') + self.QMessageBoxCritical(value) + + return True + + self.dataChanged.emit(index, index) + self.layoutChanged.emit() + + return False + + @staticmethod + def QMessageBoxCritical(value): + msg = QMessageBox() + msg.setIcon(QMessageBox.Warning) + msg.setText("{} : Valeur saisie incorrecte ".format(value)) + msg.setInformativeText("Seules les valeurs numériques sont autorisées.") + msg.setWindowTitle("Warning ") + msg.setStyleSheet("QLabel{min-width:150 px; font-size: 13px;} QPushButton{ width:20px; font-size: 12px};" + "background-color: Ligthgray ; color : gray;font-size: 8pt; color: #888a80;") + msg.exec_() + + 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): + return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled + + # @QtCore.pyqtSlot() + def insertRows(self, row, count, parent=QModelIndex()): + self.beginInsertRows(parent, row, row + count - 1) + indexes = [str(self.rowCount() + i) for i in range(count)] + left = self._data[0:row] + mid = pd.DataFrame(index=indexes, columns=self._data.columns) + right = self._data[row + count - 1:self.rowCount()] + + self._data = pd.concat([left, mid, right]) + + for i in [3]: + self._data.iloc[:, i].replace(np.nan, '', inplace=True) + + self._data.reset_index(drop=True, inplace=True) + + try: + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + except: + print("TODO") + + self.endInsertRows() + self.layoutChanged.emit() + + # @QtCore.pyqtSlot() + def removeRows(self, row, count, parent=QModelIndex()): + self.beginRemoveRows(parent, row, row + count + 1) + self._data.drop(self._data.index[row], inplace=True) + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + self.endRemoveRows() + self.layoutChanged.emit() + + def remove_rows1(self, row, count, parent=QModelIndex()): + self.beginRemoveRows(parent, row, row + count - 1) + left = self._data.iloc[0:row] + right = self._data.iloc[row + count:self.rowCount()] + + self._data = pd.concat([left, right], axis=0, ignore_index=True) + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + self.endRemoveRows() + self.layoutChanged.emit() + + def remove_rows(self, list_row_selected, parent=QModelIndex()): + self.beginRemoveRows(parent, list_row_selected[0], list_row_selected[-1]) + + try: + self._data.drop(self._data.index[list_row_selected], inplace=True) + self._data.reset_index(drop=True, inplace=True) + except: + print('TODO') + try: + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + except: + print("TODO") + + self.endRemoveRows() + self.layoutChanged.emit() + + def sort(self, column, order=Qt.AscendingOrder): + self.layoutAboutToBeChanged.emit() + colname = self._data.columns.tolist()[column] + self._data.sort_values(colname, ascending=order == Qt.AscendingOrder, inplace=True) + self._data.reset_index(inplace=True, drop=True) + + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + self.layoutChanged.emit() + + def moveRowDown(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 2 + self.beginMoveRows(parent, row_to_move, row_to_move, parent, target) + block_before_row = self._data.iloc[0:row_to_move] + selected_row = self._data.iloc[row_to_move:row_to_move + 1] + after_selcted_row = self._data.iloc[row_to_move + 1:row_to_move + 2] + block_after_row = self._data.iloc[row_to_move + 2:self.rowCount()] + + self._data = pd.concat([block_before_row, after_selcted_row, selected_row, block_after_row], axis=0) + self._data.reset_index(inplace=True, drop=True) + + self.endMoveRows() + self.layoutChanged.emit() + + def moveRowUp(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 1 + self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target) + block_before_row = self._data.iloc[0:row_to_move - 1] + before_selected_row = self._data.iloc[row_to_move - 1:row_to_move] + selected_row = self._data.iloc[row_to_move:row_to_move + 1] + block_after_row = self._data.iloc[row_to_move + 1:self.rowCount()] + + self._data = pd.concat([block_before_row, selected_row, before_selected_row, block_after_row], axis=0) + self._data.reset_index(inplace=True, drop=True) + + self.endMoveRows() + self.layoutChanged.emit() + + def copyTable(self, start_selection, end_selection): + end_selection = self.rowCount() + + self._data.loc[start_selection:end_selection]\ + .to_clipboard(header=None, index=False, excel=True, sep='\t') + + def insert_df_to_idx(self, idx, df, df_insert): + """ + Args: + idx: is the index position in df where you want to insert new dataframe (df_insert) + df: dataframe + df_insert: dataframe to insert + Returns: + The dataframe df with df_insert inserted at index idx. + """ + return df.iloc[:idx, ].append(df_insert).append(df.iloc[idx:, ]).reset_index(drop=True) + + def pasteTable(self, insertion_index): + self.layoutAboutToBeChanged.emit() + df = pd.read_clipboard(header=None, skip_blank_lines=True, + sep="\t", names=self.header) + self._data = self.insert_df_to_idx(insertion_index, self._data, df) + + for i in [3]: + self._data.iloc[:, i].replace(np.nan, '', inplace=True) + + self.layoutChanged.emit() + self._data.iloc[:, 4] = projection_pointXYZ.update_station( + self.header, + self._data.values.tolist() + ) + + @property + def model_data(self): + return self._data + + @model_data.setter + def model_data(self, new_data): + self._data = new_data + self.layoutChanged.emit() + + @property + def x(self): + return self._data.iloc[:, 0].tolist() + + @property + def y(self): + return self._data.iloc[:, 1].tolist() + + @property + def z(self): + return self._data.iloc[:, 2].tolist() + + @property + def name(self): + return self._data.iloc[:, 3].tolist() + + def get_data(self): + return self._data + + @property + def station(self): + return self._data.iloc[:, 4].tolist() + + def remove_duplicates_names(self): + counter_list = [] + list_deleted_names = [] + ind_ind = [] + + for ind, name_point in enumerate(self.name): + if name_point not in counter_list: + counter_list.append(name_point) + elif len(name_point.strip()) > 0 and name_point in counter_list: + ind_ind.append(ind) + + if name_point not in list_deleted_names: + list_deleted_names.append(name_point) + + for ind in ind_ind: + self._data.iat[ind, 3] = "" + + def data_contains_nan(self) -> bool: + """ + Returns: + Returns True if the QTableView() contains np.nan + """ + return self._data.isnull().values.any() + + def delete_empty_rows(self): + self.layoutAboutToBeChanged.emit() + + self._data.dropna(inplace=True) + self._data.reset_index(drop=True, inplace=True) + + self.layoutChanged.emit() + + def valide_all_changes(self): + self.profile.x = self._data.iloc[:, 0] + self.profile.y = self._data.iloc[:, 1] + self.profile.z = self._data.iloc[:, 2] + self.profile.ld = self._data.iloc[:, 3] + + +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): + """ + Args: + parent: + option: + index: + Returns: + Le widget (éditeur) pour éditer l'item se trouvant à l'index index. + """ + index.model().data(index, Qt.DisplayRole) + return QtWidgets.QLineEdit(parent) + + def setEditorData(self, editor, index): + """ + Args: + editor: l'éditeur + index: l'index + Returns: permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant + à l'index index. + """ + value = index.model().data(index, Qt.DisplayRole) + editor.setText(str(value)) + + def setModelData(self, editor, model, index): + """ + Args: + editor: l'éditeur + model: le modèle + index: l'index + Returns: permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index + identifié par le paramètre index + """ + model.setData(index, editor.text()) + + if not self.setModelDataEvent is None: + self.setModelDataEvent() + + def updateEditorGeometry(self, editor, option, index): + """ + Args: + editor: l'éditeur + option: + index: l'index + Returns: Permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change + """ + editor.setGeometry(option.rect) diff --git a/src/View/Geometry/qtableview_reach.py b/src/View/Geometry/qtableview_reach.py new file mode 100644 index 0000000000000000000000000000000000000000..3c892051d15fd5015eea2a61003ec9d3c402abfe --- /dev/null +++ b/src/View/Geometry/qtableview_reach.py @@ -0,0 +1,1038 @@ +# -*- 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 +) + +from Model.Geometry import Reach + +_translate = QCoreApplication.translate + +class PandasModelEditable(QAbstractTableModel): + def __init__(self, reach, header=None, profiles_type=None): + QAbstractTableModel.__init__(self) + data_list = [] + + self._data = reach + + if header is None: + self.headers = [ + _translate("MainWindow_reach", "Nom"), + _translate("MainWindow_reach", "Pk"), + _translate("MainWindow_reach", "Type") + ] + else: + self.headers = header + + if profiles_type is None: + self.profiles_type = [ + "XYZ", "AC", + # "LC", "R", "T", "RT" + ] + + def rowCount(self, parent=QModelIndex()): + return len(self._data.profile) + + def columnCount(self, parent=QModelIndex()): + return len(self.headers) + + def data(self, index, role=Qt.DisplayRole): + if index.isValid(): + if role == Qt.DisplayRole and index.column() == 0: + value = self._data.profile[index.row()].name + return self._data.profile[index.row()].name + + if role == Qt.DisplayRole and index.column() == 1: + return "%.4f" % float(self._data.profile[index.row()].pk) + + if role == Qt.DisplayRole and index.column() == 2: + return self._data.profile[index.row()].profile_type + + column_count = self.columnCount() + for column in range(0, column_count): + if index.column() == column and role == Qt.TextAlignmentRole: + return Qt.AlignHCenter | Qt.AlignVCenter + + if role == Qt.BackgroundRole and index.column() == 1: + return QtGui.QColor(Qt.lightGray) + + if role == Qt.BackgroundRole and index.column() == 2: + color = QtGui.QColor() + data_i_profile_type = self._data.profile[index.row()].profile_type + + if data_i_profile_type == self.profiles_type[1]: + color = QtGui.QColor("magenta") + elif data_i_profile_type == self.profiles_type[2]: + color = QtGui.QColor("red") + else: + color = QtGui.QColor("lightgreen") + + return color + if role == Qt.ForegroundRole and index.column() == 0: + if self._data.profile[index.row()].name == "Amont": + return QtGui.QColor("Green") + elif self._data.profile[index.row()].name == "Aval": + 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 "not implemented" + else: + return "%d" % (section + 1) + + if orientation == Qt.Vertical and role == Qt.DisplayRole: + return self.headers[section] + + return None + + def setData(self, index, value, role=Qt.EditRole): + row = index.row() + column = index.column() + + if role == Qt.EditRole and index.column() != 2: + try: + if role == Qt.EditRole and index.column() == 0: + self._data.profile[index.row()].name = value + + if role == Qt.EditRole and index.column() == 1: + self._data.profile[index.row()].pk = float(value) + + self.dataChanged.emit(index, index) + except: + self.QMessageBoxCritical(value) + + return True + + self.dataChanged.emit(index, index) + self.layoutChanged.emit() + + return False + + @staticmethod + def QMessageBoxCritical(value): + msg = QMessageBox() + msg.setIcon(QMessageBox.Warning) + msg.setText("{} : Valeur saisie incorrecte".format(value)) + msg.setInformativeText("Seules les valeurs numériques sont autorisées.") + msg.setWindowTitle("Warning ") + msg.setStyleSheet("QLabel{min-width:250 px; font-size: 13px;} QPushButton{ width:20px; font-size: 12px};" + "background-color: Ligthgray ; color : gray;font-size: 8pt; color: #888a80;") + msg.exec_() + + 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): + if index.column() == 2: + return Qt.ItemIsEnabled | Qt.ItemIsSelectable + else: + return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled + + # @QtCore.pyqtSlot() + def insertRows(self, row, count, parent=QModelIndex()): + self.beginInsertRows(parent, row, row + count - 1) + + self._data.add_profile(row) + self._data.profile[row].profile_type = "XYZ" + + self.endInsertRows() + self.layoutChanged.emit() + + def insertRows_(self, row, rowt=1, parent=QModelIndex()): + self.beginInsertRows(parent, row, rowt) + self._data.add_profile(row) + self.endInsertRows() + self.layoutChanged.emit() + + def remove_rows(self, list_row_selected, parent=QModelIndex()): + self.beginRemoveRows(parent, list_row_selected[0], list_row_selected[-1]) + + if len(list_row_selected) >= 1: + if len(list_row_selected) == 1: + self._data.delete_profile(list_row_selected[0]) + elif len(list_row_selected) > 1: + self._data.delete_profile_rows(list_row_selected) + + self.endRemoveRows() + self.layoutChanged.emit() + + def removeRows_(self, position, rows, parent=QModelIndex()): + self.beginRemoveRows(QModelIndex(), position, position + 1) + self._data.delete_profile(position) + self.endRemoveRows() + self.layoutChanged.emit() + + def export_reach(self, filename): + with open(f"{filename}", "w") as file_st: + for index_pro in range(len(self._data.profile)): + file_st.write(" ") + for head in self._data.headers[index_pro]: + file_st.write(str(head)) + file_st.write(" ") + file_st.write("\n") + + for point in self._data.profile[index_pro].points: + print(point) + + for i in [point.x, point.y, point.z, point.name]: + if isinstance(i, float): + if i >= 0: + file_st.write(" " + str("%.4f" % i)) + else: + file_st.write(" " + str("%.4f" % i)) + else: + file_st.write(" " + str(i)) + + file_st.write("\n") + file_st.write(" 999.9990 999.9990 999.9990") + file_st.write("\n") + + def sort_data(self, _reverse): + self.layoutAboutToBeChanged.emit() + self._data.sort_descending(_reverse) + self.layoutAboutToBeChanged.emit() + self.layoutChanged.emit() + + def moveRowDown(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 2 + self.beginMoveRows(parent, row_to_move, row_to_move, parent, target) + self._data.move_down_profile(row_to_move) + self.endMoveRows() + self.layoutChanged.emit() + + def moveRowUp(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 1 + self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target) + self._data.move_up_profile(row_to_move) + self.endMoveRows() + self.layoutChanged.emit() + + def graphique(self, ax): + self.layoutChanged.emit() + self.ax = ax + for i in range(len(self._data)): + self.ax.plot(self._data[i][1].iloc[:, 0], + self._data[i][1].iloc[:, 1], + color='r', lw=1., markersize=3, marker='+') + + def get_profile_selected(self, index): + return self._data.profile[index] + + def get_profile_selected_identifier(self, index): + return self._data.profile[index].headers[0] + + def get_profile_name(self, index): + return self._data.profile[index].name + + def get_profile_via_identifier(self, identifier): + profile, = [ + profile for profile in self._data.profile + if profile.headers[0] == str(identifier) + ] + return profile + + def get_z_min(self): + return self._data.get_bief_list_z_min() + + def get_z_min_i(self, index): + return self._data.profile[index].z_min + + def get_z_max(self): + """ + Returns: liste des z_max du profile + """ + return self._data.get_bief_list_z_max() + + def get_z_max_i(self, index): + return self._data.profile[index].z_max + + def get_pk(self): + """ + Returns: liste des Pk + """ + return self._data.get_bief_pk() + + def get_pk_i(self, index): + return self._data.profile[index].pk + + def get_x(self): + return self._data.get_bief_x() + + def get_y(self): + return self._data.get_bief_y() + + def get_x_complete_list_ld(self): + return self._data.get_bief_x_complete_ld() + + def get_y_complete_list_ld(self): + return self._data.get_bief_y_complete_ld() + + def get_z_complete_list_ld(self): + return self._data.get_bief_z_complete_ld() + + def get_pk_complete_list_ld(self): + return self._data.get_bief_pk_complete_guideline() + + def get_x_incomplete_list_ld(self): + pass + + def x_complete_guideline(self): + return [ + [ + profile.x_all_points_complete_guideline(name) + for profile in self._data.profile + ] for name in self._data.complete_guideline() + ] + + def y_complete_guideline(self): + return [ + [ + profile.y_all_points_complete_guideline(name) + for profile in self._data.profile + ] for name in self._data.complete_guideline() + ] + + def z_complete_guideline(self): + return [ + [ + profile.z_all_points_complete_guideline(name) + for profile in self._data.profile + ] for name in self._data.complete_guideline() + ] + + def get_station(self, index): + return self._data.get_station(index) + + def get_z(self): + return self._data.get_bief_z() + + def get_x_profile_i(self, index): + return self._data.profile[index].x + + def get_y_profile_i(self, index): + return self._data.profile[index].y + + def get_z_profile_i(self, index): + return self._data.profile[index].z + + def get_ld_profile_i(self, index): + return self._data.profile[index].ld + + def get_pk_profile_i(self, index): + return self._data.profile[index].pk + + def get_complete_list_ld(self): + return self._data.complete_guideline() + + def get_incomplete_list_ld(self): + return self._data.incomplete_guideline() + + def model_data(self): + return self._data + + def add_ld(self, *args): + lst = [] + for i in args: + list_i = [] + for j in range(len(i)): + l = i[1] + list_i.append(l) + lst.extend(list_i) + return lst, len(lst) + + def size_list_named_points(self): + return len(self.get_all_maned_points()) + + +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): + """retourne le widget (éditeur) pour éditer l'item se trouvant à l'index index.""" + index.model().data(index, Qt.DisplayRole) + return QtWidgets.QLineEdit(parent) + + def setEditorData(self, editor, index): + """permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant à l'index + 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 + print('Donnée éditée dans la case [{},{}] :'.format(index.row(), index.column()), value) + + def setModelData(self, editor, model, index): + """permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index identifié + par le paramètre index """ + model.setData(index, editor.currentItem().text()) + + if not self.setModelDataEvent is None: + self.setModelDataEvent() + + def updateEditorGeometry(self, editor, option, index): + """permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change""" + editor.setGeometry(option.rect) + + +class Delegate11(QtWidgets.QItemDelegate): + def __init__(self, owner, choices): + super().__init__(owner) + self.items = choices + + def createEditor(self, parent, option, index): + self.editor = QtWidgets.QComboBox(parent) + self.editor.currentIndexChanged.connect(self.commit_editor) + self.editor.addItems(self.items) + return self.editor + + def paint(self, painter, option, index): + value = index.data(Qt.DisplayRole) + style = QtWidgets.QApplication.style() + opt = QtWidgets.QStyleOptionComboBox() + opt.text = str(value) + opt.rect = option.rect + style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter) + QtWidgets.QItemDelegate.paint(self, painter, option, index) + + def commit_editor(self): + editor = self.sender() + color = QtGui.QColor() + if editor.currentText() == "ProfilXYZ": + color = QtGui.QColor("lightgreen") + elif editor.currentText() == "ProfilAC": + color = QtGui.QColor("red") + qss = """QComboBox{background-color : %s;}""" % (color.name(),) + editor.setStyleSheet(qss) + self.commitData.emit(editor) + + def setEditorData(self, editor, index): + editor.blockSignals(True) + editor.setCurrentIndex(index.row()) # replacement + editor.blockSignals(False) + + @pyqtSlot() + def currentIndexChanged(self): + self.commit_editor.emit(self.sender()) + + def setModelData(self, editor, model, index): + value = editor.currentText() + model.setData(index, Qt.DisplayRole, QVariant(value)) + + 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 commit_editor(self): + editor = self.sender() + color = QtGui.QColor() + + if editor.currentText() == "ProfilXYZ": + color = QtGui.QColor("lightgreen") + elif editor.currentText() == "ProfilAC": + color = QtGui.QColor("red") + + qss = """QComboBox{background-color : %s;}""" % (color.name(),) + editor.setStyleSheet(qss) + self.commitData.emit(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()) + + +class PandasModelEditableCreateReach(QAbstractTableModel): + # TODO : Cette calsse permet juste d'éviter que l'application plante lorsque 'on essaye d'éditer le tablueau. Le + # problème se trouve au niveau du délégué QtWidgets.QStyledItemDelegate. Il faudra prendre en compte le cas où + # on part de zero pour creer un bief. Pour l'instant, le prototype ne gère que l'importation d'une géométrie. + # + def __init__(self, data, headers): + QAbstractTableModel.__init__(self) + data_list = [] + self._data = data + self.headers = headers + + def rowCount(self, parent=QModelIndex()): # parent=None): + return len(self._data) + + def columnCount(self, parent=QModelIndex()): # parent=None): + return len(self.headers) + + def data(self, index, role=Qt.DisplayRole): + if index.isValid(): + if role == Qt.DisplayRole and index.column() == 0: + value = self._data[index.row()][0][5] + return self._data[index.row()][0][5] + + if role == Qt.DisplayRole and index.column() == 1: + return "%.4f" % float(self._data[index.row()][0][4]) + + column_count = self.columnCount() + for column in range(0, column_count): + if index.column() == column and role == Qt.TextAlignmentRole: + return Qt.AlignHCenter | Qt.AlignVCenter + + if role == Qt.BackgroundRole and index.column() == 1: + return QtGui.QColor(Qt.lightGray) + + return QVariant() # None + + 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 "not implemented" + else: + return "%d" % (section + 1) + + if orientation == Qt.Vertical and role == Qt.DisplayRole: + return self._data[section] + + return None + + def setData(self, index, value, role=Qt.EditRole): + row = index.row() + column = index.column() + + if role == Qt.EditRole and index.column() != 2: + try: + if role == Qt.EditRole and index.column() == 0: + self._data[index.row()][0][5] = value + + if role == Qt.EditRole and index.column() == 1: + self._data[index.row()][0][4] = float(value) + self.dataChanged.emit(index, index) + except: + print('TODO') + return True + + 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): + if index.column() == 2: + return Qt.ItemIsEnabled + else: + return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled + + # @QtCore.pyqtSlot() + def insertRows(self, row, count, parent=QModelIndex()): + self.beginInsertRows(parent, row, row + count - 1) + indexes = [str(self.rowCount() + i) for i in range(count)] + self._data.insert(row, [[self.rowCount() + 1, 0, 0, 0, np.nan, ''], + pd.DataFrame({'x': [0], 'y': [0], 'z': [0], 'ld': ['']})]) + self.endInsertRows() + self.layoutChanged.emit() + + # @QtCore.pyqtSlot() + def removeRows1(self, row, count, parent=QModelIndex()): + self.beginRemoveRows(parent, row, row) + self._data.pop(row) + self.endRemoveRows() + self.layoutChanged.emit() + + def removeRows(self, position, rows, parent=QModelIndex()): + self.beginRemoveRows(QModelIndex(), position, position + rows - 1) + + for i in range(rows): + del (self._data[position]) + + self.endRemoveRows() + self.layoutChanged.emit() + + return True + + # # @QtCore.pyqtSlot() + def sort_data(self, _reverse): + self.layoutAboutToBeChanged.emit() + + if not _reverse: + self._data = sorted(self._data, key=lambda x: float(x[0][4]), reverse=False) + else: + self._data = sorted(self._data, key=lambda x: float(x[0][4]), reverse=True) + + self.layoutAboutToBeChanged.emit() + self.layoutChanged.emit() + + def moveRowDown(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 2 + self.beginMoveRows(parent, row_to_move, row_to_move, parent, target) + self._data[row_to_move], self._data[row_to_move + 1] = self._data[row_to_move + 1], self._data[row_to_move] + self.endMoveRows() + self.layoutChanged.emit() + + def moveRowUp(self, row_to_move, parent=QModelIndex()): + target = row_to_move + 1 + + self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target) + self._data[row_to_move], self._data[row_to_move - 1] = self._data[row_to_move - 1], self._data[row_to_move] + self.endMoveRows() + self.layoutChanged.emit() + + def graphique(self, ax): + self.layoutChanged.emit() + self.ax = ax + + for i in range(len(self._data)): + self.ax.plot(self._data[i][1].iloc[:, 0], + self._data[i][1].iloc[:, 1], + color='r', lw=1., markersize=3, marker='+') + + def get_z_min(self): + z_min = [] + + for row in range(self.rowCount()): + z_min.append(min(self._data[row][1].iloc[:, 2].tolist())) + + return z_min + + def get_z_max(self): + """ + Returns: liste des z_max du profile + """ + z_max = [] + + for row in range(self.rowCount()): + z_max.append(max(self._data[row][1].iloc[:, 2].tolist())) + + return z_max + + def get_pk(self): + """ + Returns: liste des Pk + """ + Pk = [ + float(self._data[row][0][4]) for row in range(self.rowCount()) + ] + + return Pk + + def get_x(self): + d = time.time() + x = [] + + for row in range(self.rowCount()): + lst = self._data[row][1].iloc[:, 0] # .tolist() + x.append(lst) + + return x + + def get_y(self): + y = [] + + for row in range(self.rowCount()): + lst = self._data[row][1].iloc[:, 1] # .tolist() + y.append(lst) + + return y + + def get_z(self): + z = [] + + for row in range(self.rowCount()): + z.append(float(self._data[row][1].iloc[:, 2])) + + return z + + def add_ld(self, *args): + lst = [] + + for i in args: + list_i = [] + for j in range(len(i)): + l = i[1] + list_i.append(l) + lst.extend(list_i) + + return lst, len(lst) + + def get_all_maned_points(self): + """ + Returns: la liste qui est l'union disjointe des listes de nom de points de tous les profils du profile + """ + # je considère une union disjointe au lieu d'une intersection pour prendre en compte d'éventuelles lignes + # directrices qui ne sont définies qu'à une partie du profile ie qui ne relient pas les deux deux profils + # extrêmes. + # NB : la liste retournée est "alphanumériquement" ordonnée ?! + liste = [data[1].iloc[:, 3].tolist() for data in self._data] + disjoint_union_ld = reduce(np.union1d, liste) + disjoint_union_ld_list = [ + element for element in disjoint_union_ld.tolist() if len(element.strip()) != 0 + ] + + return disjoint_union_ld_list + + def size_list_named_points(self): + return len(self.get_all_maned_points()) + + def get_complete_list_ld(self): + liste = [data[1].iloc[:, 3] for data in self._data] + intersection_ld = reduce(np.intersect1d, liste) + intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""] + return intersection_ld_list + + def get_incomplete_list_ld(self): + """ + Returns : liste de noms de points nommés incomplets, c'est-à -dire, des points nommés définis que + sur un ou quelques profils seulement. Il s'agit de la liste des lignes directrices "incomplètes". + """ + liste = [data[1].iloc[:, 3] for data in self._data] + disjoint_union_ld = reduce(np.union1d, liste) + disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""] + diff_union_instersect_ld = list( + set(disjoint_union_ld_list).symmetric_difference(set(self.get_complete_list_ld()))) + return diff_union_instersect_ld + + def get_x_y_named_points(self): + """ + Returns : les coordonnées (x,y) des points nommés "complets" et (éventuellement) "incomplets" + """ + list_x_y_ld = [] + list_x_y_ld_incomplete = [] + + liste = [data[1].iloc[:, 3] for data in self._data] + intersection_ld = reduce(np.intersect1d, liste) + intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""] + disjoint_union_ld = reduce(np.union1d, liste) + disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""] + diff_union_instersect_ld = list(set(disjoint_union_ld_list).symmetric_difference(set(intersection_ld_list))) + + if len(intersection_ld_list) != 0: + for data in self._data: + df = data[1] # .iloc[row, 3] + col_name = df.columns[3] # colonne (Name) des lignes directrices + col_x = df.columns[0] + col_y = df.columns[1] + list_name = df[col_name].to_list() # liste de noms de lignes directrices + list_x = df[col_x].to_list() + list_y = df[col_y].to_list() + list_ld = [[list_x[list_name.index(name)], list_y[list_name.index(name)], name] + for name in intersection_ld_list] + list_x_y_ld.append(list_ld) + if len(diff_union_instersect_ld) != 0: + list_ld_incomlete = [ + [list_x[list_name.index(name)], list_y[list_name.index(name)], name] + for name in diff_union_instersect_ld + if name in list_name + ] + + if len(list_ld_incomlete) != 0: + [list_x_y_ld_incomplete.append(i) for i in list_ld_incomlete] + + x_y_list_named_pt_i = [ + [listeld[i] for listeld in list_x_y_ld] + for i in range(len(intersection_ld_list)) + ] + + liste_x_ld = [[element[0] for element in x_y_list_named_pt_ii] + for x_y_list_named_pt_ii in x_y_list_named_pt_i] + + liste_y_ld = [[element[1] for element in x_y_list_named_pt_ii] + for x_y_list_named_pt_ii in x_y_list_named_pt_i] + + x_y_ld = [np.array([liste_x_ld[i], liste_y_ld[i]]) for i in range(len(x_y_list_named_pt_i))] + + if len(diff_union_instersect_ld) != 0: + x_y_list_named_pt_i_incomplete = [ + list(v) for _, v in + groupby(sorted(list_x_y_ld_incomplete, key=itemgetter(2)), + key=itemgetter(2)) + ] + + liste_x_ld_incomplete = [[element[0] for element in x_y_list_named_pt_ii] for x_y_list_named_pt_ii in + x_y_list_named_pt_i_incomplete] + liste_y_ld_incomplete = [[element[1] for element in x_y_list_named_pt_ii] for x_y_list_named_pt_ii in + x_y_list_named_pt_i_incomplete] + x_y_ld_incomplete = [np.array([liste_x_ld_incomplete[i], liste_y_ld_incomplete[i]]) for i in + range(len(x_y_list_named_pt_i_incomplete))] + else: + x_y_ld_incomplete = [] + return x_y_ld, x_y_ld_incomplete + else: + print("TODO") + return [] + + def get_pk_z_named_points(self): + list_pk_z_ld = [] + + for row in range(self.rowCount()): + df = self._data[row][1] # .iloc[row, 3] + col_name = df.columns[3] # colonne (Name) des lignes directrices + col_z = df.columns[2] + list_name = df[col_name].to_list() # liste de noms de lignes directrices + list_z = df[col_z].to_list() + Pk_i = float(self._data[row][0][4]) # le ième Pk + list_ld = [ + [Pk_i, list_z[list_name.index(name)], name] + for name in self.get_all_maned_points() + ] + + if len(list_ld) > 0: + list_pk_z_ld.append(list_ld) + + mon_list = [ + [listeld[i] for listeld in list_pk_z_ld] + for i in range(len(self.get_all_maned_points())) + ] + + liste_pk_ld = [ + [element[0] for element in mon_list[i]] + for i in range(len(mon_list)) + ] + liste_z_ld = [ + [element[1] for element in mon_list[i]] + for i in range(len(mon_list)) + ] + pk_z_ld = [ + np.array([liste_pk_ld[i], liste_z_ld[i]]) + for i in range(len(mon_list)) + ] + + return pk_z_ld + + def get_pk_z_ld(self): + """ + Returns: liste de [Pk, z, ld] par profil + liste de listes contenant les z des points nommés (lignes directrices) par profil + """ + + list_pk_z_ld = [] + list_pk_z_ld_incomplete = [] + + start = time.time() + liste = [data[1].iloc[:, 3].tolist() for data in self._data] + + intersection_ld = reduce(np.intersect1d, liste) + intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""] + disjoint_union_ld = reduce(np.union1d, liste) + disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""] + diff_union_instersect_ld = list(set(disjoint_union_ld_list).symmetric_difference(set(intersection_ld_list))) + + for row in range(self.rowCount()): + df = self._data[row][1] # .iloc[row, 3] + col_name = df.columns[3] # colonne (Name) des lignes directrices + col_z = df.columns[2] + list_name = df[col_name].to_list() # liste de noms de lignes directrices + list_z = df[col_z].to_list() + Pk_i = float(self._data[row][0][4]) # le ième Pk + + list_ld = [[Pk_i, list_z[list_name.index(name)], name] for name in + intersection_ld_list] # disjoint_union_ld_list] + + if len(diff_union_instersect_ld) != 0: + list_ld_incomlete = [[Pk_i, list_z[list_name.index(name)], name] for name in diff_union_instersect_ld if + name in list_name] + list_pk_z_ld_incomplete.append(list_ld_incomlete) + + if len(list_ld) > 0: + list_pk_z_ld.append(list_ld) + + my_list = [] + ll = [] + + for listeld in list_pk_z_ld: + ll.append(listeld[1]) + + ll0 = [listeld[1] for listeld in list_pk_z_ld] + mon_list = [[listeld[i] for listeld in list_pk_z_ld] for i in range(len(intersection_ld_list))] + liste_z_ld = [[el[1] for el in mon_list[i]] for i in range(len(mon_list))] + liste_pk_ld = [[el[0] for el in mon_list[i]] for i in range(len(mon_list))] + pk_z_ld = [np.array([liste_pk_ld[i], liste_z_ld[i]]) for i in range(len(mon_list))] + + for j in range(len(list_pk_z_ld[0])): + lst = [el[j][1] for el in list_pk_z_ld] + my_list.append(lst) + + return my_list # liste de listes contenant les z des points nommés (lignes directrices) par profil + + def model_data(self): + return self._data + + +class Delegate(QtWidgets.QStyledItemDelegate): + # Lorsque l'on souhaite uniquement personnaliser l'édition des éléments dans une vue et non le rendu, + # on doit redéfinir quatre méthodes + def __init__(self, parent=None, setModelDataEvent=None): + super(Delegate, self).__init__(parent) + self.setModelDataEvent = setModelDataEvent + + def createEditor(self, parent, option, index): + """retourne le widget (éditeur) pour éditer l'item se trouvant à l'index index.""" + index.model().data(index, Qt.DisplayRole) + return QtWidgets.QLineEdit(parent) + + def setEditorData(self, editor, index): + """permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant à l'index + 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 + print('Donnée éditée dans la case [{},{}] :'.format(index.row(), index.column()), value) + + def setModelData(self, editor, model, index): + """permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index identifié + par le paramètre index """ + model.setData(index, editor.text()) + if not self.setModelDataEvent is None: + self.setModelDataEvent() + + def updateEditorGeometry(self, editor, option, index): + """permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change""" + editor.setGeometry(option.rect) + + +class Delegate11(QtWidgets.QItemDelegate): + def __init__(self, owner, choices): + super().__init__(owner) + self.items = choices + + def createEditor(self, parent, option, index): + self.editor = QtWidgets.QComboBox(parent) + self.editor.currentIndexChanged.connect(self.commit_editor) + self.editor.addItems(self.items) + return self.editor + + def paint(self, painter, option, index): + value = index.data(Qt.DisplayRole) + style = QtWidgets.QApplication.style() + opt = QtWidgets.QStyleOptionComboBox() + opt.text = str(value) + opt.rect = option.rect + style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter) + QtWidgets.QItemDelegate.paint(self, painter, option, index) + + + def commit_editor(self): + editor = self.sender() + color = QtGui.QColor() + if editor.currentText() == "ProfilXYZ": + color = QtGui.QColor("lightgreen") + elif editor.currentText() == "ProfilAC": + color = QtGui.QColor("red") + qss = """QComboBox{background-color : %s;}""" % (color.name(),) + editor.setStyleSheet(qss) + self.commitData.emit(editor) + + def setEditorData(self, editor, index): + editor.blockSignals(True) + editor.setCurrentIndex(index.row()) # replacement + editor.blockSignals(False) + + @pyqtSlot() + def currentIndexChanged(self): + self.commit_editor.emit(self.sender()) + + def setModelData(self, editor, model, index): + value = editor.currentText() + model.setData(index, Qt.DisplayRole, QVariant(value)) + + 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 commit_editor(self): + editor = self.sender() + color = QtGui.QColor() + if editor.currentText() == "ProfilXYZ": + color = QtGui.QColor("lightgreen") + elif editor.currentText() == "ProfilAC": + color = QtGui.QColor("red") + qss = """QComboBox{background-color : %s;}""" % (color.name(),) + editor.setStyleSheet(qss) + self.commitData.emit(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()) diff --git a/src/View/GeometryWindow.py b/src/View/GeometryWindow.py new file mode 100644 index 0000000000000000000000000000000000000000..ed58635d6c51c5742186f3c0cc876445e6347941 --- /dev/null +++ b/src/View/GeometryWindow.py @@ -0,0 +1,934 @@ +import os +import pathlib +import sys +import csv +import time + +from PyQt5 import QtWidgets +from PyQt5.QtCore import ( + QModelIndex, Qt, QSettings, pyqtSlot, + QItemSelectionModel, QCoreApplication, QSize +) +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QFileDialog, QCheckBox +) + +from View.Geometry.mainwindow_ui_reach import Ui_MainWindow +from View.Geometry import qtableview_reach +from View.Geometry import window_profileXYZ + +_translate = QCoreApplication.translate + +class GeomatryWindow(ASubWindow): + def __init__(self, parent=None): + self.parent = parent + super(MainReach, self).__init__() + + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + self.tableView = self.ui.tableView + self.tableView_header = self.ui.tableView_header + self.model = None + self.filename = None + self.setup_model() + self.setup_connections() + + def setup_connections(self): + self.ui.btn_open.clicked.connect(self.open_file_dialog) + self.ui.btn_sort_asc.clicked.connect(self.sort_ascending) + self.ui.btn_sort_desc.clicked.connect(self.sort_descending) + self.ui.btn_move_up.clicked.connect(self.move_row_up) + self.ui.btn_move_down.clicked.connect(self.move_row_down) + self.ui.btn_end_editing.clicked.connect(self.handleSave) + self.ui.btn_add.clicked.connect(self.insert_row) + self.ui.btn_delete.clicked.connect(self.delete_row) + self.ui.btn_edit.clicked.connect(self.edit_profile) + self.ui.verticalSlider.valueChanged.connect(self.changed_slider_value) + self.tableView.selectionModel().selectionChanged.connect(self.changed_slider_value) + self.ui.btn_slider_up.clicked.connect(self.decrement_value_slider) + self.ui.btn_slider_down.clicked.connect(self.increment_value_slider) + self.ui.btn_move_up.clicked.connect(self.changed_profile_slot) + + def setup_model(self): + self.model = qtableview_reach.PandasModelEditableCreateReach([], self.ui.tableView_header) + self.tableView.setModel(self.model) + self.tableView.setItemDelegate(qtableview_reach.Delegate()) + + def open_file_dialog(self): + options = QFileDialog.Options() + DEFAULT_DIRECTORY = '/home/' + settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', ) # application='MyApp', ) + current_dir = settings.value('current_directory', DEFAULT_DIRECTORY, type=str) + options |= QFileDialog.DontUseNativeDialog + self.filename, _ = QtWidgets.QFileDialog.getOpenFileName( + self, + _translate("MainWindow_reach", "Ouvrir un fichier"), + current_dir, + _translate("MainWindow_reach", + "Fichiers .ST (*.ST)") + ";;" + + _translate("MainWindow_reach", + "Fichiers textes (*.txt)") + ";; " + + _translate("MainWindow_reach", + "Tous les fichiers (*)"), + options=options + ) + + current_dir = os.path.split(self.filename)[0] or DEFAULT_DIRECTORY + settings.setValue('current_directory', current_dir) + if self.filename != "": + size = os.stat(self.filename).st_size + + self.bief_name = pathlib.Path(self.filename).stem + self.setWindowTitle(f"{self.ui.mainwindow_title} {self.bief_name}") + self.model = qtableview_reach.PandasModelEditable(self.filename, self.tableView_header) + self.tableView.setModel(self.model) + self.update_text_label() + + self.update_profile_windows() + self.graphic_1() + self.graphic_2() + self.graphic_3() + self.fichier_ouvert = True + + # Profile selection when line change in table + self.tableView.selectionModel().selectionChanged.connect( + self.select_current_profile + ) + + # Update plot when profile data change + self.model.dataChanged.connect(self.update_graphic_1) + self.model.dataChanged.connect(self.update_graphic_2) + self.tableView.selectionModel().selectionChanged.connect(self.update_graphic_1) + self.tableView.selectionModel().selectionChanged.connect(self.update_graphic_2) + + + def update_text_label(self): + """ + Returns: Cette méthode met à jour le texte (en haut à gauche) indiquant le nombre de profils et + l'ordre des sections. + """ + if self.filename: + if self.model.rowCount() > 1: + self.ui.label_section_order.setText( + f"{self.model.rowCount()}" + " " + + _translate("MainWindow_reach", "profils") + + f"{self.ui.text_section_order}" + ) + else: + self.ui.label_section_order.setText( + f"{self.model.rowCount()}" + " " + + _translate("MainWindow_reach", "profil") + + f"{self.ui.text_section_order}" + ) + + def messagebox_profile_editing(self): + msg_box = QtWidgets.QMessageBox() + msg_box.setIcon(QtWidgets.QMessageBox.Information) + msg_box.setWindowTitle(_translate("MainWindow_reach", + "Édition des profils sélectionnés")) + msg_box.setText(_translate("MainWindow_reach", + "Vous avez sélectionné plus de 5 profils." + " \nSeuls les 5 premiers seront édités.")) + msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) + + return_value = msg_box.exec() + if return_value == QtWidgets.QMessageBox.Ok: + print('OK clicked') + + def edit_profile(self): + list_selected_row = list( + set([index.row() for index in self.tableView.selectedIndexes()]) + ) + + self.tableView.model().blockSignals(True) + + if len(list_selected_row) > 5: + self.messagebox_profile_editing() + + for selected_row in list_selected_row[:5]: + selected_row = int(selected_row) + profile_identifier = self.model.get_profile_selected_identifier(selected_row) + Pk = self.model.get_pk_i(selected_row) + profile_name = self.model.get_profile_name(selected_row) + + if len(self.list_second_window) == 0: + self.second_window = window_profileXYZ.View( + selected_row + 1, + self.model.get_profile_via_identifier(profile_identifier), + pk=Pk, profile_name="", parent=self + ) + self.second_window.window_title( + pk=Pk, + profile_name=profile_name, + profile_selected_num=selected_row + ) + self.second_window.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) + self.list_second_window.append(self.second_window) + self.second_window.show() + self.list_row.append(profile_identifier) + + else: + if profile_identifier in self.list_row: + self.list_second_window[self.list_row.index(profile_identifier)]\ + .window_title( + pk=Pk, profile_name=profile_name, + profile_selected_num=selected_row + ) + + self.list_second_window[ + self.list_row.index(profile_identifier) + ].setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) + + self.list_second_window[ + self.list_row.index(profile_identifier) + ].show() + else: + second_window1 = window_profileXYZ.View( + selected_row + 1, + self.model.get_profile_via_identifier(profile_identifier), + pk=Pk, profile_name="", parent=self + ) + second_window1.window_title( + pk=Pk, profile_name=profile_name, + profile_selected_num=selected_row + ) + + second_window1.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) + second_window1.show() + self.list_row.append(profile_identifier) + self.list_second_window.append(second_window1) + + widgetList = QApplication.topLevelWidgets() + numWindows = len(widgetList) + self.tableView.model().blockSignals(False) + + def wind_profile_changed(self): + self.second_window\ + .datachanged_signal[bool]\ + .connect(self.changed_profile_slot) + + pyqtSlot(bool) + + def changed_profile_slot(self, status): + print("status du changement ", status) + self.update_view1 = status + print(self.update_view1) + + def update_graphic_1_profile(self): + if self.update_view1: + self.update_graphic_1() + print("update_graphic_1_profile") + + def print_data(self): + print(self.model_data.profile) + + def update_profile_windows(self): + self.list_second_window = [] + self.list_row = [] + + def graphic_1(self): + self.ui.canvas_1.axes.cla() + self.ui.canvas_1.axes.grid(color='grey', linestyle='--', linewidth=0.5) + self.ui.canvas_1.axes.set_xlabel( + _translate("MainWindow_reach", "X (m)"), color='green', fontsize=12 + ) + self.ui.canvas_1.axes.set_ylabel( + _translate("MainWindow_reach", "Y (m)"), color='green', fontsize=12 + ) + + self.get_x = self.model.get_x() + self.get_y = self.model.get_y() + self.line_xy = [ + self.ui.canvas_1.axes.plot(x, y, color='r', lw=1., markersize=3, marker='+') + for x, y in zip(self.get_x, self.get_y) + ] + + self.get_x_complete_list_ld = self.model.get_x_complete_list_ld() + self.get_y_complete_list_ld = self.model.get_y_complete_list_ld() + self.line_ld_1 = self.ui.canvas_1.axes.plot( + self.get_x_complete_list_ld, + self.get_y_complete_list_ld + ) + + self.plot_selected_1, = self.ui.canvas_1.axes.plot( + self.model.get_x_profile_i(0), + self.model.get_y_profile_i(0), lw=1., markersize=3, + marker='+', color="b" + ) + + self.plot_selected_1.set_visible(False) + self.before_plot_selected_1, = self.ui.canvas_1.axes.plot( + self.model.get_x_profile_i(0), + self.model.get_y_profile_i(0), lw=1., markersize=3, + marker='+', color="k", linestyle="--" + ) + + self.before_plot_selected_1.set_visible(False) + + self.after_plot_selected_1, = self.ui.canvas_1.axes.plot( + self.model.get_x_profile_i(0), + self.model.get_y_profile_i(0), lw=1., markersize=3, + marker='+', color="m", linestyle='--' + ) + + self.after_plot_selected_1.set_visible(False) + + self.ui.canvas_1.figure.tight_layout() + self.ui.canvas_1.figure.canvas.draw_idle() + self.ui.toolbar_1.update() + + def graphic_2(self): + self.tableView.model().blockSignals(True) + + self.ui.canvas_2.axes.cla() + self.ui.canvas_2.axes.grid(color='grey', linestyle='--', linewidth=0.5) + self.ui.canvas_2.axes.set_xlabel( + _translate("MainWindow_reach", "Pk (m)"), color='green', fontsize=12 + ) + self.ui.canvas_2.axes.set_ylabel( + _translate("MainWindow_reach", "Cote (m)"), color='green', fontsize=12 + ) + + self.get_pk = self.model.get_pk() + self.get_z_min = self.model.get_z_min() + self.get_z_max = self.model.get_z_max() + + self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines( + x=self.get_pk, + ymin=self.get_z_min, ymax=self.get_z_max, + color='r', lw=1. + ) + + self.plot_selected_2, = self.ui.canvas_2.axes.plot( + (self.get_pk[0], self.get_pk[0]), + (self.get_z_min[0], self.get_z_max[0]), + color='b', lw=1.8 + ) + self.plot_selected_2.set_visible(False) + + self.before_plot_selected_2, = self.ui.canvas_2.axes.plot( + (self.get_pk[0], self.get_pk[0]), + (self.get_z_min[0], self.get_z_max[0]), + color='k', lw=1.6, linestyle='--' + ) + self.before_plot_selected_2.set_visible(False) + + self.after_plot_selected_2, = self.ui.canvas_2.axes.plot( + (self.get_pk[0], self.get_pk[0]), + (self.get_z_min[0], self.get_z_max[0]), + color='m', lw=1.6, linestyle='--' + ) + self.after_plot_selected_2.set_visible(False) + + try: + self.line_pk_zld = [ + self.ui.canvas_2.axes.plot( + self.model.get_pk(), i, lw=1. + ) for i in self.model.z_complete_guideline() + ] + except: + print("TODO") + + self.line_pk_zmin, = self.ui.canvas_2.axes.plot( + self.get_pk, self.get_z_min, + linestyle=":", lw=1.8, + color='lightgrey' + ) + + self.tableView.model().blockSignals(False) + + self.ui.canvas_2.figure.tight_layout() + self.ui.canvas_2.figure.canvas.draw_idle() + self.ui.toolbar_2.update() + + def update_graphic_1(self): + self.tableView.model().blockSignals(True) + + for ind in range(self.model.rowCount()): + self.line_xy[ind][0].set_data( + self.model.get_x_profile_i(ind), + self.model.get_y_profile_i(ind) + ) + + for i in range(len(self.line_ld_1)): + self.line_ld_1[i].set_data( + [x[i] for x in self.get_x_complete_list_ld], + [y[i] for y in self.get_y_complete_list_ld] + ) + + self.tableView.model().blockSignals(False) + self.ui.canvas_1.figure.canvas.draw_idle() + + def update_graphic_2(self): + self.tableView.model().blockSignals(True) + + get_pk = self.model.get_pk() + get_z_min = self.model.get_z_min() + get_z_max = self.model.get_z_max() + + self.line_pk_zmin.set_data(get_pk, get_z_min) + + self.line_pk_zmin_zmax.remove() + self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines( + x=get_pk, + ymin=get_z_min, ymax=get_z_max, + color='r', lw=1. + ) + + try: + for i in range(len(self.line_pk_zld)): + self.line_pk_zld[i][0].set_data( + get_pk, self.model.z_complete_guideline()[i] + ) + except: + print("TODO") + + self.tableView.model().blockSignals(False) + self.ui.canvas_2.axes.autoscale_view(True, True, True) + self.ui.canvas_2.figure.canvas.draw_idle() + + def graphic_3(self): + self.tableView.model().blockSignals(True) + + selected_profile = 0 + station = self.model.get_station(selected_profile) # L'abscisse en travers + station_plus_1 = self.model.get_station(selected_profile + 1) + elevation = self.model.get_z_profile_i(selected_profile) + elevation_i_plus_1 = self.model.get_z_profile_i(selected_profile + 1) + ld = self.model.get_ld_profile_i(selected_profile) + + self.ui.canvas_3.axes.cla() + self.ui.canvas_3.axes.grid(color='grey', linestyle='--', linewidth=0.5) + self.ui.canvas_3.axes.set_xlabel( + _translate("MainWindow_reach", "Abscisse en travers (m)"), + color='green', fontsize=12 + ) + self.ui.canvas_3.axes.set_ylabel( + _translate("MainWindow_reach", "Cote (m)"), + color='green', fontsize=12 + ) + self.ui.canvas_3.figure.tight_layout() + + label_profile_i_minus_1 = _translate("MainWindow_reach", "Profil précédent") + label_profile_i = _translate("MainWindow_reach", "Profil sélectionné") + label_profile_i_plus_1 = _translate("MainWindow_reach", "Profil suivant") + color_profile_i_minus_1 = "k" # 'grey' + color_profile_i = 'b' + color_profile_i_plus_1 = 'm' + + self.profile_i_minus_1, = self.ui.canvas_3.axes.plot( + [], [], label=label_profile_i_minus_1, lw=1.8, + linestyle='--', color=color_profile_i_minus_1 + ) + self.profile_i, = self.ui.canvas_3.axes.plot( + station, elevation, label=label_profile_i, + color=color_profile_i, lw=1.8 + ) + + self.profile_i_plus_1, = self.ui.canvas_3.axes.plot( + station_plus_1, elevation_i_plus_1, + label=label_profile_i_plus_1, + color=color_profile_i_plus_1, lw=1.6, linestyle='--' + ) + self.annotation_3 = [] + self.complete_list_ld = self.model.get_complete_list_ld() + self.incomplete_list_ld = self.model.get_incomplete_list_ld() + + line_2d = [[line_2D] for line_2D in self.line_ld_1] + self.color_complete_ld = self.get_line_ld_colors(line_2d) + self.color_incomplete_ld = 2 * ["#000000"] + + x_ld_complete = [] + y_ld_complete = [] + color_scat_complete_ld = [] + x_ld_incomplete = [] + y_ld_incomplete = [] + color_scat_incomplete_ld = [] + + for i, txt in enumerate(list(ld)): + if txt.strip() in self.complete_list_ld: + annotation_3 = self.ui.canvas_3.axes.annotate( + txt, (station[i], elevation[i]), + horizontalalignment='left', + verticalalignment='top', annotation_clip=True, + fontsize=10, + color=self.color_complete_ld[ + self.complete_list_ld.index(txt) + ] + ) + + annotation_3.set_position((station[i] + 0., elevation[i] + 0.)) + self.annotation_3.append(annotation_3) + + x_ld_complete.append(station[i]) + y_ld_complete.append(elevation[i]) + color_scat_complete_ld.append(self.color_complete_ld[self.complete_list_ld.index(txt)]) + elif txt.strip() in self.incomplete_list_ld: + annotate = self.ui.canvas_3.axes.annotate( + txt, (station[i], elevation[i]), horizontalalignment='left', + verticalalignment='top', annotation_clip=True, fontsize=11, + color=self.color_incomplete_ld[ + self.incomplete_list_ld.index(txt) + ], + size=10 + ) + + self.annotation_3.append(annotate) + + x_ld_incomplete.append(station[i]) + y_ld_incomplete.append(elevation[i]) + color_scat_incomplete_ld.append( + self.color_incomplete_ld[self.incomplete_list_ld.index(txt)] + ) + + self.tableView.model().blockSignals(False) + + self.ui.canvas_3.axes.legend(fancybox=True, shadow=True, fontsize=8) + self.ui.canvas_3.figure.tight_layout() + + self.ui.canvas_3.figure.canvas.draw_idle() + self.ui.toolbar_3.update() + + def update_annotate_3(self, ind: int): + self.tableView.model().blockSignals(True) + + for a in self.annotation_3: + a.remove() + + self.annotation_3[:] = [] + + x = self.get_station(ind) + y = self.get_elevation(ind) + ld = self.model.get_ld_profile_i(ind) + get_complete_list_ld = self.model.get_complete_list_ld() + get_incomplete_list_ld = self.model.get_incomplete_list_ld() + + try: + x_ld_complete = [] + color_scat_complete_ld = [] + x_ld_incomplete = [] + color_scat_incomplete_ld = [] + for i, txt in enumerate(list(ld)): + if txt in get_complete_list_ld: + annotate = self.ui.canvas_3.axes.annotate( + txt, (x[i], y[i]), horizontalalignment='left', + verticalalignment='top', annotation_clip=True, + fontsize=11, + color=self.color_complete_ld[ + get_complete_list_ld.index(txt) + ], + size=10 + ) + self.annotation_3.append(annotate) + x_ld_complete.append([x[i], y[i]]) + color_scat_complete_ld.append( + self.color_complete_ld[self.complete_list_ld.index(txt)] + ) + elif txt in get_incomplete_list_ld: + annotate = self.ui.canvas_3.axes.annotate( + txt, (x[i], y[i]), horizontalalignment='left', + verticalalignment='top', annotation_clip=True, + fontsize=11, + color=self.color_incomplete_ld[ + get_incomplete_list_ld.index(txt) + ], + size=10 + ) + self.annotation_3.append(annotate) + x_ld_incomplete.append([x[i], y[i]]) + color_scat_incomplete_ld.append( + self.color_incomplete_ld[get_incomplete_list_ld.index(txt)] + ) + except: + print("FIXME") + + self.tableView.model().blockSignals(False) + self.ui.canvas_3.figure.canvas.draw_idle() + + def get_line_ld_colors(self, line_2d): + colors = [] + + for line in line_2d: + colors.append(line[0].get_color()) + + return colors + + def get_station(self, ind: int): + return self.model.get_station(ind) + + def get_elevation(self, ind: int): + return self.model.get_z_profile_i(ind) + + def update_graphic_3(self, ind: int): + self.tableView.model().blockSignals(True) + + selected_profile = ind + print(selected_profile) + + if selected_profile == 0: + self.profile_i_minus_1.set_data([], []) + self.profile_i.set_data( + self.get_station(selected_profile), + self.get_elevation(selected_profile) + ) + self.profile_i_plus_1.set_data( + self.get_station(selected_profile + 1), + self.get_elevation(selected_profile + 1) + ) + elif selected_profile == self.model.rowCount() - 1: + self.profile_i_minus_1.set_data( + self.get_station(selected_profile - 1), + self.get_elevation(selected_profile - 1) + ) + self.profile_i.set_data( + self.get_station(selected_profile), + self.get_elevation(selected_profile) + ) + self.profile_i_plus_1.set_data([], []) + elif 0 < selected_profile < self.model.rowCount() - 1: + self.profile_i_minus_1.set_data( + self.get_station(selected_profile - 1), + self.get_elevation(selected_profile - 1) + ) + self.profile_i.set_data( + self.get_station(selected_profile), + self.get_elevation(selected_profile) + ) + self.profile_i_plus_1.set_data( + self.get_station(selected_profile + 1), + self.get_elevation(selected_profile + 1) + ) + + self.tableView.model().blockSignals(False) + + self.update_annotate_3(selected_profile) + + self.ui.canvas_3.axes.relim() + self.ui.canvas_3.axes.autoscale_view() + self.ui.canvas_3.figure.canvas.draw_idle() + + def select_plot_graphic_1(self, ind: int): + self.tableView.model().blockSignals(True) + + if 0 <= ind < self.model.rowCount(): + self.plot_selected_1.set_data(self.model.get_x_profile_i(ind), + self.model.get_y_profile_i(ind)) + self.plot_selected_1.set_visible(True) + self.tableView.model().blockSignals(False) + + def select_plot_graphic_2(self, ind: int): + get_pk_i = self.get_pk_i(ind) + get_z_min_i = self.get_z_min_i(ind) + get_z_max_i = self.get_z_max_i(ind) + + if 0 <= ind < self.model.rowCount(): + self.plot_selected_2.set_data((get_pk_i, get_pk_i), + (get_z_min_i, get_z_max_i)) + self.plot_selected_2.set_visible(True) + + def select_before_plot_selected_1(self, ind: int): + if 0 <= ind < self.model.rowCount(): + t0 = time.time() + + self.before_plot_selected_1.set_data( + self.model.get_x_profile_i(ind), + self.model.get_y_profile_i(ind) + ) + + self.before_plot_selected_1.set_visible(True) + self.ui.canvas_1.figure.canvas.draw_idle() + + def select_after_plot_selected_1(self, ind: int): + if 0 <= ind < self.model.rowCount(): + self.after_plot_selected_1.set_data( + self.model.get_x_profile_i(ind), + self.model.get_y_profile_i(ind) + ) + self.after_plot_selected_1.set_visible(True) + self.ui.canvas_1.figure.canvas.draw_idle() + + def select_before_plot_selected_2(self, ind: int): + if 0 <= ind < self.model.rowCount(): + t0 = time.time() + get_pk_i = self.get_pk_i(ind) + get_z_min_i = self.get_z_min_i(ind) + get_z_max_i = self.get_z_max_i(ind) + + self.before_plot_selected_2.set_data( + (get_pk_i, get_pk_i), + (get_z_min_i, get_z_max_i) + ) + + self.before_plot_selected_2.set_visible(True) + self.ui.canvas_2.figure.canvas.draw_idle() + + def select_after_plot_selected_2(self, ind: int): + if 0 <= ind < self.model.rowCount(): + t0 = time.time() + get_pk_i = self.get_pk_i(ind) + get_z_min_i = self.get_z_min_i(ind) + get_z_max_i = self.get_z_max_i(ind) + self.after_plot_selected_2.set_data( + (get_pk_i, get_pk_i), + (get_z_min_i, get_z_max_i) + ) + self.after_plot_selected_2.set_visible(True) + self.ui.canvas_2.figure.canvas.draw_idle() + + def select_row_profile_slider(self, ind: int = 0): + if self.tableView is not None: + selectionModel = self.tableView.selectionModel() + index = self.tableView.model().index(ind, 0) + + selectionModel.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + + self.tableView.scrollTo(index) + + def select_current_profile(self): + self.tableView.model().blockSignals(True) + + if len(self.tableView.selectedIndexes()) > 0: + row = self.index_selected_row + + self.ui.verticalSlider.setValue(row) + self.select_plot_graphic_1(row) + self.select_plot_graphic_2(row) + + if row == 0: + self.before_plot_selected_1.set_visible(False) + self.select_after_plot_selected_1(row + 1) + self.before_plot_selected_2.set_visible(False) + self.select_after_plot_selected_2(row + 1) + elif 0 < row < self.model.rowCount() - 1: + self.select_before_plot_selected_1(row - 1) + self.select_after_plot_selected_1(row + 1) + self.select_before_plot_selected_2(row - 1) + self.select_after_plot_selected_2(row + 1) + elif row == self.model.rowCount() - 1: + self.after_plot_selected_1.set_visible(False) + self.select_before_plot_selected_1(row - 1) + self.after_plot_selected_2.set_visible(False) + self.select_before_plot_selected_2(row - 1) + self.tableView.model().blockSignals(False) + + self.update_graphic_3(row) + + self.ui.canvas_1.figure.canvas.draw_idle() + self.ui.canvas_2.figure.canvas.draw_idle() + + def changed_slider_value(self): + self.tableView.model().blockSignals(True) + + if self.filename is not None: + self.ui.verticalSlider.setMaximum(self.model.rowCount() - 1) + + slider_value = self.ui.verticalSlider.value() + + pk = self.model.get_pk_profile_i(slider_value) + self.ui.vertical_slider_label.setText( + _translate("MainWindow_reach", "Pk : ") + + f"{pk}" + "\n" + + _translate("MainWindow_reach", + "Profil N° : ") + + f"{slider_value + 1}" + ) + + self.select_plot_graphic_1(slider_value) + self.select_plot_graphic_2(slider_value) + self.select_row_profile_slider(slider_value) + + self.tableView.model().blockSignals(False) + + def increment_value_slider(self): + if 0 <= self.ui.verticalSlider.value() < self.model.rowCount() - 1: + self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() + 1) + + def decrement_value_slider(self): + if 0 < self.ui.verticalSlider.value() < self.model.rowCount(): + self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() - 1) + + def insert_row(self): + if len(self.tableView.selectedIndexes()) == 0: + self.model.insertRows(self.model.rowCount(), 1) + else: + row = self.index_selected_row + self.model.insertRows(row + 1, 1) + + if self.filename is not None: + self.graphic_1() + self.graphic_2() + self.select_current_profile() + + def delete_row(self): + rows = list(set( + [index.row() for index in self.tableView.selectedIndexes()] + )) + + if len(rows) > 0: + self.model.remove_rows(rows) + + self.update_graphic_1() + self.select_current_profile() + + self.graphic_2() + self.changed_slider_value() + + @property + def index_selected_row(self): + return [index.row() for index in self.tableView.selectedIndexes()][0] + + def sort_ascending(self): + self.model.sort_data(True) + self.select_current_profile() + self.changed_slider_value() + + self.update_graphic_2() + self.update_graphic_3(self.index_selected_row) + + def sort_descending(self): + self.model.sort_data(False) + + self.select_current_profile() + self.changed_slider_value() + + self.update_graphic_2() + self.update_graphic_3(self.index_selected_row) + + def move_row_down(self): + row = self.index_selected_row + + if row < self.model.rowCount() - 1: + self.model.moveRowDown(row) + self.update_graphic_3(row + 1) + self.ui.canvas_3.axes.relim() + self.ui.canvas_3.axes.autoscale_view() + + if row < self.model.rowCount() - 2: + self.select_before_plot_selected_1(row) + self.select_after_plot_selected_1(row + 2) + self.select_before_plot_selected_2(row) + self.select_after_plot_selected_2(row + 2) + + if row == self.model.rowCount() - 2: + self.select_before_plot_selected_1(self.model.rowCount() - 2) + self.after_plot_selected_1.set_visible(False) + self.select_before_plot_selected_2(self.model.rowCount() - 2) + self.after_plot_selected_2.set_visible(False) + + self.update_graphic_2() + + def move_row_up(self): + row = self.index_selected_row + + if 0 < row <= self.model.rowCount() - 1: + self.model.moveRowUp(row) + + if row == 1: + self.select_after_plot_selected_1(row) + self.before_plot_selected_1.set_visible(False) + self.select_after_plot_selected_2(row) + self.before_plot_selected_2.set_visible(False) + + elif row == self.model.rowCount() - 1: + self.select_before_plot_selected_1(row - 2) + self.select_after_plot_selected_1(row) + self.select_before_plot_selected_2(row - 2) + self.select_after_plot_selected_2(row) + + else: + self.select_before_plot_selected_1(row - 2) + self.select_after_plot_selected_1(row) + self.select_before_plot_selected_2(row - 2) + self.select_after_plot_selected_2(row) + + self.update_graphic_2() + self.update_graphic_3(row - 1) + self.ui.canvas_3.axes.relim() + self.ui.canvas_3.axes.autoscale_view() + + def handleSave(self): + options = QFileDialog.Options() + DEFAULT_DIRECTORY = '/home/' + settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', ) + current_dir = settings.value('current_directory', DEFAULT_DIRECTORY, type=str) + options |= QFileDialog.DontUseNativeDialog + + filename, self.filters = QFileDialog.getSaveFileName( + self, + filter=_translate("MainWindow_reach", + "Fichiers .ST(*.ST ou *.st)") + + ";; " + + _translate("MainWindow_reach", "Tous les fichiers " + "(*)"), + options=options + ) + + current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY + + if filename != '': + self.model.export_reach(filename) + + def handleOpen(self): + self.filename, self.filterName = QFileDialog.getOpenFileName(self) + + if self.filename != '': + with open(self.filename, 'r') as f: + reader = csv.reader(f, delimiter='\t') + header = next(reader) + buf = [] + for row in reader: + row[0] = QCheckBox("-") + buf.append(row) + + self.model = None + self.model = qtableview_reach.PandasModelEditable(buf) + self.tableView.setModel(self.model) + self.filename = '' + + def get_lignes_directrices(self): + liste_lignes_directrices = [ + data[1].iloc[:, 3].tolist() for data in self.model._data + ] + return liste_lignes_directrices + + def get_x(self): + return self.model.get_x() + + def get_y(self): + return self.model.get_y() + + def get_z(self): + return self.model.get_z() + + def get_z_min(self): + return self.model.get_z_min() + + def get_z_min_i(self, index): + return self.model.get_z_min_i(index) + + def get_z_max_i(self, index): + return self.model.get_z_max_i(index) + + def get_z_max(self): + return self.model.get_z_max() + + def get_pk(self): + return self.model.get_pk() + + def get_pk_i(self, index): + return self.model.get_pk_i(index) + + def get_pk_z_ld(self): + return self.model.get_pk_z_ld() + + def get_pk_z_ld_bis(self): + return self.model.get_pk_z_ld_bis() + + @property + def model_data(self): + return self.model.model_data() diff --git a/src/View/Plot/MplCanvas.py b/src/View/Plot/MplCanvas.py new file mode 100644 index 0000000000000000000000000000000000000000..fa4c3ffffd6a2929ac54d9d31dac1cf33d2bb480 --- /dev/null +++ b/src/View/Plot/MplCanvas.py @@ -0,0 +1,34 @@ +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg +from matplotlib.figure import Figure + + +class MplCanvas(FigureCanvasQTAgg): + + def __init__(self, width=5, height=4, dpi=100): + fig = Figure(figsize=(width, height), dpi=dpi) + super(MplCanvas, self).__init__(fig) + + self.axes = fig.add_subplot(111) + self.axes.format_coord = lambda x, y: '(x = ' + format(x, '1.4f') + ', \t' + ' y = ' + format(y, '1.4f') + ')' + self.axes.grid(color='green', linestyle='--', linewidth=0.5) + self.axes.yaxis.tick_left() + self.axes.xaxis.tick_bottom() + self.axes.spines[['top', 'right']].set_color('none') + self.figure.tight_layout() + self.add_arrows() + + def add_arrows(self): + al = 8. + arrowprops = dict( + clip_on=True, + # frac=1., + headwidth=5., + facecolor='k' + ) + kwargs = dict( + xycoords='axes fraction', + textcoords='offset points', + arrowprops=arrowprops, + ) + self.axes.annotate("", (1, 0), xytext=(-al, 0), **kwargs) + self.axes.annotate("", (0, 1), xytext=(0, -al), **kwargs) diff --git a/src/View/Plot/mpl_canvas_onpick_event.py b/src/View/Plot/mpl_canvas_onpick_event.py new file mode 100644 index 0000000000000000000000000000000000000000..6a7e3f12a865b2422dbcdc8786d8ed923a5d998c --- /dev/null +++ b/src/View/Plot/mpl_canvas_onpick_event.py @@ -0,0 +1,488 @@ +from time import time + +import numpy as np +from PyQt5 import QtWidgets +from PyQt5.QtCore import QItemSelectionModel, Qt +from PyQt5.QtWidgets import QApplication +from shapely.geometry.polygon import Polygon as ShapelyPolygon + + +class OnpickEvent(object): + def __init__(self, ax, x, y, x_carto, y_carto, tableview=None): + """ + Args: + ax: objet Axes. + x: abscisse x1 du graphe (f(x1) = y1) à tracer. + y: ordonnée y1 du graphe (f(x1) = y1) à tracer. + x_carto: (vecteur) abscisse des points (X,Y,Z) du profil. + y_carto: (vecteur) abscisse des points (X,Y,Z) du profil + tableview: tableau (de type QtableView) 'associé' au grahique. + """ + self.ax = ax + self.x = x + self.y = y + self.x_carto = x_carto + self.y_carto = y_carto + self.tableView = tableview + self.counter_onclick = 0 + # self.select_point_plot() + self.count = 0 + self.annotation_onclick = self.ax.annotate("", xytext=(np.mean(self.x), np.mean(self.y)), + xy=(np.mean(self.x), np.mean(self.y)), + horizontalalignment='center', + fontsize=8, + # fontstyle='italic', + fontweight='bold', + alpha=0.7 + + ) + self.annotation_onclick.set_visible(False) + self.pos_x = 0 + self.zomm_xmin_xmax = self.ax.get_xlim() + self.plot_selec() + # self.select_point_plot() + self._largeur_miroir, = self.ax.plot(self.x[1], self.y[1], + color='blue', lw=1.2, ls=":") + + self.pt = [] + self.tableView.selectionModel().selectionChanged.connect(self.update_select_point_point) + + def select_row_pt_clicked(self, ind: int = 0): + """ + Args: + ind: Indice de la ligne où se trouve le point le plus proche 'visé'. + Returns: Sélectionne la ligne (du tableau) correspondant au point le plus proche 'visé' à la suite de l'événement onpick. + """ + if self.tableView is not None: + selectionModel = self.tableView.selectionModel() + index = self.tableView.model().index(ind, 0) + selectionModel.select(index, + QItemSelectionModel.Rows | QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select) + self.tableView.scrollTo(index) + + def select_qtableview_row(self, event): + if self.tableView is not None: + self.tableView.setFocus() + ind = self.indice_points_onpick(event) + dataidx_ecran = self.index_pt_plus_proche_ecran(event) + self.select_row_pt_clicked(ind[dataidx_ecran]) + + def select_point_plot(self): + """ + Returns: sélectionne le(s) point(s) du graphique correspondant à la/aux ligne(s) sélectionnée(s) dans le tableau. + """ + if self.tableView is not None: + rows = list(set([index.row() for index in self.tableView.selectedIndexes()])) + for row in rows: + pass + + def update_select_point_point(self): + if self.tableView is not None: + rows = list(set([index.row() for index in self.tableView.selectedIndexes()])) + if len(rows) > 1: + for row in rows: + self.pt1 = self.ax.plot(self.x[row], self.y[row], '+', c='Blue', markersize=7) + self.pt.append(self.pt1) + self.update_select_point_point_bis(self.x[row], self.y[row]) + + elif len(rows) == 1: + for row in rows: + try: + [pl[0].set_data([], []) for pl in self.pt if len(self.pt) > 1] + except: + print("Probleme de mise à jour ... update_select_point_point()") + + try: + self.update_select_point_point_bis(self.x[row], self.y[row]) + except: + print("index introuvable pour la mise à jour de l'affichage de la sélection du point." + "Editer les cases en 'nan'.") + + self.ax.figure.canvas.draw_idle() + + def plot_selec(self): + self.point_selec, = self.ax.plot(self.x[0], self.y[0], '+', c='Blue', markersize=7) + self.point_selec.set_visible(False) + + def update_select_point_point_bis(self, x_ind, y_ind): + self.point_selec.set_data(x_ind, y_ind) + self.point_selec.set_visible(True) + self.ax.figure.canvas.draw_idle() + + def plot_selection_point(self, x, y): + """ + Args: + x: abscisse + y: ordonnée + Returns: sélectionne le point du graphique correspond à la ligne sélectionnée dans le tableau. + """ + if self.tableView is not None: + self.select_point, = self.ax.plot(x, y, '+', c='Blue', markersize=7) + else: + self.select_point, = self.ax.plot([], []) + + def geometrie_sans_rebord(self): + rebord = True + z_sans_rebord = [i for i in self.y] + x_sans_rebord = [i for i in self.x] + + while rebord: + if z_sans_rebord[1] >= z_sans_rebord[0]: + z_sans_rebord.pop(0) + x_sans_rebord.pop(0) + else: + rebord = False + + rebord = True + while rebord: + if z_sans_rebord[-1] <= z_sans_rebord[-2]: + z_sans_rebord.pop() + x_sans_rebord.pop() + else: + rebord = False + + z_berge_basse = min(z_sans_rebord[0], z_sans_rebord[-1]) + + return z_berge_basse, z_sans_rebord, x_sans_rebord + + @property + def z_berge_basse(self): + return self.geometrie_sans_rebord()[0] + + @property + def z_sans_rebord(self): + return self.geometrie_sans_rebord()[1] + + @property + def x_sans_rebord(self): + return self.geometrie_sans_rebord()[2] + + @property + def z_fond(self): + return np.array(self.z_sans_rebord) + + @property + def z_point_bas(self): + """ + Returns: la cote (Zmin) du point le plus bas. + """ + return min(self.y) + + @property + def delta_x(self): + """" + Returns: la longueur entre les limites de la vue sur l'axe des x, c'est-à -dire |x_max_visible - x_min_visible|. + """ + xgauche, xdroite = self.ax.get_xlim() + delta_x = abs(xdroite - xgauche) + return delta_x + + @property + def delta_y(self): + """ + Returns: la longueur entre les limites de la vue sur l'axe des y, c'est à dire |y_max_visible - y_min_visible|. + """ + ybas, yhaut = self.ax.get_ylim() + delta_y = abs(yhaut - ybas) + return delta_y + + @staticmethod + def indice_points_onpick(event): + """ + Args: event + Returns: le(s) indexe(s) du/des point(s) (plus précisement les coordonnées de points) capturé(s) + par l'événement onpick (voir picker) + """ + return event.ind + + def points_onpick(self, event): + """ + Args: + event: + Returns: une array contenant les coordonées des points qui se trouvent dans la zone définie par l'événement + onpick (voir picker) + + """ + thisline = event.artist + xdata = thisline.get_xdata() + ydata = thisline.get_ydata() + points_onpick = np.array([(xdata[i], ydata[i]) for i in self.indice_points_onpick(event)]) + return points_onpick + + def distance_normee(self, event): + """ + Args: + event: + Returns: la liste des distances normées (en m) entre les points situés dans la région définie par l'événement + onpick (voir picker). + """ + ind = event.ind + thisline = event.artist + xdata = thisline.get_xdata() + ydata = thisline.get_ydata() + points_onpick = np.array([(xdata[i], ydata[i]) for i in ind]) + distances_normees = [ + (((x - event.mouseevent.xdata) / self.delta_x) ** 2 + + ((y - event.mouseevent.ydata) / self.delta_y) ** 2) ** (1 / 2) + for (x, y) in points_onpick + ] + return distances_normees + + def position_souris(self, event): + """ + Args: + event: + Returns: la position de la souris + """ + self.pos_souris = [(event.mouseevent.xdata, event.mouseevent.ydata)] + return self.pos_souris + + def distance_ecran(self, event): + """ + Args: + event: + Returns: la liste des distances 'visuelles' entre les points situés dans la région définie par l'événement + onpick (voir picker). + """ + bbox = self.ax.get_window_extent()\ + .transformed(self.ax.figure.dpi_scale_trans.inverted()) + ratio_w_sur_h = bbox.width / bbox.height + distances_ecran = [ + (((x - event.mouseevent.xdata) / (self.delta_x * ratio_w_sur_h)) ** 2 + + ((y - event.mouseevent.ydata) / self.delta_y) ** 2) ** (1 / 2) + for (x, y) in self.points_onpick(event) + ] + + return distances_ecran + + def distances(self, event): + """ + Args: event: + Returns: la liste des distances entre la position de la souris et tous les points se trouvant dans + la zone définie par l'événement onpick ( voir picker) + """ + distances = np.linalg.norm( + self.points_onpick(event) - self.position_souris(event), + axis=1 + ) + + return distances + + def index_pt_plus_proche_ecran(self, event): + """ + Args: + event: + Returns: indice du point le plus proche visuellement de la position du click. + """ + dataidx_ecran = np.argmin(self.distance_ecran(event)) + return dataidx_ecran + + def point_plus_proche_ecran(self, event): + point_onpick = self.points_onpick(event) + datapos_ecran = point_onpick[self.index_pt_plus_proche_ecran(event)] + + return self.points_onpick(event)[self.index_pt_plus_proche_ecran(event)] + + def index_pt_plus_proche(self, event): + """ + Args: + event: + Returns: indice du point le plus proche de la position du click. + """ + dataidx = np.argmin(self.distances(event)) + + return dataidx + + def point_plus_proche(self, event): + """ + Args: + event: + Returns: point le plus proche de la position du click + """ + point_onpick = self.points_onpick(event) + datapos = point_onpick[self.index_pt_plus_proche(event)] + + return datapos + + def annotate_onpick(self, x, y): + """ + Args: + x: abscisse du point à annoter. + y: ordonnée du point à annoter. + Returns: annote le point xy avec du texte text = xytext. + """ + return self.ax.annotate( + "X", xytext=(x, y), + xy=(x, y), fontsize=9, + bbox=dict( + boxstyle='round,pad=0.8', fc='yellow', alpha=0.75 + ), + arrowprops=dict( + arrowstyle='->', + connectionstyle='arc3,rad=0.', + color='blue' + ) + ) + + def on_ylims_change(self, event_ax): + return event_ax.get_ylim() + + def annotate_onclick(self, event): + if self.z_point_bas <= event.ydata: + self.count += 1 + + if event.ydata <= self.z_berge_basse: + A, p, L = self.calcul_ligne_eau(event.ydata) + else: + event.ydata = self.z_berge_basse + A, p, L = self.calcul_ligne_eau(event.ydata) + + etiq = f"Z = {event.ydata:.3f} m, A = {A:.3f} m\u00B2, p = {p:.3f} m, L = {L:.3f} m" + self.annotation_onclick.set_text(etiq) + x_min, x_max = self.ax.get_xlim() + self.pos_x_annotation = x_min + ((x_max - x_min) / 2) + + percent = 0 + y_ecran_lim = ((max(self.ax.set_ylim()) - min(self.ax.set_ylim())) / 2) + + if abs(y_ecran_lim) > 4: + percent = 0.05 + elif 4 < abs(y_ecran_lim) < 1.5: + percent = 0.01 + elif 0.5 < abs(y_ecran_lim) < 1.5: + percent = 0.05 + elif 0.25 < abs(y_ecran_lim) < 0.5: + percent = 0.08 + elif 0 < abs(y_ecran_lim) < 0.25: + percent = 0.25 + else: + percent = 0.1 + + cte = 0. + if abs(event.ydata) < 100: + cte = 0.05 + else: + cte = event.y * 0.1 / 100 + + self.y_pos_text_param_hydrau = event.ydata + cte + self.annotation_onclick.set_position((self.pos_x_annotation, self.y_pos_text_param_hydrau)) + + self.ax.callbacks.connect('ylim_changed', self.on_ylims_change) + + self.annotation_onclick.set_color("DarkBlue") + self.annotation_onclick.set_visible(True) + self.annotation_onclick.set_horizontalalignment('center') + + self.ax.figure.canvas.draw_idle() + + return self.annotation_onclick + + def largeur_au_miroir(self, event): + if event.ydata <= self.z_berge_basse: + self._largeur_miroir.set_data( + [min(self.x), max(self.x)], + [event.ydata, event.ydata] + ) + else: + self._largeur_miroir.set_data( + [min(self.x), max(self.x)], + [self.z_berge_basse, self.z_berge_basse] + ) + + return self._largeur_miroir + + def onpick(self, event): + modifiers = QApplication.keyboardModifiers() + + if modifiers == Qt.ControlModifier: + if event.mouseevent.inaxes == self.ax: + self.select_qtableview_row(event) + x_proche, y_proche = self.point_plus_proche_ecran(event) + self.update_select_point_point_bis(x_proche, y_proche) + + self.ax.figure.canvas.draw_idle() + + def onclick(self, event): + modifiers = QtWidgets.QApplication.keyboardModifiers() + if modifiers == Qt.ShiftModifier: + if event.inaxes == self.ax: + if self.z_point_bas < event.ydata: + try: + self.poly_col_bis.remove() + self.largeur_au_miroir(event) + except: + self.largeur_au_miroir(event) + + self.annotate_onclick(event) + self.ax.figure.canvas.draw_idle() + + def remplir_zone_mouillee(self, x, y1, y2): + """ + Args: + x: Les coordonnées x des nœuds définissant la courbe. + y1: points définisant le polygone à déssiner. + y2: points définisant le polygone à déssiner. + Returns: dessine et colorie la région définie par le polygone. + """ + return self.ax.fill_between( + x, y1=y1, y2=y2, where=y1 > y2, interpolate=True, + facecolor='skyblue', alpha=0.7 + ) + + def calcul_ligne_eau(self, val: float) -> (float, float, float): + """ + Args: + val: Valeur de la cote Z à laquelle on veut caluler A , p et L. + Returns: la valeur de la section mouillée A, du périmètre mouillé p et de la largeur au miroir L. + """ + largeur_miroir = 0. + section_mouillee_totale = 0. + perimetre_mouille_total = 0. + + if self.z_point_bas < val <= self.z_berge_basse: + z_eau = np.array([val] * (len(self.z_sans_rebord))) + self.poly_col_bis = self.remplir_zone_mouillee(self.x_sans_rebord, z_eau, self.z_sans_rebord) + liste_chemins = self.poly_col_bis.get_paths() + couleurs = ['crimson', 'pink'] * len(liste_chemins) + aire_calculee_shapely = None + perimetre_mouille_total_shapely = None + perimetre_shapely = 0. + perim_calc = 0. + + for polyg, coul in zip(liste_chemins, couleurs[0:len(liste_chemins)]): + points_polygone = polyg.vertices + xs = points_polygone[:, 0] + ys = points_polygone[:, 1] + + liste_points_miroir = [ + x for (x, y) in zip(xs, ys) if np.isclose(y, val) + ] + largeur_miroir_polygone = liste_points_miroir[-2] - liste_points_miroir[0] + largeur_miroir += largeur_miroir_polygone + + polygone_shapely = ShapelyPolygon(points_polygone) + aire_calculee_shapely = polygone_shapely.area + perimetre_shapely = polygone_shapely.length + perimetre_mouille_total_shapely = polygone_shapely.length - largeur_miroir + liste_points_fond = [ + (x, y) for (x, y) in zip(xs, ys) if not np.isclose(y, val) + ] + x_pt_prec, y_pt_prec = max(liste_points_miroir), val + + perimetre = 0 + aire = 0 + + for un_point in liste_points_fond + [(min(liste_points_miroir), val)]: + x_pt_suivant, y_pt_suivant = un_point + perimetre += ((x_pt_prec - x_pt_suivant) ** 2 + + (y_pt_prec - y_pt_suivant) ** 2) ** (1 / 2) + aire += (((val - y_pt_prec) + (val - y_pt_suivant)) * + abs(x_pt_suivant - x_pt_prec) / 2) + x_pt_prec, y_pt_prec = x_pt_suivant, y_pt_suivant + perim_calc = perimetre + + perimetre_mouille_total = perimetre_shapely - largeur_miroir + section_mouillee_totale = aire_calculee_shapely + + return section_mouillee_totale, perimetre_mouille_total, largeur_miroir diff --git a/src/View/Plot/navigation_toolbar_2qt.py b/src/View/Plot/navigation_toolbar_2qt.py new file mode 100644 index 0000000000000000000000000000000000000000..44d86f72633b7e66e4933e85607396dd2493f36f --- /dev/null +++ b/src/View/Plot/navigation_toolbar_2qt.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- + +import os +import matplotlib as mpl +from matplotlib.backends import qt_compat +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QAction +from matplotlib.backends.backend_qt5 import NavigationToolbar2QT +from PyQt5.QtCore import pyqtSignal, QSize + +_translate = QtCore.QCoreApplication.translate + +file_path = os.path.abspath(os.path.dirname(__file__)) + + +class PamHyrNavigationToolbar2QT(NavigationToolbar2QT): + """ + Cette classe est une personnalisation de la toolbar (NavigationToolbar2QT). Elle permet l'internationnalisation et + l'ajout d'autres boutons à la toolbar initiale + """ + isometric_signal = pyqtSignal(str) + + def __init__(self, canvas, parent): + """ + Args: + canvas: canvas de matplotlib + parent: parent du canvas + """ + self.my_canvas = canvas + self.toolitems = [ + (None, None, None, None), + + ('Home', _translate("Toolbar", 'Vue originale'), 'home', 'home'), + (None, None, None, None), + + ('Back', _translate("Toolbar", 'Retour à la vue précédente'), 'back', 'back'), + ('Forward', _translate("Toolbar", 'Passer à la vue suivante'), 'forward', 'forward'), + (None, None, None, None), + + ('Pan', _translate( + "Toolbar", + 'Panoramique des axes avec la souris gauche, zoom avec la droite' + ), 'move', 'pan'), + (None, None, None, None), + + ('Zoom', _translate("Toolbar", 'Zoom'), 'zoom_to_rect', 'zoom'), + (None, None, None, None), + + ('Isometric_view', _translate( + "Toolbar", 'Vue isométrique (Shift+W)' + ), '', 'isometric_view'), + (None, None, None, None), + + ('GlobalView', _translate( + "Toolbar", 'Vue globale automatique (Shift+X)' + ), '', 'non_isometric_view'), + + (None, None, None, None), + + ('Save', _translate( + "Toolbar", 'Enregistrer la figure' + ), 'filesave', 'save_figure'), + (None, None, None, None), + ] + + NavigationToolbar2QT.__init__(self, canvas, parent) + btn_size = QSize(30, 30) + + icon_zoom = QtGui.QIcon() + icon_zoom.addPixmap(QtGui.QPixmap( + f"{os.path.dirname(os.path.dirname(file_path))}/resources/icons/zoom.png" + )) + icon_btn_isometric_view = QtGui.QIcon() + icon_btn_isometric_view.addPixmap( + QtGui.QPixmap(f"{os.path.dirname(os.path.dirname(file_path))}/resources" + f"/icons/zoom_fit_11.png")) + + icon_btn_global_view = QtGui.QIcon() + icon_btn_global_view.addPixmap( + QtGui.QPixmap(f"{os.path.dirname(os.path.dirname(file_path))}/resources/icons" + f"/zoom_fit.png")) + + actions = self.findChildren(QAction) + self._actions["zoom"].setIcon(icon_zoom) + self._actions["isometric_view"].setIcon(icon_btn_isometric_view) + self._actions["non_isometric_view"].setIcon(icon_btn_global_view) + + self.addSeparator() + self.set_style_sheet() + + def save_figure(self, *args): + filetypes = self.canvas.get_supported_filetypes_grouped() + sorted_filetypes = sorted(filetypes.items()) + default_filetype = self.canvas.get_default_filetype() + + startpath = os.path.expanduser(mpl.rcParams['savefig.directory']) + start = os.path.join(startpath, self.canvas.get_default_filename()) + filters = [] + selectedFilter = None + for name, exts in sorted_filetypes: + exts_list = " ".join(['*.%s' % ext for ext in exts]) + filter = '%s (%s)' % (name, exts_list) + if default_filetype in exts: + selectedFilter = filter + filters.append(filter) + filters = ';;'.join(filters) + + fname, filter = qt_compat._getSaveFileName( + self.canvas.parent(), + _translate("MainWindow_reach", "Choisissez un nom de fichier à sauvegarder"), + start, + filters, selectedFilter) + + if fname: + if startpath != "": + mpl.rcParams['savefig.directory'] = os.path.dirname(fname) + + try: + self.canvas.figure.savefig(fname) + except Exception as e: + QtWidgets.QMessageBox.critical( + self, "Error saving file", str(e), + QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton + ) + + def cursor1(self): + print("TODO") + + def isometric_view(self): + self.my_canvas.axes.axis("equal") + self.my_canvas.figure.canvas.draw_idle() + self.isometric_signal[str].emit("vue iso") + + def non_isometric_view(self): + self.my_canvas.axes.axis("tight") + self.my_canvas.toolbar.update() + self.my_canvas.figure.canvas.draw_idle() + + def toolitems_translation(self): + self._actions['home'].setToolTip(_translate("Toolbar", "Vue originale")) + self._actions['back'].setToolTip(_translate("Toolbar", "Retour à la vue précédente")) + self._actions['forward'].setToolTip(_translate("Toolbar", "Passer à la vue suivante")) + self._actions['pan'].setToolTip(_translate("Toolbar", "Panoramique des axes avec la " + "souris gauche, zoom avec la droite")) + self._actions['zoom'].setToolTip(_translate("Toolbar", "Zoom")) + self._actions['save_figure'].setToolTip(_translate("Toolbar", "Enregistrer la figure")) + self.action_isometric_view.setToolTip(_translate("Toolbar", "Vue isométrique (Shift+W)")) + self.action_auto_global_view.setToolTip(_translate("Toolbar", "Vue globale automatique (Shift+X)")) + + def set_style_sheet(self): + self.setStyleSheet( + "QToolBar{ border: 1px solid darkGray;\n" + " gridline-color: blue;\n" + " border-radius: 4px;\n" + " border-style: solid;\n" + " background-color: #EEF6FC; \n" + " selection-background-color: #218ede;\n" + " font-size: 12px;\n" + " font-family: Helvetica\n" + "\n" + " }" + )