# Copyright (c) 2026 macro-qgis-plugin contributors.
#
#
# This file is part of macro-qgis-plugin.
#
# macro-qgis-plugin 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.
#
# macro-qgis-plugin 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 macro-qgis-plugin. If not, see <https://www.gnu.org/licenses/>.
# Copyright (c) 2025 profiler-qgis-plugin contributors.
#
#
# This file is part of profiler-qgis-plugin.
#
# profiler-qgis-plugin 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.
#
# profiler-qgis-plugin 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 profiler-qgis-plugin. If not, see <https://www.gnu.org/licenses/>.
"""Settings dialog for configuring macro plugin options and logging levels."""
import logging
from pathlib import Path
from qgis.core import QgsApplication
from qgis.gui import QgsCollapsibleGroupBox
from qgis.PyQt.QtWidgets import (
QCheckBox,
QComboBox,
QDialog,
QDialogButtonBox,
QDoubleSpinBox,
QFormLayout,
QLabel,
QLineEdit,
QSpinBox,
QVBoxLayout,
QWidget,
)
from qgis_macros.settings import SettingCategory, Settings, WidgetType
from qgis_plugin_tools.tools.custom_logging import (
LogTarget,
get_log_level_key,
get_log_level_name,
)
from qgis_plugin_tools.tools.resources import load_ui_from_file
from qgis_plugin_tools.tools.settings import set_setting
UI_CLASS: QWidget = load_ui_from_file(
str(Path(__file__).parent.joinpath("settings_dialog.ui"))
)
LOGGER = logging.getLogger(__name__)
CALIBRATION_COEFFICIENT = 1.05
LOGGING_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
[docs]
class SettingsDialog(QDialog, UI_CLASS): # type: ignore
"""Dialog for editing plugin settings and logging levels.
Originally adapted from https://github.com/nlsfi/pickLayer (GPL v3).
"""
layout_setting_items: QVBoxLayout
combo_box_log_level_file: QComboBox
combo_box_log_level_console: QComboBox
button_box: QDialogButtonBox
def __init__(self, parent: QWidget | None = None) -> None:
"""Initialize the dialog and populate settings widgets."""
super().__init__(parent)
self.setupUi(self)
self.setWindowIcon(QgsApplication.getThemeIcon("/propertyicons/settings.svg"))
self._widgets: dict[Settings, QWidget] = {}
self._groups: dict[SettingCategory, QgsCollapsibleGroupBox] = {}
self._setup_plugin_settings()
self._setup_logging_settings()
self.button_box.accepted.connect(self.close)
self.button_box.button(QDialogButtonBox.StandardButton.Reset).clicked.connect(
self._reset_settings
)
def _setup_plugin_settings(self) -> None:
for setting in Settings:
self._add_setting(setting)
def _reset_settings(self) -> None:
# Clear all items from the settings layout
while self.layout_setting_items.count():
child = self.layout_setting_items.takeAt(0)
if child.widget():
child.widget().deleteLater()
# Clear stored widgets and group boxes
self._widgets.clear()
self._groups.clear()
# Reset settings and re-add them
Settings.reset()
for setting in Settings:
self._add_setting(setting)
def _add_setting(self, setting: Settings) -> None:
"""Add a widget to the appropriate group box based on the category."""
setting_meta = setting.value
widget_type = setting_meta.widget_type
widget_config = setting_meta.widget_config
category = setting_meta.category
if category not in self._groups:
group_box = QgsCollapsibleGroupBox(category.value)
group_box.setLayout(QFormLayout())
self._groups[category] = group_box
self.layout_setting_items.addWidget(group_box)
group_layout = self._groups[category].layout()
label = QLabel(setting_meta.description)
# Create the appropriate widget based on the widget type
if widget_type == WidgetType.LINE_EDIT:
widget = QLineEdit()
widget.setText(setting.get())
widget.textChanged.connect(setting.set)
elif widget_type == WidgetType.CHECKBOX:
widget = QCheckBox()
widget.setChecked(setting.get())
widget.stateChanged.connect(setting.set)
elif widget_type == WidgetType.SPIN_BOX:
if isinstance(setting_meta.default, int):
widget = QSpinBox()
else:
widget = QDoubleSpinBox()
widget.setDecimals(3)
if widget_config:
if widget_config.minimum is not None:
widget.setMinimum(widget_config.minimum)
if widget_config.maximum is not None:
widget.setMaximum(widget_config.maximum)
if widget_config.step is not None:
widget.setSingleStep(widget_config.step)
widget.setValue(setting.get()) # noqa: QGS202
widget.valueChanged.connect(setting.set)
else:
raise NotImplementedError
# Store widget and add it to the group layout
self._widgets[setting] = widget
group_layout.addRow(label, widget)
def _setup_logging_settings(self) -> None:
self.combo_box_log_level_file.addItems(LOGGING_LEVELS)
self.combo_box_log_level_console.addItems(LOGGING_LEVELS)
self.combo_box_log_level_file.setCurrentText(get_log_level_name(LogTarget.FILE))
self.combo_box_log_level_console.setCurrentText(
get_log_level_name(LogTarget.STREAM)
)
self.combo_box_log_level_file.currentTextChanged.connect(
lambda level: set_setting(get_log_level_key(LogTarget.FILE), level)
)
self.combo_box_log_level_console.currentTextChanged.connect(
lambda level: set_setting(get_log_level_key(LogTarget.STREAM), level)
)