Pamhyr2/src/View/Geometry/Profile/ProfileWindow.py

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