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