Merge branch 'scenario-dev-pa' into scenarios.

This commit is contained in:
parouby committed 2025-11-20 15:44:52 +01:00
commit b666162bdf
27 files changed
+2034 -83

No files matched your search

+3 -1
View File
@@ -17,6 +17,7 @@
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
@@ -92,7 +93,8 @@ class ReplWindow(PamhyrWindow):
value = exec(rich_code)
value = self.__debug_exec_result__
except Exception as e:
value = f"<font color=\"red\">" + str(e) + "</font>"
value = f"<font color=\"red\">" + str(e) + "</font>\n"
value += f"<font color=\"grey\">{traceback.format_exc()}</font>"
# Display code
msg = f"<font color=\"grey\"> # " + code + " #</font>"
+289
View File
@@ -0,0 +1,289 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 logging
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtWidgets import (
QLabel, QPlainTextEdit, QPushButton, QCheckBox,
QFileDialog, QVBoxLayout, QDoubleSpinBox,
)
from PyQt5.QtCore import (
QSettings
)
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.UndoCommand import (
SetCommand
)
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
class EditGeoTIFFWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFF"
_pamhyr_name = "Edit GeoTIFF"
def __init__(self, study=None, config=None, geotiff=None,
trad=None, undo=None, parent=None):
super(EditGeoTIFFWindow, self).__init__(
title=self._pamhyr_name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self._geotiff = geotiff
self._file_name = geotiff.file_name
self._hash_data.append(self._geotiff)
self._undo = undo
self.setup_values()
self.setup_graph()
self.setup_connection()
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout,
"verticalLayout_geotiff")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.plot_layout.addWidget(self._toolbar)
self.plot_layout.addWidget(self.canvas)
self.plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
trad=self._trad,
toolbar=None,
parent=self
)
self.plot.update()
self._plot_img = None
memfile = self._geotiff.memfile
if memfile is not None:
self.draw_geotiff(memfile=memfile)
def setup_values(self):
self.set_check_box("checkBox", self._geotiff.enabled)
self.set_line_edit_text("lineEdit_name", self._geotiff.name)
self.set_line_edit_text("lineEdit_description",
self._geotiff.description)
bounds = list(self._geotiff.coordinates.values())
self._set_values_from_bounds(bounds)
self._set_default_values_from_bounds(bounds)
self._reset_spinboxes()
if self._study.is_read_only():
self.set_check_box_enable("checkBox", False)
self.set_line_edit_enable("lineEdit_name", False)
self.set_line_edit_enable("lineEdit_path", False)
self.set_plaintext_edit_enable("plainTextEdit", False)
def _set_values_from_bounds(self, bounds):
self._values = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _set_default_values_from_bounds(self, bounds):
self._values_default = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _reset_spinboxes(self):
for key in self._values:
self._reset_spinbox(key)
def _reset_spinbox(self, key):
self.set_double_spin_box(
f"doubleSpinBox_{key}", self._values_default[key]
)
def setup_connection(self):
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.close)
self.find(QPushButton, "pushButton_ok")\
.clicked.connect(self.accept)
self.find(QPushButton, "pushButton_import")\
.clicked.connect(self._import)
self.find(QPushButton, "pushButton_bottom")\
.clicked.connect(lambda: self._reset_spinbox("bottom"))
self.find(QPushButton, "pushButton_top")\
.clicked.connect(lambda: self._reset_spinbox("top"))
self.find(QPushButton, f"pushButton_left")\
.clicked.connect(lambda: self._reset_spinbox("left"))
self.find(QPushButton, f"pushButton_right")\
.clicked.connect(lambda: self._reset_spinbox("right"))
self.find(QDoubleSpinBox, f"doubleSpinBox_bottom")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("bottom")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_top")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("top")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_left")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("left")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_right")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("right")
)
def update_values_from_spinbox(self, key):
self._values[key] = self.get_double_spin_box(f"doubleSpinBox_{key}")
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img.set_extent((left, right, bottom, top))
self.plot.idle()
def draw_geotiff(self, memfile=None):
if not _rasterio_loaded:
return
if memfile is None:
if self._file_name == "":
return
with rasterio.open(self._file_name) as data:
img = data.read()
b = data.bounds[:] # left, bottom, right, top
if b[2] > b[0] and b[1] < b[3]:
coord = [b[1], b[3], b[0], b[2]]
else:
xlim = self.canvas.axes.get_xlim()
ylim = self.canvas.axes.get_ylim()
coord = ylim + xlim
self._set_values_from_bounds(coord)
self._set_default_values_from_bounds(coord)
else:
with memfile.open() as gt:
img = gt.read()
if self._plot_img is not None:
self._plot_img.remove()
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
self.plot.idle()
self._reset_spinboxes()
def _import(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_geotiff"],
self._trad["file_all"],
]
filename, _ = QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if filename != "":
self._file_name = filename
self.draw_geotiff()
def accept(self):
if self._study.is_editable():
is_enabled = self.get_check_box("checkBox")
name = self.get_line_edit_text("lineEdit_name")
description = self.get_line_edit_text("lineEdit_description")
coord_bottom = self.get_double_spin_box("doubleSpinBox_bottom")
coord_top = self.get_double_spin_box("doubleSpinBox_top")
coord_left = self.get_double_spin_box("doubleSpinBox_left")
coord_right = self.get_double_spin_box("doubleSpinBox_right")
self._undo.push(
SetCommand(
self._geotiff, enabled=is_enabled,
name=name, description=description,
coordinates_bottom=coord_bottom,
coordinates_top=coord_top,
coordinates_left=coord_left,
coordinates_right=coord_right,
file_name=self._file_name,
)
)
self._propagate_update(key=Modules.GEOTIFF)
self.close()
+96
View File
@@ -0,0 +1,96 @@
# List.py -- Pamhyr
# Copyright (C) 2024-2025 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 logging
from functools import reduce
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant,
)
from PyQt5.QtGui import (
QColor, QBrush,
)
from View.Tools.PamhyrList import PamhyrListModel
from View.GeoTIFF.UndoCommand import (
AddCommand, DelCommand
)
logger = logging.getLogger()
class ListModel(PamhyrListModel):
def get_true_data_row(self, row):
el = self._data.get(row)
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == el,
enumerate(self._data._lst)
)
), 0
)
def data(self, index, role):
row = index.row()
column = index.column()
file = self._data.files[row]
if role == Qt.ForegroundRole:
color = Qt.gray
if file.is_enabled():
color = QColor("black")
else:
color = QColor("grey")
return QBrush(color)
if role == Qt.ItemDataRole.DisplayRole:
text = f"{file.name}: '{file.description}'"
if not file.is_enabled():
text += " (disabled)"
return text
return QVariant()
def add(self, row):
row = self.get_true_data_row(row)
self._undo.push(
AddCommand(
self._data, row
)
)
self.update()
def delete(self, row):
self._undo.push(
DelCommand(
self._data, self._data.files[row]
)
)
self.update()
+35
View File
@@ -0,0 +1,35 @@
# Translate.py -- Pamhyr
# Copyright (C) 2024-2025 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 -*-
from PyQt5.QtCore import QCoreApplication
from View.Translate import MainTranslate
_translate = QCoreApplication.translate
class GeoTIFFTranslate(MainTranslate):
def __init__(self):
super(GeoTIFFTranslate, self).__init__()
self._dict["GeoTIFF files"] = _translate(
"GeoTIFF", "GeoTIFF files"
)
self._dict["Edit additional file"] = _translate(
"GeoTIFF", "Edit GeoTIFF file"
)
+81
View File
@@ -0,0 +1,81 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2024-2025 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 -*-
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
class SetCommand(QUndoCommand):
def __init__(self, geotiff, **kwargs):
QUndoCommand.__init__(self)
self._geotiff = geotiff
self._new = kwargs
self._old = None
def undo(self):
f = self._geotiff
for key in self._old:
f[key] = self._old[key]
def redo(self):
f = self._geotiff
if self._old is None:
self._old = {}
for key in self._new:
self._old[key] = f[key]
for key in self._new:
f[key] = self._new[key]
class AddCommand(QUndoCommand):
def __init__(self, files, row):
QUndoCommand.__init__(self)
self._files = files
self._row = row
self._new = None
def undo(self):
self._new.set_as_deleted()
def redo(self):
if self._new is None:
self._new = self._files.new(self._row)
else:
self._new.set_as_not_deleted()
class DelCommand(QUndoCommand):
def __init__(self, files, line):
QUndoCommand.__init__(self)
self._files = files
self._line = line
def undo(self):
self._line.set_as_not_deleted()
def redo(self):
self._line.set_as_deleted()
+217
View File
@@ -0,0 +1,217 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 -*-
from tools import trace, timer
from PyQt5.QtWidgets import (
QAction, QListView, QVBoxLayout,
)
from matplotlib.patches import Rectangle
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from View.GeoTIFF.List import ListModel
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.Edit.Window import EditGeoTIFFWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
class GeoTIFFListWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFFList"
_pamhyr_name = "GeoTIFF files"
def __init__(self, study=None, config=None,
parent=None):
trad = GeoTIFFTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
super(GeoTIFFListWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self.setup_list()
self.setup_graph()
self.setup_connections()
def setup_list(self):
lst = self.find(QListView, "listView")
self._list = ListModel(
list_view=lst,
data=self._study.river.geotiff,
undo=self._undo_stack,
trad=self._trad,
)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout.addWidget(self._toolbar)
self._plot_layout.addWidget(self.canvas)
self._plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
geotiff=self._study.river.geotiff,
trad=self._trad,
toolbar=None,
parent=self
)
self._plot_rect = []
self._plot.update()
def setup_connections(self):
if self._study.is_editable():
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QListView, "listView")\
.selectionModel()\
.selectionChanged\
.connect(self._update_rectangle)
def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key and Modules.GEOTIFF not in key:
return
self.update()
def update(self):
self._list.update()
self._plot.update()
self._update_rectangle()
def _update_rectangle(self):
for rect in self._plot_rect:
rect.remove()
self._plot_rect = []
rows = self.selected_rows()
if len(rows) <= 0:
return
for row in rows:
files = self._study.river.geotiff.files
if len(files) <= row:
continue
geotiff = files[row]
coord = geotiff.coordinates
xy = (coord["left"], coord["bottom"])
width = abs(coord["right"] - coord["left"])
height = abs(coord["top"] - coord["bottom"])
rect = Rectangle(
xy, width, height,
edgecolor='red', facecolor='none',
lw=2
)
self._plot_rect.append(rect)
self.canvas.axes.add_patch(
rect
)
self._plot.idle()
def selected_rows(self):
lst = self.find(QListView, f"listView")
return list(map(lambda i: i.row(), lst.selectedIndexes()))
def add(self):
rows = self.selected_rows()
if len(rows) > 0:
row = rows[0]
else:
row = 0
self._list.add(row)
def delete(self):
rows = self.selected_rows()
if len(rows) == 0:
return
self._list.delete(rows[0])
self.update()
def edit(self):
rows = self.selected_rows()
for row in rows:
geotiff = self._study.river.geotiff.files[row]
if self.sub_window_exists(
EditGeoTIFFWindow,
data=[self._study, self._config, geotiff]
):
continue
win = EditGeoTIFFWindow(
study=self._study,
config=self._config,
geotiff=geotiff,
trad=self._trad,
undo=self._undo_stack,
parent=self,
)
win.show()
def _undo(self):
self._undo_stack.undo()
self.update()
def _redo(self):
self._undo_stack.redo()
self.update()
+103 -17
View File
@@ -47,7 +47,7 @@ from PyQt5.QtWidgets import (
QMainWindow, QApplication, QAction,
QFileDialog, QShortcut, QMenu, QToolBar,
QMessageBox, QProgressDialog, QTabWidget,
QDialog, QVBoxLayout, QLabel,
QDialog, QVBoxLayout, QLabel, QInputDialog,
)
from PyQt5.uic import loadUi
@@ -79,6 +79,7 @@ from View.Frictions.Window import FrictionsWindow
from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.AdditionalFiles.Window import AddFileListWindow
from View.GeoTIFF.Window import GeoTIFFListWindow
from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import (
@@ -157,7 +158,7 @@ define_model_action = [
"action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif",
"action_menu_d90", "action_menu_dif", "action_menu_edit_geotiff"
]
action = (
@@ -276,8 +277,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_new": self.open_new_study,
"action_menu_edit": self.open_edit_study,
"action_menu_open": self.open_model,
"action_menu_save": self.save_study,
"action_menu_save_as": self.save_as_study,
"action_menu_save": lambda: self.save_study(),
"action_menu_save_as": lambda: self.save_as_study(),
"action_menu_numerical_parameter": self.open_solver_parameters,
"action_menu_edit_scenarios": self.open_scenarios,
"action_menu_edit_network": self.open_network,
@@ -297,6 +298,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.open_reach_sediment_layers,
"action_menu_additional_file": self.open_additional_files,
"action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_edit_geotiff": self.open_geotiff,
"action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results,
"action_menu_open_results_from_file": self.open_results_from_file,
@@ -313,7 +315,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_about": self.open_about,
# ToolBar action
"action_toolBar_open": self.open_model,
"action_toolBar_save": self.save_study,
"action_toolBar_save": lambda: self.save_study(),
"action_toolBar_close": self.close_model,
"action_toolBar_run_solver": self.run_lasest_solver,
# Current actions
@@ -606,6 +608,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return None
def get_last_results(self, solver):
if self._study is None:
return None
results = self._study.results
if solver in results:
return self._study.results[solver]
return None
@last_results.setter
def last_results(self, results):
if self._study is None:
@@ -637,7 +649,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
logger.info(f"Open Study - {self._study.name}")
self.set_title()
def save_study(self):
def _save(self, source):
self.save_study(progress_parent=source)
def save_study(self, progress_parent=None):
"""Save current study
Save current study, if study as no associate file, open a
@@ -667,11 +682,15 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._backup_timer.blockSignals(True)
self._save_mutex.lock()
parent = self
if progress_parent is not None:
parent = progress_parent
sql_request_count = self._study.sql_save_request_count()
progress = QProgressDialog(
"Saving...", None,
0, sql_request_count,
parent=self
parent=parent
)
progress.setWindowModality(Qt.WindowModal)
progress.setValue(0)
@@ -684,6 +703,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
progress=lambda: progress.setValue(progress.value() + 1)
)
progress.close()
status += " Done"
logger.info(status)
self.statusbar.showMessage(status, 3000)
@@ -758,6 +778,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
progress.setValue(progress.value() + 1)
progress.close()
def save_as_study_single_scenario(self, sid=-1):
sql_request_count = self._study.sql_save_request_count()
@@ -782,6 +804,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
status += " Done"
progress.close()
logger.info(status)
self.statusbar.showMessage(status, 3000)
@@ -1339,6 +1362,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
self.additonal_files.show()
def open_geotiff(self):
if self._study is not None:
if self.sub_window_exists(
GeoTIFFListWindow,
data=[self._study, None]
):
return
self.geotiff = GeoTIFFListWindow(
study=self._study, parent=self
)
self.geotiff.show()
def open_rep_lines(self):
if self._study is not None:
if self.sub_window_exists(
@@ -1523,14 +1559,14 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
# If no specific results, get last results
if results is None:
def reading_fn():
self._tmp_results = self.last_results
self._tmp_results = solver.results(
self._study,
self._solver_workdir(solver),
)
if self.last_results is None:
if solver == self._last_solver:
def reading_fn():
self._tmp_results = solver.results(
self._study,
self._solver_workdir(solver),
)
self._tmp_results = self.last_results
# Open from file
if type(results) is str:
@@ -1555,13 +1591,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
dlg.exec_()
results = self._tmp_results
# self.last_results = results
# No results available
if results is None:
self.msg_open_results_no_results()
return
# results does not have values, for example if geometry missmatch
if not results.is_valid:
self.msg_open_results_invalid_results()
return
if results.get('study_revision') != self._study.status.version:
@@ -1587,6 +1626,20 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
res.show()
def msg_open_results_no_results(self):
self.message_box(
window_title=self._trad["Warning"],
text=self._trad["mb_open_results_title"],
informative_text=self._trad["mb_open_results_no_results_msg"]
)
def msg_open_results_invalid_results(self):
self.message_box(
window_title=self._trad["Error"],
text=self._trad["mb_open_results_title"],
informative_text=self._trad["mb_open_results_invalid_results_msg"]
)
def open_solver_results_adists(self, solver, results=None):
def reading_fn():
self._tmp_results = results
@@ -1662,16 +1715,49 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return workdir
def is_solver_workdir_exists(self, solver, scenario=None):
return os.path.exists(
self._solver_workdir(solver, scenario)
)
def open_last_results(self):
if self._last_solver is None:
return
solver_type = self._study.results
solver_name, ok = QInputDialog.getItem(
self, self._trad['Solver'],
self._trad['Solver'] + ":",
list(
map(
lambda s: s.name,
filter(
lambda s: (self.is_solver_workdir_exists(s)
or s._type in solver_type),
self.conf.solvers
)
)
)
)
if not ok:
return
solver = next(
filter(
lambda s: s.name == solver_name,
self.conf.solvers
)
)
if self._last_solver._type == "mage8":
self.open_solver_results(self._last_solver,
self.last_results)
self.open_solver_results(
solver, # self.last_results
)
elif self._last_solver._type == "adistswc":
self.open_solver_results_adists(self._last_solver,
self.last_results)
self.open_solver_results_adists(
solver, # self.last_results
)
def open_results_from_file(self):
if self._study is None:
+16
View File
@@ -27,6 +27,20 @@ from View.Tools.PamhyrWidget import PamhyrWidget
from PyQt5.QtWidgets import QVBoxLayout
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
@@ -104,10 +118,12 @@ class WidgetInfo(PamhyrWidget):
self.plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
geotiff=self._study.river.geotiff,
trad=self.parent._trad,
toolbar=self._toolbar_xy,
parent=self
)
self.plot.update()
def set_network_values(self):
+65 -6
View File
@@ -27,12 +27,26 @@ from PyQt5.QtCore import (
)
from PyQt5.QtWidgets import QApplication, QTableView
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
_translate = QCoreApplication.translate
class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
table=None, parent=None):
def __init__(self, canvas=None, trad=None, data=None, geotiff=None,
toolbar=None, table=None, parent=None):
super(PlotXY, self).__init__(
canvas=canvas,
trad=trad,
@@ -43,10 +57,14 @@ class PlotXY(PamhyrPlot):
)
self._data = data
self._geotiff = geotiff
self.label_x = self._trad["x"]
self.label_y = self._trad["y"]
self.parent = parent
self._plot_img = {}
self._isometric_axis = True
self._auto_relim_update = True
@@ -69,7 +87,12 @@ class PlotXY(PamhyrPlot):
if data.reach.number_profiles != 0:
self.draw_xy(data.reach)
self.draw_lr(data.reach)
self.idle()
if self._geotiff is not None:
self.draw_geotiff(self._geotiff.files)
self.idle()
return
def draw_xy(self, reach):
@@ -78,9 +101,9 @@ class PlotXY(PamhyrPlot):
line_xy.append(np.column_stack(xy))
line_xy_collection = collections.LineCollection(
line_xy,
colors=self.color_plot_river_bottom
)
line_xy,
colors=self.color_plot_river_bottom
)
self.canvas.axes.add_collection(line_xy_collection)
def draw_lr(self, reach):
@@ -113,6 +136,42 @@ class PlotXY(PamhyrPlot):
)
self.line_lr.append(line)
def draw_geotiff(self, lst):
if not _rasterio_loaded:
return
for img in self._plot_img:
self._plot_img[img].remove()
self._plot_img = {}
for geotiff in lst:
if geotiff.is_deleted():
continue
memfile = geotiff.memfile
if memfile is None:
continue
with memfile.open() as gt:
img = gt.read()
coords = geotiff.coordinates
left = coords["left"]
right = coords["right"]
bottom = coords["bottom"]
top = coords["top"]
self._plot_img[geotiff] = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
if not geotiff.is_enabled():
self._plot_img[geotiff].set(alpha=0.5)
self.idle()
@timer
def update(self):
self.draw()
+53
View File
@@ -31,6 +31,20 @@ from PyQt5.QtCore import (
)
from PyQt5.QtWidgets import QApplication, QTableView
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
_translate = QCoreApplication.translate
logger = logging.getLogger()
@@ -52,6 +66,8 @@ class PlotXY(PamhyrPlot):
self.line_gl = []
self.overflow = []
self._plot_img = {}
self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps)
self._current_reach_id = reach_id
@@ -153,6 +169,7 @@ class PlotXY(PamhyrPlot):
reach = results.river.reach(self._current_reach_id)
reaches = results.river.reachs
self.draw_geotiff()
self.draw_profiles(reach, reaches)
self.draw_water_elevation(reach)
self.draw_water_elevation_max(reach)
@@ -166,6 +183,7 @@ class PlotXY(PamhyrPlot):
if reach.geometry.number_profiles == 0:
self._init = False
return
self.line_xy = []
# TODO uncomment to draw all the reaches
# self.draw_other_profiles(reaches)
@@ -306,6 +324,41 @@ class PlotXY(PamhyrPlot):
alpha=0.7
)
def draw_geotiff(self):
if not _rasterio_loaded:
return
lst = self._data[0]._study.river._geotiff.lst
for img in self._plot_img:
self._plot_img[img].remove()
self._plot_img = {}
for geotiff in lst:
memfile = geotiff.memfile
if memfile is None:
return
with memfile.open() as gt:
img = gt.read()
coords = geotiff.coordinates
left = coords["left"]
right = coords["right"]
bottom = coords["bottom"]
top = coords["top"]
self._plot_img[geotiff] = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
if not geotiff.is_enabled():
self._plot_img[geotiff].set(alpha=0.5)
self.idle()
def set_reach(self, reach_id):
self._current_reach_id = reach_id
self._current_profile_id = 0
+91 -40
View File
@@ -56,9 +56,12 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QSlider, QLabel, QWidget, QGridLayout, QTabBar, QInputDialog
QSlider, QLabel, QWidget, QGridLayout, QTabBar,
QInputDialog,
)
from Model.Results.Results import AdditionalData
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
@@ -144,11 +147,14 @@ class ResultsWindow(PamhyrWindow):
profile_id=[0])
self.update_table_selection_solver(0)
self.update_plot_additional_data()
def setup_table(self):
self._table = {}
for t in ["reach", "profile", "raw_data", "solver"]:
table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel(
table_view=table,
table_headers=self._trad.get_dict(f"table_headers_{t}"),
@@ -157,8 +163,11 @@ class ResultsWindow(PamhyrWindow):
opt_data=t,
parent=self
)
self._table[t]._timestamp = self._timestamps[
self._slider_time.value()]
self._slider_time.value()
]
if len(self._results) <= 1:
table = self.find(QTableView, f"tableView_solver")
table.hide()
@@ -189,12 +198,15 @@ class ResultsWindow(PamhyrWindow):
def setup_plots(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
tab_widget = self.find(QTabWidget, f"tabWidget")
tab_widget.setTabsClosable(True)
tab_widget.tabCloseRequested.connect(self.delete_tab)
tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None)
self.canvas.setObjectName("canvas")
self.toolbar = PamhyrPlotToolbar(
self.canvas, self, items=[
@@ -202,6 +214,7 @@ class ResultsWindow(PamhyrWindow):
"iso", "back/forward"
]
)
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.toolbar)
self.plot_layout.addWidget(self.canvas)
@@ -1217,8 +1230,7 @@ class ResultsWindow(PamhyrWindow):
extent=[b[0], b[2], b[1], b[3]])
else:
dlg = CoordinatesDialog(
xlim,
ylim,
xlim, ylim,
trad=self._trad,
parent=self
)
@@ -1231,7 +1243,6 @@ class ResultsWindow(PamhyrWindow):
return
def import_data(self):
file_types = [
self._trad["file_csv"],
self._trad["file_all"],
@@ -1242,41 +1253,61 @@ class ResultsWindow(PamhyrWindow):
callback=lambda f: self.read_csv_file(f[0]),
default_suffix=".csv",
file_filter=file_types,
directory=self._results[self._current_results[0]]._repertory,
)
def read_csv_file(self, filename):
if filename == "":
return
sep = " "
x, y = self.read_csv_file_data(filename)
data = self.read_csv_file_format(x, y)
def is_float(string):
if string.replace(".", "").isnumeric():
return True
else:
return False
results = self._results[self._current_results[0]]
data_lst = results.get("additional_data")
data_lst.append(
AdditionalData(
study=self._study,
data=data
)
)
self.update_plot_additional_data()
def read_csv_file_data(self, filename):
sep = ","
x = []
y = []
with open(filename, 'r', newline='') as f:
lines = f.readlines()
x = []
y = []
for line in lines:
if line[0] != "*" and line[0] != "#" and line[0] != "$":
row = line.split(sep)
if len(row) >= 2:
if is_float(row[0]) and is_float(row[1]):
x.append(float(row[0]))
y.append(float(row[1]))
if len(row) < 2:
continue
try:
fx, fy = float(row[0]), float(row[1])
x.append(fx)
y.append(fy)
except Exception as e:
continue
return x, y
def read_csv_file_format(self, x, y):
data_type_lst = ['Q(t)', 'Z(t)', 'Z(x)']
data_type, ok = QInputDialog.getItem(
self, 'Data type', 'Chose the type of data:', data_type_lst)
self, 'Data type',
'Chose the type of data:',
data_type_lst
)
if not ok:
return
legend, ok = QInputDialog.getText(self, 'Legend', 'Legend:')
if not ok:
return
@@ -1291,25 +1322,45 @@ class ResultsWindow(PamhyrWindow):
tmp_unit = {'Z': ' (m)',
'Q': ' (m³/s)'}
data = {'type_x': tmp_dict[data_type[2]],
'type_y': tmp_dict[data_type[0]],
'legend': legend,
'unit': tmp_unit[data_type[0]],
'x': x,
'y': y}
data = {
'type_x': tmp_dict[data_type[2]],
'type_y': tmp_dict[data_type[0]],
'legend': legend,
'unit': tmp_unit[data_type[0]],
'x': x, 'y': y
}
if data_type == 'Z(x)':
line = self.canvas_2.axes.plot(x, y, marker="+",
label=legend + ' (m)')
self.plot_rkc.canvas.draw_idle()
self.plot_rkc.update_idle()
if data_type == 'Q(t)':
line = self.canvas_4.axes.plot(x, y, marker="+",
label=legend + ' (m³/s)')
self.plot_h._line.append(line)
self.plot_h.enable_legend()
self.plot_h.canvas.draw_idle()
self.plot_h.update_idle
return data
for p in self._additional_plot:
self._additional_plot[p].add_imported_plot(data)
def update_plot_additional_data(self):
results = self._results[self._current_results[0]]
for data in results.get("additional_data"):
data = data._data
x, y = data['x'], data['y']
legend = data['legend']
unit = data['unit']
if (
data['type_x'] == 'water_elevation' and
data['type_y'] == 'time'
):
line = self.canvas_2.axes.plot(
x, y, marker="+",
label=legend + ' ' + unit
)
self.plot_rkc.canvas.draw_idle()
self.plot_rkc.update_idle()
if data['type_x'] == 'time' and data['type_y'] == 'discharge':
line = self.canvas_4.axes.plot(
x, y, marker="+",
label=legend + ' ' + unit
)
self.plot_h._line.append(line)
self.plot_h.enable_legend()
self.plot_h.canvas.draw_idle()
self.plot_h.update_idle()
for p in self._additional_plot:
self._additional_plot[p].add_imported_plot(data)
-5
View File
@@ -53,11 +53,6 @@ class ResultsTranslate(MainTranslate):
"Results",
"Max water elevation"
)
self._dict["file_all"] = _translate("Results", "All files (*)")
self._dict["file_geotiff"] = _translate(
"Results", "GeoTIFF file (*.tiff *.tif)")
self._dict["file_csv"] = _translate(
"Results", "CSV file (*.csv)")
self._dict["ImageCoordinates"] = _translate(
"Results", "Image coordinates"
)
+14
View File
@@ -182,6 +182,7 @@ class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
self._set_title()
self._set_icon()
self._setup_save_sc()
def _set_title(self):
title = self._title
@@ -196,12 +197,25 @@ class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
self.ui.setWindowTitle(title)
def _setup_save_sc(self):
if self._parent is None:
return
self._save_sc = QShortcut(QKeySequence("Ctrl+S"), self)
self._save_sc.activated.connect(lambda: self._save(self))
def closeEvent(self, event):
self._close_sub_window()
self._propagate_update(Modules.WINDOW_LIST)
super(PamhyrWindow, self).closeEvent(event)
def _save(self, source):
if self._parent is None:
return
return self._parent._save(source)
class PamhyrDialog(ASubWindow, ListedSubWindow, PamhyrWindowTools):
_pamhyr_ui = "dummy"
+27
View File
@@ -54,6 +54,18 @@ class CommonWordTranslate(PamhyrTranslate):
self._dict["method"] = _translate("CommonWord", "Method")
# Files
self._dict["open_file"] = _translate(
"CommonWord", "Open file"
)
self._dict["file_all"] = _translate("CommonWord", "All files (*)")
self._dict["file_geotiff"] = _translate(
"CommonWord", "GeoTIFF file (*.tiff *.tif)"
)
self._dict["file_csv"] = _translate(
"CommonWord", "CSV file (*.csv)"
)
class UnitTranslate(CommonWordTranslate):
def __init__(self):
@@ -180,6 +192,9 @@ class MainTranslate(UnitTranslate):
)
# Message box
self._dict["Error"] = _translate(
"MainWindow", "Error"
)
self._dict["Warning"] = _translate(
"MainWindow", "Warning"
)
@@ -234,6 +249,17 @@ class MainTranslate(UnitTranslate):
self._dict["mb_diff_results_param_msg"] = _translate(
"MainWindow", "Results comparison parameters is invalid"
)
self._dict["mb_open_results_title"] = _translate(
"MainWindow", "Open results"
)
self._dict["mb_open_results_no_results_msg"] = _translate(
"MainWindow", "No results found"
)
self._dict["mb_open_results_invalid_results_msg"] = _translate(
"MainWindow", "Failed to read results"
)
self._dict["mb_diff_results_compatibility_msg"] = _translate(
"MainWindow",
"Results comparison with two "
@@ -247,3 +273,4 @@ class MainTranslate(UnitTranslate):
self._dict["Cancel"] = _translate("MainWindow", "Cancel")
self._dict["Save"] = _translate("MainWindow", "Save")
self._dict["Close"] = _translate("MainWindow", "Close")
self._dict["Solver"] = _translate("MainWindow", "Solver")
+1 -1
View File
@@ -63,7 +63,7 @@ class WaitingDialog(PamhyrDialog):
". ", ". ",
".. ", ".. ",
"...", "..."],
["o ", " o ", " o ", " o", " o ", " o "],
["o ", " o ", " o ", " o", " o ", " o "],
["█▓▒░", "▓█▓▒", "▒▓█▓", "░▒▓█", "▒▓█▓", "▓█▓▒"],
"▖▘▝▗",
"αβγδεζηθικλμνξοπρστυφχψω",
+264
View File
@@ -0,0 +1,264 @@
<?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>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_ok">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Informations</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Enabled</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_description">
<property name="toolTip">
<string>The relative file path on executable directory</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_name"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>GeoTIFF file</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_import">
<property name="text">
<string>Import GeoTIFF file</string>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout_geotiff"/>
</widget>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="2">
<widget class="QPushButton" name="pushButton_left">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_right">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Right coordinate</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pushButton_right">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_left">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton_bottom">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButton_top">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_right">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_top">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_top">
<property name="text">
<string>Top coordinate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_bottom">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_left">
<property name="text">
<string>Left coordinate</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_bottom">
<property name="text">
<string>Bottom coordinate</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
+84
View File
@@ -0,0 +1,84 @@
<?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>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListView" name="listView"/>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QToolBar" name="toolBar">
<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_delete"/>
<addaction name="action_edit"/>
</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 a new file</string>
</property>
</action>
<action name="action_delete">
<property name="icon">
<iconset>
<normaloff>ressources/del.png</normaloff>ressources/del.png</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected file(s)</string>
</property>
</action>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit</string>
</property>
<property name="toolTip">
<string>Edit file</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
+6
View File
@@ -129,6 +129,7 @@
<string>&amp;Geometry</string>
</property>
<addaction name="action_menu_edit_geometry"/>
<addaction name="action_menu_edit_geotiff"/>
</widget>
<widget class="QMenu" name="menu_run">
<property name="locale">
@@ -811,6 +812,11 @@
<string>Compare results</string>
</property>
</action>
<action name="action_menu_edit_geotiff">
<property name="text">
<string>GeoTIFF</string>
</property>
</action>
</widget>
<resources/>
<connections>