Merge branch 'new_design_main_page' into dev_dylan

dev_dylan
Dylan Jeannin 2026-09-08 17:31:54 +02:00
commit 6d4dc3a22b
5 changed files with 222 additions and 17 deletions

View File

@ -61,6 +61,7 @@ from View.WaitingDialog import WaitingDialog
from View.MainWindowTabInfo import WidgetInfo from View.MainWindowTabInfo import WidgetInfo
from View.MainWindowTabChecker import WidgetChecker from View.MainWindowTabChecker import WidgetChecker
from View.MainWindowTabGraph import WidgetGraph
from View.Configure.Window import ConfigureWindow from View.Configure.Window import ConfigureWindow
from View.Study.Window import NewStudyWindow from View.Study.Window import NewStudyWindow
@ -235,6 +236,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
def setup_tab(self): def setup_tab(self):
self.setup_tab_info() self.setup_tab_info()
self.setup_tab_graph()
self.setup_tab_checker() self.setup_tab_checker()
def setup_tab_info(self): def setup_tab_info(self):
@ -261,6 +263,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._trad["tab_checker_name"] self._trad["tab_checker_name"]
) )
def setup_tab_graph(self):
tab_widget = self.findChild(QTabWidget, "tabWidget")
self._tab_widget_graph = WidgetGraph(
study=self._study, trad=self._trad, parent=self
)
tab_widget.insertTab(
0, self._tab_widget_graph,
self._trad["tab_graph_name"]
)
tab_widget.setCurrentWidget(self._tab_widget_graph)
def enable_actions(self, action: str, enable: bool): def enable_actions(self, action: str, enable: bool):
"""Enable of disable an action componant """Enable of disable an action componant
@ -545,6 +560,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._do_propagate_update_rec(window, keys) self._do_propagate_update_rec(window, keys)
self._tab_widget_checker.update(modules=keys) self._tab_widget_checker.update(modules=keys)
self._tab_widget_graph.update()
def _do_propagate_update_info_tab(self, keys): def _do_propagate_update_info_tab(self, keys):
modules = Modules.modelling_list() modules = Modules.modelling_list()
@ -570,6 +586,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._tab_widget_info.study = self._study self._tab_widget_info.study = self._study
self._tab_widget_info.update() self._tab_widget_info.update()
if self._tab_widget_graph.study != self._study:
self._tab_widget_graph.study = self._study
self._tab_widget_graph.update()
if self._tab_widget_checker.study != self._study: if self._tab_widget_checker.study != self._study:
self._tab_widget_checker.study = self._study self._tab_widget_checker.study = self._study
self._tab_widget_checker.update(modules=Modules.STUDY) self._tab_widget_checker.update(modules=Modules.STUDY)
@ -1287,6 +1307,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return return
self.network = NetworkWindow(study=self._study, parent=self) self.network = NetworkWindow(study=self._study, parent=self)
self.network.networkChanged.connect(
self._tab_widget_graph.update, Qt.QueuedConnection
)
self.network.show() self.network.show()
def open_scenarios(self): def open_scenarios(self):

View File

@ -0,0 +1,139 @@
# MainWindowTabGraph.py -- Pamhyr
# Copyright (C) 2026 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.
# -*- coding: utf-8 -*-
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.PlotXY import PlotXY
from View.Network.GraphWidget import GraphWidget
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QWidget, QGroupBox, QLabel, QHBoxLayout, QVBoxLayout, QSplitter,
)
class WidgetGraph(QWidget):
"""Read-only network graph displayed in the main window."""
def __init__(self, study=None, trad=None, parent=None):
super(WidgetGraph, self).__init__(parent)
self._study = study
self._trad = trad
self._plot_river = None
layout = QHBoxLayout(self)
self._splitter = QSplitter(Qt.Horizontal, self)
self._splitter.setChildrenCollapsible(False)
layout.addWidget(self._splitter)
self._graph_widget = None
self._graph_group = QGroupBox(
self._trad["graph_network_title"], self
)
self._graph_layout = QVBoxLayout(self._graph_group)
self._graph_title = QLabel(self._graph_group)
self._graph_title.setTextFormat(Qt.PlainText)
self._graph_title.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self._graph_title.setWordWrap(True)
self._graph_layout.addWidget(self._graph_title)
self._update_graph_title()
self._splitter.addWidget(self._graph_group)
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas_graph")
self.toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_group = QGroupBox(
self._trad["graph_geographic_title"], self
)
self._plot_layout = QVBoxLayout(self._plot_group)
self._plot_layout.addWidget(self.toolbar)
self._plot_layout.addWidget(self.canvas, 1)
self._splitter.addWidget(self._plot_group)
self._splitter.setStretchFactor(0, 1)
self._splitter.setStretchFactor(1, 1)
self._splitter.setSizes([1000, 1000])
self.plot = PlotXY(
canvas=self.canvas,
data=None,
trad=self._trad,
toolbar=self.toolbar,
parent=self
)
@property
def study(self):
return self._study
@study.setter
def study(self, study):
self._study = study
self._update_graph_title()
def _update_graph_title(self):
self._graph_title.setText(
self._study.name if self._study is not None else ""
)
def update(self):
river = self._study.river if self._study is not None else None
view = None
if river is not None and river is self._plot_river:
axes = self.canvas.axes
if axes.has_data():
view = (axes.get_xlim(), axes.get_ylim(),
axes.get_aspect(), axes.get_adjustable())
self._update_graph_title()
if (self._graph_widget is not None
and (self._study is None
or self._graph_widget.graph is not self._study.river)):
self._graph_layout.removeWidget(self._graph_widget)
self._graph_widget.deleteLater()
self._graph_widget = None
if self._study is not None and self._graph_widget is None:
self._graph_widget = GraphWidget(
self._study.river,
parent=self,
only_display=True,
trad=self._trad,
)
self._graph_layout.addWidget(self._graph_widget, 1)
elif self._graph_widget is not None:
self._graph_widget.display_update()
data = None
geotiff = None
if self._study is not None:
data = self._study.river.enable_edges()
geotiff = self._study.river.geotiff
self.plot = PlotXY(
canvas=self.canvas,
data=data,
geotiff=geotiff,
trad=self._trad,
toolbar=self.toolbar,
parent=self
)
self.plot.update()
self._plot_river = river
if view is not None:
x_limits, y_limits, aspect, adjustable = view
self.canvas.axes.set_aspect(aspect, adjustable=adjustable)
self.canvas.axes.set_xlim(x_limits)
self.canvas.axes.set_ylim(y_limits)
self.canvas.draw_idle()

View File

@ -448,6 +448,7 @@ class NewEdgeLine(QGraphicsItem):
class GraphWidget(QGraphicsView): class GraphWidget(QGraphicsView):
changeEdge = pyqtSignal(object) changeEdge = pyqtSignal(object)
changeNode = pyqtSignal(object) changeNode = pyqtSignal(object)
currentEdgeChanged = pyqtSignal()
def __init__(self, graph, parent=None, def __init__(self, graph, parent=None,
min_size=(400, 400), max_size=None, min_size=(400, 400), max_size=None,
@ -476,6 +477,7 @@ class GraphWidget(QGraphicsView):
self.m_origin_x = 0.0 self.m_origin_x = 0.0
self.m_origin_y = 0.0 self.m_origin_y = 0.0
self.clicked = False self.clicked = False
self._panning = False
self.setup_scene(min_size, max_size, size) self.setup_scene(min_size, max_size, size)
@ -491,8 +493,8 @@ class GraphWidget(QGraphicsView):
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorViewCenter) self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
self.scale(1, 1) self.scale(0.7, 0.7)
self.previousScale = 1 self.previousScale = 0.7
self.centerOn(1000.0, 1000.0) self.centerOn(1000.0, 1000.0)
if min_size: if min_size:
@ -667,6 +669,13 @@ class GraphWidget(QGraphicsView):
Returns: Returns:
Nothing Nothing
""" """
# Scene items are destroyed by clear(); do not retain hover/selection
# references to them when refreshing another view of the same graph.
self._selected_item = None
self._selected_new_edge_src_node = None
self._current_edge = None
self._current_moved_node = None
self.tmp_line = None
self.scene().clear() self.scene().clear()
self.create_items() self.create_items()
@ -875,6 +884,9 @@ class GraphWidget(QGraphicsView):
if previous_edge: if previous_edge:
previous_edge.update() previous_edge.update()
edge.update()
if previous_edge is not edge:
self.currentEdgeChanged.emit()
except Exception as e: except Exception as e:
logger.warning(str(e)) logger.warning(str(e))
@ -937,6 +949,8 @@ class GraphWidget(QGraphicsView):
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self._only_display: if self._only_display:
if event.button() == Qt.LeftButton:
self._start_pan(event)
return return
if event.button() == Qt.RightButton \ if event.button() == Qt.RightButton \
@ -959,6 +973,12 @@ class GraphWidget(QGraphicsView):
self._selected_new_edge_src_node = None self._selected_new_edge_src_node = None
items = self.items(event.pos()) items = self.items(event.pos())
if event.button() == Qt.LeftButton and not any(
isinstance(item, (NodeItem, EdgeItem, NodeText))
for item in items
):
self._start_pan(event)
return
if items and type(items[0]) is EdgeItem: if items and type(items[0]) is EdgeItem:
edge = items[0] edge = items[0]
if edge: if edge:
@ -1007,7 +1027,10 @@ class GraphWidget(QGraphicsView):
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
self.clicked = False self.clicked = False
if self._only_display: if self._panning or self._only_display:
self._panning = False
self.viewport().unsetCursor()
event.accept()
return return
locked = self.graph._status.is_read_only() locked = self.graph._status.is_read_only()
@ -1029,7 +1052,28 @@ class GraphWidget(QGraphicsView):
self.update() self.update()
super(GraphWidget, self).mouseReleaseEvent(event) super(GraphWidget, self).mouseReleaseEvent(event)
def _start_pan(self, event):
self.clicked = True
self._panning = True
self.m_origin_x = event.x()
self.m_origin_y = event.y()
self.viewport().setCursor(Qt.ClosedHandCursor)
event.accept()
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
if self._panning or self._only_display:
if self._panning and event.buttons() & Qt.LeftButton:
dx = event.x() - self.m_origin_x
dy = event.y() - self.m_origin_y
horizontal = self.horizontalScrollBar()
vertical = self.verticalScrollBar()
horizontal.setValue(horizontal.value() - dx)
vertical.setValue(vertical.value() - dy)
self.m_origin_x = event.x()
self.m_origin_y = event.y()
event.accept()
return
pos = self.mapToScene(event.pos()) pos = self.mapToScene(event.pos())
locked = self.graph._status.is_read_only() locked = self.graph._status.is_read_only()
@ -1061,20 +1105,6 @@ class GraphWidget(QGraphicsView):
# If state is "move" # If state is "move"
if self._state == "move": if self._state == "move":
# Move on scene
if not self.selected_item():
if event.buttons() & Qt.LeftButton:
old_p = self.mapToScene(
int(self.m_origin_x), int(self.m_origin_y)
)
new_p = self.mapToScene(event.pos())
translation = new_p - old_p
self.translate(translation.x(), translation.y())
self.m_origin_x = event.x()
self.m_origin_y = event.y()
# Propagate event # Propagate event
self.update() self.update()
super(GraphWidget, self).mouseMoveEvent(event) super(GraphWidget, self).mouseMoveEvent(event)

View File

@ -58,6 +58,8 @@ _translate = QCoreApplication.translate
class NetworkWindow(PamhyrWindow): class NetworkWindow(PamhyrWindow):
networkChanged = pyqtSignal()
_pamhyr_ui = "Network" _pamhyr_ui = "Network"
_pamhyr_name = "River network" _pamhyr_name = "River network"
@ -140,6 +142,8 @@ class NetworkWindow(PamhyrWindow):
self._graph_layout.addWidget(self._graph_widget) self._graph_layout.addWidget(self._graph_widget)
def setup_connections(self): def setup_connections(self):
self._undo_stack.indexChanged.connect(self.networkChanged)
self._graph_widget.currentEdgeChanged.connect(self.networkChanged)
self._nodes_model.dataChanged.connect(self.update) self._nodes_model.dataChanged.connect(self.update)
self._reachs_model.dataChanged.connect( self._reachs_model.dataChanged.connect(
self._graph_widget.display_update) self._graph_widget.display_update)

View File

@ -175,6 +175,15 @@ class MainTranslate(UnitTranslate):
self._dict["tab_checker_name"] = _translate( self._dict["tab_checker_name"] = _translate(
"MainWindow", "Checks" "MainWindow", "Checks"
) )
self._dict["tab_graph_name"] = _translate(
"MainWindow", "Graph"
)
self._dict["graph_network_title"] = _translate(
"MainWindow", "Hydrographic network"
)
self._dict["graph_geographic_title"] = _translate(
"MainWindow", "Geographic view"
)
self._dict["open_debug"] = _translate( self._dict["open_debug"] = _translate(
"MainWindow", "Open debug window" "MainWindow", "Open debug window"