diff --git a/src/View/Geometry/GeometryWindow.py b/src/View/Geometry/GeometryWindow.py index dbda85c22d02dffbf742a9f8c5005edf294befbf..c3325acc132b045463b1943d53207b0c35d90089 100644 --- a/src/View/Geometry/GeometryWindow.py +++ b/src/View/Geometry/GeometryWindow.py @@ -158,16 +158,23 @@ class GeometryWindow(QMainWindow, WindowToolKit): def edit_profile(self): self.tableView.model().blockSignals(True) - for index in self.tableView.selectedIndexes(): - profile = self._reach.profile(index.row()) + rows = list( + set( + (i.row() for i in self.tableView.selectedIndexes()) + ) + ) - self._profile_window.append( - ProfileWindow( - profile = profile, - parent = self, - ) + for row in rows: + profile = self._reach.profile(row) + + win = ProfileWindow( + profile = profile, + parent = self, ) + self._profile_window.append(win) + win.show() + self.tableView.model().blockSignals(False) pyqtSlot(bool) diff --git a/src/View/Geometry/Profile/Plot.py b/src/View/Geometry/Profile/Plot.py new file mode 100644 index 0000000000000000000000000000000000000000..6fc5b40aefa577b853df266195663adea1648a08 --- /dev/null +++ b/src/View/Geometry/Profile/Plot.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- + +from tools import timer, trace +from View.Plot.APlot import APlot +from View.Plot.mpl_canvas_onpick_event import OnpickEvent + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +class Plot(APlot): + def __init__(self, canvas=None, data=None, toolbar=None, table=None): + super(Plot, self).__init__( + canvas=canvas, + data=data, + toolbar=toolbar + ) + + self._table = table + + self.line_xy = [] + self.line_gl = [] + + self.before_plot_selected = None + self.plot_selected = None + self.after_plot_selected = None + + @timer + def draw(self): + x = self.data.get_station() + y = self.data.z() + gl = self.data.name + x_carto = self.data.x() + y_carto = self.data.y() + + self.canvas.axes.cla() + self.canvas.axes.grid( + color='grey', linestyle='--', linewidth=0.5 + ) + + if (len(x_carto) >= 3 and + len(y_carto) >= 3 and + len(x) >= 3): + self.profile_line2D, = self.canvas.axes.plot( + x, y, color='r', lw=1.5, + markersize=7, marker='+', + picker=30 + ) + self.canvas.axes.set_xlabel( + _translate("MainWindowProfile", + "Abscisse en travers (m)"), + color='black', fontsize=10 + ) + self.canvas.axes.set_ylabel( + _translate("MainWindowProfile", "Cote (m)"), + color='black', fontsize=10 + ) + + # Add label on graph + self.annotation = [] + for i, txt in enumerate(list(gl)): + annotation = self.canvas.axes.annotate( + txt, (x[i], y[i]), + horizontalalignment='left', + verticalalignment='top', + annotation_clip=True, + fontsize=10, color='black' + ) + annotation.set_position((x[i], y[i])) + annotation.set_color("black") + self.annotation.append(annotation) + + al = 8. + arrowprops = dict( + clip_on=True, + headwidth=5., + facecolor='k' + ) + kwargs = dict( + xycoords='axes fraction', + textcoords='offset points', + arrowprops=arrowprops, + ) + + self.canvas.axes.annotate("", (1, 0), xytext=(-al, 0), **kwargs) + self.canvas.axes.annotate("", (0, 1), xytext=(0, -al), **kwargs) + + self.canvas.axes.spines[['top', 'right']].set_color('none') + self.canvas.axes.yaxis.tick_left() + self.canvas.axes.xaxis.tick_bottom() + self.canvas.axes.set_facecolor('#F9F9F9') + self.canvas.figure.patch.set_facecolor('white') + + self.onpick_event = OnpickEvent( + self.canvas.axes, + x, y, x_carto, y_carto, + self._table + ) + self.canvas.figure.canvas\ + .mpl_connect( + 'pick_event', + self.onpick_event.onpick + ) + + self.onclick_event = OnpickEvent( + self.canvas.axes, + x, y, x_carto, y_carto, + self._table + ) + self.canvas.figure.canvas\ + .mpl_connect( + 'button_press_event', + self.onclick_event.onclick + ) + + self.canvas.figure.tight_layout() + self.canvas.figure.canvas.draw_idle() + + @timer + def update(self, ind=None): + print("TODO: implemente update") diff --git a/src/View/Geometry/Profile/ProfileWindow.py b/src/View/Geometry/Profile/ProfileWindow.py index c9bfeee3d492bc3c5f7e8088dd5bfa01e38e6c79..a7268790f638c1b7dffc966c0bf0761ad8789286 100644 --- a/src/View/Geometry/Profile/ProfileWindow.py +++ b/src/View/Geometry/Profile/ProfileWindow.py @@ -13,12 +13,12 @@ from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QCheckBox from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow +from View.Geometry.Profile.Plot import Plot + from Model.Geometry.Reach import Reach from Model.Geometry.ProfileXYZ import ProfileXYZ from View.Geometry.Profile.qtableview_profile import * -from View.Plot.mpl_canvas_onpick_event import OnpickEvent - _translate = QCoreApplication.translate @@ -36,40 +36,37 @@ class ProfileWindow(QMainWindow): self.setup_window() self.setup_model() self.setup_connections() - self.graph() + self.plot() - self._model.dataChanged.connect(self.graph) + self._model.dataChanged.connect(self.plot) self.fileName = None self.ui.tableView.installEventFilter(self) self.status_change_tableview = False - self._model.dataChanged.connect(self.tableview_is_modified) + # self._model.dataChanged.connect(self.tableview_is_modified) self.reference_data = None - self.ui.btn_go_back.setEnabled(False) - self.ui.btn_check.setEnabled(False) - self._model.dataChanged.connect(self.set_enable_cancel_btn) - self._model.dataChanged.connect(self.set_enable_validate_changes_btn) - self.ui.btn_reset.setEnabled(False) - self._model.dataChanged.connect(self.set_enable_go_back_initial_state_btn) + # self.ui.btn_go_back.setEnabled(False) + # self.ui.btn_check.setEnabled(False) + # self._model.dataChanged.connect(self.set_enable_cancel_btn) + # self._model.dataChanged.connect(self.set_enable_validate_changes_btn) + # self.ui.btn_reset.setEnabled(False) + # self._model.dataChanged.connect(self.set_enable_go_back_initial_state_btn) def setup_window(self): + header = _translate("MainWindowProfile", "Profile") + name = self._profile.name - if self._profile.name is None or self._profile.name: + if (name is None) or (name == ""): name = _translate("MainWindowProfile", "(no name)") - header = _translate("MainWindowProfile", "Profile") - self.setWindowTitle( header + " - " + f"{self._profile.reach.name}" + " - " + f"{name} ({self._profile.kp})" ) - def tableview_is_modified(self): - self.status_change_tableview = True - def setup_connections(self): self.ui.btn_sort_asc_x.clicked.connect(self.sort_X_ascending) self.ui.btn_sort_desc_x.clicked.connect(self.sort_X_descending) @@ -80,114 +77,35 @@ class ProfileWindow(QMainWindow): self.ui.btn_export.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_copy.clicked.connect(self.copyTable) - self.ui.btn_paste.clicked.connect(self.pasteTable) - self.ui.btn_check.clicked.connect(self.validate_changes) - self.ui.btn_go_back.clicked.connect(self.cancel_validate_changes) - self.ui.btn_reset.clicked.connect(self.go_back_to_initial_state) + # self.ui.btn_copy.clicked.connect(self.copyTable) + # self.ui.btn_paste.clicked.connect(self.pasteTable) + # self.ui.btn_check.clicked.connect(self.validate_changes) + # self.ui.btn_go_back.clicked.connect(self.cancel_validate_changes) + # self.ui.btn_reset.clicked.connect(self.go_back_to_initial_state) def setup_model(self): self._model = PandasModelEditable(self._profile) - self._last_saved_model_data = copy.deepcopy(self._model.model_data) - self.__initial_model_data = copy.deepcopy(self._model.model_data) self.ui.tableView.setModel(self._model) self.ui.tableView.setItemDelegate(Delegate()) @timer - def graph(self): - """ - Returns: Le tracé de la cote z en fonction de l'abscisse (calculée). - """ - x = self._profile.get_station() - y = self._profile.z() - gl = self._profile.name() - x_carto = self._profile.x() - y_carto = self._profile.y() - - self.ui.canvas.axes.cla() - self.ui.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) - - if (len(x_carto) >= 3 and - len(y_carto) >= 3 and - len(x) >= 3): - self.profile_line2D, = self.ui.canvas.axes.plot( - x, y, color='r', lw=1.5, markersize=7, marker='+', - picker=30 - ) - self.ui.canvas.axes.set_xlabel( - _translate("MainWindowProfile", - "Abscisse en travers (m)"), - color='black', fontsize=10 - ) - self.ui.canvas.axes.set_ylabel( - _translate("MainWindowProfile", "Cote (m)"), - color='black', fontsize=10 - ) - - # Add label on graph - self.annotation = [] - for i, txt in enumerate(list(gl)): - annotation = self.ui.canvas.axes.annotate( - txt, (x[i], y[i]), - horizontalalignment='left', - verticalalignment='top', - annotation_clip=True, fontsize=10, - color='black' - ) - annotation.set_position((x[i], y[i])) - annotation.set_color("black") - self.annotation.append(annotation) - - al = 8. - arrowprops = dict( - clip_on=True, - headwidth=5., - facecolor='k' - ) - kwargs = dict( - xycoords='axes fraction', - textcoords='offset points', - arrowprops=arrowprops, - ) + def plot(self): + self.ui.tableView.model().blockSignals(True) + + self._plot = Plot( + canvas = self.ui.canvas, + data = self._profile, + toolbar = None, + table = self.ui.tableView, + ) + self._plot.draw() - self.ui.canvas.axes.annotate("", (1, 0), xytext=(-al, 0), **kwargs) - self.ui.canvas.axes.annotate("", (0, 1), xytext=(0, -al), **kwargs) + self.ui.tableView.model().blockSignals(False) - self.ui.canvas.axes.spines[['top', 'right']].set_color('none') - self.ui.canvas.axes.yaxis.tick_left() - self.ui.canvas.axes.xaxis.tick_bottom() - self.ui.canvas.axes.set_facecolor('#F9F9F9') - self.ui.canvas.figure.patch.set_facecolor('white') - - try: - self.onpick_event = OnpickEvent( - self.ui.canvas.axes, - x, y, x_carto, y_carto, - self.ui.tableView - ) - self.ui.canvas\ - .figure\ - .canvas\ - .mpl_connect('pick_event', self.onpick_event.onpick) - - self.onclick_event = OnpickEvent( - self.ui.canvas.axes, - x, y, x_carto, y_carto, - self.ui.tableView - ) - self.ui.canvas\ - .figure\ - .canvas\ - .mpl_connect('button_press_event', self.onclick_event.onclick) - except: - print("TODO") - - self.ui.canvas.figure.tight_layout() - self.ui.canvas.figure.canvas.draw_idle() def insert_row(self): - if len(self.tableView.selectedIndexes()) == 0: + if len(self.ui.tableView.selectedIndexes()) == 0: self._tablemodel.insert_row(self._tablemodel.rowCount()) else: row = self.index_selected_row() @@ -197,7 +115,7 @@ class ProfileWindow(QMainWindow): rows = sorted( list( set( - [index.row() for index in self.tableView.selectedIndexes()] + [index.row() for index in self.ui.tableView.selectedIndexes()] ) ) ) @@ -207,19 +125,19 @@ class ProfileWindow(QMainWindow): def sort_X_ascending(self): self._model.sort('x', order=Qt.AscendingOrder) - self.graph() + self.plot() def sort_X_descending(self): self._model.sort('x', order=Qt.DescendingOrder) - self.graph() + self.plot() def sort_Y_ascending(self): self._model.sort('y', order=Qt.AscendingOrder) - self.graph() + self.plot() def sort_Y_descending(self): self._model.sort('y', order=Qt.DescendingOrder) - self.graph() + self.plot() def move_row_down(self): rows = list( @@ -232,7 +150,7 @@ class ProfileWindow(QMainWindow): if row < self._model.rowCount() - 1: self._model.moveRowDown(row) - self.graph() + self.plot() def move_row_up(self): rows = list( @@ -245,103 +163,7 @@ class ProfileWindow(QMainWindow): if 0 < row: self._model.moveRowUp(row) - self.graph() - - def eventFilter(self, source, event): - if event.type() == QEvent.KeyPress: - if event == QtGui.QKeySequence.Copy: - self.copyTable() - return True - - elif event == QtGui.QKeySequence.Paste: - self.pasteTable() - return True - - elif event.type() == QEvent.ContextMenu: - menu = QtWidgets.QMenu() - copyAction = menu.addAction('Copy') - pasteAction = menu.addAction('Paste') - - if not self.ui.tableView.selectedIndexes(): - pass - - return True - - return super(View, self).eventFilter(source, event) - - def copySelection(self): - self.clipboard.clear() - selected = self.ui.tableView.selectedIndexes() - rows = [] - columns = [] - - for index in selected: - rows.append(index.row()) - columns.append(index.column()) - - minRow = min(rows) - minCol = min(columns) - print(minCol) - print(minRow, columns) - for index in selected: - self.clipboard.append( - (index.row() - minRow, - index.column() - minCol, - index.data_frame()) - ) - - def pasteSelection(self): - if not self.clipboard: - return - - current = self.ui.tableView.currentIndex() - if not current.isValid(): - # In the rare case that there is no current index, use the - # first row and column as target - current = self._model.index(0, 0) - - firstRow = current.row() - firstColumn = current.column() - - # optional: Get the selection model so that pasted indexes - # will be automatically selected at the end - selection = self.ui.tableView.selectionModel() - - for row, column, data in self.clipboard: - # Get the index, with rows and columns relative to the - # current index = self._model.index(firstRow + row, - # firstColumn + column) - index = self._model.index(firstRow, column) - - # Set the profile for the index - self._model.setData(index, data, Qt.EditRole) - # Add the index to the selection - selection.select(index, selection.Select) - - # Apply the selection model - self.ui.tableView.setSelectionModel(selection) - - def copyTable(self): - if len(self.ui.tableView.selectedIndexes()) == 0: - self._model.copyTable(0, self._model.rowCount()) - else: - rows = list(set([index.row() for index in self.ui.tableView.selectedIndexes()])) - rows.sort() - df = self._model.model_data.loc[rows, :] - df.to_clipboard(header=None, index=False, excel=True, sep='\t') - - def pasteTable(self): - if len(self.ui.tableView.selectedIndexes()) == 0: - self._model.pasteTable(self._model.rowCount()) - else: - rows = list(set([index.row() for index in self.ui.tableView.selectedIndexes()])) - for row in rows: - try: - self._model.pasteTable(row + 1) - except: - print("TODO") - - self.graph() + self.plot() def handleSave(self): if self.fileName is None or self.fileName == '': @@ -389,27 +211,6 @@ class ProfileWindow(QMainWindow): self.ui.tableView.setModel(self._model) self.fileName = '' - def cancel_validate_changes(self): - self._model.model_data = copy.deepcopy(self._last_saved_model_data) - self.graph() - self.ui.btn_go_back.setEnabled(False) - - def validate_changes(self): - self.remove_duplicates_point_names() - self.delete_empty_rows() - self._last_saved_model_data = copy.deepcopy(self._model.model_data) - self.ui.btn_check.setEnabled(False) - self.ui.btn_go_back.setEnabled(False) - self.status_change_tableview = False - self.graph() - self._model.valide_all_changes() - self.parent.update_graphic_2() - self.parent.update_graphic_1() - self.selected_row = list(set( - [index.row() for index in self.parent.tableView.selectedIndexes()] - )) - self.parent.update_graphic_3(self.selected_row[0]) - def set_enable_validate_changes_btn(self): self.ui.btn_check.setEnabled(True) @@ -451,49 +252,26 @@ class ProfileWindow(QMainWindow): if list_deleted_names: self.msg_box_check_duplication_names(list_deleted_names) - def go_back_to_initial_state(self): - - reply = QtWidgets.QMessageBox.question( - self, - _translate("MainWindowProfile", "Retour à l'état initial "), - _translate("MainWindowProfile", - "Voulez-vous vraiment annuler toutes " - "les modifications?\n\n " - "\tOui : Revenir à l'état initial\n" - "\tNon : Garder l'état actuel des " - "données du profil"), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No - ) - - if reply == QtWidgets.QMessageBox.Yes: - self._model.model_data = copy.deepcopy(self.__initial_model_data) - self._model.valide_all_changes() - self.graph() - self.ui.btn_reset.setEnabled(False) - self.ui.btn_check.setEnabled(False) - self.ui.btn_go_back.setEnabled(False) - elif reply == QtWidgets.QMessageBox.No: - pass - def closeEvent(self, event): - if self.status_change_tableview: - reply = QtWidgets.QMessageBox.question( - self, - _translate("MainWindowProfile", "Terminer l'édition du profil "), - _translate("MainWindowProfile", "Voulez-vous vraiment quitter " - "?\n Oui : Valider et quitter\n" - "Non : Annuler"), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No - ) - - if reply == QtWidgets.QMessageBox.Yes: - self.validate_changes() - self.graph() - event.accept() - else: - event.ignore() - self.ui.btn_check.setEnabled(True) - self.ui.btn_go_back.setEnabled(True) + print("TODO: Close") + # if self.status_change_tableview: + # reply = QtWidgets.QMessageBox.question( + # self, + # _translate("MainWindowProfile", "Terminer l'édition du profil "), + # _translate("MainWindowProfile", "Voulez-vous vraiment quitter " + # "?\n Oui : Valider et quitter\n" + # "Non : Annuler"), + # QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No + # ) + + # if reply == QtWidgets.QMessageBox.Yes: + # self.validate_changes() + # self.plot() + # event.accept() + # else: + # event.ignore() + # self.ui.btn_check.setEnabled(True) + # self.ui.btn_go_back.setEnabled(True) def msg_box_check_duplication_names(self, list_deleted_names): # name_point,list_deleted_names,counter_list): if len(list_deleted_names) == 1: diff --git a/src/View/Geometry/Profile/mainwindow_ui_profile.py b/src/View/Geometry/Profile/mainwindow_ui_profile.py index 2ca48f87c9f4651af8afc3c72c83e2b03d30112a..98a632e3ccd21be79363c2fb90c785364ab3e6ed 100644 --- a/src/View/Geometry/Profile/mainwindow_ui_profile.py +++ b/src/View/Geometry/Profile/mainwindow_ui_profile.py @@ -11,7 +11,7 @@ from PyQt5.QtWidgets import ( 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))}/View/ui/ressources/" +icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../View/ui/ressources/" class Ui_MainWindow(object): def setupUi(self, MainWindowProfile):