QGIS API Documentation 3.27.0-Master (1c05421486)
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
37void 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
53QVariantMap 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
62void 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
78void 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
92bool 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
129bool 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
149bool 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
180QList<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
215void 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::networkContentFetcherRegistry()->fetch( ui, Qgis::ActionStart::Immediate );
221 }
222
223 if ( ui.isEmpty() )
224 {
226 }
227 else
228 {
230 }
231 d->mUiFormPath = ui;
232}
233
234bool 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
247bool 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
255void 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
264void 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
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
281void 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
295void QgsEditFormConfig::setInitFunction( const QString &function )
296{
297 d.detach();
298 d->mInitFunction = function;
299}
300
302{
303 return d->mInitCode;
304}
305
306void QgsEditFormConfig::setInitCode( const QString &code )
307{
308 d.detach();
309 d->mInitCode = code;
310}
311
313{
314 return d->mInitFilePath;
315}
316
317void 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::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
352void 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
517void 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
545void 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 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).
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.
bool operator==(const QgsEditFormConfig &o) const
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:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
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: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.
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.