mirror of https://gitlab.com/pamhyr/pamhyr2
373 lines
12 KiB
Python
373 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import copy
|
|
import sys
|
|
import csv
|
|
from time import time
|
|
|
|
from tools import trace, timer
|
|
|
|
from PyQt5.QtGui import (
|
|
QKeySequence,
|
|
)
|
|
from PyQt5.QtCore import (
|
|
QModelIndex, Qt, QEvent, QCoreApplication
|
|
)
|
|
from PyQt5.QtWidgets import (
|
|
QApplication, QMainWindow, QFileDialog, QCheckBox,
|
|
QUndoStack, QShortcut,
|
|
)
|
|
|
|
from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow
|
|
|
|
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 *
|
|
|
|
_translate = QCoreApplication.translate
|
|
|
|
|
|
class ProfileWindow(QMainWindow):
|
|
def __init__(self, profile=None, parent=None):
|
|
self.parent = parent
|
|
super(ProfileWindow, self).__init__(self.parent)
|
|
|
|
self.ui = Ui_MainWindow()
|
|
self.ui.setupUi(self)
|
|
|
|
self._profile = profile
|
|
self._model = None
|
|
|
|
self.setup_window()
|
|
self.setup_sc()
|
|
self.setup_model()
|
|
self.setup_connections()
|
|
self.plot()
|
|
|
|
self._model.dataChanged.connect(self.update_plot)
|
|
self.fileName = None
|
|
|
|
# self.ui.tableView.installEventFilter(self)
|
|
# self._model.dataChanged.connect(self.tableview_is_modified)
|
|
|
|
# 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 (name is None) or (name == ""):
|
|
name = _translate("MainWindowProfile", "(no name)")
|
|
|
|
self.setWindowTitle(
|
|
header + " - " +
|
|
f"{self._profile.reach.name}" + " - " +
|
|
f"{name} ({self._profile.kp})"
|
|
)
|
|
|
|
def setup_sc(self):
|
|
self._undo_stack = QUndoStack()
|
|
|
|
self.undo_sc = QShortcut(QKeySequence.Undo, self)
|
|
self.redo_sc = QShortcut(QKeySequence.Redo, self)
|
|
self.copy_sc = QShortcut(QKeySequence.Copy, self)
|
|
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
|
|
|
def setup_model(self):
|
|
self._model = TableEditableModel(
|
|
profile = self._profile,
|
|
undo = self._undo_stack
|
|
)
|
|
|
|
self.ui.tableView.setModel(self._model)
|
|
self.ui.tableView.setItemDelegate(Delegate())
|
|
|
|
def setup_connections(self):
|
|
self.ui.btn_sort_asc_x.clicked.connect(self.sort_X_ascending)
|
|
self.ui.btn_sort_desc_x.clicked.connect(self.sort_X_descending)
|
|
self.ui.btn_sort_asc_y.clicked.connect(self.sort_Y_ascending)
|
|
self.ui.btn_sort_desc_y.clicked.connect(self.sort_Y_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_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.undo_sc.activated.connect(self.undo)
|
|
self.redo_sc.activated.connect(self.redo)
|
|
self.copy_sc.activated.connect(self.copy)
|
|
self.paste_sc.activated.connect(self.paste)
|
|
|
|
def plot(self):
|
|
self.ui.tableView.model().blockSignals(True)
|
|
|
|
self._plot = Plot(
|
|
canvas = self.ui.canvas,
|
|
data = self._profile,
|
|
toolbar = None,
|
|
table = self.ui.tableView,
|
|
)
|
|
self._plot.draw()
|
|
|
|
self.ui.tableView.model().blockSignals(False)
|
|
|
|
def update_plot(self):
|
|
self.ui.tableView.model().blockSignals(True)
|
|
|
|
# TODO: Do not rebuild all graph
|
|
self._plot.update()
|
|
|
|
self.ui.tableView.model().blockSignals(False)
|
|
|
|
def index_selected_row(self):
|
|
return self.ui.tableView\
|
|
.selectionModel()\
|
|
.selectedRows()[0]\
|
|
.row()
|
|
|
|
def insert_row(self):
|
|
if len(self.ui.tableView.selectedIndexes()) == 0:
|
|
self._model.insert_row(self._model.rowCount())
|
|
else:
|
|
row = self.index_selected_row()
|
|
self._model.insert_row(row + 1)
|
|
self.update_plot()
|
|
|
|
def delete_row(self):
|
|
rows = sorted(
|
|
list(
|
|
set(
|
|
[index.row() for index in self.ui.tableView.selectedIndexes()]
|
|
)
|
|
)
|
|
)
|
|
|
|
if len(rows) > 0:
|
|
self._model.remove_rows(rows)
|
|
self.update_plot()
|
|
|
|
def sort_X_ascending(self):
|
|
self._model.sort('x', order=Qt.AscendingOrder)
|
|
self.update_plot()
|
|
|
|
def sort_X_descending(self):
|
|
self._model.sort('x', order=Qt.DescendingOrder)
|
|
self.update_plot()
|
|
|
|
def sort_Y_ascending(self):
|
|
self._model.sort('y', order=Qt.AscendingOrder)
|
|
self.update_plot()
|
|
|
|
def sort_Y_descending(self):
|
|
self._model.sort('y', order=Qt.DescendingOrder)
|
|
self.update_plot()
|
|
|
|
def move_row_down(self):
|
|
rows = list(
|
|
set(
|
|
[index.row() for index in self.ui.tableView.selectedIndexes()]
|
|
)
|
|
)
|
|
|
|
for row in rows:
|
|
if row < self._model.rowCount() - 1:
|
|
self._model.move_row_down(row)
|
|
|
|
self.update_plot()
|
|
|
|
def move_row_up(self):
|
|
rows = list(
|
|
set(
|
|
[index.row() for index in self.ui.tableView.selectedIndexes()]
|
|
)
|
|
)
|
|
|
|
for row in rows:
|
|
if 0 < row:
|
|
self._model.move_row_up(row)
|
|
|
|
self.update_plot()
|
|
|
|
def copy(self):
|
|
rows = self.tableView\
|
|
.selectionModel()\
|
|
.selectedRows()
|
|
|
|
self._clipboard = []
|
|
|
|
for row in rows:
|
|
self._clipboard.append(
|
|
deepcopy(
|
|
self._reach.profile(row.row())
|
|
)
|
|
)
|
|
|
|
def paste(self):
|
|
row = self.index_selected_row()
|
|
self._model.paste(row, self._clipboard)
|
|
self.select_current_profile()
|
|
|
|
def undo(self):
|
|
self._model.undo()
|
|
self.update_plot()
|
|
|
|
def redo(self):
|
|
self._model.redo()
|
|
self.update_plot()
|
|
|
|
def handleSave(self):
|
|
if self.fileName is None or self.fileName == '':
|
|
self.fileName, self.filters = QFileDialog.getSaveFileName(
|
|
self, filter="CSV files (*.csv)"
|
|
)
|
|
|
|
if self.fileName != '':
|
|
with open(self.fileName, 'w') as stream:
|
|
csvout = csv.writer(stream, delimiter='\t', quotechar=' ',
|
|
escapechar=None,
|
|
quoting=csv.QUOTE_NONNUMERIC,
|
|
lineterminator='\n')
|
|
|
|
for row in range(self._model.rowCount(QModelIndex())):
|
|
rowdata = []
|
|
for column in range(self._model.columnCount(QModelIndex())):
|
|
item = self._model.index(
|
|
row, column, QModelIndex()
|
|
).data(Qt.DisplayRole)
|
|
|
|
if item is not None:
|
|
rowdata.append(item)
|
|
|
|
if item == 'nan':
|
|
rowdata.remove(item)
|
|
|
|
csvout.writerow(rowdata)
|
|
|
|
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 = TableEditableModel(buf)
|
|
self.ui.tableView.setModel(self._model)
|
|
self.fileName = ''
|
|
|
|
def set_enable_validate_changes_btn(self):
|
|
self.ui.btn_check.setEnabled(True)
|
|
|
|
def set_enable_cancel_btn(self):
|
|
self.ui.btn_go_back.setEnabled(True)
|
|
|
|
def set_enable_go_back_initial_state_btn(self):
|
|
self.ui.btn_reset.setEnabled(True)
|
|
|
|
def delete_empty_rows(self):
|
|
if self._model.data_contains_nan():
|
|
buttonReply = QtWidgets.QMessageBox.question(
|
|
self,
|
|
_translate("MainWindowProfile",
|
|
"Suppression les lignes incomplètes"),
|
|
_translate("MainWindowProfile",
|
|
"Supprimer les lignes des cellules"
|
|
" non renseignées ?"),
|
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
|
|
)
|
|
|
|
if buttonReply == QtWidgets.QMessageBox.Yes:
|
|
self._model.delete_empty_rows()
|
|
|
|
if buttonReply == QtWidgets.QMessageBox.No:
|
|
pass
|
|
|
|
def remove_duplicates_point_names(self):
|
|
counter_list = []
|
|
list_deleted_names = []
|
|
|
|
for ind, name_point in enumerate(self._model.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:
|
|
if name_point not in list_deleted_names:
|
|
list_deleted_names.append(name_point)
|
|
|
|
if list_deleted_names:
|
|
self.msg_box_check_duplication_names(list_deleted_names)
|
|
|
|
def closeEvent(self, event):
|
|
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:
|
|
text = _translate("MainWindowProfile",
|
|
"Le nom ''{}'' est dupliqué."
|
|
" \n\nYes : Ne garder que la première occurrence. \nNo :"
|
|
" Annuler la suppression.".format(list_deleted_names[0]))
|
|
else:
|
|
text = _translate("MainWindowProfile",
|
|
"Les noms suivants : \n{} sont dupliqués"
|
|
" \n\nYes : Ne garder que la première occurrence de "
|
|
"chaque nom. \nNo :"
|
|
" Annuler la suppression.".format(list_deleted_names))
|
|
|
|
buttonReply = QtWidgets.QMessageBox.question(
|
|
self, _translate("MainWindowProfile",
|
|
"Suppression des noms répétés"),
|
|
text,
|
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
|
|
)
|
|
|
|
if buttonReply == QtWidgets.QMessageBox.Yes:
|
|
self._model.remove_duplicates_names()
|
|
|
|
def ask_quit(self):
|
|
choice = QtWidgets.QMessageBox.question(
|
|
self,
|
|
_translate("MainWindowProfile", "Quittez ?"),
|
|
_translate("MainWindowProfile",
|
|
"Etes-vous sûr de vouloir quitter ?"),
|
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
|
|
)
|
|
return choice == QtWidgets.QMessageBox.Yes
|
|
|
|
def initial_state_model(self, profile_initial_state):
|
|
return profile_initial_state
|