Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds wrapper widget to monitor state changes to child widgets and emi… #290

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions geest/gui/layer_detail_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from qgis.PyQt.QtCore import Qt, pyqtSignal
from .toggle_switch import ToggleSwitch
from geest.utilities import resources_path
from geest.gui.widgets import GeestWidgetFactory
from geest.gui.widgets import GeestConfigWidget


class LayerDetailDialog(QDialog):
Expand Down Expand Up @@ -208,13 +208,19 @@ def get_widget_for_value(self, key, value):
return line_edit

def add_config_widgets(self, layout):
#---------
widgets_container = GeestWidgetFactory.create_widgets(self.layer_data, self)
if widgets_container and widgets_container.layout().count() > 0:
layout.addWidget(widgets_container)
config_widget = GeestConfigWidget(self.layer_data)
if config_widget.widgets:
layout.addWidget(config_widget)
# connect to the stateChanged signal
config_widget.stateChanged.connect(self.handle_config_change)
else:
print("No configuration widgets were created for this layer.")
#----------

# Optionally, add this method to handle configuration changes
def handle_config_change(self, new_config):
# Update your layer_data or perform any other necessary actions
self.layer_data = new_config
print("Configuration updated:", new_config)

def accept_changes(self):
"""Handle the OK button by applying changes and closing the dialog."""
Expand Down
6 changes: 5 additions & 1 deletion geest/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .geest_config_widget import GeestConfigWidget
from .geest_widget_factory import GeestWidgetFactory

__all__ = ['GeestWidgetFactory']
__all__ = [
"GeestConfigWidget",
"GeestWidgetFactory",
]
170 changes: 170 additions & 0 deletions geest/gui/widgets/geest_config_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
from qgis.PyQt.QtWidgets import (
QWidget, QVBoxLayout, QRadioButton, QLineEdit,
QSpinBox, QDoubleSpinBox, QComboBox
)
from qgis.PyQt.QtCore import pyqtSignal
from qgis.gui import QgsMapLayerComboBox
#from qgis.core import QgsVectorLayer, QgsRasterLayer
from qgis.core import QgsProviderRegistry
from .geest_widget_factory import GeestWidgetFactory


class GeestConfigWidget(QWidget):
stateChanged = pyqtSignal(dict)

def __init__(self, config_dict):
super().__init__()
self.original_config = config_dict
self.modified_config = config_dict.copy()
self.widgets = {}
print(f"Initializing GeestConfigWidget with config: {config_dict}")
self.create_widgets()
self.setup_connections()

def create_widgets(self):
layout = QVBoxLayout()
self.setLayout(layout)

print("Calling GeestWidgetFactory.create_widgets")
widgets_container = GeestWidgetFactory.create_widgets(self.original_config, self)

if widgets_container is None:
print("GeestWidgetFactory.create_widgets returned None")
return

if not isinstance(widgets_container, QWidget):
print(f"GeestWidgetFactory.create_widgets returned unexpected type: {type(widgets_container)}")
return

if widgets_container.layout() is None:
print("widgets_container has no layout")
return

if widgets_container.layout().count() > 0:
print(f"Adding container with {widgets_container.layout().count()} items")
layout.addWidget(widgets_container)
self.find_and_store_widgets(widgets_container)
else:
print("No widgets were created by GeestWidgetFactory")

print(self.dump_widget_hierarchy(widgets_container))

def find_and_store_widgets(self, container):
print("Starting to find and store widgets")
self.recursive_find_and_store_widgets(container)
print(f"Total widgets stored: {len(self.widgets)}")

def recursive_find_and_store_widgets(self, widget, depth=0):
print(" " * depth + f"Examining widget: {type(widget).__name__}")
use_key = widget.property("use_key")
if use_key:
if isinstance(widget, QRadioButton):
if use_key not in self.widgets:
self.widgets[use_key] = {}
self.widgets[use_key]["radio"] = widget
print(" " * depth + f"Stored QRadioButton for key: {use_key}")
elif isinstance(widget, (QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QgsMapLayerComboBox)):
if use_key not in self.widgets:
self.widgets[use_key] = {}
self.widgets[use_key]["widget"] = widget
print(" " * depth + f"Stored {type(widget).__name__} for key: {use_key}")

if widget.layout():
for i in range(widget.layout().count()):
item = widget.layout().itemAt(i)
if item.widget():
self.recursive_find_and_store_widgets(item.widget(), depth + 1)

def setup_connections(self):
print("Setting up connections")
for key, widgets in self.widgets.items():
radio = widgets.get("radio")
widget = widgets.get("widget")
if radio:
radio.toggled.connect(lambda checked, k=key: self.handle_option_change(k, checked))
print(f"Set up radio connection for {key}")
if widget:
if isinstance(widget, QgsMapLayerComboBox):
print(f"Setting up connection for QgsMapLayerComboBox: {key}")
widget.layerChanged.connect(lambda layer, k=key: self.update_layer_path(k, layer))
elif isinstance(widget, QLineEdit):
widget.textChanged.connect(lambda text, k=key: self.update_sub_widget_state(k, text))
elif isinstance(widget, (QSpinBox, QDoubleSpinBox)):
widget.valueChanged.connect(lambda value, k=key: self.update_sub_widget_state(k, value))
elif isinstance(widget, QComboBox):
widget.currentTextChanged.connect(lambda text, k=key: self.update_sub_widget_state(k, text))
print(f"Set up widget connection for {key}: {type(widget).__name__}")

def update_layer_path(self, key, layer):
print(f"update_layer_path called for {key}") # Debug print
if layer:
provider_key = layer.providerType()
uri = layer.dataProvider().dataSourceUri()
print(f"Layer URI: {uri}")
decoded = QgsProviderRegistry.instance().decodeUri(provider_key, uri)
print(f"Decoded URI: {decoded}")
path = decoded.get('path') or decoded.get('url') or decoded.get('layerName')
if path:
print(f"Path found: {path}")
self.update_sub_widget_state(key, path)
else:
print(f"Unable to determine path for layer {layer.name()} with provider {provider_key}")
self.update_sub_widget_state(key, uri) # Fallback to using the full URI
else:
print(f"No layer selected for {key}")
self.update_sub_widget_state(key, None)

def handle_option_change(self, option, checked):
if checked:
for key, widgets in self.widgets.items():
widget = widgets.get("widget")
if widget:
widget.setEnabled(key == option)

for key in self.widgets.keys():
self.modified_config[key] = 1 if key == option else 0

self.stateChanged.emit(self.get_state())

def update_sub_widget_state(self, option, value):
if value is not None:
self.modified_config[option] = value
self.stateChanged.emit(self.get_state())
else:
print(f"Received None value for option: {option}")
self.modified_config[option] = "0"
self.stateChanged.emit(self.get_state())

def get_state(self):
return self.modified_config.copy()

def reset_to_original(self):
self.modified_config = self.original_config.copy()
self.update_widgets_from_config()
self.stateChanged.emit(self.get_state())

def update_widgets_from_config(self):
for key, value in self.modified_config.items():
if key in self.widgets:
widgets = self.widgets[key]
radio = widgets.get("radio")
widget = widgets.get("widget")
if radio:
radio.setChecked(bool(value))
if widget:
if isinstance(widget, QLineEdit):
widget.setText(str(value))
elif isinstance(widget, (QSpinBox, QDoubleSpinBox)):
widget.setValue(float(value) if isinstance(widget, QDoubleSpinBox) else int(value))
elif isinstance(widget, (QComboBox, QgsMapLayerComboBox)):
widget.setCurrentText(str(value))

def dump_widget_hierarchy(self, widget, level=0):
output = []
output.append(" " * level + f"{widget.__class__.__name__}")
if hasattr(widget, 'layout') and widget.layout():
for i in range(widget.layout().count()):
item = widget.layout().itemAt(i)
if item.widget():
output.append(self.dump_widget_hierarchy(item.widget(), level + 1))
return "\n".join(output)
45 changes: 35 additions & 10 deletions geest/gui/widgets/geest_widget_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def create_widgets(layer_data: dict, parent=None):
option_container.setLayout(option_layout)

radio_button = QRadioButton(mapping["label"])
radio_button.setProperty("use_key", use_key) # Set property here
radio_group.addButton(radio_button, id=idx)
option_layout.addWidget(radio_button)

Expand All @@ -175,6 +176,7 @@ def create_widgets(layer_data: dict, parent=None):

widget = GeestWidgetFactory.create_specific_widget(mapping, layer_data)
if widget:
widget.setProperty("use_key", use_key)
option_layout.addWidget(widget)

main_layout.addWidget(option_container)
Expand Down Expand Up @@ -239,18 +241,24 @@ def create_specific_widget(mapping: dict, layer_data: dict):
elif subtype_mapped == "point":
widget.setFilters(QgsMapLayerProxyModel.PointLayer)
else:
print(
f"Invalid layer subtype '{layer_type}' for '{mapping.get('label')}'. Defaulting to all vector layers.")
print(f"Invalid layer subtype '{layer_type}' for '{mapping.get('label')}'. Defaulting to all vector layers.")
widget.setFilters(QgsMapLayerProxyModel.VectorLayer)
else:
print(f"Unknown layer type '{layer_type}' for '{mapping.get('label')}'. Defaulting to all layers.")
widget.setFilters(QgsMapLayerProxyModel.All)
widget.setToolTip(mapping.get("tooltip", ""))
return widget

# Check if layers are available
if widget.count() == 0:
label = QLabel("<No appropriate layer found>")
return label

else:
widget.setToolTip(mapping.get("tooltip", ""))
return widget

elif widget_type == "csv_to_point":
container = QWidget()
layout = QHBoxLayout()
layout = QVBoxLayout()
container.setLayout(layout)

# Create QgsFileWidget
Expand All @@ -260,17 +268,35 @@ def create_specific_widget(mapping: dict, layer_data: dict):
mapping.get("tooltip", "Select a CSV file containing longitude and latitude columns."))
layout.addWidget(file_widget)

# Create ComboBoxes for Longitude and Latitude
# Create layouts for longitude and latitude columns
lon_layout = QVBoxLayout()
lat_layout = QVBoxLayout()

# Create and add label for Longitude
lon_label = QLabel("Longitude column")
lon_layout.addWidget(lon_label)

# Create ComboBox for Longitude
longitude_combo = QComboBox()
longitude_combo.setPlaceholderText("Longitude Column")
longitude_combo.setEnabled(False)
longitude_combo.setToolTip("Select the column for longitude.")
layout.addWidget(longitude_combo)
lon_layout.addWidget(longitude_combo)

# Create and add label for Latitude
lat_label = QLabel("Latitude column")
lat_layout.addWidget(lat_label)

# Create ComboBox for Latitude
latitude_combo = QComboBox()
latitude_combo.setPlaceholderText("Latitude Column")
latitude_combo.setEnabled(False)
latitude_combo.setToolTip("Select the column for latitude.")
layout.addWidget(latitude_combo)
lat_layout.addWidget(latitude_combo)

# Add the longitude and latitude layouts to the main layout
layout.addLayout(lon_layout)
layout.addLayout(lat_layout)

# Connect file selection to populate and auto-fill combo boxes
file_widget.fileChanged.connect(
Expand Down Expand Up @@ -329,5 +355,4 @@ def populate_csv_columns(file_path: str, lon_combo: QComboBox, lat_combo: QCombo
lon_combo.clear()
lat_combo.clear()
lon_combo.setEnabled(False)
lat_combo.setEnabled(False)

lat_combo.setEnabled(False)
Loading