Skip to content

Commit 24bde35

Browse files
author
Patrick Valsecchi
committed
Smarter default edit widgets with plugins to pick them
Now the widgets factories can give a score on how good they could handle a widget. Additionaly, plugins can be added to choose a widget factory in function of an external information. One of them uses a table in PostgresQL to allow specification of the widget type and configuration. I took the opportunity to remove a few deprecated method in relation to this.
1 parent 235204f commit 24bde35

File tree

61 files changed

+909
-234
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+909
-234
lines changed

doc/api_break.dox

+17
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ place of a null pointer.</li>
492492

493493
<ul>
494494
<li>Does no longer inherit QObject
495+
<li>widgetType() and widgetConfig() now reflect only the user configured values.
496+
QgsEditorWidgetRegistry::instance()->findBest() must be used instead.</li>
495497
</ul>
496498

497499
\subsection qgis_api_break_3_0_QgsExpression QgsExpression
@@ -590,6 +592,13 @@ and the new ramp can be retrieved after executing the dialog by calling ramp().<
590592
plugins calling this method will need to be updated.</li>
591593
</ul>
592594

595+
\subsection qgis_api_break_3_0_QgsEditorWidgetRegistry QgsEditorWidgetRegistry
596+
597+
<ul>
598+
<li>The signature of isFieldSupported() has been changed to return an unsigned (how good it supports the given field)
599+
and to const-correct it.</li>
600+
</ul>
601+
593602
\subsection qgis_api_break_3_0_QgsGroupWMSDataDialog QgsGroupWMSDataDialog
594603

595604
<ul>
@@ -817,6 +826,14 @@ plugins calling this method will need to be updated.</li>
817826

818827
<ul>
819828
<li>setMapRenderer() has been removed. Use setMapSettings() instead.</li>
829+
<li>excludeAttributesWMS() and setExcludeAttributesWMS() have been renamed to excludeAttributesWms() and
830+
setExcludeAttributesWms()</li>
831+
<li>excludeAttributesWFS() and setExcludeAttributesWFS() have been renamed to excludeAttributesWfs() and
832+
setExcludeAttributesWfs()</li>
833+
<li>editorWidgetV2() and editorWidgetV2Config() have been removed and QgsEditorWidgetRegistry::instance()->findBest() must be used instead.</li>
834+
<li>setEditorWidgetV2(), setEditorWidgetV2Config() have been removed and their equivalent in editFormConfig() must be used instead.</li>
835+
<li>setCheckedState() is removed. Use editFormConfig()->setWidgetConfig()` instead.</li>
836+
<li>valueMap(), valueRelation(), dateFormat(), widgetSize() have been removed. Use QgsEditorWidgetRegistry::instance()->findBest().config() instead.</li>
820837
</ul>
821838

822839
\subsection qgis_api_break_3_0_QgsRenderContext QgsRenderContext

python/core/qgseditformconfig.sip

+9-39
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,6 @@ class QgsEditFormConfig
142142
*/
143143
void setWidgetType( int fieldIdx, const QString& widgetType );
144144

145-
/**
146-
* Get the id for the editor widget used to represent the field at the given index
147-
*
148-
* @param fieldIdx The index of the field
149-
*
150-
* @return The id for the editor widget or a NULL string if not applicable
151-
*/
152-
QString widgetType( int fieldIdx ) const;
153-
154145
/**
155146
* Get the id for the editor widget used to represent the field at the given index
156147
*
@@ -177,40 +168,14 @@ class QgsEditFormConfig
177168
*/
178169
void setWidgetConfig( int attrIdx, const QgsEditorWidgetConfig& config );
179170

180-
/**
181-
* Set the editor widget config for a widget.
182-
*
183-
* Example:
184-
* \code{.py}
185-
* layer.setWidgetConfig( 'relation_id', { 'nm-rel': 'other_relation' } )
186-
* \endcode
187-
*
188-
* @param widgetName The name of the widget or field to configure
189-
* @param config The config to set for this field
190-
*
191-
* @see setWidgetType() for a list of widgets and choose the widget to see the available options.
192-
*
193-
* @note not available in python bindings
194-
*/
195-
// void setWidgetConfig( const QString& widgetName, const QgsEditorWidgetConfig& config );
196-
197-
/**
198-
* Get the configuration for the editor widget used to represent the field at the given index
199-
*
200-
* @param fieldIdx The index of the field
201-
*
202-
* @return The configuration for the editor widget or an empty config if the field does not exist
203-
*/
204-
QgsEditorWidgetConfig widgetConfig( int fieldIdx ) const;
205-
206171
/**
207172
* Get the configuration for the editor widget used to represent the field with the given name
208173
*
209-
* @param widgetName The name of the widget. This can be a field name or the name of an additional widget.
174+
* @param fieldName The name of the field.
210175
*
211176
* @return The configuration for the editor widget or an empty config if the field does not exist
212177
*/
213-
QgsEditorWidgetConfig widgetConfig( const QString& widgetName ) const;
178+
QgsEditorWidgetConfig widgetConfig( const QString& fieldName ) const;
214179

215180
/**
216181
* Remove the configuration for the editor widget used to represent the field at the given index
@@ -224,11 +189,11 @@ class QgsEditFormConfig
224189
/**
225190
* Remove the configuration for the editor widget used to represent the field with the given name
226191
*
227-
* @param widgetName The name of the widget. This can be a field name or the name of an additional widget.
192+
* @param fieldName The name of the field.
228193
*
229194
* @return true if successful, false if the field does not exist
230195
*/
231-
bool removeWidgetConfig( const QString& widgetName );
196+
bool removeWidgetConfig( const QString& fieldName );
232197

233198
/**
234199
* This returns true if the field is manually set to read only or if the field
@@ -372,4 +337,9 @@ class QgsEditFormConfig
372337
* Deserialize drag and drop designer elements.
373338
*/
374339
QgsAttributeEditorElement* attributeEditorElementFromDomElement( QDomElement &elem, QgsAttributeEditorElement* parent );
340+
341+
/**
342+
* Parse the XML for the config of one editor widget.
343+
*/
344+
static QgsEditorWidgetConfig parseEditorWidgetConfig( const QDomElement& cfgElem );
375345
};

python/core/qgsfield.sip

+45
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,35 @@
1+
/** \ingroup core
2+
* Holder for the widget type and its configuration for a field.
3+
*/
4+
class QgsEditorWidgetSetup
5+
{
6+
%TypeHeaderCode
7+
#include <qgsfield.h>
8+
%End
9+
public:
10+
/**
11+
* Constructor
12+
*/
13+
QgsEditorWidgetSetup( const QString& type, const QgsEditorWidgetConfig& config );
14+
QgsEditorWidgetSetup();
15+
16+
/**
17+
* @return the widget type to use
18+
*/
19+
QString type() const;
20+
21+
/**
22+
* @return the widget configuration to used
23+
*/
24+
QgsEditorWidgetConfig config() const;
25+
26+
/**
27+
* @return true if there is no widget configured.
28+
*/
29+
bool isNull() const;
30+
};
31+
32+
133
/** \class QgsField
234
* \ingroup core
335
* Encapsulate a field in an attribute table or data source.
@@ -179,6 +211,19 @@ class QgsField
179211
//! Allows direct construction of QVariants from fields.
180212
operator QVariant() const;
181213

214+
/**
215+
* Set the editor widget setup for the field.
216+
*
217+
* @param v The value to set
218+
*/
219+
void setEditorWidgetSetup( const QgsEditorWidgetSetup& v );
220+
221+
/**
222+
* Get the editor widget setup for the field.
223+
*
224+
* @return the value
225+
*/
226+
const QgsEditorWidgetSetup& editorWidgetSetup() const;
182227
}; // class QgsField
183228

184229

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/***************************************************************************
2+
qgseditorwidgetautoconf.sip
3+
---------------------
4+
begin : July 2016
5+
copyright : (C) 2016 by Patrick Valsecchi
6+
email : patrick.valsecchi at camptocamp.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
/**
17+
* Base class for plugins allowing to pick automatically a widget type for editing fields.
18+
*
19+
* @note added in QGIS 3.0
20+
*/
21+
class QgsEditorWidgetAutoConfPlugin
22+
{
23+
%TypeHeaderCode
24+
#include <qgseditorwidgetautoconf.h>
25+
%End
26+
27+
public:
28+
/**
29+
* Typical scores are:
30+
* * 0: no matching type found.
31+
* * 10: a widget has been guessed from the type of field.
32+
* * 20: a widget has been determined from an external configuration (for example a database table)
33+
*
34+
* @param vl The vector layer for which this widget will be created
35+
* @param fieldName The field name on the specified layer for which this widget will be created
36+
* @param score Where the score is returned (default to 0)
37+
*
38+
* @return and integer value rating how good is the setup provided by this plugin.
39+
*/
40+
virtual QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName, int& score /Out/ ) const = 0;
41+
};

python/gui/editorwidgets/core/qgseditorwidgetfactory.sip

+10-4
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,23 @@ class QgsEditorWidgetFactory
170170
*/
171171
virtual QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx );
172172

173-
private:
174173
/**
175174
* This method allows disabling this editor widget type for a certain field.
176-
* By default, it returns true for all fields.
175+
* By default, it returns 5 for every fields.
177176
* Reimplement this if you only support certain fields.
178177
*
178+
* Typical return values are:
179+
* * 0: not supported
180+
* * 5: maybe support (for example, Datetime support strings depending on their content)
181+
* * 10: basic support (this is what returns TextEdit for example, since it supports everything in a crude way)
182+
* * 20: specialised support
183+
*
179184
* @param vl
180185
* @param fieldIdx
181-
* @return True if the field is supported.
186+
* @return 0 if the field is not supported or a bigger number if it can (the widget with the biggest number will be
187+
* taken by default). The default implementation returns 5..
182188
*
183189
* @see supportsField( QgsVectorLayer* vl, fieldIdx )
184190
*/
185-
virtual bool isFieldSupported( QgsVectorLayer* vl, int fieldIdx );
191+
virtual unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const;
186192
};

python/gui/editorwidgets/core/qgseditorwidgetregistry.sip

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* *
1414
***************************************************************************/
1515

16+
1617
/**
1718
* This class manages all known edit widget factories
1819
*/
@@ -43,6 +44,17 @@ class QgsEditorWidgetRegistry : QObject
4344
*/
4445
static void initEditors( QgsMapCanvas* mapCanvas = 0, QgsMessageBar* messageBar = 0 );
4546

47+
/**
48+
* Find the best editor widget and its configuration for a given field.
49+
*
50+
* @param vl The vector layer for which this widget will be created
51+
* @param fieldIdx The field index on the specified layer for which this widget will be created
52+
*
53+
* @return The id of the widget type to use and its config
54+
*/
55+
QgsEditorWidgetSetup findBest( const QgsVectorLayer* vl, const QString& fieldName ) const;
56+
57+
4658
/**
4759
* Create an attribute editor widget wrapper of a given type for a given field.
4860
* The editor may be NULL if you want the widget wrapper to create a default widget.
@@ -116,4 +128,11 @@ class QgsEditorWidgetRegistry : QObject
116128
* @return true, if successful, false, if the widgetId is already in use or widgetFactory is NULL
117129
*/
118130
bool registerWidget( const QString& widgetId, QgsEditorWidgetFactory* widgetFactory /Transfer/ );
131+
132+
/**
133+
* Register a new auto-conf plugin.
134+
*
135+
* @param plugin The plugin (ownership is transfered)
136+
*/
137+
void registerAutoConfPlugin( QgsEditorWidgetAutoConfPlugin* plugin );
119138
};

python/gui/gui.sip

+1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
%Include effects/qgspainteffectwidget.sip
261261

262262
%Include editorwidgets/core/qgseditorconfigwidget.sip
263+
%Include editorwidgets/core/qgseditorwidgetautoconf.sip
263264
%Include editorwidgets/core/qgseditorwidgetfactory.sip
264265
%Include editorwidgets/core/qgseditorwidgetregistry.sip
265266
%Include editorwidgets/core/qgseditorwidgetwrapper.sip

src/app/ogr/qgsvectorlayersaveasdialog.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
249249
bool foundFieldThatCanBeExportedAsDisplayedValue = false;
250250
for ( int i = 0; i < mLayer->fields().size(); ++i )
251251
{
252-
if ( mLayer->editFormConfig().widgetType( i ) != "TextEdit" &&
253-
QgsEditorWidgetRegistry::instance()->factory( mLayer->editFormConfig().widgetType( i ) ) )
252+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, mLayer->fields()[i].name() );
253+
if ( setup.type() != "TextEdit" &&
254+
QgsEditorWidgetRegistry::instance()->factory( setup.type() ) )
254255
{
255256
foundFieldThatCanBeExportedAsDisplayedValue = true;
256257
break;
@@ -285,10 +286,11 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
285286

286287
if ( foundFieldThatCanBeExportedAsDisplayedValue )
287288
{
289+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, mLayer->fields()[i].name() );
288290
QgsEditorWidgetFactory *factory = nullptr;
289291
if ( flags == Qt::ItemIsEnabled &&
290-
mLayer->editFormConfig().widgetType( i ) != "TextEdit" &&
291-
( factory = QgsEditorWidgetRegistry::instance()->factory( mLayer->editFormConfig().widgetType( i ) ) ) )
292+
setup.type() != "TextEdit" &&
293+
( factory = QgsEditorWidgetRegistry::instance()->factory( setup.type() ) ) )
292294
{
293295
item = new QTableWidgetItem( tr( "Use %1" ).arg( factory->name() ) );
294296
item->setFlags(( selectAllFields ) ? ( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ) : Qt::ItemIsUserCheckable );

src/app/qgisapp.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -6149,11 +6149,11 @@ QVariant QgisAppFieldValueConverter::convert( int idx, const QVariant& value )
61496149
{
61506150
return value;
61516151
}
6152-
QgsEditorWidgetFactory *factory = QgsEditorWidgetRegistry::instance()->factory( mLayer->editFormConfig().widgetType( idx ) );
6152+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, mLayer->fields().field( idx ).name() );
6153+
QgsEditorWidgetFactory *factory = QgsEditorWidgetRegistry::instance()->factory( setup.type() );
61536154
if ( factory )
61546155
{
6155-
QgsEditorWidgetConfig cfg( mLayer->editFormConfig().widgetConfig( idx ) );
6156-
return QVariant( factory->representValue( mLayer, idx, cfg, QVariant(), value ) );
6156+
return QVariant( factory->representValue( mLayer, idx, setup.config(), QVariant(), value ) );
61576157
}
61586158
return value;
61596159
}

src/app/qgsattributetabledialog.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ void QgsAttributeTableDialog::columnBoxInit()
391391
if ( idx < 0 )
392392
continue;
393393

394-
if ( mLayer->editFormConfig().widgetType( idx ) != "Hidden" )
394+
if ( QgsEditorWidgetRegistry::instance()->findBest( mLayer, field.name() ).type() != "Hidden" )
395395
{
396396
QIcon icon = mLayer->fields().iconForField( idx );
397397
QString alias = mLayer->attributeDisplayName( idx );
@@ -527,10 +527,9 @@ void QgsAttributeTableDialog::filterColumnChanged( QObject* filterAction )
527527
int fldIdx = mLayer->fieldNameIndex( fieldName );
528528
if ( fldIdx < 0 )
529529
return;
530-
const QString widgetType = mLayer->editFormConfig().widgetType( fldIdx );
531-
const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig().widgetConfig( fldIdx );
530+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, fieldName );
532531
mCurrentSearchWidgetWrapper = QgsEditorWidgetRegistry::instance()->
533-
createSearchWidget( widgetType, mLayer, fldIdx, widgetConfig, mFilterContainer, mEditorContext );
532+
createSearchWidget( setup.type(), mLayer, fldIdx, setup.config(), mFilterContainer, mEditorContext );
534533
if ( mCurrentSearchWidgetWrapper->applyDirectly() )
535534
{
536535
connect( mCurrentSearchWidgetWrapper, SIGNAL( expressionChanged( QString ) ), SLOT( filterQueryChanged( QString ) ) );

src/app/qgsfieldsproperties.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,9 @@ void QgsFieldsProperties::loadRelations()
392392
if ( nmrel.fieldPairs().at( 0 ).referencingField() != relation.fieldPairs().at( 0 ).referencingField() )
393393
nmCombo->addItem( QString( "%1 (%2)" ).arg( nmrel.referencedLayer()->name(), nmrel.fieldPairs().at( 0 ).referencedField() ), nmrel.id() );
394394

395-
QgsEditorWidgetConfig cfg = mLayer->editFormConfig().widgetConfig( relation.id() );
395+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, relation.id() );
396396

397-
QVariant nmrelcfg = cfg.value( "nm-rel" );
397+
const QVariant nmrelcfg = setup.config().value( "nm-rel" );
398398

399399
int idx = nmCombo->findData( nmrelcfg.toString() );
400400

@@ -1028,8 +1028,9 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
10281028
mNotNull = layer->editFormConfig().notNull( idx );
10291029
mConstraint = layer->editFormConfig().expression( idx );
10301030
mConstraintDescription = layer->editFormConfig().expressionDescription( idx );
1031-
mEditorWidgetType = layer->editFormConfig().widgetType( idx );
1032-
mEditorWidgetConfig = layer->editFormConfig().widgetConfig( idx );
1031+
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer, layer->fields().field( idx ).name() );
1032+
mEditorWidgetType = setup.type();
1033+
mEditorWidgetConfig = setup.config();
10331034
}
10341035

10351036
/*

0 commit comments

Comments
 (0)