allow to set specific settings editors + add test

This commit is contained in:
Denis Rouzaud 2024-10-14 23:03:24 +02:00 committed by Nyall Dawson
parent d5aead3d0e
commit 18974480fb
14 changed files with 155 additions and 41 deletions

View File

@ -17,7 +17,7 @@
***************************************************************************
"""
from qgis.PyQt.QtWidgets import QWidget, QComboBox
from qgis.PyQt.QtWidgets import QComboBox
from qgis.core import QgsSettingsEntryBase
from qgis.gui import QgsSettingsEditorWidgetWrapper
@ -62,19 +62,23 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper):
return None
def setWidgetFromVariant(self, value):
if self.editor:
idx = self.editor.findData(value)
self.editor.setCurrentIndex(idx)
if self.editor and value is not None:
ok = True
if isinstance(value, str):
value, ok = self.setting.metaEnum().keyToValue(value)
if ok:
idx = self.editor.findData(int(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):
def configureEditorPrivate(self, editor: QComboBox, setting: QgsSettingsEntryBase):
self.setting = setting
if isinstance(editor, QComboBox):
self.editor = editor
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)

View File

@ -34,12 +34,19 @@ Adds an editor widget ``wrapper`` to the registry
If an editor widget with same id already exists, the wrapper is deleted and ``False`` is returned.
%End
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const;
void addWrapperForSetting( QgsSettingsEditorWidgetWrapper *wrapper /Transfer/, const QgsSettingsEntryBase *setting /KeepReference/ );
%Docstring
Adds an editor widget ``wrapper`` for a specific setting to the registry
.. versionadded:: 3.40
%End
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const /Factory/;
%Docstring
Returns a new instance of the editor widget for the given ``id``
%End
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = 0 ) const /Factory/;
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = 0 ) const /KeepReference/;
%Docstring
Creates an editor widget for the given ``setting`` using the corresponding registered wrapper
%End

View File

@ -23,7 +23,7 @@ Base class for settings editor wrappers
#include "qgssettingseditorwidgetwrapper.h"
%End
public:
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget ) /Factory/;
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget );
%Docstring
Creates a wrapper from the definition stored in a ``widget`` created by :py:func:`~QgsSettingsEditorWidgetWrapper.createEditor`
%End
@ -44,12 +44,12 @@ This id of the type of settings it handles
This mostly correspond to the content of :py:class:`Qgis`.SettingsType but it's a string since custom Python implementation are possible.
%End
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = 0 ) const = 0;
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = 0 ) const = 0 /Factory/;
%Docstring
Creates a new instance of the editor wrapper so it can be configured for a widget and a setting
%End
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = 0 );
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = 0 ) /KeepReference/;
%Docstring
Creates the editor widget for the given ``setting``
%End
@ -77,7 +77,7 @@ Returns the value from the widget as a variant
The wrapper must be configured before calling this medthod
%End
virtual void setWidgetFromVariant( const QVariant &value ) const = 0;
virtual bool setWidgetFromVariant( const QVariant &value ) const = 0;
%Docstring
Sets the ``value`` of the widget
The wrapper must be configured before calling this medthod
@ -106,12 +106,12 @@ Returns the dynamic key parts
protected:
virtual QWidget *createEditorPrivate( QWidget *parent = 0 ) const = 0;
virtual QWidget *createEditorPrivate( QWidget *parent = 0 ) const = 0 /KeepReference,Factory/;
%Docstring
Creates the widgets
%End
virtual bool configureEditorPrivate( QWidget *editor, const QgsSettingsEntryBase *setting ) = 0;
virtual bool configureEditorPrivate( QWidget *editor /TransferBack/, const QgsSettingsEntryBase *setting /KeepReference/ ) = 0;
%Docstring
Configures an existing ``editor`` widget
%End

View File

@ -36,7 +36,7 @@ Constructor
virtual bool setSettingFromWidget() const = 0;
virtual void setWidgetFromVariant( const QVariant &value ) const;
virtual bool setWidgetFromVariant( const QVariant &value ) const;
virtual bool setWidgetValue( const U &value ) const = 0;
%Docstring

View File

@ -17,7 +17,7 @@
***************************************************************************
"""
from qgis.PyQt.QtWidgets import QWidget, QComboBox
from qgis.PyQt.QtWidgets import QComboBox
from qgis.core import QgsSettingsEntryBase
from qgis.gui import QgsSettingsEditorWidgetWrapper
@ -62,16 +62,20 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper):
return None
def setWidgetFromVariant(self, value):
if self.editor:
idx = self.editor.findData(value)
self.editor.setCurrentIndex(idx)
if self.editor and value is not None:
ok = True
if isinstance(value, str):
value, ok = self.setting.metaEnum().keyToValue(value)
if ok:
idx = self.editor.findData(int(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):
def configureEditorPrivate(self, editor: QComboBox, setting: QgsSettingsEntryBase):
self.setting = setting
if isinstance(editor, QComboBox):
self.editor = editor

View File

@ -34,12 +34,19 @@ Adds an editor widget ``wrapper`` to the registry
If an editor widget with same id already exists, the wrapper is deleted and ``False`` is returned.
%End
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const;
void addWrapperForSetting( QgsSettingsEditorWidgetWrapper *wrapper /Transfer/, const QgsSettingsEntryBase *setting /KeepReference/ );
%Docstring
Adds an editor widget ``wrapper`` for a specific setting to the registry
.. versionadded:: 3.40
%End
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const /Factory/;
%Docstring
Returns a new instance of the editor widget for the given ``id``
%End
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = 0 ) const /Factory/;
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = 0 ) const /KeepReference/;
%Docstring
Creates an editor widget for the given ``setting`` using the corresponding registered wrapper
%End

View File

@ -23,7 +23,7 @@ Base class for settings editor wrappers
#include "qgssettingseditorwidgetwrapper.h"
%End
public:
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget ) /Factory/;
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget );
%Docstring
Creates a wrapper from the definition stored in a ``widget`` created by :py:func:`~QgsSettingsEditorWidgetWrapper.createEditor`
%End
@ -44,12 +44,12 @@ This id of the type of settings it handles
This mostly correspond to the content of :py:class:`Qgis`.SettingsType but it's a string since custom Python implementation are possible.
%End
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = 0 ) const = 0;
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = 0 ) const = 0 /Factory/;
%Docstring
Creates a new instance of the editor wrapper so it can be configured for a widget and a setting
%End
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = 0 );
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = 0 ) /KeepReference/;
%Docstring
Creates the editor widget for the given ``setting``
%End
@ -77,7 +77,7 @@ Returns the value from the widget as a variant
The wrapper must be configured before calling this medthod
%End
virtual void setWidgetFromVariant( const QVariant &value ) const = 0;
virtual bool setWidgetFromVariant( const QVariant &value ) const = 0;
%Docstring
Sets the ``value`` of the widget
The wrapper must be configured before calling this medthod
@ -106,12 +106,12 @@ Returns the dynamic key parts
protected:
virtual QWidget *createEditorPrivate( QWidget *parent = 0 ) const = 0;
virtual QWidget *createEditorPrivate( QWidget *parent = 0 ) const = 0 /KeepReference,Factory/;
%Docstring
Creates the widgets
%End
virtual bool configureEditorPrivate( QWidget *editor, const QgsSettingsEntryBase *setting ) = 0;
virtual bool configureEditorPrivate( QWidget *editor /TransferBack/, const QgsSettingsEntryBase *setting /KeepReference/ ) = 0;
%Docstring
Configures an existing ``editor`` widget
%End

View File

@ -36,7 +36,7 @@ Constructor
virtual bool setSettingFromWidget() const = 0;
virtual void setWidgetFromVariant( const QVariant &value ) const;
virtual bool setWidgetFromVariant( const QVariant &value ) const;
virtual bool setWidgetValue( const U &value ) const = 0;
%Docstring

View File

@ -81,6 +81,11 @@ bool QgsSettingsEditorWidgetRegistry::addWrapper( QgsSettingsEditorWidgetWrapper
return true;
}
void QgsSettingsEditorWidgetRegistry::addWrapperForSetting( QgsSettingsEditorWidgetWrapper *wrapper, const QgsSettingsEntryBase *setting )
{
mSpecificWrappers.insert( setting, wrapper );
}
QgsSettingsEditorWidgetWrapper *QgsSettingsEditorWidgetRegistry::createWrapper( const QString &id, QObject *parent ) const
{
QgsSettingsEditorWidgetWrapper *wrapper = mWrappers.value( id );
@ -97,6 +102,10 @@ QgsSettingsEditorWidgetWrapper *QgsSettingsEditorWidgetRegistry::createWrapper(
QWidget *QgsSettingsEditorWidgetRegistry::createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent ) const
{
if ( mSpecificWrappers.contains( setting ) )
{
return mSpecificWrappers.value( setting )->createEditor( setting, dynamicKeyPartList, parent );
}
QgsSettingsEditorWidgetWrapper *eww = createWrapper( setting->typeId(), parent );
if ( eww )
return eww->createEditor( setting, dynamicKeyPartList, parent );

View File

@ -45,14 +45,21 @@ class GUI_EXPORT QgsSettingsEditorWidgetRegistry
*/
bool addWrapper( QgsSettingsEditorWidgetWrapper *wrapper SIP_TRANSFER );
/**
* Adds an editor widget \a wrapper for a specific setting to the registry
* \since QGIS 3.40
*/
void addWrapperForSetting( QgsSettingsEditorWidgetWrapper *wrapper SIP_TRANSFER, const QgsSettingsEntryBase *setting SIP_KEEPREFERENCE );
//! Returns a new instance of the editor widget for the given \a id
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const;
QgsSettingsEditorWidgetWrapper *createWrapper( const QString &id, QObject *parent ) const SIP_FACTORY;
//! Creates an editor widget for the given \a setting using the corresponding registered wrapper
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = nullptr ) const SIP_FACTORY;
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent = nullptr ) const SIP_KEEPREFERENCE;
private:
QMap<QString, QgsSettingsEditorWidgetWrapper *> mWrappers;
QMap<const QgsSettingsEntryBase *, QgsSettingsEditorWidgetWrapper *> mSpecificWrappers;
};
#endif // QGSSETTINGSEDITORREGISTRY_H

View File

@ -36,7 +36,7 @@ class GUI_EXPORT QgsSettingsEditorWidgetWrapper : public QObject
Q_OBJECT
public:
//! Creates a wrapper from the definition stored in a \a widget created by createEditor()
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget ) SIP_FACTORY;
static QgsSettingsEditorWidgetWrapper *fromWidget( const QWidget *widget );
//! Constructor
QgsSettingsEditorWidgetWrapper( QObject *parent = nullptr );
@ -50,10 +50,10 @@ class GUI_EXPORT QgsSettingsEditorWidgetWrapper : public QObject
virtual QString id() const = 0;
//! Creates a new instance of the editor wrapper so it can be configured for a widget and a setting
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = nullptr ) const = 0;
virtual QgsSettingsEditorWidgetWrapper *createWrapper( QObject *parent = nullptr ) const = 0 SIP_FACTORY;
//! Creates the editor widget for the given \a setting
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = nullptr );
QWidget *createEditor( const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList(), QWidget *parent = nullptr ) SIP_KEEPREFERENCE;
//! Configures the \a editor according the setting
bool configureEditor( QWidget *editor, const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList = QStringList() );
@ -80,7 +80,7 @@ class GUI_EXPORT QgsSettingsEditorWidgetWrapper : public QObject
* Sets the \a value of the widget
* The wrapper must be configured before calling this medthod
*/
virtual void setWidgetFromVariant( const QVariant &value ) const = 0;
virtual bool setWidgetFromVariant( const QVariant &value ) const = 0;
/**
* Configure the settings update behavior when a widget value is changed.
@ -103,10 +103,10 @@ class GUI_EXPORT QgsSettingsEditorWidgetWrapper : public QObject
protected:
//! Creates the widgets
virtual QWidget *createEditorPrivate( QWidget *parent = nullptr ) const = 0;
virtual QWidget *createEditorPrivate( QWidget *parent = nullptr ) const = 0 SIP_KEEPREFERENCE SIP_FACTORY;
//! Configures an existing \a editor widget
virtual bool configureEditorPrivate( QWidget *editor, const QgsSettingsEntryBase *setting ) = 0;
virtual bool configureEditorPrivate( QWidget *editor SIP_TRANSFERBACK, const QgsSettingsEntryBase *setting SIP_KEEPREFERENCE ) = 0;
/**
* Enables automatic update, which causes the setting to be updated immediately when the widget

View File

@ -63,9 +63,9 @@ class QgsSettingsEditorWidgetWrapperTemplate : public QgsSettingsEditorWidgetWra
virtual bool setSettingFromWidget() const override = 0;
void setWidgetFromVariant( const QVariant &value ) const override
bool setWidgetFromVariant( const QVariant &value ) const override
{
setWidgetValue( mSetting->convertFromVariant( value ) );
return setWidgetValue( mSetting->convertFromVariant( value ) );
}
//! Sets the widget value

View File

@ -486,7 +486,10 @@ void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIn
{
QgsSettingsEditorWidgetWrapper *eww = QgsSettingsEditorWidgetWrapper::fromWidget( editor );
if ( eww )
eww->setWidgetFromVariant( index.model()->data( index, Qt::DisplayRole ) );
{
QVariant value = index.model()->data( index, Qt::DisplayRole );
eww->setWidgetFromVariant( value );
}
}
void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const

View File

@ -0,0 +1,73 @@
"""
Test the PyQgsSettingsRegistry classes
Run with: ctest -V -R PyQgsSettingsRegistry
.. note:: 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 QComboBox, QSpinBox
from qgis.core import (
QgsLocatorFilter,
QgsSettingsTree,
QgsSettingsEntryEnumFlag,
QgsSettingsEntryInteger,
)
from qgis.gui import QgsGui, QgsSettingsEditorWidgetWrapper, QgsSettingsEnumEditorWidgetWrapper
import unittest
from qgis.testing import start_app, QgisTestCase
start_app()
PLUGIN_NAME = "UnitTestSettingsRegistry"
class PyQgsSettingsRegistry(QgisTestCase):
def setUp(self):
self.settings_node = QgsSettingsTree.createPluginTreeNode(pluginName=PLUGIN_NAME)
def tearDown(self):
QgsSettingsTree.unregisterPluginTreeNode(PLUGIN_NAME)
def test_settings_registry(self):
int_setting = QgsSettingsEntryInteger("int_setting", self.settings_node, 77)
registry = QgsGui.settingsEditorWidgetRegistry()
editor = registry.createEditor(int_setting, [])
self.assertIsInstance(editor, QSpinBox)
wrapper = QgsSettingsEditorWidgetWrapper.fromWidget(editor)
self.assertEqual(editor.value(), 77)
editor.setValue(6)
self.assertEqual(wrapper.variantValueFromWidget(), 6)
wrapper.setSettingFromWidget()
self.assertEqual(int_setting.value(), 6)
def test_settings_registry_custom_enumflag_py(self):
self.priority_setting = QgsSettingsEntryEnumFlag("priority", self.settings_node, QgsLocatorFilter.Priority.High)
registry = QgsGui.settingsEditorWidgetRegistry()
registry.addWrapperForSetting(QgsSettingsEnumEditorWidgetWrapper(), self.priority_setting)
self.editor = registry.createEditor(self.priority_setting, [])
self.assertIsInstance(self.editor, QComboBox)
self.assertEqual(self.editor.currentData(), QgsLocatorFilter.Priority.High)
self.editor.setCurrentIndex(self.editor.findData(QgsLocatorFilter.Priority.Low))
wrapper = QgsSettingsEditorWidgetWrapper.fromWidget(self.editor)
self.assertEqual(wrapper.variantValueFromWidget(), QgsLocatorFilter.Priority.Low)
wrapper.setSettingFromWidget()
self.assertEqual(self.priority_setting.value(), QgsLocatorFilter.Priority.Low)
if __name__ == '__main__':
unittest.main()