Skip to content

Commit 7169079

Browse files
Patrick Valsecchim-kuhn
Patrick Valsecchi
authored andcommitted
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 a1cb2be commit 7169079

File tree

66 files changed

+950
-304
lines changed

Some content is hidden

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

66 files changed

+950
-304
lines changed

doc/api_break.dox

+19-1
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,10 @@ place of a null pointer.</li>
506506
\subsection qgis_api_break_3_0_QgsEditFormConfig QgsEditFormConfig
507507

508508
<ul>
509-
<li>Does no longer inherit QObject
509+
<li>Does no longer inherit QObject</li>
510+
<li>widgetType() and widgetConfig() now reflect only the user configured values.
511+
QgsEditorWidgetRegistry::instance()->findBest() must be used instead.</li>
512+
<li>widgetType(), widgetConfig(), setWidgetType(), setWidgetConfig() and removeWidgetConfig() now only take a string as first parameter. Access by index has been removed.</li>
510513
</ul>
511514

512515
\subsection qgis_api_break_3_0_QgsExpression QgsExpression
@@ -612,6 +615,13 @@ and the new ramp can be retrieved after executing the dialog by calling ramp().<
612615
plugins calling this method will need to be updated.</li>
613616
</ul>
614617

618+
\subsection qgis_api_break_3_0_QgsEditorWidgetRegistry QgsEditorWidgetRegistry
619+
620+
<ul>
621+
<li>The signature of isFieldSupported() has been changed to return an unsigned (how good it supports the given field)
622+
and to const-correct it.</li>
623+
</ul>
624+
615625
\subsection qgis_api_break_3_0_QgsGroupWMSDataDialog QgsGroupWMSDataDialog
616626

617627
<ul>
@@ -847,6 +857,14 @@ plugins calling this method will need to be updated.</li>
847857

848858
<ul>
849859
<li>setMapRenderer() has been removed. Use setMapSettings() instead.</li>
860+
<li>excludeAttributesWMS() and setExcludeAttributesWMS() have been renamed to excludeAttributesWms() and
861+
setExcludeAttributesWms()</li>
862+
<li>excludeAttributesWFS() and setExcludeAttributesWFS() have been renamed to excludeAttributesWfs() and
863+
setExcludeAttributesWfs()</li>
864+
<li>editorWidgetV2() and editorWidgetV2Config() have been removed and QgsEditorWidgetRegistry::instance()->findBest() must be used instead.</li>
865+
<li>setEditorWidgetV2(), setEditorWidgetV2Config() have been removed and their equivalent in editFormConfig() must be used instead.</li>
866+
<li>setCheckedState() is removed. Use editFormConfig()->setWidgetConfig()` instead.</li>
867+
<li>valueMap(), valueRelation(), dateFormat(), widgetSize() have been removed. Use QgsEditorWidgetRegistry::instance()->findBest().config() instead.</li>
850868
</ul>
851869

852870
\subsection qgis_api_break_3_0_QgsRenderContext QgsRenderContext

python/core/core.sip

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
%Include qgsdistancearea.sip
4949
%Include qgseditformconfig.sip
5050
%Include qgseditorwidgetconfig.sip
51+
%Include qgseditorwidgetsetup.sip
5152
%Include qgserror.sip
5253
%Include qgsexpression.sip
5354
%Include qgsexpressioncontext.sip

python/core/qgseditformconfig.sip

+13-52
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,10 @@ class QgsEditFormConfig
137137
* <li>WebView (QgsWebViewWidgetWrapper)</li>
138138
* </ul>
139139
*
140-
* @param fieldIdx Index of the field
140+
* @param fieldName The name of the field
141141
* @param widgetType Type id of the editor widget to use
142142
*/
143-
void setWidgetType( int fieldIdx, const QString& widgetType );
144-
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;
143+
void setWidgetType( const QString& widgetName, const QString& widgetType );
153144

154145
/**
155146
* Get the id for the editor widget used to represent the field at the given index
@@ -160,23 +151,6 @@ class QgsEditFormConfig
160151
*/
161152
QString widgetType( const QString& fieldName ) const;
162153

163-
/**
164-
* Set the editor widget config for a field.
165-
*
166-
* Python: Will accept a map.
167-
*
168-
* Example:
169-
* \code{.py}
170-
* layer.setWidgetConfig( 1, { 'Layer': 'otherlayerid_1234', 'Key': 'Keyfield', 'Value': 'ValueField' } )
171-
* \endcode
172-
*
173-
* @param attrIdx Index of the field
174-
* @param config The config to set for this field
175-
*
176-
* @see setWidgetType() for a list of widgets and choose the widget to see the available options.
177-
*/
178-
void setWidgetConfig( int attrIdx, const QgsEditorWidgetConfig& config );
179-
180154
/**
181155
* Set the editor widget config for a widget.
182156
*
@@ -185,50 +159,32 @@ class QgsEditFormConfig
185159
* layer.setWidgetConfig( 'relation_id', { 'nm-rel': 'other_relation' } )
186160
* \endcode
187161
*
188-
* @param widgetName The name of the widget or field to configure
162+
* @param fieldName The name of the field to configure
189163
* @param config The config to set for this field
190164
*
191165
* @see setWidgetType() for a list of widgets and choose the widget to see the available options.
192166
*
193167
* @note not available in python bindings
194168
*/
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;
169+
void setWidgetConfig( const QString& fieldName, const QgsEditorWidgetConfig& config );
205170

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;
214-
215-
/**
216-
* Remove the configuration for the editor widget used to represent the field at the given index
217-
*
218-
* @param fieldIdx The index of the field
219-
*
220-
* @return true if successful, false if the field does not exist
221-
*/
222-
bool removeWidgetConfig( int fieldIdx );
178+
QgsEditorWidgetConfig widgetConfig( const QString& fieldName ) const;
223179

224180
/**
225181
* Remove the configuration for the editor widget used to represent the field with the given name
226182
*
227-
* @param widgetName The name of the widget. This can be a field name or the name of an additional widget.
183+
* @param fieldName The name of the field.
228184
*
229185
* @return true if successful, false if the field does not exist
230186
*/
231-
bool removeWidgetConfig( const QString& widgetName );
187+
bool removeWidgetConfig( const QString& fieldName );
232188

233189
/**
234190
* This returns true if the field is manually set to read only or if the field
@@ -372,4 +328,9 @@ class QgsEditFormConfig
372328
* Deserialize drag and drop designer elements.
373329
*/
374330
QgsAttributeEditorElement* attributeEditorElementFromDomElement( QDomElement &elem, QgsAttributeEditorElement* parent );
331+
332+
/**
333+
* Parse the XML for the config of one editor widget.
334+
*/
335+
static QgsEditorWidgetConfig parseEditorWidgetConfig( const QDomElement& cfgElem );
375336
};

python/core/qgseditorwidgetsetup.sip

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/** \ingroup core
2+
* Holder for the widget type and its configuration for a field.
3+
*/
4+
class QgsEditorWidgetSetup
5+
{
6+
%TypeHeaderCode
7+
#include <qgseditorwidgetsetup.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+

python/core/qgsfield.sip

+13
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ class QgsField
220220
//! Allows direct construction of QVariants from fields.
221221
operator QVariant() const;
222222

223+
/**
224+
* Set the editor widget setup for the field.
225+
*
226+
* @param v The value to set
227+
*/
228+
void setEditorWidgetSetup( const QgsEditorWidgetSetup& v );
229+
230+
/**
231+
* Get the editor widget setup for the field.
232+
*
233+
* @return the value
234+
*/
235+
const QgsEditorWidgetSetup& editorWidgetSetup() const;
223236
}; // class QgsField
224237

225238

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
@@ -258,6 +258,7 @@
258258
%Include effects/qgspainteffectwidget.sip
259259

260260
%Include editorwidgets/core/qgseditorconfigwidget.sip
261+
%Include editorwidgets/core/qgseditorwidgetautoconf.sip
261262
%Include editorwidgets/core/qgseditorwidgetfactory.sip
262263
%Include editorwidgets/core/qgseditorwidgetregistry.sip
263264
%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 ) ) );

0 commit comments

Comments
 (0)