QGIS API Documentation  2.99.0-Master (37c43df)
qgseditorwidgetregistry.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgseditorwidgetregistry.cpp
3  --------------------------------------
4  Date : 24.4.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
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 
17 
19 #include "qgsmessagelog.h"
20 #include "qgsproject.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsmaplayerregistry.h"
23 #include "qgseditorwidgetwrapper.h"
24 #include "qgssearchwidgetwrapper.h"
25 
26 // Editors
29 #include "qgscolorwidgetfactory.h"
30 #include "qgsdatetimeeditfactory.h"
34 #include "qgshiddenwidgetfactory.h"
36 #include "qgslistwidgetfactory.h"
37 #include "qgsphotowidgetfactory.h"
38 #include "qgsrangewidgetfactory.h"
42 #include "qgsuuidwidgetfactory.h"
45 #ifdef WITH_QTWEBKIT
47 #endif
48 
49 
51 {
52  static QgsEditorWidgetRegistry sInstance;
53  return &sInstance;
54 }
55 
57 {
59  reg->registerWidget( QStringLiteral( "TextEdit" ), new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
60  reg->registerWidget( QStringLiteral( "Classification" ), new QgsClassificationWidgetWrapperFactory( tr( "Classification" ) ) );
61  reg->registerWidget( QStringLiteral( "Range" ), new QgsRangeWidgetFactory( tr( "Range" ) ) );
62  reg->registerWidget( QStringLiteral( "UniqueValues" ), new QgsUniqueValueWidgetFactory( tr( "Unique Values" ) ) );
63  reg->registerWidget( QStringLiteral( "FileName" ), new QgsFileNameWidgetFactory( tr( "File Name" ) ) );
64  reg->registerWidget( QStringLiteral( "ValueMap" ), new QgsValueMapWidgetFactory( tr( "Value Map" ) ) );
65  reg->registerWidget( QStringLiteral( "Enumeration" ), new QgsEnumerationWidgetFactory( tr( "Enumeration" ) ) );
66  reg->registerWidget( QStringLiteral( "Hidden" ), new QgsHiddenWidgetFactory( tr( "Hidden" ) ) );
67  reg->registerWidget( QStringLiteral( "CheckBox" ), new QgsCheckboxWidgetFactory( tr( "Check Box" ) ) );
68  reg->registerWidget( QStringLiteral( "ValueRelation" ), new QgsValueRelationWidgetFactory( tr( "Value Relation" ) ) );
69  reg->registerWidget( QStringLiteral( "UuidGenerator" ), new QgsUuidWidgetFactory( tr( "Uuid Generator" ) ) );
70  reg->registerWidget( QStringLiteral( "Photo" ), new QgsPhotoWidgetFactory( tr( "Photo" ) ) );
71 #ifdef WITH_QTWEBKIT
72  reg->registerWidget( QStringLiteral( "WebView" ), new QgsWebViewWidgetFactory( tr( "Web View" ) ) );
73 #endif
74  reg->registerWidget( QStringLiteral( "Color" ), new QgsColorWidgetFactory( tr( "Color" ) ) );
75  reg->registerWidget( QStringLiteral( "RelationReference" ), new QgsRelationReferenceFactory( tr( "Relation Reference" ), mapCanvas, messageBar ) );
76  reg->registerWidget( QStringLiteral( "DateTime" ), new QgsDateTimeEditFactory( tr( "Date/Time" ) ) );
77  reg->registerWidget( QStringLiteral( "ExternalResource" ), new QgsExternalResourceWidgetFactory( tr( "External Resource" ) ) );
78  reg->registerWidget( QStringLiteral( "KeyValue" ), new QgsKeyValueWidgetFactory( tr( "Key/Value" ) ) );
79  reg->registerWidget( QStringLiteral( "List" ), new QgsListWidgetFactory( tr( "List" ) ) );
80 }
81 
83 {
84  connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
85  // connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
86 
87  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QgsMapLayer* ) ), this, SLOT( mapLayerWillBeRemoved( QgsMapLayer* ) ) );
88  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( mapLayerAdded( QgsMapLayer* ) ) );
89 }
90 
92 {
93  qDeleteAll( mWidgetFactories );
94 }
95 
96 QgsEditorWidgetSetup QgsEditorWidgetRegistry::findBest( const QgsVectorLayer* vl, const QString& fieldName ) const
97 {
98  const QString fromConfig = vl->editFormConfig().widgetType( fieldName );
99  if ( !fromConfig.isNull() )
100  {
101  return QgsEditorWidgetSetup( fromConfig, vl->editFormConfig().widgetConfig( fieldName ) );
102  }
103  return mAutoConf.editorWidgetSetup( vl, fieldName );
104 }
105 
106 QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent, const QgsAttributeEditorContext &context )
107 {
108  const QString fieldName = vl->fields().field( fieldIdx ).name();
109  const QgsEditorWidgetSetup setup = findBest( vl, fieldName );
110  return create( setup.type(), vl, fieldIdx, setup.config(), editor, parent, context );
111 }
112 
113 QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* editor, QWidget* parent, const QgsAttributeEditorContext &context )
114 {
115  if ( mWidgetFactories.contains( widgetId ) )
116  {
117  QgsEditorWidgetWrapper* ww = mWidgetFactories[widgetId]->create( vl, fieldIdx, editor, parent );
118 
119  if ( ww )
120  {
121  ww->setConfig( config );
122  ww->setContext( context );
123  // Make sure that there is a widget created at this point
124  // so setValue() et al won't crash
125  ww->widget();
126 
127  // If we tried to set a widget which is not supported by this wrapper
128  if ( !ww->valid() )
129  {
130  delete ww;
131  QString wid = findSuitableWrapper( editor, QStringLiteral( "TextEdit" ) );
132  ww = mWidgetFactories[wid]->create( vl, fieldIdx, editor, parent );
133  ww->setConfig( config );
134  ww->setContext( context );
135  }
136 
137  return ww;
138  }
139  }
140 
141  return nullptr;
142 }
143 
144 QgsSearchWidgetWrapper* QgsEditorWidgetRegistry::createSearchWidget( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* parent, const QgsAttributeEditorContext &context )
145 {
146  if ( mWidgetFactories.contains( widgetId ) )
147  {
148  QgsSearchWidgetWrapper* ww = mWidgetFactories[widgetId]->createSearchWidget( vl, fieldIdx, parent );
149 
150  if ( ww )
151  {
152  ww->setConfig( config );
153  ww->setContext( context );
154  // Make sure that there is a widget created at this point
155  // so setValue() et al won't crash
156  ww->widget();
157  ww->clearWidget();
158  return ww;
159  }
160  }
161  return nullptr;
162 }
163 
164 QgsEditorConfigWidget* QgsEditorWidgetRegistry::createConfigWidget( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
165 {
166  if ( mWidgetFactories.contains( widgetId ) )
167  {
168  return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
169  }
170  return nullptr;
171 }
172 
173 QString QgsEditorWidgetRegistry::name( const QString& widgetId )
174 {
175  if ( mWidgetFactories.contains( widgetId ) )
176  {
177  return mWidgetFactories[widgetId]->name();
178  }
179 
180  return QString();
181 }
182 
183 const QMap<QString, QgsEditorWidgetFactory*>& QgsEditorWidgetRegistry::factories()
184 {
185  return mWidgetFactories;
186 }
187 
189 {
190  return mWidgetFactories.value( widgetId );
191 }
192 
193 bool QgsEditorWidgetRegistry::registerWidget( const QString& widgetId, QgsEditorWidgetFactory* widgetFactory )
194 {
195  if ( !widgetFactory )
196  {
197  QgsMessageLog::instance()->logMessage( QStringLiteral( "QgsEditorWidgetRegistry: Factory not valid." ) );
198  return false;
199  }
200  else if ( mWidgetFactories.contains( widgetId ) )
201  {
202  QgsMessageLog::instance()->logMessage( QStringLiteral( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
203  return false;
204  }
205  else
206  {
207  mWidgetFactories.insert( widgetId, widgetFactory );
208 
209  // Use this factory as default where it provides the heighest priority
210  QHash<const char*, int> types = widgetFactory->supportedWidgetTypes();
211  QHash<const char*, int>::ConstIterator it;
212  it = types.constBegin();
213 
214  for ( ; it != types.constEnd(); ++it )
215  {
216  if ( it.value() > mFactoriesByType[it.key()].first )
217  {
218  mFactoriesByType[it.key()] = qMakePair( it.value(), widgetId );
219  }
220  }
221 
222  return true;
223  }
224 }
225 
226 void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
227 {
228  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
229  {
230  return;
231  }
232 
233  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
234  Q_ASSERT( vectorLayer );
235 
236  QDomNodeList editTypeNodes = layerElem.namedItem( QStringLiteral( "edittypes" ) ).childNodes();
237 
238  QgsEditFormConfig formConfig = vectorLayer->editFormConfig();
239 
240  for ( int i = 0; i < editTypeNodes.size(); i++ )
241  {
242  QDomNode editTypeNode = editTypeNodes.at( i );
243  QDomElement editTypeElement = editTypeNode.toElement();
244 
245  QString name = editTypeElement.attribute( QStringLiteral( "name" ) );
246 
247  int idx = vectorLayer->fields().lookupField( name );
248  if ( idx == -1 )
249  continue;
250 
251  QString ewv2Type = editTypeElement.attribute( QStringLiteral( "widgetv2type" ) );
253 
254  if ( mWidgetFactories.contains( ewv2Type ) )
255  {
256  formConfig.setWidgetType( name, ewv2Type );
257  QDomElement ewv2CfgElem = editTypeElement.namedItem( QStringLiteral( "widgetv2config" ) ).toElement();
258 
259  if ( !ewv2CfgElem.isNull() )
260  {
261  cfg = mWidgetFactories[ewv2Type]->readEditorConfig( ewv2CfgElem, vectorLayer, idx );
262  }
263 
264  formConfig.setReadOnly( idx, ewv2CfgElem.attribute( QStringLiteral( "fieldEditable" ), QStringLiteral( "1" ) ) != QLatin1String( "1" ) );
265  formConfig.setLabelOnTop( idx, ewv2CfgElem.attribute( QStringLiteral( "labelOnTop" ), QStringLiteral( "0" ) ) == QLatin1String( "1" ) );
266  if ( ewv2CfgElem.attribute( QStringLiteral( "notNull" ), QStringLiteral( "0" ) ) == QLatin1String( "1" ) )
267  {
268  // upgrade from older config
270  }
271  if ( !ewv2CfgElem.attribute( QStringLiteral( "constraint" ), QString() ).isEmpty() )
272  {
273  // upgrade from older config
274  vectorLayer->setConstraintExpression( idx, ewv2CfgElem.attribute( QStringLiteral( "constraint" ), QString() ),
275  ewv2CfgElem.attribute( QStringLiteral( "constraintDescription" ), QString() ) );
276  }
277 
278  formConfig.setWidgetConfig( name, cfg );
279  }
280  else
281  {
282  QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
283  }
284  }
285 
286  vectorLayer->setEditFormConfig( formConfig );
287 }
288 
289 void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc ) const
290 {
291  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
292  {
293  return;
294  }
295 
296  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
297  if ( !vectorLayer )
298  {
299  return;
300  }
301 
302  QDomNode editTypesNode = doc.createElement( QStringLiteral( "edittypes" ) );
303 
304  QgsFields fields = vectorLayer->fields();
305  for ( int idx = 0; idx < fields.count(); ++idx )
306  {
307  const QgsField field = fields.at( idx );
308  const QString& widgetType = vectorLayer->editFormConfig().widgetType( field.name() );
309  if ( widgetType.isNull() )
310  {
311  // Don't save widget config if it is not manually edited
312  continue;
313  }
314  if ( !mWidgetFactories.contains( widgetType ) )
315  {
316  QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
317  continue;
318  }
319 
320 
321  QDomElement editTypeElement = doc.createElement( QStringLiteral( "edittype" ) );
322  editTypeElement.setAttribute( QStringLiteral( "name" ), field.name() );
323  editTypeElement.setAttribute( QStringLiteral( "widgetv2type" ), widgetType );
324 
325  if ( mWidgetFactories.contains( widgetType ) )
326  {
327  QDomElement ewv2CfgElem = doc.createElement( QStringLiteral( "widgetv2config" ) );
328  ewv2CfgElem.setAttribute( QStringLiteral( "fieldEditable" ), !vectorLayer->editFormConfig().readOnly( idx ) );
329  ewv2CfgElem.setAttribute( QStringLiteral( "labelOnTop" ), vectorLayer->editFormConfig().labelOnTop( idx ) );
330 
331  mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig().widgetConfig( field.name() ), ewv2CfgElem, doc, vectorLayer, idx );
332 
333  editTypeElement.appendChild( ewv2CfgElem );
334  }
335 
336  editTypesNode.appendChild( editTypeElement );
337  }
338 
339  layerElem.appendChild( editTypesNode );
340 }
341 
342 void QgsEditorWidgetRegistry::mapLayerWillBeRemoved( QgsMapLayer* mapLayer )
343 {
344  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
345 
346  if ( vl )
347  {
348  disconnect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
349  disconnect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
350  }
351 }
352 
353 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
354 {
355  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
356 
357  if ( vl )
358  {
359  connect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
360  connect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
361  }
362 }
363 
364 void QgsEditorWidgetRegistry::readSymbology( const QDomElement& element, QString& errorMessage )
365 {
366  Q_UNUSED( errorMessage )
367  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
368 
369  Q_ASSERT( vl );
370 
371  readMapLayer( vl, element );
372 }
373 
374 void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage )
375 {
376  Q_UNUSED( errorMessage )
377  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
378 
379  Q_ASSERT( vl );
380 
381  writeMapLayer( vl, element, doc );
382 }
383 
384 QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor, const QString& defaultWidget )
385 {
386  QMap<const char*, QPair<int, QString> >::ConstIterator it;
387 
388  QString widgetid;
389 
390  // Editor can be null
391  if ( editor )
392  {
393  int weight = 0;
394 
395  it = mFactoriesByType.constBegin();
396  for ( ; it != mFactoriesByType.constEnd(); ++it )
397  {
398  if ( editor->staticMetaObject.className() == it.key() )
399  {
400  // if it's a perfect match: return it directly
401  return it.value().second;
402  }
403  else if ( editor->inherits( it.key() ) )
404  {
405  // if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
406  if ( it.value().first > weight )
407  {
408  weight = it.value().first;
409  widgetid = it.value().second;
410  }
411  }
412  }
413  }
414 
415  if ( widgetid.isNull() )
416  widgetid = defaultWidget;
417  return widgetid;
418 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:291
Manages an editor widget Widget and wrapper share the same parent.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
This class should be subclassed for every configurable editor widget type.
QString name
Definition: qgsfield.h:55
QgsEditorWidgetFactory * factory(const QString &widgetId)
Get a factory for the given widget type id.
This class manages all known edit widget factories.
This class contains context information for attribute editor widgets.
Manages an editor widget Widget and wrapper share the same parent.
Factory for widgets for editing a QVariantList or a QStringList.
QgsEditorWidgetConfig config() const
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
Container of fields for a vector layer.
Definition: qgsfields.h:36
QgsEditorConfigWidget * createConfigWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
Creates a configuration widget.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:95
static QgsEditorWidgetRegistry * instance()
This class is a singleton and has therefore to be accessed with this method instead of a constructor...
int count() const
Return number of items.
Definition: qgsfields.cpp:117
void setWidgetConfig(const QString &fieldName, const QgsEditorWidgetConfig &config)
Set the editor widget config for a widget.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:106
QgsFields fields() const
Returns the list of fields of this layer.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:137
void setLabelOnTop(int idx, bool onTop)
If this is set to true, the widget at the given index will receive its label on the previous line whi...
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
virtual void clearWidget()
Clears the widget&#39;s current value and resets it back to the default state.
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
void setConfig(const QgsEditorWidgetConfig &config)
Will set the config of this wrapper to the specified config.
QVariantMap QgsEditorWidgetConfig
Holds a set of configuration parameters for a editor widget wrapper.
virtual bool valid() const =0
Return true if the widget has been properly initialized.
QgsEditorWidgetWrapper * create(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *editor, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Create an attribute editor widget wrapper of a given type for a given field.
void setWidgetType(const QString &fieldName, const QString &widgetType)
Set the editor widget type for a field.
const QMap< QString, QgsEditorWidgetFactory * > & factories()
Get access to all registered factories.
QString widgetType(const QString &fieldName) const
Get the id for the editor widget used to represent the field at the given index Don&#39;t use this direct...
Every attribute editor widget needs a factory, which inherits this class.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:47
QgsEditFormConfig editFormConfig
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsEditorWidgetConfig widgetConfig(const QString &fieldName) const
Get the configuration for the editor widget used to represent the field with the given name Don&#39;t use...
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
Holder for the widget type and its configuration for a field.
Factory for widgets for editing a QVariantMap.
static QgsMessageLog * instance()
QString name(const QString &widgetId)
Get the human readable name for a widget type.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:348
void setConstraintExpression(int index, const QString &expression, const QString &description=QString())
Set the constraint expression for the specified field index.
QWidget * widget()
Access the widget managed by this wrapper.
bool registerWidget(const QString &widgetId, QgsEditorWidgetFactory *widgetFactory)
Register a new widget factory with the given id.
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
void setEditFormConfig(const QgsEditFormConfig &editFormConfig)
Get the configuration of the form used to represent this vector layer.
virtual QHash< const char *, int > supportedWidgetTypes()
Returns a list of widget types which this editor widget supports.
Represents a vector layer which manages a vector based data sets.
QgsField field(int fieldIdx) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:142
static void initEditors(QgsMapCanvas *mapCanvas=nullptr, QgsMessageBar *messageBar=nullptr)
Registers all the default widgets.