diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index f7fdcce68048d219d0c782b8c6ae3bd347761cdf..d9b9b45bd79e0f6d65588ead647ff335377a1e61 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -24,6 +24,8 @@ import subprocess from queue import Queue from functools import reduce from platformdirs import user_cache_dir + +from Solver.AdisTS import AdisTS from tools import logger_exception from PyQt5 import QtGui @@ -75,6 +77,7 @@ from View.RunSolver.WindowAdisTS import SelectSolverWindowAdisTS, SolverLogWindo from View.CheckList.Window import CheckListWindow from View.CheckList.WindowAdisTS import CheckListWindowAdisTS from View.Results.Window import ResultsWindow +from View.Results.WindowAdisTS import ResultsWindowAdisTS from View.Results.ReadingResultsDialog import ReadingResultsDialog from View.Debug.Window import ReplWindow from View.OutputKpAdisTS.Window import OutputKpAdisTSWindow @@ -1398,6 +1401,63 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): ) res.show() + def open_solver_results_adists(self, solver, results=None): + def reading_fn(): + self._tmp_results = results + + # If no specific results, get last results + if results is None: + def reading_fn(): + self._tmp_results = self._last_results + + if self._last_results is None: + def reading_fn(): + self._tmp_results = solver.results( + self._study, + self._solver_workdir(solver), + ) + + # Open from file + if type(results) is str: + logger.info(f"Open results from {os.path.dirname(results)}") + + name = os.path.basename(results).replace(".BIN", "") + + def reading_fn(): + self._tmp_results = solver.results( + self._study, + os.path.dirname(results), + name=name + ) + + dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self) + dlg.exec_() + results = self._tmp_results + + # No results available + if results is None: + return + + # Windows already opened + if self.sub_window_exists( + ResultsWindowAdisTS, + data=[ + self._study, + None, # No config + solver, + results + ] + ): + return + + res = ResultsWindowAdisTS( + study=self._study, + solver=solver, + results=results, + parent=self + ) + res.show() + def _solver_workdir(self, solver): workdir = os.path.join( os.path.dirname(self._study.filename), diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 22be35d5132c144e39511968e5a74aa820d16891..03e7668b7ee7fe3d2be41b4ee9a2d64e66868579 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -45,8 +45,12 @@ _translate = QCoreApplication.translate class TableModel(PamhyrTableModel): def _setup_lst(self): _river = self._data.river + print("data: ", self._data) + print("river: ", _river) + print("reaches: ", _river.reachs) if self._opt_data == "reach": self._lst = _river.reachs + print("optreach: ", self._lst) elif self._opt_data == "profile": self._lst = _river.reach(0).profiles elif self._opt_data == "raw_data": diff --git a/src/View/Results/WindowAdisTS.py b/src/View/Results/WindowAdisTS.py new file mode 100644 index 0000000000000000000000000000000000000000..628068092fb0ee0ade950801863bbbb6734e023b --- /dev/null +++ b/src/View/Results/WindowAdisTS.py @@ -0,0 +1,643 @@ +# Window.py -- Pamhyr +# Copyright (C) 2023-2024 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 <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +import os +import csv +import logging + +from datetime import datetime +from tools import trace, timer, logger_exception + +from View.Tools.PamhyrWindow import PamhyrWindow + +from PyQt5.QtGui import ( + QKeySequence, QIcon, QPixmap, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, + QCoreApplication, QModelIndex, pyqtSlot, + QItemSelectionModel, QTimer, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QPushButton, QLineEdit, + QFileDialog, QTableView, QAbstractItemView, + QUndoStack, QShortcut, QAction, QItemDelegate, + QComboBox, QVBoxLayout, QHeaderView, QTabWidget, + QSlider, QLabel, QWidget, QGridLayout, +) + +from View.Tools.Plot.PamhyrCanvas import MplCanvas +from View.Tools.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.CustomPlot.Plot import CustomPlot +from View.Results.CustomPlot.CustomPlotValuesSelectionDialog import ( + CustomPlotValuesSelectionDialog, +) + +from View.Results.Table import TableModel +from View.Results.translate import ResultsTranslate +from View.Stricklers.Window import StricklersWindow + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + + +class ResultsWindowAdisTS(PamhyrWindow): + _pamhyr_ui = "ResultsAdisTS" + _pamhyr_name = "Results" + + def _path_file(self, filename): + return os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "..", "ui", "ressources", filename + ) + ) + + def __init__(self, study=None, config=None, + solver=None, results=None, + parent=None): + self._solver = solver + self._results = results + + trad = ResultsTranslate() + name = ( + trad[self._pamhyr_name] + " - " + + study.name + " - " + + self._solver.name + ) + + super(ResultsWindowAdisTS, self).__init__( + title=name, + study=study, + config=config, + trad=trad, + parent=parent + ) + + self._hash_data.append(self._solver) + self._hash_data.append(self._results) + + self._additional_plot = {} + + try: + #print("timestamps results: ", self._results) + #self._timestamps = sorted(list(self._results.get("timestamps"))) + + print("setup table adists results") + + self.setup_table() + #self.setup_plots() + #self.setup_slider() + #self.setup_statusbar() + #self.setup_connections() + except Exception as e: + logger_exception(e) + return + + def setup_table(self): + print("setup table adists results") + print("results study name: ", self._results.study.name) + print("results study river: ", self._results.study.river) + self._table = {} + for t in ["reach", "profile"]:#, "raw_data"]: + print("t: ", t) + table = self.find(QTableView, f"tableView_{t}") + self._table[t] = TableModel( + table_view=table, + table_headers=self._trad.get_dict(f"table_headers_{t}"), + data=self._results, + undo=self._undo_stack, + opt_data=t + ) + + 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) + + self._icon_start = QIcon() + self._icon_start.addPixmap( + QPixmap(self._path_file("media-playback-start.png")) + ) + + self._icon_pause = QIcon() + self._icon_pause.addPixmap( + QPixmap(self._path_file("media-playback-pause.png")) + ) + self._button_play = self.find(QPushButton, f"playButton") + self._button_play.setIcon(self._icon_start) + self._button_back = self.find(QPushButton, f"backButton") + self._button_next = self.find(QPushButton, f"nextButton") + self._button_first = self.find(QPushButton, f"firstButton") + self._button_last = self.find(QPushButton, f"lastButton") + self._timer = QTimer(self) + + def setup_plots(self): + self.canvas = MplCanvas(width=5, height=4, dpi=100) + self.canvas.setObjectName("canvas") + self.toolbar = PamhyrPlotToolbar( + self.canvas, self, items=[ + "home", "move", "zoom", "save", + "iso", "back/forward" + ] + ) + 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, + trad=self._trad, + toolbar=self.toolbar, + display_current=True + ) + 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, items=[ + "home", "move", "zoom", "save", + "iso", "back/forward" + ] + ) + 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, + trad=self._trad, + 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, items=[ + "home", "move", "zoom", "save", + "iso", "back/forward" + ] + ) + 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, + trad=self._trad, + 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, items=[ + "home", "move", "zoom", "save", + "iso", "back/forward" + ] + ) + 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, + trad=self._trad, + toolbar=self.toolbar_4 + ) + self.plot_h.draw() + + def closeEvent(self, event): + try: + self._timer.stop() + except Exception as e: + logger_exception(e) + + super(ResultsWindowAdisTS, self).closeEvent(event) + + 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): + # Action + actions = { + "action_reload": self._reload, + "action_add": self._add_custom_plot, + "action_export": self.export, + } + + for action in actions: + self.find(QAction, action).triggered.connect( + actions[action] + ) + + # Table and Plot + fun = { + "reach": self._set_current_reach, + "profile": self._set_current_profile, + "raw_data": self._set_current_profile_raw_data, + } + + for t in ["reach", "profile", "raw_data"]: + 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) + self._button_play.setChecked(False) + self._button_play.clicked.connect(self._pause) + self._button_back.clicked.connect(self._back) + self._button_next.clicked.connect(self._next) + self._button_first.clicked.connect(self._first) + self._button_last.clicked.connect(self._last) + self._timer.timeout.connect(self._update_slider) + + 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) + + self._table["profile"].update(ind) + self._table["raw_data"].update(ind) + + def update_table_selection_profile(self, ind): + for t in ["profile", "raw_data"]: + table = self.find(QTableView, f"tableView_{t}") + 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) + + for plot in self._additional_plot: + self._additional_plot[plot].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) + + for plot in self._additional_plot: + self._additional_plot[plot].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) + + for plot in self._additional_plot: + self._additional_plot[plot].set_timestamp(timestamp) + + self._table["raw_data"].timestamp = timestamp + + self.update_statusbar() + + def _get_current_reach(self): + table = self.find(QTableView, f"tableView_reach") + indexes = table.selectedIndexes() + if len(indexes) == 0: + return 0 + + return indexes[0].row() + + def _get_current_profile(self): + table = self.find(QTableView, f"tableView_profile") + indexes = table.selectedIndexes() + if len(indexes) == 0: + return 0 + + return indexes[0].row() + + def _get_current_timestamp(self): + return self._timestamps[ + self._slider_time.value() + ] + + 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_raw_data(self): + table = self.find(QTableView, f"tableView_raw_data") + 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) + + def _set_current_timestamp(self): + timestamp = self._timestamps[self._slider_time.value()] + self.update(timestamp=timestamp) + + def _reload_plots(self): + self.plot_xy.results = self._results + self.plot_ac.results = self._results + self.plot_kpc.results = self._results + self.plot_h.results = self._results + + self.plot_xy.draw() + self.plot_ac.draw() + self.plot_kpc.draw() + self.plot_h.draw() + + def _reload_slider(self): + 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 _reload(self): + logger.debug("Reload results...") + self._results = self._results.reload() + + self._timestamps = sorted(list(self._results.get("timestamps"))) + + self._reload_plots() + self._reload_slider() + + def _add_custom_plot(self): + dlg = CustomPlotValuesSelectionDialog(parent=self) + if dlg.exec(): + x, y = dlg.value + self.create_new_tab_custom_plot(x, y) + + def create_new_tab_custom_plot(self, x: str, y: list): + name = f"{x}: {','.join(y)}" + wname = f"tab_custom_{x}_{y}" + + tab_widget = self.find(QTabWidget, f"tabWidget") + + # This plot already exists + if name in self._additional_plot: + tab_widget.setCurrentWidget( + tab_widget.findChild(QWidget, wname) + ) + return + + widget = QWidget() + grid = QGridLayout() + + widget.setObjectName(wname) + + canvas = MplCanvas(width=5, height=4, dpi=100) + canvas.setObjectName(f"canvas_{x}_{y}") + toolbar = PamhyrPlotToolbar( + canvas, self + ) + + plot = CustomPlot( + x, y, + self._get_current_reach(), + self._get_current_profile(), + self._get_current_timestamp(), + data=self._results, + canvas=canvas, + toolbar=toolbar, + parent=self, + ) + plot.draw() + + # Add plot to additional plot + self._additional_plot[name] = plot + + grid.addWidget(toolbar, 0, 0) + grid.addWidget(canvas, 1, 0) + widget.setLayout(grid) + tab_widget.addTab(widget, name) + + 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() + + # play / pause buttons + def _update_slider(self): + if self._slider_time.value() == self._slider_time.maximum(): + self._slider_time.setValue(self._slider_time.minimum()) + else: + self._slider_time.setValue(self._slider_time.value()+1) + + def _next(self): + self._slider_time.setValue(self._slider_time.value()+1) + + def _back(self): + self._slider_time.setValue(self._slider_time.value()-1) + + def _first(self): + self._slider_time.setValue(self._slider_time.minimum()) + + def _last(self): + self._slider_time.setValue(self._slider_time.maximum()) + + def _pause(self): + if self._button_play.isChecked(): + self._button_next.setEnabled(False) + self._button_back.setEnabled(False) + self._button_first.setEnabled(False) + self._button_last.setEnabled(False) + self._timer.start(100) + self._button_play.setIcon(self._icon_pause) + else: + self._timer.stop() + self._button_next.setEnabled(True) + self._button_back.setEnabled(True) + self._button_first.setEnabled(True) + self._button_last.setEnabled(True) + self._button_play.setIcon(self._icon_start) + + def export(self): + self.file_dialog( + select_file=False, + callback=lambda d: self.export_to(d[0]) + ) + + def export_to(self, directory): + for reach in self._results.river.reachs: + self.export_reach(reach, directory) + + def export_reach(self, reach, directory): + name = reach.name + name = name.replace(" ", "-") + + file_name = os.path.join( + directory, + f"reach_{name}.csv" + ) + + with open(file_name, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + writer.writerow(["name", "kp", "data-file"]) + for profile in reach.profiles: + p_file_name = os.path.join( + directory, + f"cs_{profile.geometry.id}.csv" + ) + + writer.writerow([ + profile.name, + profile.kp, + p_file_name + ]) + + self.export_profile(reach, profile, p_file_name) + + def export_profile(self, reach, profile, file_name): + with open(file_name, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + + writer.writerow(["timestamp", "z", "q"]) + timestamps = sorted(self._results.get("timestamps")) + + for ts in timestamps: + writer.writerow([ + ts, + profile.get_ts_key(ts, "Z"), + profile.get_ts_key(ts, "Q"), + ]) diff --git a/src/View/RunSolver/WindowAdisTS.py b/src/View/RunSolver/WindowAdisTS.py index bdf473c7870a541cd0dc266884b646dd3eaf0762..a048757a7e9b16d61285fbd0343d7b8aee9c0063 100644 --- a/src/View/RunSolver/WindowAdisTS.py +++ b/src/View/RunSolver/WindowAdisTS.py @@ -412,7 +412,7 @@ class SolverLogWindowAdisTS(PamhyrWindow): dlg.exec_() self._parent.set_results(self._solver, self._results) - self._parent.open_solver_results(self._solver, self._results) + self._parent.open_solver_results_adists(self._solver, self._results) self._solver.has_results_loaded() diff --git a/src/View/ui/ResultsAdisTS.ui b/src/View/ui/ResultsAdisTS.ui new file mode 100644 index 0000000000000000000000000000000000000000..c7c319a0bd6fe066d6ba46bcf777cb628e277905 --- /dev/null +++ b/src/View/ui/ResultsAdisTS.ui @@ -0,0 +1,284 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1280</width> + <height>720</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <property name="locale"> + <locale language="English" country="Europe"/> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="minimumSize"> + <size> + <width>874</width> + <height>0</height> + </size> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSplitter" name="splitter_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QTableView" name="tableView_reach"/> + <widget class="QWidget" name="layoutWidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTableView" name="tableView_profile"/> + </item> + <item> + <widget class="QSlider" name="verticalSlider_profile"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="invertedAppearance"> + <bool>true</bool> + </property> + <property name="invertedControls"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QWidget" name="layoutWidget"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="firstButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>ressources/media-skip-backward.png</normaloff>ressources/media-skip-backward.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="backButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>ressources/media-seek-backward.png</normaloff>ressources/media-seek-backward.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="playButton"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="mouseTracking"> + <bool>true</bool> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::DefaultContextMenu</enum> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>ressources/media-playback-start.png</normaloff> + <normalon>ressources/media-playback-pause.png</normalon>ressources/media-playback-start.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nextButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>ressources/media-seek-forward.png</normaloff>ressources/media-seek-forward.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="lastButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>ressources/media-skip-forward.png</normaloff>ressources/media-skip-forward.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="horizontalSlider_time"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>Raw data</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"/> + </item> + <item> + <widget class="QTableView" name="tableView_raw_data"/> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Water elevation</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QSplitter" name="splitter_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QWidget" name="verticalLayoutWidget"> + <layout class="QVBoxLayout" name="verticalLayout"/> + </widget> + <widget class="QWidget" name="verticalLayoutWidget_2"> + <layout class="QVBoxLayout" name="verticalLayout_2"/> + </widget> + </widget> + <widget class="QWidget" name="verticalLayoutWidget_3"> + <layout class="QVBoxLayout" name="verticalLayout_3"/> + </widget> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Hydrograph</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_hydrograph"/> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1280</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + <widget class="QToolBar" name="toolBar"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="windowTitle"> + <string>toolBar</string> + </property> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + <addaction name="action_add"/> + <addaction name="action_export"/> + <addaction name="action_reload"/> + </widget> + <action name="action_add"> + <property name="icon"> + <iconset> + <normaloff>ressources/add.png</normaloff>ressources/add.png</iconset> + </property> + <property name="text"> + <string>Add</string> + </property> + <property name="toolTip"> + <string>Add custom visualization</string> + </property> + </action> + <action name="action_reload"> + <property name="icon"> + <iconset> + <normaloff>ressources/reload.png</normaloff>ressources/reload.png</iconset> + </property> + <property name="text"> + <string>Reload</string> + </property> + </action> + <action name="action_export"> + <property name="icon"> + <iconset> + <normaloff>ressources/export.png</normaloff>ressources/export.png</iconset> + </property> + <property name="text"> + <string>Export</string> + </property> + <property name="toolTip"> + <string>Export raw data</string> + </property> + <property name="shortcut"> + <string>Ctrl+E</string> + </property> + </action> + </widget> + <resources/> + <connections/> +</ui>