QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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.h"
17
18#include "qgsapplication.h"
22#include "qgseditformconfig_p.h"
23#include "qgslogger.h"
24#include "qgsmessagelog.h"
26#include "qgspathresolver.h"
27#include "qgsproject.h"
28#include "qgsreadwritecontext.h"
29#include "qgsrelationmanager.h"
30#include "qgsxmlutils.h"
31
32#include <QUrl>
33
34#include "moc_qgseditformconfig.cpp"
35
37 : d( new QgsEditFormConfigPrivate() )
38{
39}
40
41void QgsEditFormConfig::setDataDefinedFieldProperties( const QString &fieldName, const QgsPropertyCollection &properties )
42{
43 d.detach();
44 d->mDataDefinedFieldProperties[ fieldName ] = properties;
45}
46
48{
49 return d->mDataDefinedFieldProperties.value( fieldName );
50}
51
53{
54 return QgsEditFormConfigPrivate::propertyDefinitions();
55}
56
57QVariantMap QgsEditFormConfig::widgetConfig( const QString &widgetName ) const
58{
59 const int fieldIndex = d->mFields.indexOf( widgetName );
60 if ( fieldIndex != -1 )
61 return d->mFields.at( fieldIndex ).editorWidgetSetup().config();
62 else
63 return d->mWidgetConfigs.value( widgetName );
64}
65
66void QgsEditFormConfig::setFields( const QgsFields &fields )
67{
68 d.detach();
69 d->mFields = fields;
70
71 if ( !d->mConfiguredRootContainer )
72 {
73 d->mInvisibleRootContainer->clear();
74 for ( int i = 0; i < d->mFields.size(); ++i )
75 {
76 QgsAttributeEditorField *field = new QgsAttributeEditorField( d->mFields.at( i ).name(), i, d->mInvisibleRootContainer );
77 d->mInvisibleRootContainer->addChildElement( field );
78 }
79 }
80}
81
82void QgsEditFormConfig::onRelationsLoaded()
83{
84 const QList<QgsAttributeEditorElement *> relations = d->mInvisibleRootContainer->findElements( Qgis::AttributeEditorType::Relation );
85
86 for ( QgsAttributeEditorElement *relElem : relations )
87 {
88 QgsAttributeEditorRelation *rel = dynamic_cast< QgsAttributeEditorRelation * >( relElem );
89 if ( !rel )
90 continue;
91
92 rel->init( QgsProject::instance()->relationManager() ); // skip-keyword-check
93 }
94}
95
96bool QgsEditFormConfig::legacyUpdateRelationWidgetInTabs( QgsAttributeEditorContainer *container, const QString &widgetName, const QVariantMap &config )
97{
98 const QList<QgsAttributeEditorElement *> children = container->children();
99 for ( QgsAttributeEditorElement *child : children )
100 {
101 if ( child->type() == Qgis::AttributeEditorType::Container )
102 {
103 QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( child );
104 if ( legacyUpdateRelationWidgetInTabs( container, widgetName, config ) )
105 {
106 //return when a relation has been set in a child or child child...
107 return true;
108 }
109 }
110 else if ( child->type() == Qgis::AttributeEditorType::Relation )
111 {
112 QgsAttributeEditorRelation *relation = dynamic_cast< QgsAttributeEditorRelation * >( child );
113 if ( relation )
114 {
115 if ( relation->relation().id() == widgetName )
116 {
117 if ( config.contains( QStringLiteral( "nm-rel" ) ) )
118 {
119 relation->setNmRelationId( config[QStringLiteral( "nm-rel" )] );
120 }
121 if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) )
122 {
123 relation->setForceSuppressFormPopup( config[QStringLiteral( "force-suppress-popup" )].toBool() );
124 }
125 return true;
126 }
127 }
128 }
129 }
130 return false;
131}
132
133bool QgsEditFormConfig::setWidgetConfig( const QString &widgetName, const QVariantMap &config )
134{
135 if ( d->mFields.indexOf( widgetName ) != -1 )
136 {
137 QgsDebugError( QStringLiteral( "Trying to set a widget config for a field on QgsEditFormConfig. Use layer->setEditorWidgetSetup() instead." ) );
138 return false;
139 }
140
141 //for legacy use it writes the relation editor configuration into the first instance of the widget
142 if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) || config.contains( QStringLiteral( "nm-rel" ) ) )
143 {
144 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 ) );
145 legacyUpdateRelationWidgetInTabs( d->mInvisibleRootContainer, widgetName, config );
146 }
147
148 d.detach();
149 d->mWidgetConfigs[widgetName] = config;
150 return true;
151}
152
153bool QgsEditFormConfig::removeWidgetConfig( const QString &widgetName )
154{
155 d.detach();
156 return d->mWidgetConfigs.remove( widgetName ) != 0;
157}
158
160 : d( o.d )
161{
162}
163
166
168{
169 if ( &o == this )
170 return *this;
171
172 d = o.d;
173 return *this;
174}
175
177{
178 return d == o.d;
179}
180
182{
183 d.detach();
184 d->mInvisibleRootContainer->addChildElement( data );
185}
186
187QList<QgsAttributeEditorElement *> QgsEditFormConfig::tabs() const
188{
189 return d->mInvisibleRootContainer->children();
190}
191
193{
194 d.detach();
195 d->mInvisibleRootContainer->clear();
196}
197
199{
200 return d->mInvisibleRootContainer;
201}
202
204{
205 return d->mEditorLayout;
206}
207
209{
210 d.detach();
211 d->mEditorLayout = editorLayout;
212
213 if ( editorLayout == Qgis::AttributeFormLayout::DragAndDrop )
214 d->mConfiguredRootContainer = true;
215}
216
218{
219 return d->mUiFormPath;
220}
221
222void QgsEditFormConfig::setUiForm( const QString &ui )
223{
224 if ( !ui.isEmpty() && !QUrl::fromUserInput( ui ).isLocalFile() )
225 {
226 // any existing download will not be restarted!
228 }
229
230 if ( ui.isEmpty() )
231 {
233 }
234 else
235 {
237 }
238 d->mUiFormPath = ui;
239}
240
241bool QgsEditFormConfig::readOnly( int idx ) const
242{
243 if ( idx >= 0 && idx < d->mFields.count() )
244 {
245 if ( d->mFields.fieldOrigin( idx ) == Qgis::FieldOrigin::Join
246 || d->mFields.fieldOrigin( idx ) == Qgis::FieldOrigin::Expression )
247 return true;
248 if ( d->mFields.at( idx ).isReadOnly() )
249 return true;
250 return !d->mFieldEditables.value( d->mFields.at( idx ).name(), true );
251 }
252 else
253 return false;
254}
255
256bool QgsEditFormConfig::labelOnTop( int idx ) const
257{
258 if ( idx >= 0 && idx < d->mFields.count() )
259 return d->mLabelOnTop.value( d->mFields.at( idx ).name(), false );
260 else
261 return false;
262}
263
265{
266 if ( idx >= 0 && idx < d->mFields.count() )
267 {
268 d.detach();
269 d->mFieldEditables[ d->mFields.at( idx ).name()] = !readOnly;
270 }
271}
272
273void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
274{
275 if ( idx >= 0 && idx < d->mFields.count() )
276 {
277 d.detach();
278 d->mLabelOnTop[ d->mFields.at( idx ).name()] = onTop;
279 }
280}
281
283{
284 if ( index >= 0 && index < d->mFields.count() )
285 return d->mReuseLastValuePolicy.value( d->mFields.at( index ).name(), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed ) != Qgis::AttributeFormReuseLastValuePolicy::NotAllowed;
286 else
287 return false;
288}
289
290void QgsEditFormConfig::setReuseLastValue( int index, bool reuse )
291{
292 if ( index >= 0 && index < d->mFields.count() )
293 {
294 d.detach();
295 d->mReuseLastValuePolicy[ d->mFields.at( index ).name()] = reuse ? Qgis::AttributeFormReuseLastValuePolicy::AllowedDefaultOn : Qgis::AttributeFormReuseLastValuePolicy::NotAllowed;
296 }
297}
298
300{
301 if ( index >= 0 && index < d->mFields.count() )
302 return d->mReuseLastValuePolicy.value( d->mFields.at( index ).name(), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed );
303 else
305}
306
308{
309 if ( index >= 0 && index < d->mFields.count() )
310 {
311 d.detach();
312 d->mReuseLastValuePolicy[ d->mFields.at( index ).name()] = policy;
313 }
314}
315
317{
318 return d->mInitFunction;
319}
320
321void QgsEditFormConfig::setInitFunction( const QString &function )
322{
323 d.detach();
324 d->mInitFunction = function;
325}
326
328{
329 return d->mInitCode;
330}
331
332void QgsEditFormConfig::setInitCode( const QString &code )
333{
334 d.detach();
335 d->mInitCode = code;
336}
337
339{
340 return d->mInitFilePath;
341}
342
343void QgsEditFormConfig::setInitFilePath( const QString &filePath )
344{
345 d.detach();
346 d->mInitFilePath = filePath;
347
348 // if this is an URL, download file as there is a good chance it will be used later
349 if ( !filePath.isEmpty() && !QUrl::fromUserInput( filePath ).isLocalFile() )
350 {
351 // any existing download will not be restarted!
353 }
354}
355
357{
358 return d->mInitCodeSource;
359}
360
366
368{
369 return d->mSuppressForm;
370}
371
373{
374 d.detach();
375 d->mSuppressForm = s;
376}
377
378void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &context )
379{
380 const QgsReadWriteContextCategoryPopper p = context.enterCategory( QObject::tr( "Edit form config" ) );
381
382 d.detach();
383
384 const QDomNode editFormNode = node.namedItem( QStringLiteral( "editform" ) );
385 if ( !editFormNode.isNull() )
386 {
387 const QDomElement e = editFormNode.toElement();
388 const bool tolerantRemoteUrls = e.hasAttribute( QStringLiteral( "tolerant" ) );
389 if ( !e.text().isEmpty() )
390 {
391 const QString uiFormPath = context.pathResolver().readPath( e.text() );
392 // <= 3.2 had a bug where invalid ui paths would get written into projects on load
393 // to avoid restoring these invalid paths, we take a less-tolerant approach for older (untrustworthy) projects
394 // and only set ui forms paths IF they are local files OR start with "http(s)".
395 const bool localFile = QFileInfo::exists( uiFormPath );
396 if ( localFile || tolerantRemoteUrls || uiFormPath.startsWith( QLatin1String( "http" ) ) )
397 setUiForm( uiFormPath );
398 }
399 }
400
401 const QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
402 if ( !editFormInitNode.isNull() )
403 {
404 d->mInitFunction = editFormInitNode.toElement().text();
405 }
406
407 const QDomNode editFormInitCodeSourceNode = node.namedItem( QStringLiteral( "editforminitcodesource" ) );
408 if ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() )
409 {
410 setInitCodeSource( static_cast< Qgis::AttributeFormPythonInitCodeSource >( editFormInitCodeSourceNode.toElement().text().toInt() ) );
411 }
412
413 const QDomNode editFormInitCodeNode = node.namedItem( QStringLiteral( "editforminitcode" ) );
414 if ( !editFormInitCodeNode.isNull() )
415 {
416 setInitCode( editFormInitCodeNode.toElement().text() );
417 }
418
419 // Temporary < 2.12 b/w compatibility "dot" support patch
420 // \see: https://github.com/qgis/QGIS/pull/2498
421 // For b/w compatibility, check if there's a dot in the function name
422 // and if yes, transform it in an import statement for the module
423 // and set the PythonInitCodeSource to CodeSourceDialog
424 const int dotPos = d->mInitFunction.lastIndexOf( '.' );
425 if ( dotPos >= 0 ) // It's a module
426 {
428 setInitCode( QStringLiteral( "from %1 import %2\n" ).arg( d->mInitFunction.left( dotPos ), d->mInitFunction.mid( dotPos + 1 ) ) );
429 setInitFunction( d->mInitFunction.mid( dotPos + 1 ) );
430 }
431
432 const QDomNode editFormInitFilePathNode = node.namedItem( QStringLiteral( "editforminitfilepath" ) );
433 if ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() )
434 {
435 setInitFilePath( context.pathResolver().readPath( editFormInitFilePathNode.toElement().text() ) );
436 }
437
438 const QDomNode fFSuppNode = node.namedItem( QStringLiteral( "featformsuppress" ) );
439 if ( fFSuppNode.isNull() )
440 {
442 }
443 else
444 {
445 const QDomElement e = fFSuppNode.toElement();
446 d->mSuppressForm = static_cast< Qgis::AttributeFormSuppression >( e.text().toInt() );
447 }
448
449 // tab display
450 const QDomNode editorLayoutNode = node.namedItem( QStringLiteral( "editorlayout" ) );
451 if ( editorLayoutNode.isNull() )
452 {
454 }
455 else
456 {
457 if ( editorLayoutNode.toElement().text() == QLatin1String( "uifilelayout" ) )
458 {
459 d->mEditorLayout = Qgis::AttributeFormLayout::UiFile;
460 }
461 else if ( editorLayoutNode.toElement().text() == QLatin1String( "tablayout" ) )
462 {
464 }
465 else
466 {
468 }
469 }
470
471 d->mFieldEditables.clear();
472 const QDomNodeList editableNodeList = node.namedItem( QStringLiteral( "editable" ) ).toElement().childNodes();
473 for ( int i = 0; i < editableNodeList.size(); ++i )
474 {
475 const QDomElement editableElement = editableNodeList.at( i ).toElement();
476 d->mFieldEditables.insert( editableElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( editableElement.attribute( QStringLiteral( "editable" ) ).toInt() ) );
477 }
478
479 d->mLabelOnTop.clear();
480 const QDomNodeList labelOnTopNodeList = node.namedItem( QStringLiteral( "labelOnTop" ) ).toElement().childNodes();
481 for ( int i = 0; i < labelOnTopNodeList.size(); ++i )
482 {
483 const QDomElement labelOnTopElement = labelOnTopNodeList.at( i ).toElement();
484 d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
485 }
486
487 d->mReuseLastValuePolicy.clear();
488 // Compatibility with QGIS projects saved prior to 4.0
489 const QDomNodeList reuseLastValueNodeList = node.namedItem( QStringLiteral( "reuseLastValue" ) ).toElement().childNodes();
490 for ( int i = 0; i < reuseLastValueNodeList.size(); ++i )
491 {
492 const QDomElement reuseLastValueElement = reuseLastValueNodeList.at( i ).toElement();
493 d->mReuseLastValuePolicy.insert( reuseLastValueElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( reuseLastValueElement.attribute( QStringLiteral( "reuseLastValue" ) ).toInt() ) ? Qgis::AttributeFormReuseLastValuePolicy::AllowedDefaultOn : Qgis::AttributeFormReuseLastValuePolicy::NotAllowed );
494 }
495 const QDomNodeList reuseLastValuePolicyNodeList = node.namedItem( QStringLiteral( "reuseLastValuePolicy" ) ).toElement().childNodes();
496 for ( int i = 0; i < reuseLastValuePolicyNodeList.size(); ++i )
497 {
498 const QDomElement reuseLastValuePolicyElement = reuseLastValuePolicyNodeList.at( i ).toElement();
499 d->mReuseLastValuePolicy.insert( reuseLastValuePolicyElement.attribute( QStringLiteral( "name" ) ), qgsEnumKeyToValue( reuseLastValuePolicyElement.attribute( QStringLiteral( "reuseLastValuePolicy" ) ), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed ) );
500 }
501
502 // Read data defined field properties
503 const QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
504 for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
505 {
506 const QDomElement DDElement = fieldDDPropertiesNodeList.at( i ).toElement();
507 QgsPropertyCollection collection;
508 collection.readXml( DDElement, propertyDefinitions() );
509 d->mDataDefinedFieldProperties.insert( DDElement.attribute( QStringLiteral( "name" ) ), collection );
510 }
511
512 const QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();
513
514 for ( int i = 0; i < widgetsNodeList.size(); ++i )
515 {
516 const QDomElement widgetElement = widgetsNodeList.at( i ).toElement();
517 const QVariant config = QgsXmlUtils::readVariant( widgetElement.firstChildElement( QStringLiteral( "config" ) ) );
518
519 d->mWidgetConfigs[widgetElement.attribute( QStringLiteral( "name" ) )] = config.toMap();
520 }
521
522 // tabs and groups display info
523 const QDomNode attributeEditorFormNode = node.namedItem( QStringLiteral( "attributeEditorForm" ) );
524 if ( !attributeEditorFormNode.isNull() )
525 {
526 const QDomNodeList attributeEditorFormNodeList = attributeEditorFormNode.toElement().childNodes();
527
528 if ( attributeEditorFormNodeList.size() )
529 {
530 d->mConfiguredRootContainer = true;
531 clearTabs();
532
533 for ( int i = 0; i < attributeEditorFormNodeList.size(); i++ )
534 {
535 QDomElement elem = attributeEditorFormNodeList.at( i ).toElement();
536
537 fixLegacyConfig( elem );
538
539 const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
540 QgsAttributeEditorElement *attributeEditorWidget = QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, nullptr );
541 if ( attributeEditorWidget )
542 addTab( attributeEditorWidget );
543 }
544
545 onRelationsLoaded();
546 }
547 }
548}
549
550void QgsEditFormConfig::fixLegacyConfig( QDomElement &el ) const
551{
552 // recursive method to move widget config into attribute element config
553
554 if ( el.tagName() == QLatin1String( "attributeEditorRelation" ) )
555 {
556 if ( !el.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) )
557 {
558 // pre QGIS 3.16 compatibility - the widgets section is read before
559 const bool forceSuppress = widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "force-suppress-popup" ), false ).toBool();
560 el.setAttribute( QStringLiteral( "forceSuppressFormPopup" ), forceSuppress ? 1 : 0 );
561 }
562 if ( !el.hasAttribute( QStringLiteral( "nmRelationId" ) ) )
563 {
564 // pre QGIS 3.16 compatibility - the widgets section is read before
565 el.setAttribute( QStringLiteral( "nmRelationId" ), widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "nm-rel" ) ).toString() );
566 }
567 }
568
569 const QDomNodeList children = el.childNodes();
570 for ( int i = 0; i < children.size(); i++ )
571 {
572 QDomElement child = children.at( i ).toElement();
573 fixLegacyConfig( child );
574 el.replaceChild( child, children.at( i ) );
575 }
576}
577
578void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &context ) const
579{
580 QDomDocument doc( node.ownerDocument() );
581
582 QDomElement efField = doc.createElement( QStringLiteral( "editform" ) );
583 efField.setAttribute( QStringLiteral( "tolerant" ), QStringLiteral( "1" ) );
584 const QDomText efText = doc.createTextNode( context.pathResolver().writePath( uiForm() ) );
585 efField.appendChild( efText );
586 node.appendChild( efField );
587
588 QDomElement efiField = doc.createElement( QStringLiteral( "editforminit" ) );
589 if ( !initFunction().isEmpty() )
590 efiField.appendChild( doc.createTextNode( initFunction() ) );
591 node.appendChild( efiField );
592
593 QDomElement eficsField = doc.createElement( QStringLiteral( "editforminitcodesource" ) );
594 eficsField.appendChild( doc.createTextNode( QString::number( static_cast< int >( initCodeSource() ) ) ) );
595 node.appendChild( eficsField );
596
597 QDomElement efifpField = doc.createElement( QStringLiteral( "editforminitfilepath" ) );
598 efifpField.appendChild( doc.createTextNode( context.pathResolver().writePath( initFilePath() ) ) );
599 node.appendChild( efifpField );
600
601 QDomElement eficField = doc.createElement( QStringLiteral( "editforminitcode" ) );
602 eficField.appendChild( doc.createCDATASection( initCode() ) );
603 node.appendChild( eficField );
604
605 QDomElement fFSuppElem = doc.createElement( QStringLiteral( "featformsuppress" ) );
606 const QDomText fFSuppText = doc.createTextNode( QString::number( static_cast< int >( suppress() ) ) );
607 fFSuppElem.appendChild( fFSuppText );
608 node.appendChild( fFSuppElem );
609
610 // tab display
611 QDomElement editorLayoutElem = doc.createElement( QStringLiteral( "editorlayout" ) );
612 switch ( layout() )
613 {
615 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "uifilelayout" ) ) );
616 break;
617
619 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "tablayout" ) ) );
620 break;
621
623 default:
624 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "generatedlayout" ) ) );
625 break;
626 }
627
628 node.appendChild( editorLayoutElem );
629
630 // tabs and groups of edit form
631 if ( !tabs().empty() && d->mConfiguredRootContainer )
632 {
633 QDomElement tabsElem = doc.createElement( QStringLiteral( "attributeEditorForm" ) );
634 const QDomElement rootElem = d->mInvisibleRootContainer->toDomElement( doc );
635 const QDomNodeList elemList = rootElem.childNodes();
636 while ( !elemList.isEmpty() )
637 {
638 tabsElem.appendChild( elemList.at( 0 ) );
639 }
640 node.appendChild( tabsElem );
641 }
642
643 QDomElement editableElem = doc.createElement( QStringLiteral( "editable" ) );
644 for ( auto editIt = d->mFieldEditables.constBegin(); editIt != d->mFieldEditables.constEnd(); ++editIt )
645 {
646 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
647 fieldElem.setAttribute( QStringLiteral( "name" ), editIt.key() );
648 fieldElem.setAttribute( QStringLiteral( "editable" ), editIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
649 editableElem.appendChild( fieldElem );
650 }
651 node.appendChild( editableElem );
652
653 QDomElement labelOnTopElem = doc.createElement( QStringLiteral( "labelOnTop" ) );
654 for ( auto labelOnTopIt = d->mLabelOnTop.constBegin(); labelOnTopIt != d->mLabelOnTop.constEnd(); ++labelOnTopIt )
655 {
656 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
657 fieldElem.setAttribute( QStringLiteral( "name" ), labelOnTopIt.key() );
658 fieldElem.setAttribute( QStringLiteral( "labelOnTop" ), labelOnTopIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
659 labelOnTopElem.appendChild( fieldElem );
660 }
661 node.appendChild( labelOnTopElem );
662
663 QDomElement reuseLastValuePolicyElem = doc.createElement( QStringLiteral( "reuseLastValuePolicy" ) );
664 for ( auto reuseLastValuePolicyIt = d->mReuseLastValuePolicy.constBegin(); reuseLastValuePolicyIt != d->mReuseLastValuePolicy.constEnd(); ++reuseLastValuePolicyIt )
665 {
666 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
667 fieldElem.setAttribute( QStringLiteral( "name" ), reuseLastValuePolicyIt.key() );
668 fieldElem.setAttribute( QStringLiteral( "reuseLastValuePolicy" ), qgsEnumValueToKey( reuseLastValuePolicyIt.value() ) );
669 reuseLastValuePolicyElem.appendChild( fieldElem );
670 }
671 node.appendChild( reuseLastValuePolicyElem );
672
673 // Store data defined field properties
674 QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
675 for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
676 {
677 QDomElement ddPropsElement = doc.createElement( QStringLiteral( "field" ) );
678 ddPropsElement.setAttribute( QStringLiteral( "name" ), it.key() );
679 it.value().writeXml( ddPropsElement, propertyDefinitions() );
680 ddFieldPropsElement.appendChild( ddPropsElement );
681 }
682 node.appendChild( ddFieldPropsElement );
683
684 QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );
685
686 QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
687
688 while ( configIt != d->mWidgetConfigs.constEnd() )
689 {
690 QDomElement widgetElem = doc.createElement( QStringLiteral( "widget" ) );
691 widgetElem.setAttribute( QStringLiteral( "name" ), configIt.key() );
692 // widgetElem.setAttribute( "notNull", );
693
694 QDomElement configElem = QgsXmlUtils::writeVariant( configIt.value(), doc );
695 configElem.setTagName( QStringLiteral( "config" ) );
696 widgetElem.appendChild( configElem );
697 widgetsElem.appendChild( widgetElem );
698 ++configIt;
699 }
700
701 node.appendChild( widgetsElem );
702}
703
705{
706 return QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, parent );
707}
AttributeFormReuseLastValuePolicy
Attribute form policy for reusing last entered values.
Definition qgis.h:5529
@ AllowedDefaultOn
Reuse of last values allowed and enabled by default.
Definition qgis.h:5531
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:5530
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5500
@ Default
Use the application-wide setting.
Definition qgis.h:5501
AttributeFormPythonInitCodeSource
The Python init code source for attribute forms.
Definition qgis.h:5515
@ Dialog
Use the Python code provided in the dialog.
Definition qgis.h:5518
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5485
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
Definition qgis.h:5487
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
Definition qgis.h:5486
@ UiFile
Load a .ui file for the layout. Needs to be configured.
Definition qgis.h:5488
@ Immediate
Action will start immediately.
Definition qgis.h:1145
@ Expression
Field is calculated from an expression.
Definition qgis.h:1709
@ Join
Field originates from a joined layer.
Definition qgis.h:1707
@ Container
A container.
Definition qgis.h:5452
@ Relation
A relation.
Definition qgis.h:5454
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
A container for attribute editors, used to group them visually in the attribute form if it is set to ...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
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.
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.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
Q_DECL_DEPRECATED QgsAttributeEditorElement * attributeEditorElementFromDomElement(QDomElement &elem, QgsAttributeEditorElement *parent, const QString &layerId=QString(), const QgsReadWriteContext &context=QgsReadWriteContext())
Deserialize drag and drop designer elements.
Qgis::AttributeFormPythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
Q_DECL_DEPRECATED bool reuseLastValue(int index) const
If this returns true, the widget at the given index will reuse the last value within a QGIS session w...
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.
bool operator==(const QgsEditFormConfig &o) const
QgsEditFormConfig(const QgsEditFormConfig &o)
void setSuppress(Qgis::AttributeFormSuppression s)
Sets 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(Qgis::AttributeFormLayout 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...
Qgis::AttributeFormSuppression suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting).
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...
Qgis::AttributeFormReuseLastValuePolicy reuseLastValuePolicy(int index) const
Returns the reuse of last value policy for an attribute index.
void setInitCodeSource(Qgis::AttributeFormPythonInitCodeSource initCodeSource)
Sets if Python code shall be used for edit form initialization and its origin.
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.
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
Q_DECL_DEPRECATED void setReuseLastValue(int index, bool reuse)
Sets whether the widget at the given index will reuse the last value within a QGIS session when creat...
void setUiForm(const QString &ui)
Set path to the .ui form.
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.
Qgis::AttributeFormLayout layout() const
Gets the active layout style for the attribute editor for this layer.
void setReuseLastValuePolicy(int index, Qgis::AttributeFormReuseLastValuePolicy policy)
Sets the reuse of last value policy for an attribute index.
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:46
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
Allows entering a context category and takes care of leaving this category on deletion of the class.
A container for the context for various read/write operations on objects.
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.
QString id
Definition qgsrelation.h:45
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.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6817
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
#define QgsDebugError(str)
Definition qgslogger.h:57
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.