QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgseditformconfig.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgseditformconfig.cpp
3  ---------------------
4  begin : November 2015
5  copyright : (C) 2015 by 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 
16 #include "qgseditformconfig_p.h"
17 #include "qgseditformconfig.h"
19 #include "qgspathresolver.h"
20 #include "qgsproject.h"
21 #include "qgsreadwritecontext.h"
22 #include "qgsrelationmanager.h"
23 #include "qgslogger.h"
24 #include "qgsxmlutils.h"
25 #include "qgsapplication.h"
26 #include "qgsmessagelog.h"
30 #include <QUrl>
31 
33  : d( new QgsEditFormConfigPrivate() )
34 {
35 }
36 
37 void QgsEditFormConfig::setDataDefinedFieldProperties( const QString &fieldName, const QgsPropertyCollection &properties )
38 {
39  d.detach();
40  d->mDataDefinedFieldProperties[ fieldName ] = properties;
41 }
42 
44 {
45  return d->mDataDefinedFieldProperties.value( fieldName );
46 }
47 
49 {
50  return QgsEditFormConfigPrivate::propertyDefinitions();
51 }
52 
53 QVariantMap QgsEditFormConfig::widgetConfig( const QString &widgetName ) const
54 {
55  const int fieldIndex = d->mFields.indexOf( widgetName );
56  if ( fieldIndex != -1 )
57  return d->mFields.at( fieldIndex ).editorWidgetSetup().config();
58  else
59  return d->mWidgetConfigs.value( widgetName );
60 }
61 
62 void QgsEditFormConfig::setFields( const QgsFields &fields )
63 {
64  d.detach();
65  d->mFields = fields;
66 
67  if ( !d->mConfiguredRootContainer )
68  {
69  d->mInvisibleRootContainer->clear();
70  for ( int i = 0; i < d->mFields.size(); ++i )
71  {
72  QgsAttributeEditorField *field = new QgsAttributeEditorField( d->mFields.at( i ).name(), i, d->mInvisibleRootContainer );
73  d->mInvisibleRootContainer->addChildElement( field );
74  }
75  }
76 }
77 
78 void QgsEditFormConfig::onRelationsLoaded()
79 {
80  const QList<QgsAttributeEditorElement *> relations = d->mInvisibleRootContainer->findElements( QgsAttributeEditorElement::AeTypeRelation );
81 
82  for ( QgsAttributeEditorElement *relElem : relations )
83  {
84  QgsAttributeEditorRelation *rel = dynamic_cast< QgsAttributeEditorRelation * >( relElem );
85  if ( !rel )
86  continue;
87 
89  }
90 }
91 
92 bool QgsEditFormConfig::legacyUpdateRelationWidgetInTabs( QgsAttributeEditorContainer *container, const QString &widgetName, const QVariantMap &config )
93 {
94  const QList<QgsAttributeEditorElement *> children = container->children();
95  for ( QgsAttributeEditorElement *child : children )
96  {
97  if ( child->type() == QgsAttributeEditorElement::AeTypeContainer )
98  {
99  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( child );
100  if ( legacyUpdateRelationWidgetInTabs( container, widgetName, config ) )
101  {
102  //return when a relation has been set in a child or child child...
103  return true;
104  }
105  }
106  else if ( child->type() == QgsAttributeEditorElement::AeTypeRelation )
107  {
108  QgsAttributeEditorRelation *relation = dynamic_cast< QgsAttributeEditorRelation * >( child );
109  if ( relation )
110  {
111  if ( relation->relation().id() == widgetName )
112  {
113  if ( config.contains( QStringLiteral( "nm-rel" ) ) )
114  {
115  relation->setNmRelationId( config[QStringLiteral( "nm-rel" )] );
116  }
117  if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) )
118  {
119  relation->setForceSuppressFormPopup( config[QStringLiteral( "force-suppress-popup" )].toBool() );
120  }
121  return true;
122  }
123  }
124  }
125  }
126  return false;
127 }
128 
129 bool QgsEditFormConfig::setWidgetConfig( const QString &widgetName, const QVariantMap &config )
130 {
131  if ( d->mFields.indexOf( widgetName ) != -1 )
132  {
133  QgsDebugMsg( QStringLiteral( "Trying to set a widget config for a field on QgsEditFormConfig. Use layer->setEditorWidgetSetup() instead." ) );
134  return false;
135  }
136 
137  //for legacy use it writes the relation editor configuration into the first instance of the widget
138  if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) || config.contains( QStringLiteral( "nm-rel" ) ) )
139  {
140  QgsMessageLog::logMessage( QStringLiteral( "Deprecation Warning: Trying to set a relation config directly on the relation %1. Relation settings should be done for the specific widget instance instead. Use attributeEditorRelation->setNmRelationId() or attributeEditorRelation->setForceSuppressFormPopup() instead." ).arg( widgetName ) );
141  legacyUpdateRelationWidgetInTabs( d->mInvisibleRootContainer, widgetName, config );
142  }
143 
144  d.detach();
145  d->mWidgetConfigs[widgetName] = config;
146  return true;
147 }
148 
149 bool QgsEditFormConfig::removeWidgetConfig( const QString &widgetName )
150 {
151  d.detach();
152  return d->mWidgetConfigs.remove( widgetName ) != 0;
153 }
154 
156  : d( o.d )
157 {
158 }
159 
161 {}
162 
164 {
165  d = o.d;
166  return *this;
167 }
168 
170 {
171  return d == o.d;
172 }
173 
175 {
176  d.detach();
177  d->mInvisibleRootContainer->addChildElement( data );
178 }
179 
180 QList<QgsAttributeEditorElement *> QgsEditFormConfig::tabs() const
181 {
182  return d->mInvisibleRootContainer->children();
183 }
184 
186 {
187  d.detach();
188  d->mInvisibleRootContainer->clear();
189 }
190 
192 {
193  return d->mInvisibleRootContainer;
194 }
195 
197 {
198  return d->mEditorLayout;
199 }
200 
202 {
203  d.detach();
204  d->mEditorLayout = editorLayout;
205 
206  if ( editorLayout == TabLayout )
207  d->mConfiguredRootContainer = true;
208 }
209 
211 {
212  return d->mUiFormPath;
213 }
214 
215 void QgsEditFormConfig::setUiForm( const QString &ui )
216 {
217  if ( !ui.isEmpty() && !QUrl::fromUserInput( ui ).isLocalFile() )
218  {
219  // any existing download will not be restarted!
220  QgsApplication::instance()->networkContentFetcherRegistry()->fetch( ui, Qgis::ActionStart::Immediate );
221  }
222 
223  if ( ui.isEmpty() )
224  {
226  }
227  else
228  {
230  }
231  d->mUiFormPath = ui;
232 }
233 
234 bool QgsEditFormConfig::readOnly( int idx ) const
235 {
236  if ( idx >= 0 && idx < d->mFields.count() )
237  {
238  if ( d->mFields.fieldOrigin( idx ) == QgsFields::OriginJoin
239  || d->mFields.fieldOrigin( idx ) == QgsFields::OriginExpression )
240  return true;
241  return !d->mFieldEditables.value( d->mFields.at( idx ).name(), true );
242  }
243  else
244  return false;
245 }
246 
247 bool QgsEditFormConfig::labelOnTop( int idx ) const
248 {
249  if ( idx >= 0 && idx < d->mFields.count() )
250  return d->mLabelOnTop.value( d->mFields.at( idx ).name(), false );
251  else
252  return false;
253 }
254 
255 void QgsEditFormConfig::setReadOnly( int idx, bool readOnly )
256 {
257  if ( idx >= 0 && idx < d->mFields.count() )
258  {
259  d.detach();
260  d->mFieldEditables[ d->mFields.at( idx ).name()] = !readOnly;
261  }
262 }
263 
264 void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
265 {
266  if ( idx >= 0 && idx < d->mFields.count() )
267  {
268  d.detach();
269  d->mLabelOnTop[ d->mFields.at( idx ).name()] = onTop;
270  }
271 }
272 
273 bool QgsEditFormConfig::reuseLastValue( int index ) const
274 {
275  if ( index >= 0 && index < d->mFields.count() )
276  return d->mReuseLastValue.value( d->mFields.at( index ).name(), false );
277  else
278  return false;
279 }
280 
281 void QgsEditFormConfig::setReuseLastValue( int index, bool reuse )
282 {
283  if ( index >= 0 && index < d->mFields.count() )
284  {
285  d.detach();
286  d->mReuseLastValue[ d->mFields.at( index ).name()] = reuse;
287  }
288 }
289 
291 {
292  return d->mInitFunction;
293 }
294 
295 void QgsEditFormConfig::setInitFunction( const QString &function )
296 {
297  d.detach();
298  d->mInitFunction = function;
299 }
300 
302 {
303  return d->mInitCode;
304 }
305 
306 void QgsEditFormConfig::setInitCode( const QString &code )
307 {
308  d.detach();
309  d->mInitCode = code;
310 }
311 
313 {
314  return d->mInitFilePath;
315 }
316 
317 void QgsEditFormConfig::setInitFilePath( const QString &filePath )
318 {
319  d.detach();
320  d->mInitFilePath = filePath;
321 
322  // if this is an URL, download file as there is a good chance it will be used later
323  if ( !filePath.isEmpty() && !QUrl::fromUserInput( filePath ).isLocalFile() )
324  {
325  // any existing download will not be restarted!
326  QgsApplication::instance()->networkContentFetcherRegistry()->fetch( filePath, Qgis::ActionStart::Immediate );
327  }
328 }
329 
331 {
332  return d->mInitCodeSource;
333 }
334 
336 {
337  d.detach();
338  d->mInitCodeSource = initCodeSource;
339 }
340 
342 {
343  return d->mSuppressForm;
344 }
345 
347 {
348  d.detach();
349  d->mSuppressForm = s;
350 }
351 
352 void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &context )
353 {
354  const QgsReadWriteContextCategoryPopper p = context.enterCategory( QObject::tr( "Edit form config" ) );
355 
356  d.detach();
357 
358  const QDomNode editFormNode = node.namedItem( QStringLiteral( "editform" ) );
359  if ( !editFormNode.isNull() )
360  {
361  const QDomElement e = editFormNode.toElement();
362  const bool tolerantRemoteUrls = e.hasAttribute( QStringLiteral( "tolerant" ) );
363  if ( !e.text().isEmpty() )
364  {
365  const QString uiFormPath = context.pathResolver().readPath( e.text() );
366  // <= 3.2 had a bug where invalid ui paths would get written into projects on load
367  // to avoid restoring these invalid paths, we take a less-tolerant approach for older (untrustworthy) projects
368  // and only set ui forms paths IF they are local files OR start with "http(s)".
369  const bool localFile = QFileInfo::exists( uiFormPath );
370  if ( localFile || tolerantRemoteUrls || uiFormPath.startsWith( QLatin1String( "http" ) ) )
371  setUiForm( uiFormPath );
372  }
373  }
374 
375  const QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
376  if ( !editFormInitNode.isNull() )
377  {
378  d->mInitFunction = editFormInitNode.toElement().text();
379  }
380 
381  const QDomNode editFormInitCodeSourceNode = node.namedItem( QStringLiteral( "editforminitcodesource" ) );
382  if ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() )
383  {
384  setInitCodeSource( static_cast< QgsEditFormConfig::PythonInitCodeSource >( editFormInitCodeSourceNode.toElement().text().toInt() ) );
385  }
386 
387  const QDomNode editFormInitCodeNode = node.namedItem( QStringLiteral( "editforminitcode" ) );
388  if ( !editFormInitCodeNode.isNull() )
389  {
390  setInitCode( editFormInitCodeNode.toElement().text() );
391  }
392 
393  // Temporary < 2.12 b/w compatibility "dot" support patch
394  // \see: https://github.com/qgis/QGIS/pull/2498
395  // For b/w compatibility, check if there's a dot in the function name
396  // and if yes, transform it in an import statement for the module
397  // and set the PythonInitCodeSource to CodeSourceDialog
398  const int dotPos = d->mInitFunction.lastIndexOf( '.' );
399  if ( dotPos >= 0 ) // It's a module
400  {
402  setInitCode( QStringLiteral( "from %1 import %2\n" ).arg( d->mInitFunction.left( dotPos ), d->mInitFunction.mid( dotPos + 1 ) ) );
403  setInitFunction( d->mInitFunction.mid( dotPos + 1 ) );
404  }
405 
406  const QDomNode editFormInitFilePathNode = node.namedItem( QStringLiteral( "editforminitfilepath" ) );
407  if ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() )
408  {
409  setInitFilePath( context.pathResolver().readPath( editFormInitFilePathNode.toElement().text() ) );
410  }
411 
412  const QDomNode fFSuppNode = node.namedItem( QStringLiteral( "featformsuppress" ) );
413  if ( fFSuppNode.isNull() )
414  {
415  d->mSuppressForm = QgsEditFormConfig::SuppressDefault;
416  }
417  else
418  {
419  const QDomElement e = fFSuppNode.toElement();
420  d->mSuppressForm = static_cast< QgsEditFormConfig::FeatureFormSuppress >( e.text().toInt() );
421  }
422 
423  // tab display
424  const QDomNode editorLayoutNode = node.namedItem( QStringLiteral( "editorlayout" ) );
425  if ( editorLayoutNode.isNull() )
426  {
427  d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
428  }
429  else
430  {
431  if ( editorLayoutNode.toElement().text() == QLatin1String( "uifilelayout" ) )
432  {
433  d->mEditorLayout = QgsEditFormConfig::UiFileLayout;
434  }
435  else if ( editorLayoutNode.toElement().text() == QLatin1String( "tablayout" ) )
436  {
437  d->mEditorLayout = QgsEditFormConfig::TabLayout;
438  }
439  else
440  {
441  d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
442  }
443  }
444 
445  d->mFieldEditables.clear();
446  const QDomNodeList editableNodeList = node.namedItem( QStringLiteral( "editable" ) ).toElement().childNodes();
447  for ( int i = 0; i < editableNodeList.size(); ++i )
448  {
449  const QDomElement editableElement = editableNodeList.at( i ).toElement();
450  d->mFieldEditables.insert( editableElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( editableElement.attribute( QStringLiteral( "editable" ) ).toInt() ) );
451  }
452 
453  d->mLabelOnTop.clear();
454  const QDomNodeList labelOnTopNodeList = node.namedItem( QStringLiteral( "labelOnTop" ) ).toElement().childNodes();
455  for ( int i = 0; i < labelOnTopNodeList.size(); ++i )
456  {
457  const QDomElement labelOnTopElement = labelOnTopNodeList.at( i ).toElement();
458  d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
459  }
460 
461  d->mReuseLastValue.clear();
462  const QDomNodeList reuseLastValueNodeList = node.namedItem( QStringLiteral( "reuseLastValue" ) ).toElement().childNodes();
463  for ( int i = 0; i < reuseLastValueNodeList.size(); ++i )
464  {
465  const QDomElement reuseLastValueElement = reuseLastValueNodeList.at( i ).toElement();
466  d->mReuseLastValue.insert( reuseLastValueElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( reuseLastValueElement.attribute( QStringLiteral( "reuseLastValue" ) ).toInt() ) );
467  }
468 
469  // Read data defined field properties
470  const QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
471  for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
472  {
473  const QDomElement DDElement = fieldDDPropertiesNodeList.at( i ).toElement();
474  QgsPropertyCollection collection;
475  collection.readXml( DDElement, propertyDefinitions() );
476  d->mDataDefinedFieldProperties.insert( DDElement.attribute( QStringLiteral( "name" ) ), collection );
477  }
478 
479  const QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();
480 
481  for ( int i = 0; i < widgetsNodeList.size(); ++i )
482  {
483  const QDomElement widgetElement = widgetsNodeList.at( i ).toElement();
484  const QVariant config = QgsXmlUtils::readVariant( widgetElement.firstChildElement( QStringLiteral( "config" ) ) );
485 
486  d->mWidgetConfigs[widgetElement.attribute( QStringLiteral( "name" ) )] = config.toMap();
487  }
488 
489  // tabs and groups display info
490  const QDomNode attributeEditorFormNode = node.namedItem( QStringLiteral( "attributeEditorForm" ) );
491  if ( !attributeEditorFormNode.isNull() )
492  {
493  const QDomNodeList attributeEditorFormNodeList = attributeEditorFormNode.toElement().childNodes();
494 
495  if ( attributeEditorFormNodeList.size() )
496  {
497  d->mConfiguredRootContainer = true;
498  clearTabs();
499 
500  for ( int i = 0; i < attributeEditorFormNodeList.size(); i++ )
501  {
502  QDomElement elem = attributeEditorFormNodeList.at( i ).toElement();
503 
504  fixLegacyConfig( elem );
505 
506  const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
507  QgsAttributeEditorElement *attributeEditorWidget = QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, nullptr );
508  if ( attributeEditorWidget )
509  addTab( attributeEditorWidget );
510  }
511 
512  onRelationsLoaded();
513  }
514  }
515 }
516 
517 void QgsEditFormConfig::fixLegacyConfig( QDomElement &el )
518 {
519  // recursive method to move widget config into attribute element config
520 
521  if ( el.tagName() == QLatin1String( "attributeEditorRelation" ) )
522  {
523  if ( !el.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) )
524  {
525  // pre QGIS 3.16 compatibility - the widgets section is read before
526  const bool forceSuppress = widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "force-suppress-popup" ), false ).toBool();
527  el.setAttribute( QStringLiteral( "forceSuppressFormPopup" ), forceSuppress ? 1 : 0 );
528  }
529  if ( !el.hasAttribute( QStringLiteral( "nmRelationId" ) ) )
530  {
531  // pre QGIS 3.16 compatibility - the widgets section is read before
532  el.setAttribute( QStringLiteral( "nmRelationId" ), widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "nm-rel" ) ).toString() );
533  }
534  }
535 
536  const QDomNodeList children = el.childNodes();
537  for ( int i = 0; i < children.size(); i++ )
538  {
539  QDomElement child = children.at( i ).toElement();
540  fixLegacyConfig( child );
541  el.replaceChild( child, children.at( i ) );
542  }
543 }
544 
545 void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &context ) const
546 {
547  QDomDocument doc( node.ownerDocument() );
548 
549  QDomElement efField = doc.createElement( QStringLiteral( "editform" ) );
550  efField.setAttribute( QStringLiteral( "tolerant" ), QStringLiteral( "1" ) );
551  const QDomText efText = doc.createTextNode( context.pathResolver().writePath( uiForm() ) );
552  efField.appendChild( efText );
553  node.appendChild( efField );
554 
555  QDomElement efiField = doc.createElement( QStringLiteral( "editforminit" ) );
556  if ( !initFunction().isEmpty() )
557  efiField.appendChild( doc.createTextNode( initFunction() ) );
558  node.appendChild( efiField );
559 
560  QDomElement eficsField = doc.createElement( QStringLiteral( "editforminitcodesource" ) );
561  eficsField.appendChild( doc.createTextNode( QString::number( initCodeSource() ) ) );
562  node.appendChild( eficsField );
563 
564  QDomElement efifpField = doc.createElement( QStringLiteral( "editforminitfilepath" ) );
565  efifpField.appendChild( doc.createTextNode( context.pathResolver().writePath( initFilePath() ) ) );
566  node.appendChild( efifpField );
567 
568  QDomElement eficField = doc.createElement( QStringLiteral( "editforminitcode" ) );
569  eficField.appendChild( doc.createCDATASection( initCode() ) );
570  node.appendChild( eficField );
571 
572  QDomElement fFSuppElem = doc.createElement( QStringLiteral( "featformsuppress" ) );
573  const QDomText fFSuppText = doc.createTextNode( QString::number( suppress() ) );
574  fFSuppElem.appendChild( fFSuppText );
575  node.appendChild( fFSuppElem );
576 
577  // tab display
578  QDomElement editorLayoutElem = doc.createElement( QStringLiteral( "editorlayout" ) );
579  switch ( layout() )
580  {
582  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "uifilelayout" ) ) );
583  break;
584 
586  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "tablayout" ) ) );
587  break;
588 
590  default:
591  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "generatedlayout" ) ) );
592  break;
593  }
594 
595  node.appendChild( editorLayoutElem );
596 
597  // tabs and groups of edit form
598  if ( !tabs().empty() && d->mConfiguredRootContainer )
599  {
600  QDomElement tabsElem = doc.createElement( QStringLiteral( "attributeEditorForm" ) );
601  const QDomElement rootElem = d->mInvisibleRootContainer->toDomElement( doc );
602  const QDomNodeList elemList = rootElem.childNodes();
603  while ( !elemList.isEmpty() )
604  {
605  tabsElem.appendChild( elemList.at( 0 ) );
606  }
607  node.appendChild( tabsElem );
608  }
609 
610  QDomElement editableElem = doc.createElement( QStringLiteral( "editable" ) );
611  for ( auto editIt = d->mFieldEditables.constBegin(); editIt != d->mFieldEditables.constEnd(); ++editIt )
612  {
613  QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
614  fieldElem.setAttribute( QStringLiteral( "name" ), editIt.key() );
615  fieldElem.setAttribute( QStringLiteral( "editable" ), editIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
616  editableElem.appendChild( fieldElem );
617  }
618  node.appendChild( editableElem );
619 
620  QDomElement labelOnTopElem = doc.createElement( QStringLiteral( "labelOnTop" ) );
621  for ( auto labelOnTopIt = d->mLabelOnTop.constBegin(); labelOnTopIt != d->mLabelOnTop.constEnd(); ++labelOnTopIt )
622  {
623  QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
624  fieldElem.setAttribute( QStringLiteral( "name" ), labelOnTopIt.key() );
625  fieldElem.setAttribute( QStringLiteral( "labelOnTop" ), labelOnTopIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
626  labelOnTopElem.appendChild( fieldElem );
627  }
628  node.appendChild( labelOnTopElem );
629 
630  QDomElement reuseLastValueElem = doc.createElement( QStringLiteral( "reuseLastValue" ) );
631  for ( auto reuseLastValueIt = d->mReuseLastValue.constBegin(); reuseLastValueIt != d->mReuseLastValue.constEnd(); ++reuseLastValueIt )
632  {
633  QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
634  fieldElem.setAttribute( QStringLiteral( "name" ), reuseLastValueIt.key() );
635  fieldElem.setAttribute( QStringLiteral( "reuseLastValue" ), reuseLastValueIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
636  reuseLastValueElem.appendChild( fieldElem );
637  }
638  node.appendChild( reuseLastValueElem );
639 
640  // Store data defined field properties
641  QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
642  for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
643  {
644  QDomElement ddPropsElement = doc.createElement( QStringLiteral( "field" ) );
645  ddPropsElement.setAttribute( QStringLiteral( "name" ), it.key() );
646  it.value().writeXml( ddPropsElement, propertyDefinitions() );
647  ddFieldPropsElement.appendChild( ddPropsElement );
648  }
649  node.appendChild( ddFieldPropsElement );
650 
651  QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );
652 
653  QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
654 
655  while ( configIt != d->mWidgetConfigs.constEnd() )
656  {
657  QDomElement widgetElem = doc.createElement( QStringLiteral( "widget" ) );
658  widgetElem.setAttribute( QStringLiteral( "name" ), configIt.key() );
659  // widgetElem.setAttribute( "notNull", );
660 
661  QDomElement configElem = QgsXmlUtils::writeVariant( configIt.value(), doc );
662  configElem.setTagName( QStringLiteral( "config" ) );
663  widgetElem.appendChild( configElem );
664  widgetsElem.appendChild( widgetElem );
665  ++configIt;
666  }
667 
668  node.appendChild( widgetsElem );
669 }
670 
672 {
673  return QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, parent );
674 }
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
static QgsAttributeEditorElement * create(const QDomElement &element, const QString &layerId, const QgsFields &fields, const QgsReadWriteContext &context, QgsAttributeEditorElement *parent=nullptr)
Constructs the editor element from the given element.
This element will load a field's widget onto the form.
This element will load a relation editor onto the form.
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
bool init(QgsRelationManager *relManager)
Initializes the relation from the id.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup.
Contains configuration settings for an editor form.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
bool operator==(const QgsEditFormConfig &o)
Q_DECL_DEPRECATED QgsAttributeEditorElement * attributeEditorElementFromDomElement(QDomElement &elem, QgsAttributeEditorElement *parent, const QString &layerId=QString(), const QgsReadWriteContext &context=QgsReadWriteContext())
Deserialize drag and drop designer elements.
bool reuseLastValue(int index) const
If this returns true, the widget at the given index will remember the previously entered value from t...
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 ...
QString initCode() const
Gets Python code for edit form initialization.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns data defined property definitions.
void readXml(const QDomNode &node, QgsReadWriteContext &context)
Read XML information Deserialize on project load.
void setInitFunction(const QString &function)
Set Python function for edit form initialization.
QVariantMap widgetConfig(const QString &widgetName) const
Gets the configuration for the editor widget with the given name.
void addTab(QgsAttributeEditorElement *data)
Adds a new element to the invisible root container in the layout.
QString initFilePath() const
Gets Python external file path for edit form initialization.
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
EditorLayout
The different types to layout the attribute editor.
@ TabLayout
Use a layout with tabs and group boxes. Needs to be configured.
@ UiFileLayout
Load a .ui file for the layout. Needs to be configured.
@ GeneratedLayout
Autogenerate a simple tabular layout for the form.
FeatureFormSuppress
Types of feature form suppression after feature creation.
@ SuppressDefault
Use the application-wide setting.
FeatureFormSuppress suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting)
bool setWidgetConfig(const QString &widgetName, const QVariantMap &config)
Set the editor widget config for a widget which is not for a simple field.
void setLayout(EditorLayout editorLayout)
Sets the active layout style for the attribute editor for this layer.
void clearTabs()
Clears all the tabs for the attribute editor form with EditorLayout::TabLayout.
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 uiForm() const
Returns the path or URL to the .ui form.
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...
PythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
PythonInitCodeSource
The Python init code source options.
@ CodeSourceDialog
Use the Python code provided in the dialog.
void setDataDefinedFieldProperties(const QString &fieldName, const QgsPropertyCollection &properties)
Set data defined properties for fieldName to properties.
void writeXml(QDomNode &node, const QgsReadWriteContext &context) const
Write XML information Serialize on project save.
QString initFunction() const
Gets Python function for edit form initialization.
QgsEditFormConfig()
Create a new edit form config.
void setSuppress(FeatureFormSuppress s)
Sets type of feature form pop-up suppression after feature creation (overrides app setting)
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
void setReuseLastValue(int index, bool reuse)
Sets whether the widget at the given index will remember the previously entered value from this QGIS ...
void setUiForm(const QString &ui)
Set path to the .ui form.
EditorLayout layout() const
Gets the active layout style for the attribute editor for this layer.
void setInitCodeSource(PythonInitCodeSource initCodeSource)
Sets if Python code shall be used for edit form initialization and its origin.
QgsEditFormConfig & operator=(const QgsEditFormConfig &o)
void setInitFilePath(const QString &filePath)
Set Python external file path for edit form initialization.
void setInitCode(const QString &code)
Set Python code for edit form initialization.
bool removeWidgetConfig(const QString &widgetName)
Remove the configuration for the editor widget with the given name.
Container of fields for a vector layer.
Definition: qgsfields.h:45
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsFetchedContent * fetch(const QString &url, Qgis::ActionStart fetchingMode=Qgis::ActionStart::Deferred, const QString &authConfig=QString())
Initialize a download for the given URL.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QgsRelationManager * relationManager
Definition: qgsproject.h:111
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Allows entering a context category and takes care of leaving this category on deletion of the class.
The class is used as a container of context for various read/write operations on other objects.
MAYBE_UNUSED NODISCARD QgsReadWriteContextCategoryPopper enterCategory(const QString &category, const QString &details=QString()) const
Push a category to the stack.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
Q_GADGET QString id
Definition: qgsrelation.h:46
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.