From 08aeb133a0d58f437bbaa4ba3d5ed8975c011331 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 8 Oct 2024 22:19:49 +0200 Subject: [PATCH 1/6] use a custom type id for python implementation otherwise, QgsSettingsEditorWidgetRegistry is creating a wrapper for this setting type and expects the cpp implemetnation (QgsSettingsEntryBaseTemplate and not QgsSettingsEntryBase) --- python/PyQt6/core/additions/qgssettingsentry.py | 10 +++++++++- python/core/additions/qgssettingsentry.py | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/python/PyQt6/core/additions/qgssettingsentry.py b/python/PyQt6/core/additions/qgssettingsentry.py index 227a8b4f06e2..ca55c90e4f66 100644 --- a/python/PyQt6/core/additions/qgssettingsentry.py +++ b/python/PyQt6/core/additions/qgssettingsentry.py @@ -59,6 +59,15 @@ def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgi parent = pluginName super().__init__(key, parent, defaultValueStr, description, options) + def metaEnum(self): + return self.__metaEnum + + def typeId(self): + """ + Defines a custom id since this class has not the same API as the cpp implementation + """ + return "py-enumflag" + def value(self, dynamicKeyPart=None): """ Get settings value. @@ -138,5 +147,4 @@ def settingsType(self): """ Get the settings entry type. """ - return self.SettingsType.EnumFlag diff --git a/python/core/additions/qgssettingsentry.py b/python/core/additions/qgssettingsentry.py index 227a8b4f06e2..ca55c90e4f66 100644 --- a/python/core/additions/qgssettingsentry.py +++ b/python/core/additions/qgssettingsentry.py @@ -59,6 +59,15 @@ def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgi parent = pluginName super().__init__(key, parent, defaultValueStr, description, options) + def metaEnum(self): + return self.__metaEnum + + def typeId(self): + """ + Defines a custom id since this class has not the same API as the cpp implementation + """ + return "py-enumflag" + def value(self, dynamicKeyPart=None): """ Get settings value. @@ -138,5 +147,4 @@ def settingsType(self): """ Get the settings entry type. """ - return self.SettingsType.EnumFlag From 69993b48363e9420cebff49b433d8447ae42480c Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 8 Oct 2024 20:58:00 +0200 Subject: [PATCH 2/6] Python implementation of enum settings editor widget wrapper --- python/PyQt6/gui/__init__.py.in | 4 + .../qgssettingsenumflageditorwrapper.py | 88 +++++++++++++++++++ python/gui/__init__.py.in | 4 + .../qgssettingsenumflageditorwrapper.py | 88 +++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py create mode 100644 python/gui/additions/qgssettingsenumflageditorwrapper.py diff --git a/python/PyQt6/gui/__init__.py.in b/python/PyQt6/gui/__init__.py.in index 664ca33cb7dd..8839a73aab87 100644 --- a/python/PyQt6/gui/__init__.py.in +++ b/python/PyQt6/gui/__init__.py.in @@ -24,6 +24,7 @@ __copyright__ = '(C) 2014, Nathan Woodrow' from qgis.PyQt import QtCore from qgis._gui import * from qgis.core import Qgis as _Qgis +from .additions.qgssettingsenumflageditorwrapper import PyQgsSettingsEnumEditorWidgetWrapper @MONKEYPATCH_INJECTIONS@ @@ -76,3 +77,6 @@ QgsMapLayerAction.EnabledOnlyWhenEditable = _Qgis.MapLayerActionFlag.EnabledOnly QgsMapLayerAction.EnabledOnlyWhenEditable.is_monkey_patched = True QgsMapLayerAction.EnabledOnlyWhenEditable.__doc__ = "Action should be shown only for editable layers" QgsMapLayerAction.Flags = _Qgis.MapLayerActionFlags + +# Classes patched +QgsSettingsEnumEditorWidgetWrapper = PyQgsSettingsEnumEditorWidgetWrapper diff --git a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py new file mode 100644 index 000000000000..c1c87db19885 --- /dev/null +++ b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + qgssettingsenumflageditorwrapper.py + --------------------- + Date : October 2024 + Copyright : (C) 2021 by Denis Rouzaud + Email : denis@opengis.ch +*************************************************************************** +* * +* 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 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +from qgis.PyQt.QtWidgets import QWidget, QComboBox + +from qgis.core import QgsSettingsEntryBase, QgsSettingsEditorWidgetWrapper + + +class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): + """ + A settings editor widget wrapper for enum settings as PyQgsSettingsEntryEnumFlag + """ + + def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None): + self.setting = editor + self.editor = setting + self.displayStrings = {} + super().__init__(parent) + if editor is not None and setting is not None: + if displayStrings: + self.displayStrings = displayStrings + self.configureEditor(editor, setting) + + def id(self): + return 'py-enum' + + def createWrapper(self, parent=None): + return PyQgsSettingsEnumEditorWidgetWrapper(parent) + + def setWidgetFromSetting(self): + if self.setting: + return self.setWidgetFromVariant(self.setting.value(self.dynamicKeyPartList())) + return False + + def setSettingFromWidget(self): + if self.editor: + self.setting.setValue(self.variantValueFromWidget(), self.dynamicKeyPartList()) + return True + else: + return False + + def variantValueFromWidget(self): + if self.editor: + return self.setting.defaultValue().__class__(self.editor.currentData()) + return None + + def setWidgetFromVariant(self, value): + if self.editor: + idx = self.editor.findData(value) + self.editor.setCurrentIndex(idx) + return idx >= 0 + return False + + def createEditorPrivate(self, parent=None): + return QComboBox(parent) + + def configureEditorPrivate(self, editor: QWidget, setting: QgsSettingsEntryBase): + self.setting = setting + self.editor = editor + if editor is not None: + for i in range(self.setting.metaEnum().keyCount()): + value = self.setting.metaEnum().value(i) + key = self.setting.metaEnum().key(i) + text = self.displayStrings.get(value, key) + self.editor.addItem(text, value) + return True + else: + return False + + def enableAutomaticUpdatePrivate(self): + self.editor.currentIndexChanged.connect( + lambda: self.setting.setValue(self.editor.currentData(), self.dynamicKeyPartList())) diff --git a/python/gui/__init__.py.in b/python/gui/__init__.py.in index 5b120c3b6e9d..c46330929a01 100644 --- a/python/gui/__init__.py.in +++ b/python/gui/__init__.py.in @@ -24,6 +24,7 @@ __copyright__ = '(C) 2014, Nathan Woodrow' from qgis.PyQt import QtCore from qgis._gui import * from qgis.core import Qgis as _Qgis +from .additions.qgssettingsenumflageditorwrapper import PyQgsSettingsEnumEditorWidgetWrapper @MONKEYPATCH_INJECTIONS@ @@ -83,3 +84,6 @@ QgsSettingsBoolEditorWidgetWrapper = QgsSettingsBoolCheckBoxWrapper QgsSettingsIntegerEditorWidgetWrapper = QgsSettingsIntegerSpinBoxWrapper QgsSettingsDoubleEditorWidgetWrapper = QgsSettingsDoubleSpinBoxWrapper QgsSettingsColorEditorWidgetWrapper = QgsSettingsColorButtonWrapper + +# Classes patched +QgsSettingsEnumEditorWidgetWrapper = PyQgsSettingsEnumEditorWidgetWrapper diff --git a/python/gui/additions/qgssettingsenumflageditorwrapper.py b/python/gui/additions/qgssettingsenumflageditorwrapper.py new file mode 100644 index 000000000000..c1c87db19885 --- /dev/null +++ b/python/gui/additions/qgssettingsenumflageditorwrapper.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + qgssettingsenumflageditorwrapper.py + --------------------- + Date : October 2024 + Copyright : (C) 2021 by Denis Rouzaud + Email : denis@opengis.ch +*************************************************************************** +* * +* 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 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +from qgis.PyQt.QtWidgets import QWidget, QComboBox + +from qgis.core import QgsSettingsEntryBase, QgsSettingsEditorWidgetWrapper + + +class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): + """ + A settings editor widget wrapper for enum settings as PyQgsSettingsEntryEnumFlag + """ + + def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None): + self.setting = editor + self.editor = setting + self.displayStrings = {} + super().__init__(parent) + if editor is not None and setting is not None: + if displayStrings: + self.displayStrings = displayStrings + self.configureEditor(editor, setting) + + def id(self): + return 'py-enum' + + def createWrapper(self, parent=None): + return PyQgsSettingsEnumEditorWidgetWrapper(parent) + + def setWidgetFromSetting(self): + if self.setting: + return self.setWidgetFromVariant(self.setting.value(self.dynamicKeyPartList())) + return False + + def setSettingFromWidget(self): + if self.editor: + self.setting.setValue(self.variantValueFromWidget(), self.dynamicKeyPartList()) + return True + else: + return False + + def variantValueFromWidget(self): + if self.editor: + return self.setting.defaultValue().__class__(self.editor.currentData()) + return None + + def setWidgetFromVariant(self, value): + if self.editor: + idx = self.editor.findData(value) + self.editor.setCurrentIndex(idx) + return idx >= 0 + return False + + def createEditorPrivate(self, parent=None): + return QComboBox(parent) + + def configureEditorPrivate(self, editor: QWidget, setting: QgsSettingsEntryBase): + self.setting = setting + self.editor = editor + if editor is not None: + for i in range(self.setting.metaEnum().keyCount()): + value = self.setting.metaEnum().value(i) + key = self.setting.metaEnum().key(i) + text = self.displayStrings.get(value, key) + self.editor.addItem(text, value) + return True + else: + return False + + def enableAutomaticUpdatePrivate(self): + self.editor.currentIndexChanged.connect( + lambda: self.setting.setValue(self.editor.currentData(), self.dynamicKeyPartList())) From d9296002fdf47b17e9511c5ae4ef84993c2c526d Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 9 Oct 2024 07:19:55 +0200 Subject: [PATCH 3/6] fix import --- python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py | 3 ++- python/gui/additions/qgssettingsenumflageditorwrapper.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py index c1c87db19885..eca1975a2695 100644 --- a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py @@ -19,7 +19,8 @@ from qgis.PyQt.QtWidgets import QWidget, QComboBox -from qgis.core import QgsSettingsEntryBase, QgsSettingsEditorWidgetWrapper +from qgis.core import QgsSettingsEntryBase +from qgis.gui import QgsSettingsEditorWidgetWrapper class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): diff --git a/python/gui/additions/qgssettingsenumflageditorwrapper.py b/python/gui/additions/qgssettingsenumflageditorwrapper.py index c1c87db19885..eca1975a2695 100644 --- a/python/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/gui/additions/qgssettingsenumflageditorwrapper.py @@ -19,7 +19,8 @@ from qgis.PyQt.QtWidgets import QWidget, QComboBox -from qgis.core import QgsSettingsEntryBase, QgsSettingsEditorWidgetWrapper +from qgis.core import QgsSettingsEntryBase +from qgis.gui import QgsSettingsEditorWidgetWrapper class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): From f7a191519e4a924d2fea0b84b18e2fa4bd65d171 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 9 Oct 2024 12:08:45 +0200 Subject: [PATCH 4/6] fix mixup --- .../PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py | 4 ++-- python/gui/additions/qgssettingsenumflageditorwrapper.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py index eca1975a2695..cfaaeac6b5b7 100644 --- a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py @@ -29,8 +29,8 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): """ def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None): - self.setting = editor - self.editor = setting + self.setting = setting + self.editor = editor self.displayStrings = {} super().__init__(parent) if editor is not None and setting is not None: diff --git a/python/gui/additions/qgssettingsenumflageditorwrapper.py b/python/gui/additions/qgssettingsenumflageditorwrapper.py index eca1975a2695..cfaaeac6b5b7 100644 --- a/python/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/gui/additions/qgssettingsenumflageditorwrapper.py @@ -29,8 +29,8 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper): """ def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None): - self.setting = editor - self.editor = setting + self.setting = setting + self.editor = editor self.displayStrings = {} super().__init__(parent) if editor is not None and setting is not None: From e4b2d591db652d8474075d69d72c94e1c0bac2bc Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 9 Oct 2024 12:09:54 +0200 Subject: [PATCH 5/6] access dynamic key part list for editor wrappers --- .../settings/qgssettingseditorwidgetwrapper.sip.in | 7 +++++++ .../settings/qgssettingseditorwidgetwrapper.sip.in | 7 +++++++ src/gui/settings/qgssettingseditorwidgetwrapper.h | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/python/PyQt6/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in b/python/PyQt6/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in index 9837317cc65d..2d5c5fd16fdf 100644 --- a/python/PyQt6/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in +++ b/python/PyQt6/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in @@ -94,6 +94,13 @@ If not, the setting will be updated directly at each widget value change. This must called after :py:func:`~QgsSettingsEditorWidgetWrapper.createEditor` or :py:func:`~QgsSettingsEditorWidgetWrapper.configureEditor`. +.. versionadded:: 3.40 +%End + + QStringList dynamicKeyPartList() const; +%Docstring +Returns the dynamic key parts + .. versionadded:: 3.40 %End diff --git a/python/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in b/python/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in index 9837317cc65d..2d5c5fd16fdf 100644 --- a/python/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in +++ b/python/gui/auto_generated/settings/qgssettingseditorwidgetwrapper.sip.in @@ -94,6 +94,13 @@ If not, the setting will be updated directly at each widget value change. This must called after :py:func:`~QgsSettingsEditorWidgetWrapper.createEditor` or :py:func:`~QgsSettingsEditorWidgetWrapper.configureEditor`. +.. versionadded:: 3.40 +%End + + QStringList dynamicKeyPartList() const; +%Docstring +Returns the dynamic key parts + .. versionadded:: 3.40 %End diff --git a/src/gui/settings/qgssettingseditorwidgetwrapper.h b/src/gui/settings/qgssettingseditorwidgetwrapper.h index df52643c321d..70066075357d 100644 --- a/src/gui/settings/qgssettingseditorwidgetwrapper.h +++ b/src/gui/settings/qgssettingseditorwidgetwrapper.h @@ -94,6 +94,12 @@ class GUI_EXPORT QgsSettingsEditorWidgetWrapper : public QObject */ void configureAutomaticUpdate( QDialog *dialog = nullptr ); + /** + * Returns the dynamic key parts + * \since QGIS 3.40 + */ + QStringList dynamicKeyPartList() const {return mDynamicKeyPartList;} + protected: //! Creates the widgets From c2ef9ea12564c4d8bba236a5313264a4344951d8 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 9 Oct 2024 12:11:44 +0200 Subject: [PATCH 6/6] set editor only if it is a combobox --- .../PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py | 4 ++-- python/gui/additions/qgssettingsenumflageditorwrapper.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py index cfaaeac6b5b7..012c2ff6e9de 100644 --- a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py @@ -73,8 +73,8 @@ def createEditorPrivate(self, parent=None): def configureEditorPrivate(self, editor: QWidget, setting: QgsSettingsEntryBase): self.setting = setting - self.editor = editor - if editor is not None: + if isinstance(editor, QComboBox): + self.editor = editor for i in range(self.setting.metaEnum().keyCount()): value = self.setting.metaEnum().value(i) key = self.setting.metaEnum().key(i) diff --git a/python/gui/additions/qgssettingsenumflageditorwrapper.py b/python/gui/additions/qgssettingsenumflageditorwrapper.py index cfaaeac6b5b7..012c2ff6e9de 100644 --- a/python/gui/additions/qgssettingsenumflageditorwrapper.py +++ b/python/gui/additions/qgssettingsenumflageditorwrapper.py @@ -73,8 +73,8 @@ def createEditorPrivate(self, parent=None): def configureEditorPrivate(self, editor: QWidget, setting: QgsSettingsEntryBase): self.setting = setting - self.editor = editor - if editor is not None: + if isinstance(editor, QComboBox): + self.editor = editor for i in range(self.setting.metaEnum().keyCount()): value = self.setting.metaEnum().value(i) key = self.setting.metaEnum().key(i)