# Window.py -- Pamhyr # Copyright (C) 2023 INRAE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -*- coding: utf-8 -*- import logging from datetime import datetime from tools import trace, timer from View.ASubWindow import ASubMainWindow from View.ListedSubWindow import ListedSubWindow from PyQt5.QtGui import ( QKeySequence, ) from PyQt5.QtCore import ( Qt, QVariant, QAbstractTableModel, QCoreApplication, QModelIndex, pyqtSlot, QItemSelectionModel, ) from PyQt5.QtWidgets import ( QDialogButtonBox, QPushButton, QLineEdit, QFileDialog, QTableView, QAbstractItemView, QUndoStack, QShortcut, QAction, QItemDelegate, QComboBox, QVBoxLayout, QHeaderView, QTabWidget, QSlider, QLabel, ) from View.Plot.MplCanvas import MplCanvas from View.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.Results.PlotXY import PlotXY from View.Results.PlotAC import PlotAC from View.Results.PlotKPC import PlotKPC from View.Results.PlotH import PlotH from View.Results.PlotSedReach import PlotSedReach from View.Results.PlotSedProfile import PlotSedProfile from View.Results.Table import TableModel from View.Results.translate import * from View.Stricklers.Window import StricklersWindow _translate = QCoreApplication.translate logger = logging.getLogger() class ResultsWindow(ASubMainWindow, ListedSubWindow): def __init__(self, title="Results", study=None, solver=None, results=None, parent=None): self._study = study self._solver = solver self._results = results self._timestamps = sorted(list(self._results.get("timestamps"))) self.setup_title(title) super(ResultsWindow, self).__init__( name=title, ui="Results", parent=parent ) self.setup_sc() self.setup_table() self.setup_graph() self.setup_slider() self.setup_statusbar() self.setup_connections() self.ui.setWindowTitle(self._title) def setup_title(self, title): self._title = ( title + " - " + self._study.name + " - " + self._solver.name + " - " + self._results.date ) 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_table(self): self._table = {} for t in ["reach", "profile"]: table = self.find(QTableView, f"tableView_{t}") self._table[t] = TableModel( results = self._results, study = self._study, mode = t, undo = self._undo_stack, ) table.setModel(self._table[t]) table.setSelectionBehavior(QAbstractItemView.SelectRows) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.setAlternatingRowColors(True) def setup_slider(self): self._slider_profile = self.find(QSlider, f"verticalSlider_profile") default_reach = self._results.river.reach(0) self._slider_profile.setMaximum(len(default_reach.profiles) - 1) self._slider_profile.setValue(0) self._slider_time = self.find(QSlider, f"horizontalSlider_time") self._slider_time.setMaximum(len(self._timestamps) - 1) self._slider_time.setValue(len(self._timestamps) - 1) def setup_graph(self): self.canvas = MplCanvas(width=5, height=4, dpi=100) self.canvas.setObjectName("canvas") self.toolbar = PamhyrPlotToolbar( self.canvas, self ) self.plot_layout = self.find(QVBoxLayout, "verticalLayout") self.plot_layout.addWidget(self.toolbar) self.plot_layout.addWidget(self.canvas) self.plot_xy = PlotXY( canvas = self.canvas, results = self._results, reach_id = 0, profile_id = 0, toolbar = self.toolbar, display_current = False ) self.plot_xy.draw() self.canvas_2 = MplCanvas(width=5, height=4, dpi=100) self.canvas_2.setObjectName("canvas_2") self.toolbar_2 = PamhyrPlotToolbar( self.canvas_2, self ) self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2") self.plot_layout_2.addWidget(self.toolbar_2) self.plot_layout_2.addWidget(self.canvas_2) self.plot_kpc = PlotKPC( canvas = self.canvas_2, results = self._results, reach_id = 0, profile_id = 0, toolbar = self.toolbar_2 ) self.plot_kpc.draw() self.canvas_3 = MplCanvas(width=5, height=4, dpi=100) self.canvas_3.setObjectName("canvas_3") self.toolbar_3 = PamhyrPlotToolbar( self.canvas_3, self ) self.plot_layout_3 = self.find(QVBoxLayout, "verticalLayout_3") self.plot_layout_3.addWidget(self.toolbar_3) self.plot_layout_3.addWidget(self.canvas_3) self.plot_ac = PlotAC( canvas = self.canvas_3, results = self._results, reach_id = 0, profile_id = 0, toolbar = self.toolbar_3 ) self.plot_ac.draw() self.canvas_4 = MplCanvas(width=5, height=4, dpi=100) self.canvas_4.setObjectName("canvas_4") self.toolbar_4 = PamhyrPlotToolbar( self.canvas_4, self ) self.plot_layout_4 = self.find(QVBoxLayout, "verticalLayout_hydrograph") self.plot_layout_4.addWidget(self.toolbar_4) self.plot_layout_4.addWidget(self.canvas_4) self.plot_h = PlotH( canvas = self.canvas_4, results = self._results, reach_id = 0, profile_id = 0, toolbar = self.toolbar_4 ) self.plot_h.draw() self.canvas_5 = MplCanvas(width=5, height=4, dpi=100) self.canvas_5.setObjectName("canvas_5") self.toolbar_5 = PamhyrPlotToolbar( self.canvas_5, self ) self.plot_layout_5 = self.find(QVBoxLayout, "verticalLayout_sed_reach") self.plot_layout_5.addWidget(self.toolbar_5) self.plot_layout_5.addWidget(self.canvas_5) if self._study.river.has_sediment(): self.plot_sed_reach = PlotSedReach( canvas = self.canvas_5, results = self._results, study = self._study, reach_id = 0, profile_id = 0, toolbar = self.toolbar_5 ) self.plot_sed_reach.draw() self.canvas_6 = MplCanvas(width=5, height=4, dpi=100) self.canvas_6.setObjectName("canvas_6") self.toolbar_6 = PamhyrPlotToolbar( self.canvas_6, self ) self.plot_layout_6 = self.find(QVBoxLayout, "verticalLayout_sed_profile") self.plot_layout_6.addWidget(self.toolbar_6) self.plot_layout_6.addWidget(self.canvas_6) if self._study.river.has_sediment(): self.plot_sed_profile = PlotSedProfile( canvas = self.canvas_6, results = self._results, study = self._study, reach_id = 0, profile_id = 0, toolbar = self.toolbar_6 ) self.plot_sed_profile.draw() def _compute_status_label(self): # Timestamp ts = self._timestamps[self._slider_time.value()] t0 = datetime.fromtimestamp(0) fts = str( datetime.fromtimestamp(ts) - t0 ) fts.replace("days", _translate("Results", "days"))\ .replace("day", _translate("Results", "day")) # Reach table = self.find(QTableView, f"tableView_reach") indexes = table.selectedIndexes() if len(indexes) == 0: reach = self._study.river.edges()[0] else: reach = self._study.river.edges()[indexes[0].row()] # Profile table = self.find(QTableView, f"tableView_profile") indexes = table.selectedIndexes() if len(indexes) == 0: profile = reach.reach.profile(0) else: profile = reach.reach.profile(indexes[0].row()) pname = profile.name if profile.name != "" else profile.kp return (f"Reach: {reach.name} | " + f"Profile: {pname} | " + f"Timestamp : {fts} ({ts} sec)") def setup_statusbar(self): txt = self._compute_status_label() self._status_label = QLabel(txt) self.statusbar.addPermanentWidget(self._status_label) def update_statusbar(self): txt = self._compute_status_label() self._status_label.setText(txt) def setup_connections(self): 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) fun = { "reach": self._set_current_reach, "profile": self._set_current_profile, } for t in ["reach", "profile"]: table = self.find(QTableView, f"tableView_{t}") table.selectionModel()\ .selectionChanged\ .connect(fun[t]) self._table[t].dataChanged.connect(fun[t]) self._slider_profile.valueChanged.connect(self._set_current_profile_slider) self._slider_time.valueChanged.connect(self._set_current_timestamp) def update_table_selection_reach(self, ind): table = self.find(QTableView, f"tableView_reach") selectionModel = table.selectionModel() index = table.model().index(ind, 0) selectionModel.select( index, QItemSelectionModel.Rows | QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Select ) table.scrollTo(index) def update_table_selection_profile(self, ind): table = self.find(QTableView, f"tableView_profile") selectionModel = table.selectionModel() index = table.model().index(ind, 0) selectionModel.select( index, QItemSelectionModel.Rows | QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Select ) table.scrollTo(index) def update(self, reach_id = None, profile_id = None, timestamp = None): if reach_id is not None: self.plot_xy.set_reach(reach_id) self.plot_ac.set_reach(reach_id) self.plot_kpc.set_reach(reach_id) self.plot_h.set_reach(reach_id) if self._study.river.has_sediment(): self.plot_sed_reach.set_reach(reach_id) self.plot_sed_profile.set_reach(reach_id) self.update_table_selection_reach(reach_id) self.update_table_selection_profile(0) if profile_id is not None: self.plot_xy.set_profile(profile_id) self.plot_ac.set_profile(profile_id) self.plot_kpc.set_profile(profile_id) self.plot_h.set_profile(profile_id) if self._study.river.has_sediment(): self.plot_sed_reach.set_profile(profile_id) self.plot_sed_profile.set_profile(profile_id) self.update_table_selection_profile(profile_id) if timestamp is not None: self.plot_xy.set_timestamp(timestamp) self.plot_ac.set_timestamp(timestamp) self.plot_kpc.set_timestamp(timestamp) # self.plot_h.set_timestamp(timestamp) if self._study.river.has_sediment(): self.plot_sed_reach.set_timestamp(timestamp) self.plot_sed_profile.set_timestamp(timestamp) self.plot_xy.draw() self.plot_ac.draw() self.plot_kpc.draw() self.plot_h.draw() if self._study.river.has_sediment(): self.plot_sed_reach.draw() self.plot_sed_profile.draw() self.update_statusbar() def _set_current_reach(self): table = self.find(QTableView, f"tableView_reach") indexes = table.selectedIndexes() if len(indexes) == 0: return self.update(reach_id = indexes[0].row()) def _set_current_profile(self): table = self.find(QTableView, f"tableView_profile") indexes = table.selectedIndexes() if len(indexes) == 0: return ind = indexes[0].row() self.update(profile_id = ind) self._slider_profile.setValue(ind) def _set_current_profile_slider(self): pid = self._slider_profile.value() self.update(profile_id = pid) return def _set_current_timestamp(self): timestamp = self._timestamps[self._slider_time.value()] self.update(timestamp = timestamp) def copy(self): logger.info("TODO: copy") def paste(self): logger.info("TODO: paste") def undo(self): self._table.undo() def redo(self): self._table.redo()