QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgslegacyhelpers.h"
20 #include "qgsmessagelog.h"
21 #include "qgsproject.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsmaplayerregistry.h"
24 
25 // Editors
28 #include "qgscolorwidgetfactory.h"
29 #include "qgsdatetimeeditfactory.h"
33 #include "qgshiddenwidgetfactory.h"
34 #include "qgsphotowidgetfactory.h"
35 #include "qgsrangewidgetfactory.h"
39 #include "qgsuuidwidgetfactory.h"
42 #ifdef WITH_QTWEBKIT
44 #endif
45 
46 
48 {
49  static QgsEditorWidgetRegistry sInstance;
50  return &sInstance;
51 }
52 
54 {
56  reg->registerWidget( "Classification", new QgsClassificationWidgetWrapperFactory( tr( "Classification" ) ) );
57  reg->registerWidget( "Range", new QgsRangeWidgetFactory( tr( "Range" ) ) );
58  reg->registerWidget( "UniqueValues", new QgsUniqueValueWidgetFactory( tr( "Unique Values" ) ) );
59  reg->registerWidget( "FileName", new QgsFileNameWidgetFactory( tr( "File Name" ) ) );
60  reg->registerWidget( "ValueMap", new QgsValueMapWidgetFactory( tr( "Value Map" ) ) );
61  reg->registerWidget( "Enumeration", new QgsEnumerationWidgetFactory( tr( "Enumeration" ) ) );
62  reg->registerWidget( "Hidden", new QgsHiddenWidgetFactory( tr( "Hidden" ) ) );
63  reg->registerWidget( "CheckBox", new QgsCheckboxWidgetFactory( tr( "Check Box" ) ) );
64  reg->registerWidget( "TextEdit", new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
65  reg->registerWidget( "ValueRelation", new QgsValueRelationWidgetFactory( tr( "Value Relation" ) ) );
66  reg->registerWidget( "UuidGenerator", new QgsUuidWidgetFactory( tr( "Uuid Generator" ) ) );
67  reg->registerWidget( "Photo", new QgsPhotoWidgetFactory( tr( "Photo" ) ) );
68 #ifdef WITH_QTWEBKIT
69  reg->registerWidget( "WebView", new QgsWebViewWidgetFactory( tr( "Web View" ) ) );
70 #endif
71  reg->registerWidget( "Color", new QgsColorWidgetFactory( tr( "Color" ) ) );
72  reg->registerWidget( "RelationReference", new QgsRelationReferenceFactory( tr( "Relation Reference" ), mapCanvas, messageBar ) );
73  reg->registerWidget( "DateTime", new QgsDateTimeEditFactory( tr( "Date/Time" ) ) );
74  reg->registerWidget( "ExternalResource", new QgsExternalResourceWidgetFactory( tr( "External Resource" ) ) );
75 }
76 
78 {
79  connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
80  // connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
81 
82  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QgsMapLayer* ) ), this, SLOT( mapLayerWillBeRemoved( QgsMapLayer* ) ) );
83  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( mapLayerAdded( QgsMapLayer* ) ) );
84 }
85 
87 {
88  qDeleteAll( mWidgetFactories );
89 }
90 
92 {
93  if ( mWidgetFactories.contains( widgetId ) )
94  {
95  QgsEditorWidgetWrapper* ww = mWidgetFactories[widgetId]->create( vl, fieldIdx, editor, parent );
96 
97  if ( ww )
98  {
99  ww->setConfig( config );
100  ww->setContext( context );
101  // Make sure that there is a widget created at this point
102  // so setValue() et al won't crash
103  ww->widget();
104 
105  // If we tried to set a widget which is not supported by this wrapper
106  if ( !ww->valid() )
107  {
108  delete ww;
109  QString wid = findSuitableWrapper( editor, "TextEdit" );
110  ww = mWidgetFactories[wid]->create( vl, fieldIdx, editor, parent );
111  ww->setConfig( config );
112  ww->setContext( context );
113  }
114 
115  return ww;
116  }
117  }
118 
119  return nullptr;
120 }
121 
123 {
124  if ( mWidgetFactories.contains( widgetId ) )
125  {
126  QgsSearchWidgetWrapper* ww = mWidgetFactories[widgetId]->createSearchWidget( vl, fieldIdx, parent );
127 
128  if ( ww )
129  {
130  ww->setConfig( config );
131  ww->setContext( context );
132  // Make sure that there is a widget created at this point
133  // so setValue() et al won't crash
134  ww->widget();
135  ww->clearWidget();
136  return ww;
137  }
138  }
139  return nullptr;
140 }
141 
143 {
144  if ( mWidgetFactories.contains( widgetId ) )
145  {
146  return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
147  }
148  return nullptr;
149 }
150 
152 {
153  if ( mWidgetFactories.contains( widgetId ) )
154  {
155  return mWidgetFactories[widgetId]->name();
156  }
157 
158  return QString();
159 }
160 
162 {
163  return mWidgetFactories;
164 }
165 
167 {
168  return mWidgetFactories.value( widgetId );
169 }
170 
172 {
173  if ( !widgetFactory )
174  {
175  QgsMessageLog::instance()->logMessage( "QgsEditorWidgetRegistry: Factory not valid." );
176  return false;
177  }
178  else if ( mWidgetFactories.contains( widgetId ) )
179  {
180  QgsMessageLog::instance()->logMessage( QString( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
181  return false;
182  }
183  else
184  {
185  mWidgetFactories.insert( widgetId, widgetFactory );
186 
187  // Use this factory as default where it provides the heighest priority
188  QMap<const char*, int> types = widgetFactory->supportedWidgetTypes();
190  it = types.constBegin();
191 
192  for ( ; it != types.constEnd(); ++it )
193  {
194  if ( it.value() > mFactoriesByType[it.key()].first )
195  {
196  mFactoriesByType[it.key()] = qMakePair( it.value(), widgetId );
197  }
198  }
199 
200  return true;
201  }
202 }
203 
204 void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
205 {
206  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
207  {
208  return;
209  }
210 
211  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
212  Q_ASSERT( vectorLayer );
213 
214  QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();
215 
216  for ( int i = 0; i < editTypeNodes.size(); i++ )
217  {
218  QDomNode editTypeNode = editTypeNodes.at( i );
219  QDomElement editTypeElement = editTypeNode.toElement();
220 
221  QString name = editTypeElement.attribute( "name" );
222 
223  int idx = vectorLayer->fieldNameIndex( name );
224  if ( idx == -1 )
225  continue;
226 
227  bool hasLegacyType;
228  QgsVectorLayer::EditType editType =
229  ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt( &hasLegacyType );
230 
231  QString ewv2Type;
233 
234  if ( hasLegacyType && editType != QgsVectorLayer::EditorWidgetV2 )
235  {
237  ewv2Type = readLegacyConfig( vectorLayer, editTypeElement, cfg );
239  }
240  else
241  ewv2Type = editTypeElement.attribute( "widgetv2type" );
242 
243  if ( mWidgetFactories.contains( ewv2Type ) )
244  {
245  vectorLayer->editFormConfig()->setWidgetType( idx, ewv2Type );
246  QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();
247 
248  if ( !ewv2CfgElem.isNull() )
249  {
250  cfg = mWidgetFactories[ewv2Type]->readEditorConfig( ewv2CfgElem, vectorLayer, idx );
251  }
252 
253  vectorLayer->editFormConfig()->setReadOnly( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) != "1" );
254  vectorLayer->editFormConfig()->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
255  vectorLayer->editFormConfig()->setNotNull( idx, ewv2CfgElem.attribute( "notNull", "0" ) == "1" );
256  vectorLayer->editFormConfig()->setExpression( idx, ewv2CfgElem.attribute( "constraint", QString() ) );
257  vectorLayer->editFormConfig()->setExpressionDescription( idx, ewv2CfgElem.attribute( "constraintDescription", QString() ) );
258 
259  vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
260  }
261  else
262  {
263  QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
264  }
265  }
266 }
267 
268 const QString QgsEditorWidgetRegistry::readLegacyConfig( QgsVectorLayer* vl, const QDomElement& editTypeElement, QgsEditorWidgetConfig& cfg )
269 {
270  QString name = editTypeElement.attribute( "name" );
271 
272  QgsVectorLayer::EditType editType = ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();
273 
275  return QgsLegacyHelpers::convertEditType( editType, cfg, vl, name, editTypeElement );
277 }
278 
279 void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc ) const
280 {
281  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
282  {
283  return;
284  }
285 
286  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
287  if ( !vectorLayer )
288  {
289  return;
290  }
291 
292  QDomNode editTypesNode = doc.createElement( "edittypes" );
293 
294  QgsFields fields = vectorLayer->fields();
295  for ( int idx = 0; idx < fields.count(); ++idx )
296  {
297  const QgsField &field = fields.at( idx );
298  const QString& widgetType = vectorLayer->editFormConfig()->widgetType( idx );
299  if ( !mWidgetFactories.contains( widgetType ) )
300  {
301  QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
302  continue;
303  }
304 
305 
306  QDomElement editTypeElement = doc.createElement( "edittype" );
307  editTypeElement.setAttribute( "name", field.name() );
308  editTypeElement.setAttribute( "widgetv2type", widgetType );
309 
310  if ( mWidgetFactories.contains( widgetType ) )
311  {
312  QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
313  ewv2CfgElem.setAttribute( "fieldEditable", !vectorLayer->editFormConfig()->readOnly( idx ) );
314  ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->editFormConfig()->labelOnTop( idx ) );
315  ewv2CfgElem.setAttribute( "notNull", vectorLayer->editFormConfig()->notNull( idx ) );
316  ewv2CfgElem.setAttribute( "constraint", vectorLayer->editFormConfig()->expression( idx ) );
317  ewv2CfgElem.setAttribute( "constraintDescription", vectorLayer->editFormConfig()->expressionDescription( idx ) );
318 
319  mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig()->widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );
320 
321  editTypeElement.appendChild( ewv2CfgElem );
322  }
323 
324  editTypesNode.appendChild( editTypeElement );
325  }
326 
327  layerElem.appendChild( editTypesNode );
328 }
329 
330 void QgsEditorWidgetRegistry::mapLayerWillBeRemoved( QgsMapLayer* mapLayer )
331 {
332  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
333 
334  if ( vl )
335  {
336  disconnect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
337  disconnect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
338  }
339 }
340 
341 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
342 {
343  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
344 
345  if ( vl )
346  {
347  connect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
348  connect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
349  }
350 }
351 
352 void QgsEditorWidgetRegistry::readSymbology( const QDomElement& element, QString& errorMessage )
353 {
354  Q_UNUSED( errorMessage )
355  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
356 
357  Q_ASSERT( vl );
358 
359  readMapLayer( vl, element );
360 }
361 
362 void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage )
363 {
364  Q_UNUSED( errorMessage )
365  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
366 
367  Q_ASSERT( vl );
368 
369  writeMapLayer( vl, element, doc );
370 }
371 
372 QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor, const QString& defaultWidget )
373 {
374  QMap<const char*, QPair<int, QString> >::ConstIterator it;
375 
376  QString widgetid;
377 
378  // Editor can be null
379  if ( editor )
380  {
381  int weight = 0;
382 
383  it = mFactoriesByType.constBegin();
384  for ( ; it != mFactoriesByType.constEnd(); ++it )
385  {
386  if ( editor->staticMetaObject.className() == it.key() )
387  {
388  // if it's a perfect match: return it directly
389  return it.value().second;
390  }
391  else if ( editor->inherits( it.key() ) )
392  {
393  // if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
394  if ( it.value().first > weight )
395  {
396  weight = it.value().first;
397  widgetid = it.value().second;
398  }
399  }
400  }
401  }
402 
403  if ( widgetid.isNull() )
404  widgetid = defaultWidget;
405  return widgetid;
406 }
const char * className() const
Manages an editor widget Widget and wrapper share the same parent.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
bool notNull(int fieldidx) const
Returns if the field at fieldidx should be treated as NOT NULL value.
virtual QMap< const char *, int > supportedWidgetTypes()
Returns a list of widget types which this editor widget supports.
bool contains(const Key &key) const
This class should be subclassed for every configurable editor widget type.
void setWidgetConfig(int attrIdx, const QgsEditorWidgetConfig &config)
Set the editor widget config for a field.
QString name
Definition: qgsfield.h:52
QDomNode appendChild(const QDomNode &newChild)
QgsEditorWidgetFactory * factory(const QString &widgetId)
Get a factory for the given widget type id.
QString attribute(const QString &name, const QString &defValue) const
This class manages all known edit widget factories.
void setExpressionDescription(int idx, const QString &descr)
Set the constraint expression description for a specific field.
This class contains context information for attribute editor widgets.
QObject * sender() const
Manages an editor widget Widget and wrapper share the same parent.
const_iterator constBegin() const
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:515
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
Container of fields for a vector layer.
Definition: qgsfield.h:252
QString expression(int idx) const
Returns the constraint expression of a specific field.
void setWidgetType(int fieldIdx, const QString &widgetType)
Set the editor widget type for a field.
QgsEditorConfigWidget * createConfigWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
Creates a configuration widget.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QDomNodeList childNodes() const
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:99
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: qgsfield.cpp:402
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:109
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:422
bool isNull() const
QgsEditFormConfig * editFormConfig() const
Get the configuration of the form used to represent this vector layer.
QgsFields fields() const
Returns the list of fields of this layer.
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...
QDomElement toElement() const
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.
const char * name() const
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.
bool inherits(const char *className) const
void setAttribute(const QString &name, const QString &value)
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.
int toInt(bool *ok, int base) const
const QMap< QString, QgsEditorWidgetFactory * > & factories()
Get access to all registered factories.
const_iterator constEnd() const
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)
QString expressionDescription(int idx) const
Returns the constraint expression description of a specific filed.
void setNotNull(int idx, bool notnull=true)
Set if the field at fieldidx should be treated as NOT NULL value.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:44
QDomNode namedItem(const QString &name) const
void setExpression(int idx, const QString &str)
Set the constraint expression for a specific field.
bool isNull() const
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:516
const Key key(const T &value) const
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
QgsEditorWidgetConfig widgetConfig(int fieldIdx) const
Get the configuration for the editor widget used to represent the field at the given index...
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
static Q_DECL_DEPRECATED const QString convertEditType(QgsVectorLayer::EditType editType, QgsEditorWidgetConfig &cfg, QgsVectorLayer *vl, const QString &name, const QDomElement &editTypeElement=QDomElement())
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
static QgsMessageLog * instance()
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:382
iterator insert(const Key &key, const T &value)
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 ...
int size() const
QDomElement createElement(const QString &tagName)
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
QString widgetType(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
Represents a vector layer which manages a vector based data sets.
modularized edit widgets
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
static void initEditors(QgsMapCanvas *mapCanvas=nullptr, QgsMessageBar *messageBar=nullptr)
Registers all the default widgets.
QDomNode at(int index) const
const T value(const Key &key) const