Skip to content

Commit d4c9878

Browse files
committed
Propagate project crs change to widget
1 parent 0a33a95 commit d4c9878

1 file changed

Lines changed: 49 additions & 17 deletions

File tree

‎loopstructural/gui/modelling/model_definition/bounding_box.py‎

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import numpy as np
44
from PyQt5.QtWidgets import QWidget
5+
from qgis.core import QgsProject
56
from qgis.PyQt import uic
67

78
from loopstructural.main.data_manager import default_bounding_box
@@ -13,7 +14,7 @@ def __init__(self, parent=None, data_manager=None):
1314
super().__init__(parent)
1415
ui_path = os.path.join(os.path.dirname(__file__), "bounding_box.ui")
1516
uic.loadUi(ui_path, self)
16-
17+
1718
# Connect bounding box spinbox signals
1819
self.originXSpinBox.valueChanged.connect(lambda x: self.onChangeExtent({'xmin': x}))
1920
self.maxXSpinBox.valueChanged.connect(lambda x: self.onChangeExtent({'xmax': x}))
@@ -23,20 +24,28 @@ def __init__(self, parent=None, data_manager=None):
2324
self.maxZSpinBox.valueChanged.connect(lambda z: self.onChangeExtent({'zmax': z}))
2425
self.useCurrentViewExtentButton.clicked.connect(self.useCurrentViewExtent)
2526
self.selectFromCurrentLayerButton.clicked.connect(self.selectFromCurrentLayer)
26-
27+
2728
# Connect CRS control signals
2829
self.useProjectCrsRadioButton.toggled.connect(self.onCrsSourceChanged)
2930
self.useCustomCrsRadioButton.toggled.connect(self.onCrsSourceChanged)
3031
self.crsSelector.crsChanged.connect(self.onCrsChanged)
31-
32+
3233
# Set up callbacks
3334
self.data_manager.set_bounding_box_update_callback(self.set_bounding_box)
3435
self.data_manager.set_model_crs_callback(self.update_crs_ui)
35-
36+
3637
# Initialize CRS UI
3738
self.initialize_crs_ui()
3839
self._update_bounding_box_styles()
3940

41+
# Connect to project CRS changes so the widget updates when the project's CRS changes
42+
try:
43+
project = getattr(self.data_manager, 'project', None) or QgsProject.instance()
44+
project.crsChanged.connect(self._onProjectCrsChanged)
45+
except Exception:
46+
# If the signal isn't available or connection fails, ignore to keep widget functional
47+
pass
48+
4049
def initialize_crs_ui(self):
4150
"""Initialize CRS controls with current settings."""
4251
# Set initial CRS selector value
@@ -46,28 +55,28 @@ def initialize_crs_ui(self):
4655
else:
4756
# Default to project CRS
4857
self.crsSelector.setCrs(self.data_manager.project.crs())
49-
58+
5059
# Set radio button based on use_project_crs setting
5160
if self.data_manager._use_project_crs:
5261
self.useProjectCrsRadioButton.setChecked(True)
5362
else:
5463
self.useCustomCrsRadioButton.setChecked(True)
55-
64+
5665
self.validate_crs()
5766

5867
def onCrsSourceChanged(self):
5968
"""Handle change in CRS source (project vs custom)."""
6069
use_project_crs = self.useProjectCrsRadioButton.isChecked()
6170
self.crsSelector.setEnabled(not use_project_crs)
62-
71+
6372
if use_project_crs:
6473
# Use project CRS
6574
success, msg = self.data_manager.set_model_crs(None, use_project_crs=True)
6675
else:
6776
# Use custom CRS
6877
crs = self.crsSelector.crs()
6978
success, msg = self.data_manager.set_model_crs(crs, use_project_crs=False)
70-
79+
7180
self.validate_crs()
7281

7382
def onCrsChanged(self):
@@ -79,7 +88,7 @@ def onCrsChanged(self):
7988

8089
def update_crs_ui(self, crs, use_project_crs):
8190
"""Update UI when model CRS changes externally.
82-
91+
8392
Parameters
8493
----------
8594
crs : QgsCoordinateReferenceSystem or None
@@ -91,45 +100,68 @@ def update_crs_ui(self, crs, use_project_crs):
91100
self.useProjectCrsRadioButton.blockSignals(True)
92101
self.useCustomCrsRadioButton.blockSignals(True)
93102
self.crsSelector.blockSignals(True)
94-
103+
95104
try:
96105
if use_project_crs:
97106
self.useProjectCrsRadioButton.setChecked(True)
98107
self.crsSelector.setEnabled(False)
108+
self.crsSelector.setCrs(crs)
109+
99110
else:
100111
self.useCustomCrsRadioButton.setChecked(True)
101112
self.crsSelector.setEnabled(True)
102113
if crs is not None and crs.isValid():
103114
self.crsSelector.setCrs(crs)
104-
115+
105116
self.validate_crs()
106117
finally:
107118
# Unblock signals
108119
self.useProjectCrsRadioButton.blockSignals(False)
109120
self.useCustomCrsRadioButton.blockSignals(False)
110121
self.crsSelector.blockSignals(False)
111122

123+
def _onProjectCrsChanged(self, crs=None):
124+
"""Handle project CRS changes and update UI when the widget is using the project CRS.
125+
126+
Accept an optional `crs` argument because different QGIS versions may emit the
127+
new CRS or emit no arguments when the project's CRS changes.
128+
"""
129+
# If the signal didn't provide a CRS, try to obtain it from the project's current CRS
130+
if crs is None:
131+
try:
132+
project = getattr(self.data_manager, 'project', None) or QgsProject.instance()
133+
crs = project.crs()
134+
except Exception:
135+
crs = None
136+
137+
# Only update the UI if the model is configured to use the project CRS
138+
try:
139+
if getattr(self.data_manager, '_use_project_crs', False):
140+
# Update the UI to reflect the new project CRS
141+
self.update_crs_ui(crs, use_project_crs=True)
142+
except Exception:
143+
pass
144+
112145
def validate_crs(self):
113146
"""Validate the selected CRS and update warning label."""
114147
crs = self.data_manager.get_model_crs()
115-
148+
116149
if crs is None or not crs.isValid():
117150
self.crsWarningLabel.setText("⚠ Invalid CRS selected. Model cannot be initialized.")
118151
return False
119-
152+
120153
if crs.isGeographic():
121154
# Safely get CRS description
122155
try:
123156
crs_desc = crs.description() or crs.authid() or "Unknown"
124157
except Exception:
125158
crs_desc = crs.authid() if hasattr(crs, 'authid') else "Unknown"
126-
159+
127160
self.crsWarningLabel.setText(
128-
f"⚠ CRS must be projected (in meters), not geographic.\n"
129-
f"Selected: {crs_desc}"
161+
f"⚠ CRS must be projected (in meters), not geographic.\n" f"Selected: {crs_desc}"
130162
)
131163
return False
132-
164+
133165
# CRS is valid and projected
134166
self.crsWarningLabel.setText("")
135167
return True

0 commit comments

Comments
 (0)