diff --git a/config.json b/config.json index a168a51d..53b40ade 100644 --- a/config.json +++ b/config.json @@ -16,7 +16,7 @@ "author": "Kartoza", "email": "info@kartoza.com", "description": "Gender Enabling Environments Spatial Tool", - "version": "0.4.1", + "version": "0.4.2", "changelog": "", "server": false } diff --git a/geest/gui/dialogs/analysis_aggregation_dialog.py b/geest/gui/dialogs/analysis_aggregation_dialog.py index 0e5d7a6b..3db1d491 100644 --- a/geest/gui/dialogs/analysis_aggregation_dialog.py +++ b/geest/gui/dialogs/analysis_aggregation_dialog.py @@ -16,9 +16,10 @@ QCheckBox, QWidget, QHBoxLayout, + QSpacerItem, ) -from qgis.PyQt.QtGui import QPixmap -from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtGui import QPixmap, QDesktopServices +from qgis.PyQt.QtCore import Qt, QUrl from qgis.core import Qgis from geest.utilities import ( resources_path, @@ -47,7 +48,7 @@ def __init__(self, analysis_item, parent=None): # Title label self.title_label = QLabel( - "Geospatial Assessment of Women Employment and Business Opportunities in the Renewable Energy Sector", + "The Gender Enabling Environments Spatial Tool", self, ) self.title_label.setWordWrap(True) @@ -179,6 +180,33 @@ def __init__(self, analysis_item, parent=None): layout.addWidget(self.table) + help_layout = QHBoxLayout() + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + self.help_icon = QPixmap(resources_path("resources", "images", "help.png")) + self.help_icon = self.help_icon.scaledToWidth(20) + self.help_label_icon = QLabel() + self.help_label_icon.setPixmap(self.help_icon) + self.help_label_icon.setScaledContents(True) + self.help_label_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.help_label_icon.setMaximumWidth(20) + self.help_label_icon.setAlignment(Qt.AlignRight) + help_layout.addWidget(self.help_label_icon) + + self.help_label = QLabel( + "For detailed instructions on how to use this tool, please refer to the GEEST User Guide." + ) + self.help_label.setOpenExternalLinks(True) + self.help_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.help_label) + self.help_label.linkActivated.connect(self.open_link_in_browser) + help_layout.addWidget(self.help_label) + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + layout.addLayout(help_layout) + # QDialogButtonBox for OK and Cancel self.button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel @@ -207,6 +235,10 @@ def __init__(self, analysis_item, parent=None): # Initial validation check self.validate_weightings() + def open_link_in_browser(self, url: str): + """Open the given URL in the user's default web browser using QDesktopServices.""" + QDesktopServices.openUrl(QUrl(url)) + def toggle_guid_column(self): """Toggle the visibility of the GUID column.""" log_message("Toggling GUID column visibility") diff --git a/geest/gui/dialogs/dimension_aggregation_dialog.py b/geest/gui/dialogs/dimension_aggregation_dialog.py index 21d63a48..424572a8 100644 --- a/geest/gui/dialogs/dimension_aggregation_dialog.py +++ b/geest/gui/dialogs/dimension_aggregation_dialog.py @@ -1,24 +1,22 @@ from qgis.PyQt.QtWidgets import ( QDialog, QDialogButtonBox, - QFrame, QHeaderView, QLabel, QDoubleSpinBox, QPushButton, QSizePolicy, QSpacerItem, - QSplitter, QTableWidget, QTableWidgetItem, - QTextEdit, QVBoxLayout, QCheckBox, QWidget, QHBoxLayout, + QSpacerItem, ) -from qgis.PyQt.QtGui import QPixmap -from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtGui import QPixmap, QDesktopServices +from qgis.PyQt.QtCore import Qt, QUrl from qgis.core import Qgis from geest.utilities import ( resources_path, @@ -51,7 +49,7 @@ def __init__(self, dimension_name, dimension_data, dimension_item, parent=None): # Title label self.title_label = QLabel( - "Geospatial Assessment of Women Employment and Business Opportunities in the Renewable Energy Sector", + "The Gender Enabling Environments Spatial Tool", self, ) self.title_label.setWordWrap(True) @@ -173,6 +171,33 @@ def __init__(self, dimension_name, dimension_data, dimension_item, parent=None): layout.addWidget(self.table) + help_layout = QHBoxLayout() + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + self.help_icon = QPixmap(resources_path("resources", "images", "help.png")) + self.help_icon = self.help_icon.scaledToWidth(20) + self.help_label_icon = QLabel() + self.help_label_icon.setPixmap(self.help_icon) + self.help_label_icon.setScaledContents(True) + self.help_label_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.help_label_icon.setMaximumWidth(20) + self.help_label_icon.setAlignment(Qt.AlignRight) + help_layout.addWidget(self.help_label_icon) + + self.help_label = QLabel( + "For detailed instructions on how to use this tool, please refer to the GEEST User Guide." + ) + self.help_label.setOpenExternalLinks(True) + self.help_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.help_label) + self.help_label.linkActivated.connect(self.open_link_in_browser) + help_layout.addWidget(self.help_label) + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + layout.addLayout(help_layout) + # QDialogButtonBox for OK and Cancel self.button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel @@ -199,6 +224,10 @@ def __init__(self, dimension_name, dimension_data, dimension_item, parent=None): # Initial validation check self.validate_weightings() + def open_link_in_browser(self, url: str): + """Open the given URL in the user's default web browser using QDesktopServices.""" + QDesktopServices.openUrl(QUrl(url)) + def toggle_guid_column(self): """Toggle the visibility of the GUID column.""" log_message("Toggling GUID column visibility") diff --git a/geest/gui/dialogs/factor_aggregation_dialog.py b/geest/gui/dialogs/factor_aggregation_dialog.py index ca19c77f..1e830aa2 100644 --- a/geest/gui/dialogs/factor_aggregation_dialog.py +++ b/geest/gui/dialogs/factor_aggregation_dialog.py @@ -12,9 +12,10 @@ QWidget, QCheckBox, QHBoxLayout, + QSpacerItem, ) -from qgis.PyQt.QtGui import QPixmap -from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtGui import QPixmap, QDesktopServices +from qgis.PyQt.QtCore import Qt, QUrl from qgis.core import Qgis from geest.utilities import resources_path, setting from ..datasource_widget_factory import DataSourceWidgetFactory @@ -51,9 +52,7 @@ def __init__(self, factor_name, factor_data, factor_item, parent=None): layout.setContentsMargins(20, 20, 20, 20) # Add padding around the layout # Title label - self.title_label = QLabel( - "Geospatial Assessment of Women Employment and Business Opportunities in the Renewable Energy Sector" - ) + self.title_label = QLabel("The Gender Enabling Environments Spatial Tool") self.title_label.setWordWrap(True) layout.addWidget(self.title_label) @@ -116,6 +115,33 @@ def __init__(self, factor_name, factor_data, factor_item, parent=None): layout.addWidget(self.table) + help_layout = QHBoxLayout() + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + self.help_icon = QPixmap(resources_path("resources", "images", "help.png")) + self.help_icon = self.help_icon.scaledToWidth(20) + self.help_label_icon = QLabel() + self.help_label_icon.setPixmap(self.help_icon) + self.help_label_icon.setScaledContents(True) + self.help_label_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.help_label_icon.setMaximumWidth(20) + self.help_label_icon.setAlignment(Qt.AlignRight) + help_layout.addWidget(self.help_label_icon) + + self.help_label = QLabel( + "For detailed instructions on how to use this tool, please refer to the GEEST User Guide." + ) + self.help_label.setOpenExternalLinks(True) + self.help_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.help_label) + self.help_label.linkActivated.connect(self.open_link_in_browser) + help_layout.addWidget(self.help_label) + help_layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + ) + layout.addLayout(help_layout) + # Buttons self.button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel @@ -143,6 +169,10 @@ def __init__(self, factor_name, factor_data, factor_item, parent=None): self.setLayout(layout) self.populate_table() # Populate the table after initializing data_sources and weightings + def open_link_in_browser(self, url: str): + """Open the given URL in the user's default web browser using QDesktopServices.""" + QDesktopServices.openUrl(QUrl(url)) + def refresh_configuration(self, attributes: dict): """Refresh the configuration widget and table. diff --git a/geest/gui/panels/tree_panel.py b/geest/gui/panels/tree_panel.py index 1f299085..b4ab979f 100644 --- a/geest/gui/panels/tree_panel.py +++ b/geest/gui/panels/tree_panel.py @@ -331,7 +331,7 @@ def working_directory_changed(self, new_directory): else: log_message( f"No model.json found in {new_directory}, using default.", - "Geest", + tag="Geest", level=Qgis.Warning, ) # copy the default model.json to the working directory @@ -343,7 +343,7 @@ def working_directory_changed(self, new_directory): except Exception as e: log_message( f"Error copying master model.json: {str(e)}", - "Geest", + tag="Geest", level=Qgis.Critical, ) self.load_json() @@ -364,7 +364,7 @@ def save_json_to_working_directory(self): if not self.working_directory: log_message( "No working directory set, cannot save JSON.", - "Geest", + tag="Geest", level=Qgis.Warning, ) try: diff --git a/geest/gui/widgets/configuration_widgets/index_score_configuration_widget.py b/geest/gui/widgets/configuration_widgets/index_score_configuration_widget.py index e058fcbd..e0997d1a 100644 --- a/geest/gui/widgets/configuration_widgets/index_score_configuration_widget.py +++ b/geest/gui/widgets/configuration_widgets/index_score_configuration_widget.py @@ -36,7 +36,7 @@ def set_internal_widgets_enabled(self, enabled: bool) -> None: except Exception as e: log_message( f"Error in set_internal_widgets_enabled: {e}", - "Geest", + tag="Geest", level=Qgis.Critical, ) diff --git a/geest/gui/widgets/datasource_widgets/acled_csv_datasource_widget.py b/geest/gui/widgets/datasource_widgets/acled_csv_datasource_widget.py index f0a014a0..9ab96ca1 100644 --- a/geest/gui/widgets/datasource_widgets/acled_csv_datasource_widget.py +++ b/geest/gui/widgets/datasource_widgets/acled_csv_datasource_widget.py @@ -101,7 +101,7 @@ def validate_csv_file(self, file_path: str) -> None: if missing_columns: error_message = f"Missing columns: {', '.join(missing_columns)}" - log_message(error_message, "Geest", Qgis.Critical) + log_message(error_message, tag="Geest", level=Qgis.Critical) QMessageBox.critical(self, "Invalid CSV", error_message) else: log_message("CSV file validation successful.") diff --git a/geest/gui/widgets/datasource_widgets/csv_datasource_widget.py b/geest/gui/widgets/datasource_widgets/csv_datasource_widget.py index 724a0217..1e8e7961 100644 --- a/geest/gui/widgets/datasource_widgets/csv_datasource_widget.py +++ b/geest/gui/widgets/datasource_widgets/csv_datasource_widget.py @@ -3,6 +3,7 @@ QLineEdit, QToolButton, QFileDialog, + QMessageBox, ) from qgis.core import Qgis from .base_datasource_widget import BaseDataSourceWidget @@ -27,7 +28,7 @@ def add_internal_widgets(self) -> None: Adds the internal widgets required for selecting the CSV firadiole and validating its format. This method is called during the widget initialization and sets up the layout for the UI components. """ - log_message("Adding internal widgets for ACLED CSV Layer Widget", "Geest") + log_message("Adding internal widgets for ACLED CSV Layer Widget") try: self.widget_key = "use_csv_to_point_layer" @@ -100,7 +101,7 @@ def validate_csv_file(self, file_path: str) -> None: if missing_columns: error_message = f"Missing columns: {', '.join(missing_columns)}" - log_message(error_message, "Geest", Qgis.Critical) + log_message(error_message, tag="Geest", level=Qgis.Critical) QMessageBox.critical(self, "Invalid CSV", error_message) else: log_message("CSV file validation successful.") diff --git a/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py b/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py index de70c260..3ab63921 100644 --- a/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py +++ b/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py @@ -156,7 +156,7 @@ def _populate_field_combo(self, shapefile_path: str) -> None: vector_layer = QgsVectorLayer(shapefile_path, "layer", "ogr") if not vector_layer.isValid(): - log_message(f"Failed to load shapefile: {shapefile_path}", "Geest") + log_message(f"Failed to load shapefile: {shapefile_path}") return # Set the vector layer on the field selection combo box, which will automatically populate it diff --git a/geest/resources/icons/help.svg b/geest/resources/icons/help.svg new file mode 100644 index 00000000..58bdf37d --- /dev/null +++ b/geest/resources/icons/help.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + diff --git a/geest/resources/images/help.png b/geest/resources/images/help.png new file mode 100644 index 00000000..ba29fd52 Binary files /dev/null and b/geest/resources/images/help.png differ diff --git a/geest/resources/models/insights.model3 b/geest/resources/models/insights.model3 new file mode 100644 index 00000000..a3d1c35a --- /dev/null +++ b/geest/resources/models/insights.model3 @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +