QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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"
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( Qgis::AttributeEditorType::Relation );
81
82 for ( QgsAttributeEditorElement *relElem : relations )
83 {
84 QgsAttributeEditorRelation *rel = dynamic_cast< QgsAttributeEditorRelation * >( relElem );
85 if ( !rel )
86 continue;
87
88 rel->init( QgsProject::instance()->relationManager() ); // skip-keyword-check
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() == Qgis::AttributeEditorType::Container )
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() == Qgis::AttributeEditorType::Relation )
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 QgsDebugError( 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
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 == Qgis::AttributeFormLayout::DragAndDrop )
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!
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 ) == Qgis::FieldOrigin::Join
239 || d->mFields.fieldOrigin( idx ) == Qgis::FieldOrigin::Expression )
240 return true;
241 if ( d->mFields.at( idx ).isReadOnly() )
242 return true;
243 return !d->mFieldEditables.value( d->mFields.at( idx ).name(), true );
244 }
245 else
246 return false;
247}
248
249bool QgsEditFormConfig::labelOnTop( int idx ) const
250{
251 if ( idx >= 0 && idx < d->mFields.count() )
252 return d->mLabelOnTop.value( d->mFields.at( idx ).name(), false );
253 else
254 return false;
255}
256
257void QgsEditFormConfig::setReadOnly( int idx, bool readOnly )
258{
259 if ( idx >= 0 && idx < d->mFields.count() )
260 {
261 d.detach();
262 d->mFieldEditables[ d->mFields.at( idx ).name()] = !readOnly;
263 }
264}
265
266void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
267{
268 if ( idx >= 0 && idx < d->mFields.count() )
269 {
270 d.detach();
271 d->mLabelOnTop[ d->mFields.at( idx ).name()] = onTop;
272 }
273}
274
276{
277 if ( index >= 0 && index < d->mFields.count() )
278 return d->mReuseLastValue.value( d->mFields.at( index ).name(), false );
279 else
280 return false;
281}
282
283void QgsEditFormConfig::setReuseLastValue( int index, bool reuse )
284{
285 if ( index >= 0 && index < d->mFields.count() )
286 {
287 d.detach();
288 d->mReuseLastValue[ d->mFields.at( index ).name()] = reuse;
289 }
290}
291
293{
294 return d->mInitFunction;
295}
296
297void QgsEditFormConfig::setInitFunction( const QString &function )
298{
299 d.detach();
300 d->mInitFunction = function;
301}
302
304{
305 return d->mInitCode;
306}
307
308void QgsEditFormConfig::setInitCode( const QString &code )
309{
310 d.detach();
311 d->mInitCode = code;
312}
313
315{
316 return d->mInitFilePath;
317}
318
319void QgsEditFormConfig::setInitFilePath( const QString &filePath )
320{
321 d.detach();
322 d->mInitFilePath = filePath;
323
324 // if this is an URL, download file as there is a good chance it will be used later
325 if ( !filePath.isEmpty() && !QUrl::fromUserInput( filePath ).isLocalFile() )
326 {
327 // any existing download will not be restarted!
329 }
330}
331
333{
334 return d->mInitCodeSource;
335}
336
338{
339 d.detach();
340 d->mInitCodeSource = initCodeSource;
341}
342
344{
345 return d->mSuppressForm;
346}
347
349{
350 d.detach();
351 d->mSuppressForm = s;
352}
353
354void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &context )
355{
356 const QgsReadWriteContextCategoryPopper p = context.enterCategory( QObject::tr( "Edit form config" ) );
357
358 d.detach();
359
360 const QDomNode editFormNode = node.namedItem( QStringLiteral( "editform" ) );
361 if ( !editFormNode.isNull() )
362 {
363 const QDomElement e = editFormNode.toElement();
364 const bool tolerantRemoteUrls = e.hasAttribute( QStringLiteral( "tolerant" ) );
365 if ( !e.text().isEmpty() )
366 {
367 const QString uiFormPath = context.pathResolver().readPath( e.text() );
368 // <= 3.2 had a bug where invalid ui paths would get written into projects on load
369 // to avoid restoring these invalid paths, we take a less-tolerant approach for older (untrustworthy) projects
370 // and only set ui forms paths IF they are local files OR start with "http(s)".
371 const bool localFile = QFileInfo::exists( uiFormPath );
372 if ( localFile || tolerantRemoteUrls || uiFormPath.startsWith( QLatin1String( "http" ) ) )
373 setUiForm( uiFormPath );
374 }
375 }
376
377 const QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
378 if ( !editFormInitNode.isNull() )
379 {
380 d->mInitFunction = editFormInitNode.toElement().text();
381 }
382
383 const QDomNode editFormInitCodeSourceNode = node.namedItem( QStringLiteral( "editforminitcodesource" ) );
384 if ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() )
385 {
386 setInitCodeSource( static_cast< Qgis::AttributeFormPythonInitCodeSource >( editFormInitCodeSourceNode.toElement().text().toInt() ) );
387 }
388
389 const QDomNode editFormInitCodeNode = node.namedItem( QStringLiteral( "editforminitcode" ) );
390 if ( !editFormInitCodeNode.isNull() )
391 {
392 setInitCode( editFormInitCodeNode.toElement().text() );
393 }
394
395 // Temporary < 2.12 b/w compatibility "dot" support patch
396 // \see: https://github.com/qgis/QGIS/pull/2498
397 // For b/w compatibility, check if there's a dot in the function name
398 // and if yes, transform it in an import statement for the module
399 // and set the PythonInitCodeSource to CodeSourceDialog
400 const int dotPos = d->mInitFunction.lastIndexOf( '.' );
401 if ( dotPos >= 0 ) // It's a module
402 {
404 setInitCode( QStringLiteral( "from %1 import %2\n" ).arg( d->mInitFunction.left( dotPos ), d->mInitFunction.mid( dotPos + 1 ) ) );
405 setInitFunction( d->mInitFunction.mid( dotPos + 1 ) );
406 }
407
408 const QDomNode editFormInitFilePathNode = node.namedItem( QStringLiteral( "editforminitfilepath" ) );
409 if ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() )
410 {
411 setInitFilePath( context.pathResolver().readPath( editFormInitFilePathNode.toElement().text() ) );
412 }
413
414 const QDomNode fFSuppNode = node.namedItem( QStringLiteral( "featformsuppress" ) );
415 if ( fFSuppNode.isNull() )
416 {
418 }
419 else
420 {
421 const QDomElement e = fFSuppNode.toElement();
422 d->mSuppressForm = static_cast< Qgis::AttributeFormSuppression >( e.text().toInt() );
423 }
424
425 // tab display
426 const QDomNode editorLayoutNode = node.namedItem( QStringLiteral( "editorlayout" ) );
427 if ( editorLayoutNode.isNull() )
428 {
430 }
431 else
432 {
433 if ( editorLayoutNode.toElement().text() == QLatin1String( "uifilelayout" ) )
434 {
435 d->mEditorLayout = Qgis::AttributeFormLayout::UiFile;
436 }
437 else if ( editorLayoutNode.toElement().text() == QLatin1String( "tablayout" ) )
438 {
440 }
441 else
442 {
444 }
445 }
446
447 d->mFieldEditables.clear();
448 const QDomNodeList editableNodeList = node.namedItem( QStringLiteral( "editable" ) ).toElement().childNodes();
449 for ( int i = 0; i < editableNodeList.size(); ++i )
450 {
451 const QDomElement editableElement = editableNodeList.at( i ).toElement();
452 d->mFieldEditables.insert( editableElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( editableElement.attribute( QStringLiteral( "editable" ) ).toInt() ) );
453 }
454
455 d->mLabelOnTop.clear();
456 const QDomNodeList labelOnTopNodeList = node.namedItem( QStringLiteral( "labelOnTop" ) ).toElement().childNodes();
457 for ( int i = 0; i < labelOnTopNodeList.size(); ++i )
458 {
459 const QDomElement labelOnTopElement = labelOnTopNodeList.at( i ).toElement();
460 d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
461 }
462
463 d->mReuseLastValue.clear();
464 const QDomNodeList reuseLastValueNodeList = node.namedItem( QStringLiteral( "reuseLastValue" ) ).toElement().childNodes();
465 for ( int i = 0; i < reuseLastValueNodeList.size(); ++i )
466 {
467 const QDomElement reuseLastValueElement = reuseLastValueNodeList.at( i ).toElement();
468 d->mReuseLastValue.insert( reuseLastValueElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( reuseLastValueElement.attribute( QStringLiteral( "reuseLastValue" ) ).toInt() ) );
469 }
470
471 // Read data defined field properties
472 const QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
473 for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
474 {
475 const QDomElement DDElement = fieldDDPropertiesNodeList.at( i ).toElement();
476 QgsPropertyCollection collection;
477 collection.readXml( DDElement, propertyDefinitions() );
478 d->mDataDefinedFieldProperties.insert( DDElement.attribute( QStringLiteral( "name" ) ), collection );
479 }
480
481 const QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();
482
483 for ( int i = 0; i < widgetsNodeList.size(); ++i )
484 {
485 const QDomElement widgetElement = widgetsNodeList.at( i ).toElement();
486 const QVariant config = QgsXmlUtils::readVariant( widgetElement.firstChildElement( QStringLiteral( "config" ) ) );
487
488 d->mWidgetConfigs[widgetElement.attribute( QStringLiteral( "name" ) )] = config.toMap();
489 }
490
491 // tabs and groups display info
492 const QDomNode attributeEditorFormNode = node.namedItem( QStringLiteral( "attributeEditorForm" ) );
493 if ( !attributeEditorFormNode.isNull() )
494 {
495 const QDomNodeList attributeEditorFormNodeList = attributeEditorFormNode.toElement().childNodes();
496
497 if ( attributeEditorFormNodeList.size() )
498 {
499 d->mConfiguredRootContainer = true;
500 clearTabs();
501
502 for ( int i = 0; i < attributeEditorFormNodeList.size(); i++ )
503 {
504 QDomElement elem = attributeEditorFormNodeList.at( i ).toElement();
505
506 fixLegacyConfig( elem );
507
508 const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
509 QgsAttributeEditorElement *attributeEditorWidget = QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, nullptr );
510 if ( attributeEditorWidget )
511 addTab( attributeEditorWidget );
512 }
513
514 onRelationsLoaded();
515 }
516 }
517}
518
519void QgsEditFormConfig::fixLegacyConfig( QDomElement &el ) const
520{
521 // recursive method to move widget config into attribute element config
522
523 if ( el.tagName() == QLatin1String( "attributeEditorRelation" ) )
524 {
525 if ( !el.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) )
526 {
527 // pre QGIS 3.16 compatibility - the widgets section is read before
528 const bool forceSuppress = widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "force-suppress-popup" ), false ).toBool();
529 el.setAttribute( QStringLiteral( "forceSuppressFormPopup" ), forceSuppress ? 1 : 0 );
530 }
531 if ( !el.hasAttribute( QStringLiteral( "nmRelationId" ) ) )
532 {
533 // pre QGIS 3.16 compatibility - the widgets section is read before
534 el.setAttribute( QStringLiteral( "nmRelationId" ), widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "nm-rel" ) ).toString() );
535 }
536 }
537
538 const QDomNodeList children = el.childNodes();
539 for ( int i = 0; i < children.size(); i++ )
540 {
541 QDomElement child = children.at( i ).toElement();
542 fixLegacyConfig( child );
543 el.replaceChild( child, children.at( i ) );
544 }
545}
546
547void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &context ) const
548{
549 QDomDocument doc( node.ownerDocument() );
550
551 QDomElement efField = doc.createElement( QStringLiteral( "editform" ) );
552 efField.setAttribute( QStringLiteral( "tolerant" ), QStringLiteral( "1" ) );
553 const QDomText efText = doc.createTextNode( context.pathResolver().writePath( uiForm() ) );
554 efField.appendChild( efText );
555 node.appendChild( efField );
556
557 QDomElement efiField = doc.createElement( QStringLiteral( "editforminit" ) );
558 if ( !initFunction().isEmpty() )
559 efiField.appendChild( doc.createTextNode( initFunction() ) );
560 node.appendChild( efiField );
561
562 QDomElement eficsField = doc.createElement( QStringLiteral( "editforminitcodesource" ) );
563 eficsField.appendChild( doc.createTextNode( QString::number( static_cast< int >( initCodeSource() ) ) ) );
564 node.appendChild( eficsField );
565
566 QDomElement efifpField = doc.createElement( QStringLiteral( "editforminitfilepath" ) );
567 efifpField.appendChild( doc.createTextNode( context.pathResolver().writePath( initFilePath() ) ) );
568 node.appendChild( efifpField );
569
570 QDomElement eficField = doc.createElement( QStringLiteral( "editforminitcode" ) );
571 eficField.appendChild( doc.createCDATASection( initCode() ) );
572 node.appendChild( eficField );
573
574 QDomElement fFSuppElem = doc.createElement( QStringLiteral( "featformsuppress" ) );
575 const QDomText fFSuppText = doc.createTextNode( QString::number( static_cast< int >( suppress() ) ) );
576 fFSuppElem.appendChild( fFSuppText );
577 node.appendChild( fFSuppElem );
578
579 // tab display
580 QDomElement editorLayoutElem = doc.createElement( QStringLiteral( "editorlayout" ) );
581 switch ( layout() )
582 {
584 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "uifilelayout" ) ) );
585 break;
586
588 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "tablayout" ) ) );
589 break;
590
592 default:
593 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "generatedlayout" ) ) );
594 break;
595 }
596
597 node.appendChild( editorLayoutElem );
598
599 // tabs and groups of edit form
600 if ( !tabs().empty() && d->mConfiguredRootContainer )
601 {
602 QDomElement tabsElem = doc.createElement( QStringLiteral( "attributeEditorForm" ) );
603 const QDomElement rootElem = d->mInvisibleRootContainer->toDomElement( doc );
604 const QDomNodeList elemList = rootElem.childNodes();
605 while ( !elemList.isEmpty() )
606 {
607 tabsElem.appendChild( elemList.at( 0 ) );
608 }
609 node.appendChild( tabsElem );
610 }
611
612 QDomElement editableElem = doc.createElement( QStringLiteral( "editable" ) );
613 for ( auto editIt = d->mFieldEditables.constBegin(); editIt != d->mFieldEditables.constEnd(); ++editIt )
614 {
615 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
616 fieldElem.setAttribute( QStringLiteral( "name" ), editIt.key() );
617 fieldElem.setAttribute( QStringLiteral( "editable" ), editIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
618 editableElem.appendChild( fieldElem );
619 }
620 node.appendChild( editableElem );
621
622 QDomElement labelOnTopElem = doc.createElement( QStringLiteral( "labelOnTop" ) );
623 for ( auto labelOnTopIt = d->mLabelOnTop.constBegin(); labelOnTopIt != d->mLabelOnTop.constEnd(); ++labelOnTopIt )
624 {
625 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
626 fieldElem.setAttribute( QStringLiteral( "name" ), labelOnTopIt.key() );
627 fieldElem.setAttribute( QStringLiteral( "labelOnTop" ), labelOnTopIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
628 labelOnTopElem.appendChild( fieldElem );
629 }
630 node.appendChild( labelOnTopElem );
631
632 QDomElement reuseLastValueElem = doc.createElement( QStringLiteral( "reuseLastValue" ) );
633 for ( auto reuseLastValueIt = d->mReuseLastValue.constBegin(); reuseLastValueIt != d->mReuseLastValue.constEnd(); ++reuseLastValueIt )
634 {
635 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
636 fieldElem.setAttribute( QStringLiteral( "name" ), reuseLastValueIt.key() );
637 fieldElem.setAttribute( QStringLiteral( "reuseLastValue" ), reuseLastValueIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
638 reuseLastValueElem.appendChild( fieldElem );
639 }
640 node.appendChild( reuseLastValueElem );
641
642 // Store data defined field properties
643 QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
644 for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
645 {
646 QDomElement ddPropsElement = doc.createElement( QStringLiteral( "field" ) );
647 ddPropsElement.setAttribute( QStringLiteral( "name" ), it.key() );
648 it.value().writeXml( ddPropsElement, propertyDefinitions() );
649 ddFieldPropsElement.appendChild( ddPropsElement );
650 }
651 node.appendChild( ddFieldPropsElement );
652
653 QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );
654
655 QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
656
657 while ( configIt != d->mWidgetConfigs.constEnd() )
658 {
659 QDomElement widgetElem = doc.createElement( QStringLiteral( "widget" ) );
660 widgetElem.setAttribute( QStringLiteral( "name" ), configIt.key() );
661 // widgetElem.setAttribute( "notNull", );
662
663 QDomElement configElem = QgsXmlUtils::writeVariant( configIt.value(), doc );
664 configElem.setTagName( QStringLiteral( "config" ) );
665 widgetElem.appendChild( configElem );
666 widgetsElem.appendChild( widgetElem );
667 ++configIt;
668 }
669
670 node.appendChild( widgetsElem );
671}
672
674{
675 return QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, parent );
676}
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5103
@ Default
Use the application-wide setting.
AttributeFormPythonInitCodeSource
The Python init code source for attribute forms.
Definition qgis.h:5118
@ Dialog
Use the Python code provided in the dialog.
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5088
@ 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.