QGIS API Documentation 3.43.0-Master (32433f7016e)
qgsattributesformproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformproperties.cpp
3 ---------------------
4 begin : August 2017
5 copyright : (C) 2017 by David Signer
6 email : david 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 "qgsactionmanager.h"
17#include "qgsaddtaborgroup.h"
21#include "moc_qgsattributesformproperties.cpp"
22#include "qgsattributetypedialog.h"
23#include "qgsattributeformcontaineredit.h"
24#include "qgsattributewidgetedit.h"
26#include "qgsqmlwidgetwrapper.h"
28#include "qgsapplication.h"
29#include "qgscodeeditor.h"
30#include "qgscodeeditorhtml.h"
40#include "qgsgui.h"
43#include "qgsfieldcombobox.h"
44#include "qgsexpressionfinder.h"
46#include "qgshelp.h"
47#include "qgsxmlutils.h"
48
50 : QWidget( parent )
51 , mLayer( layer )
52{
53 if ( !layer )
54 return;
55
56 setupUi( this );
57
58 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
59 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
60 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
61
62 // available widgets tree
63 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
65 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
66 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
67 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
68 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
69 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
71 mAvailableWidgetsTree->setContextMenuPolicy( Qt::CustomContextMenu );
72
73 // form layout tree
74 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
76 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
77 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
78 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
79 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
81
82 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
83 connect( mAvailableWidgetsTree, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
84 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
85 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
86 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
87 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
88 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
89 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
90 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
91
92 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
93 if ( !mBlockUpdates )
94 updatedFields();
95 } );
96
97 // Context menu and children actions
98 mAvailableWidgetsTreeContextMenu = new QMenu( this );
99 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
100 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
101
102 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
103 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
104
105 mAvailableWidgetsTreeContextMenu->addAction( mActionCopyWidgetConfiguration );
106 mAvailableWidgetsTreeContextMenu->addAction( mActionPasteWidgetConfiguration );
107
108 mMessageBar = new QgsMessageBar();
109 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
110 gridLayout->addWidget( mMessageBar, 0, 0 );
111}
112
122
124{
125 mAvailableWidgetsTree->clear();
126 mAvailableWidgetsTree->setSortingEnabled( false );
127 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
128 mAvailableWidgetsTree->setAcceptDrops( false );
129 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
130
131 //load Fields
132
133 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
134 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
135
136 const QgsFields fields = mLayer->fields();
137 for ( int i = 0; i < fields.size(); ++i )
138 {
139 const QgsField field = fields.at( i );
140 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
141 itemData.setShowLabel( true );
142
143 FieldConfig cfg( mLayer, i );
144
145 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
146
147 item->setData( 0, FieldConfigRole, cfg );
148 item->setData( 0, FieldNameRole, field.name() );
149
150 QString tooltip;
151 if ( !field.alias().isEmpty() )
152 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
153 else
154 tooltip = field.name();
155 item->setToolTip( 0, tooltip );
156 }
157 catitem->setExpanded( true );
158
159 //load Relations
160 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
161 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
162
163 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
164
165 for ( const QgsRelation &relation : relations )
166 {
167 QString name;
168 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
169 if ( polymorphicRelation.isValid() )
170 {
171 name = QStringLiteral( "%1 (%2)" ).arg( relation.name(), polymorphicRelation.name() );
172 }
173 else
174 {
175 name = relation.name();
176 }
177 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), name );
178 itemData.setShowLabel( true );
179 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
180 item->setData( 0, FieldNameRole, relation.id() );
181 }
182 catitem->setExpanded( true );
183
184 // Form actions
185 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
186 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
187
188 const QList<QgsAction> actions { mLayer->actions()->actions() };
189
190 for ( const auto &action : std::as_const( actions ) )
191 {
192 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
193 {
194 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
195 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
196 itemData.setShowLabel( true );
197 mAvailableWidgetsTree->addItem( catitem, itemData );
198 }
199 }
200
201 // QML/HTML widget
202 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
203 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
204
205 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
206 itemData.setShowLabel( true );
207 mAvailableWidgetsTree->addItem( catitem, itemData );
208
209 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
210 itemDataHtml.setShowLabel( true );
211 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
212
213 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
214 itemDataText.setShowLabel( true );
215 mAvailableWidgetsTree->addItem( catitem, itemDataText );
216
217 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
218 itemDataSpacer.setShowLabel( false );
219 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
220
221 catitem->setExpanded( true );
222}
223
225{
226 // tabs and groups info
227 mFormLayoutTree->clear();
228 mFormLayoutTree->setSortingEnabled( false );
229 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
230 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
231 mFormLayoutTree->setAcceptDrops( true );
232 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
233
234 const auto constTabs = mLayer->editFormConfig().tabs();
235 for ( QgsAttributeEditorElement *wdg : constTabs )
236 {
237 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
238 }
239}
240
241
243{
245 {
246 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
247 }
248 else
249 {
250 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
251 }
252 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
253 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
254
255 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
256}
257
264
266{
267 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
268
269 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
270
272 mEditFormLineEdit->setText( cfg.uiForm() );
273}
274
276{
278
279 mInitCodeSource = cfg.initCodeSource();
280 mInitFunction = cfg.initFunction();
281 mInitFilePath = cfg.initFilePath();
282 mInitCode = cfg.initCode();
283
284 if ( mInitCode.isEmpty() )
285 {
286 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
287 "QGIS forms can have a Python function that is called when the form is\n"
288 "opened.\n"
289 "\n"
290 "Use this function to add extra logic to your forms.\n"
291 "\n"
292 "Enter the name of the function in the \"Python Init function\"\n"
293 "field.\n"
294 "An example follows:\n"
295 "\"\"\"\n"
296 "from qgis.PyQt.QtWidgets import QWidget\n\n"
297 "def my_form_open(dialog, layer, feature):\n"
298 " geom = feature.geometry()\n"
299 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
300 }
301}
302
303void QgsAttributesFormProperties::loadAttributeTypeDialog()
304{
305 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
306 return;
307
308 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
309
310 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
311 const QString fieldName = item->data( 0, FieldNameRole ).toString();
312 const int index = mLayer->fields().indexOf( fieldName );
313
314 if ( index < 0 )
315 return;
316
317 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
318
319 loadAttributeTypeDialogFromConfiguration( cfg );
320
321 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
322 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
323
324 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
325 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
326
327 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
328}
329
330void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const FieldConfig &config )
331{
332 const QgsFieldConstraints constraints = config.mFieldConstraints;
333
334 mAttributeTypeDialog->setAlias( config.mAlias );
335 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
336 mAttributeTypeDialog->setComment( config.mComment );
337 mAttributeTypeDialog->setFieldEditable( config.mEditable );
338 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
339 mAttributeTypeDialog->setReuseLastValues( config.mReuseLastValues );
344 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
345 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
346 mAttributeTypeDialog->setMergePolicy( config.mMergePolicy );
347
350 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
352 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
354 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
355 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
356
357 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
358 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
360
361 // Make sure the widget is refreshed, even if
362 // the new widget type matches the current one
363 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
364 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
365}
366
367void QgsAttributesFormProperties::storeAttributeTypeDialog()
368{
370 return;
371
372 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
373 return;
374
375 FieldConfig cfg;
376
377 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
378 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
379 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
380 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
381 cfg.mAlias = mAttributeTypeDialog->alias();
382 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
383
384 QgsFieldConstraints constraints;
385 if ( mAttributeTypeDialog->notNull() )
386 {
388 }
389 else if ( mAttributeTypeDialog->notNullFromProvider() )
390 {
392 }
393
394 if ( mAttributeTypeDialog->unique() )
395 {
397 }
398 else if ( mAttributeTypeDialog->uniqueFromProvider() )
399 {
401 }
402
403 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
404 {
406 }
407
408 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
409
413
414 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
415 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
416 cfg.mFieldConstraints = constraints;
417 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
418 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
419 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
420 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
421 cfg.mMergePolicy = mAttributeTypeDialog->mergePolicy();
422
423 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
424 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
425
426 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
427
428 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
429 {
430 QTreeWidgetItem *item = *itemIt;
431 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
432 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
433 }
434}
435
436void QgsAttributesFormProperties::storeAttributeWidgetEdit()
437{
439 return;
440
441 mAttributeWidgetEdit->updateItemData();
442}
443
444void QgsAttributesFormProperties::loadAttributeWidgetEdit()
445{
446 if ( mFormLayoutTree->selectedItems().count() != 1 )
447 return;
448
449 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
450 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
451 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
452 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
453}
454
455void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
456{
457 mInfoTextWidget = new QLabel( infoText );
458 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
459 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
460}
461
462void QgsAttributesFormProperties::storeAttributeContainerEdit()
463{
465 return;
466
467 mAttributeContainerEdit->updateItemData();
468}
469
470void QgsAttributesFormProperties::loadAttributeContainerEdit()
471{
472 if ( mFormLayoutTree->selectedItems().count() != 1 )
473 return;
474
475 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
476 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
477 mAttributeContainerEdit->registerExpressionContextGenerator( this );
478 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
479 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
480 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
481}
482
483QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
484{
485 auto setCommonProperties = [widgetDef]( DnDTreeItemData &itemData ) {
486 itemData.setShowLabel( widgetDef->showLabel() );
487 itemData.setLabelStyle( widgetDef->labelStyle() );
488 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
489 itemData.setVerticalStretch( widgetDef->verticalStretch() );
490 };
491
492 QTreeWidgetItem *newWidget = nullptr;
493 switch ( widgetDef->type() )
494 {
496 {
497 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
498 setCommonProperties( itemData );
499 newWidget = tree->addItem( parent, itemData );
500 break;
501 }
502
504 {
505 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
506 const QgsAction action { actionEditor->action( mLayer ) };
507 if ( action.isValid() )
508 {
509 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
510 setCommonProperties( itemData );
511 newWidget = tree->addItem( parent, itemData );
512 }
513 else
514 {
515 QgsDebugError( QStringLiteral( "Invalid form action" ) );
516 }
517 break;
518 }
519
521 {
522 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
523 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
524 setCommonProperties( itemData );
525
526 RelationEditorConfiguration relEdConfig;
527 // relEdConfig.buttons = relationEditor->visibleButtons();
528 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
529 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
530 relEdConfig.nmRelationId = relationEditor->nmRelationId();
531 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
532 relEdConfig.label = relationEditor->label();
533 itemData.setRelationEditorConfiguration( relEdConfig );
534 newWidget = tree->addItem( parent, itemData );
535 break;
536 }
537
539 {
540 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
541
542 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
543 if ( !container )
544 break;
545
546 itemData.setColumnCount( container->columnCount() );
547 itemData.setContainerType( container->type() );
548 itemData.setBackgroundColor( container->backgroundColor() );
549 itemData.setVisibilityExpression( container->visibilityExpression() );
550 itemData.setCollapsedExpression( container->collapsedExpression() );
551 itemData.setCollapsed( container->collapsed() );
552
553 setCommonProperties( itemData );
554
555 newWidget = tree->addItem( parent, itemData );
556
557 const QList<QgsAttributeEditorElement *> children = container->children();
558 for ( QgsAttributeEditorElement *wdg : children )
559 {
560 loadAttributeEditorTreeItem( wdg, newWidget, tree );
561 }
562 break;
563 }
564
566 {
567 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
568 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
569 QmlElementEditorConfiguration qmlEdConfig;
570 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
571 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
572 setCommonProperties( itemData );
573 newWidget = tree->addItem( parent, itemData );
574 break;
575 }
576
578 {
579 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
580 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
581 HtmlElementEditorConfiguration htmlEdConfig;
582 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
583 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
584 setCommonProperties( itemData );
585 newWidget = tree->addItem( parent, itemData );
586 break;
587 }
588
590 {
591 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
592 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
593 TextElementEditorConfiguration textEdConfig;
594 textEdConfig.text = textElementEditor->text();
595 itemData.setTextElementEditorConfiguration( textEdConfig );
596 setCommonProperties( itemData );
597 newWidget = tree->addItem( parent, itemData );
598 break;
599 }
600
602 {
603 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
604 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
605 SpacerElementEditorConfiguration spacerEdConfig;
606 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
607 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
608 setCommonProperties( itemData );
609 itemData.setShowLabel( false );
610 newWidget = tree->addItem( parent, itemData );
611 break;
612 }
613
615 {
616 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
617 break;
618 }
619 }
620
621 if ( newWidget )
622 newWidget->setExpanded( true );
623
624 return newWidget;
625}
626
627
628void QgsAttributesFormProperties::onAttributeSelectionChanged()
629{
630 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
631 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
632 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
633}
634
635void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
636{
637 // when the selection changes in the DnD layout, sync the main tree
638 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
639 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
640 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
641}
642
643void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
644{
645 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
646
648 {
649 storeAttributeWidgetEdit();
650 }
652 {
653 storeAttributeTypeDialog();
654 }
655
656 clearAttributeTypeFrame();
657
658 if ( emitter->selectedItems().count() != 1 )
659 {
660 receiver->clearSelection();
661 }
662 else
663 {
664 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
665 switch ( itemData.type() )
666 {
668 {
669 receiver->selectFirstMatchingItem( itemData );
671 {
672 loadAttributeWidgetEdit();
673 }
674 else
675 {
676 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
677 }
678 break;
679 }
681 {
682 receiver->selectFirstMatchingItem( itemData );
684 {
685 loadAttributeWidgetEdit();
686 }
687 loadAttributeTypeDialog();
688 break;
689 }
691 {
692 receiver->clearSelection();
693 loadAttributeContainerEdit();
694 break;
695 }
697 {
698 receiver->selectFirstMatchingItem( itemData );
699 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
700 loadInfoWidget( action.html() );
701 break;
702 }
707 {
709 {
710 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
711 }
712 else
713 {
714 loadInfoWidget( tr( "This configuration is available with double-click" ) );
715 }
716 receiver->clearSelection();
717 break;
718 }
720 {
721 receiver->clearSelection();
722 break;
723 }
724 }
725 }
726}
727
728void QgsAttributesFormProperties::clearAttributeTypeFrame()
729{
731 {
732 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
733 mAttributeWidgetEdit->deleteLater();
734 mAttributeWidgetEdit = nullptr;
735 }
737 {
738 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
739 mAttributeTypeDialog->deleteLater();
740 mAttributeTypeDialog = nullptr;
741 }
743 {
744 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
745 mAttributeContainerEdit->deleteLater();
746 mAttributeContainerEdit = nullptr;
747 }
748 if ( mInfoTextWidget )
749 {
750 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
751 mInfoTextWidget->deleteLater();
752 mInfoTextWidget = nullptr;
753 }
754}
755
756void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
757{
758 Q_UNUSED( checked )
759 const auto selectedItemList { mFormLayoutTree->selectedItems() };
760 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
761 for ( int i = 0; i < rootItem->childCount(); ++i )
762 {
763 rootItem->child( i )->setSelected( !selectedItemList.contains( rootItem->child( i ) ) );
764 }
765}
766
767void QgsAttributesFormProperties::addContainer()
768{
769 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
770
771 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
772 {
773 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
774 if ( itemData.type() == DnDTreeItemData::Container )
775 {
776 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
777 }
778 }
779 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
780 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
781
782 if ( !dialog.exec() )
783 return;
784
785 const QString name = dialog.name();
786 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
787 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(), name, dialog.columnCount(), dialog.containerType() );
788}
789
790void QgsAttributesFormProperties::removeTabOrGroupButton()
791{
792 // deleting an item may delete any number of nested child items -- so we delete
793 // them one at a time and then see if there's any selection left
794 while ( true )
795 {
796 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
797 if ( items.empty() )
798 break;
799
800 delete items.at( 0 );
801 }
802}
803
805{
806 QgsAttributeEditorElement *widgetDef = nullptr;
807
808 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
809
810 switch ( itemData.type() )
811 {
812 //indexed here?
814 {
815 const int idx = mLayer->fields().lookupField( itemData.name() );
816 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
817 break;
818 }
819
821 {
822 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
823 widgetDef = new QgsAttributeEditorAction( action, parent );
824 break;
825 }
826
828 {
829 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
830
831 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
833 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
834 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
835 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
836 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
837 relDef->setLabel( relationEditorConfig.label );
838 widgetDef = relDef;
839 break;
840 }
841
843 {
844 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
845 container->setColumnCount( itemData.columnCount() );
846 // only top-level containers can be tabs
848 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
849 {
850 // a top container found which isn't at the top level -- reset it to a group box instead
852 }
853 container->setType( type );
854 container->setCollapsed( itemData.collapsed() );
855 container->setCollapsedExpression( itemData.collapsedExpression() );
856 container->setVisibilityExpression( itemData.visibilityExpression() );
857 container->setBackgroundColor( itemData.backgroundColor() );
858
859 for ( int t = 0; t < item->childCount(); t++ )
860 {
861 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
862 if ( element )
863 container->addChildElement( element );
864 }
865
866 widgetDef = container;
867 break;
868 }
869
871 {
872 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
873 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
874 widgetDef = element;
875 break;
876 }
877
879 {
880 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
882 widgetDef = element;
883 break;
884 }
885
887 {
888 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
889 element->setText( itemData.textElementEditorConfiguration().text );
890 widgetDef = element;
891 break;
892 }
893
895 {
896 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
898 widgetDef = element;
899 break;
900 }
901
903 break;
904 }
905
906 if ( widgetDef )
907 {
908 widgetDef->setShowLabel( itemData.showLabel() );
909 widgetDef->setLabelStyle( itemData.labelStyle() );
910 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
911 widgetDef->setVerticalStretch( itemData.verticalStretch() );
912 }
913
914 return widgetDef;
915}
916
917void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
918{
919 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
920 switch ( layout )
921 {
923 mFormLayoutWidget->setVisible( false );
924 mUiFileFrame->setVisible( false );
925 mAddTabOrGroupButton->setVisible( false );
926 mRemoveTabOrGroupButton->setVisible( false );
927 mInvertSelectionButton->setVisible( false );
928 break;
929
931 mFormLayoutWidget->setVisible( true );
932 mUiFileFrame->setVisible( false );
933 mAddTabOrGroupButton->setVisible( true );
934 mRemoveTabOrGroupButton->setVisible( true );
935 mInvertSelectionButton->setVisible( true );
936 break;
937
939 // ui file
940 mFormLayoutWidget->setVisible( false );
941 mUiFileFrame->setVisible( true );
942 mAddTabOrGroupButton->setVisible( false );
943 mRemoveTabOrGroupButton->setVisible( false );
944 mInvertSelectionButton->setVisible( false );
945 break;
946 }
947}
948
949void QgsAttributesFormProperties::mTbInitCode_clicked()
950{
951 QgsAttributesFormInitCode attributesFormInitCode;
952
953 attributesFormInitCode.setCodeSource( mInitCodeSource );
954 attributesFormInitCode.setInitCode( mInitCode );
955 attributesFormInitCode.setInitFilePath( mInitFilePath );
956 attributesFormInitCode.setInitFunction( mInitFunction );
957
958 if ( !attributesFormInitCode.exec() )
959 return;
960
961 mInitCodeSource = attributesFormInitCode.codeSource();
962 mInitCode = attributesFormInitCode.initCode();
963 mInitFilePath = attributesFormInitCode.initFilePath();
964 mInitFunction = attributesFormInitCode.initFunction();
965}
966
967void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
968{
969 QgsSettings myQSettings;
970 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
971 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
972
973 if ( uifilename.isNull() )
974 return;
975
976 const QFileInfo fi( uifilename );
977 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
978 mEditFormLineEdit->setText( uifilename );
979}
980
982{
983 storeAttributeWidgetEdit();
984 storeAttributeContainerEdit();
985 storeAttributeTypeDialog();
986}
987
989{
990 mBlockUpdates++;
991 storeAttributeWidgetEdit();
992 storeAttributeContainerEdit();
993 storeAttributeTypeDialog();
994
995 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
996
997 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
998
999 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1000 {
1001 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1002 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1003
1004 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
1005 const int idx = mLayer->fields().indexOf( fieldName );
1006
1007 //continue in case field does not exist anymore
1008 if ( idx < 0 )
1009 continue;
1010
1011 editFormConfig.setReadOnly( idx, !cfg.mEditable );
1012 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
1013 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
1014
1015 if ( cfg.mDataDefinedProperties.count() > 0 )
1016 {
1017 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
1018 }
1019
1021
1022 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
1023 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
1025 {
1027 }
1028 else
1029 {
1031 }
1033 {
1035 }
1036 else
1037 {
1039 }
1041 {
1043 }
1044 else
1045 {
1047 }
1048
1049 mLayer->setFieldAlias( idx, cfg.mAlias );
1053 }
1054
1055 // tabs and groups
1056 editFormConfig.clearTabs();
1057 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1058 {
1059 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1060 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1061 if ( editorElement )
1062 editFormConfig.addTab( editorElement );
1063 }
1064
1065 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1066
1067 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
1068
1069 editFormConfig.setInitCodeSource( mInitCodeSource );
1070 editFormConfig.setInitFunction( mInitFunction );
1071 editFormConfig.setInitFilePath( mInitFilePath );
1072 editFormConfig.setInitCode( mInitCode );
1073
1074 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
1075
1076 // write the legacy config of relation widgets to support settings read by the API
1077 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1078
1079 for ( int i = 0; i < relationContainer->childCount(); i++ )
1080 {
1081 QTreeWidgetItem *relationItem = relationContainer->child( i );
1082 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1083
1084 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1085 {
1086 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1087 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1088
1089 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1090 {
1091 QVariantMap cfg;
1092
1093 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1094 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1095
1096 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1097 break;
1098 }
1099 }
1100 }
1101
1102 mLayer->setEditFormConfig( editFormConfig );
1103 mBlockUpdates--;
1104}
1105
1106
1107/*
1108 * FieldConfig implementation
1109 */
1111{
1112 mAlias = layer->fields().at( idx ).alias();
1114 mComment = layer->fields().at( idx ).comment();
1115 mEditable = !layer->editFormConfig().readOnly( idx );
1116 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1118 mFieldConstraints = layer->fields().at( idx ).constraints();
1119 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1120 mEditorWidgetType = setup.type();
1121 mEditorWidgetConfig = setup.config();
1122 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1123 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1124 mMergePolicy = layer->fields().at( idx ).mergePolicy();
1125}
1126
1127QgsAttributesFormProperties::FieldConfig::operator QVariant()
1128{
1129 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1130}
1131
1132/*
1133 * RelationEditorConfiguration implementation
1134 */
1135
1136QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1137{
1138 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1139}
1140
1141/*
1142 * DnDTree implementation
1143 */
1144
1145QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1146{
1147 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1148 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1149 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1151 itemData.setColumnCount( columnCount );
1152 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1153 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1154 parent->addChild( newItem );
1155 newItem->setExpanded( true );
1156 return newItem;
1157}
1158
1160 : QTreeWidget( parent )
1161 , mLayer( layer )
1162{
1163 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1164}
1165
1166QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon )
1167{
1168 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1169
1170 switch ( data.type() )
1171 {
1179 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1180 break;
1181
1184 {
1185 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1186 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1187 }
1188 break;
1189 }
1190
1191 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) );
1192 newItem->setText( 0, data.displayName() );
1193 newItem->setIcon( 0, icon );
1194
1196 {
1197 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( data.name() );
1198 if ( !relation.isValid() || relation.referencedLayer() != mLayer )
1199 {
1200 newItem->setText( 0, tr( "Invalid relation" ) );
1201 newItem->setForeground( 0, QColor( 255, 0, 0 ) );
1202 }
1203 }
1204
1205 if ( index < 0 )
1206 parent->addChild( newItem );
1207 else
1208 parent->insertChild( index, newItem );
1209
1210 return newItem;
1211}
1212
1218void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1219{
1220 const QMimeData *data = event->mimeData();
1221
1222 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1223 {
1225
1226 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1227 QDataStream stream( &itemData, QIODevice::ReadOnly );
1228 stream >> itemElement;
1229
1230 // Inner drag and drop actions are always MoveAction
1231 if ( event->source() == this )
1232 {
1233 event->setDropAction( Qt::MoveAction );
1234 }
1235 }
1236 else
1237 {
1238 event->ignore();
1239 }
1240
1241 QTreeWidget::dragMoveEvent( event );
1242}
1243
1244
1245bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1246{
1247 bool bDropSuccessful = false;
1248
1249 if ( action == Qt::IgnoreAction )
1250 {
1251 bDropSuccessful = true;
1252 }
1253 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1254 {
1255 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1256 QDataStream stream( &itemData, QIODevice::ReadOnly );
1258
1259 while ( !stream.atEnd() )
1260 {
1261 stream >> itemElement;
1262
1263 QTreeWidgetItem *newItem;
1264
1265 if ( parent )
1266 {
1267 newItem = addItem( parent, itemElement, index++ );
1268 bDropSuccessful = true;
1269 }
1270 else
1271 {
1272 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1273 bDropSuccessful = true;
1274 }
1275
1277 {
1278 onItemDoubleClicked( newItem, 0 );
1279 }
1280
1282 {
1283 onItemDoubleClicked( newItem, 0 );
1284 }
1285
1287 {
1288 onItemDoubleClicked( newItem, 0 );
1289 }
1290
1292 {
1293 onItemDoubleClicked( newItem, 0 );
1294 }
1295
1296 clearSelection();
1297 newItem->setSelected( true );
1298 }
1299 }
1300
1301 return bDropSuccessful;
1302}
1303
1304void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1305{
1306 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1307 return;
1308
1309 if ( event->source() == this )
1310 {
1311 event->setDropAction( Qt::MoveAction );
1312 }
1313
1314 QTreeWidget::dropEvent( event );
1315}
1316
1318{
1319 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1320}
1321
1322#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1323QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1324#else
1325QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1326#endif
1327{
1328 if ( items.count() <= 0 )
1329 return nullptr;
1330
1331 const QStringList types = mimeTypes();
1332
1333 if ( types.isEmpty() )
1334 return nullptr;
1335
1336 QMimeData *data = new QMimeData();
1337 const QString format = types.at( 0 );
1338 QByteArray encoded;
1339 QDataStream stream( &encoded, QIODevice::WriteOnly );
1340
1341 const auto constItems = items;
1342 for ( const QTreeWidgetItem *item : constItems )
1343 {
1344 if ( item )
1345 {
1346 // Relevant information is always in the DnDTreeRole of the first column
1348 stream << itemData;
1349 }
1350 }
1351
1352 data->setData( format, encoded );
1353
1354 return data;
1355}
1356
1357void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1358{
1359 Q_UNUSED( column )
1360
1362
1363 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1364
1365 QFormLayout *baseLayout = new QFormLayout();
1366 baseData->setLayout( baseLayout );
1367 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1368 showLabelCheckbox->setChecked( itemData.showLabel() );
1369 baseLayout->addRow( showLabelCheckbox );
1370 QWidget *baseWidget = new QWidget();
1371 baseWidget->setLayout( baseLayout );
1372
1373 switch ( itemData.type() )
1374 {
1380 break;
1381
1383 {
1384 if ( mType == QgsAttributesDnDTree::Type::Drag )
1385 return;
1386
1387 QDialog dlg;
1388 dlg.setObjectName( "QML Form Configuration Widget" );
1390 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1391
1392 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1393 QSplitter *qmlSplitter = new QSplitter();
1394 QWidget *qmlConfigWiget = new QWidget();
1395 QVBoxLayout *layout = new QVBoxLayout( qmlConfigWiget );
1396 layout->setContentsMargins( 0, 0, 0, 0 );
1397 mainLayout->addWidget( qmlSplitter );
1398 qmlSplitter->addWidget( qmlConfigWiget );
1399 layout->addWidget( baseWidget );
1400
1401 QLineEdit *title = new QLineEdit( itemData.name() );
1402
1403 //qmlCode
1404 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1405 qmlCode->setEditingTimeoutInterval( 250 );
1406 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1407
1408 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1409 QgsFeature previewFeature;
1410 mLayer->getFeatures().nextFeature( previewFeature );
1411
1412 //update preview on text change
1413 connect( qmlCode, &QgsCodeEditor::editingTimeout, this, [=] {
1414 qmlWrapper->setQmlCode( qmlCode->text() );
1415 qmlWrapper->reinitWidget();
1416 qmlWrapper->setFeature( previewFeature );
1417 } );
1418
1419 //templates
1420 QComboBox *qmlObjectTemplate = new QComboBox();
1421 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1422 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1423 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1424 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1425 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [=]( int index ) {
1426 qmlCode->clear();
1427 switch ( index )
1428 {
1429 case 0:
1430 {
1431 qmlCode->setText( QString() );
1432 break;
1433 }
1434 case 1:
1435 {
1436 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1437 "\n"
1438 "Rectangle {\n"
1439 " width: 100\n"
1440 " height: 100\n"
1441 " color: \"steelblue\"\n"
1442 " Text{ text: \"A rectangle\" }\n"
1443 "}\n" ) );
1444 break;
1445 }
1446 case 2:
1447 {
1448 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1449 "import QtCharts 2.0\n"
1450 "\n"
1451 "ChartView {\n"
1452 " width: 400\n"
1453 " height: 400\n"
1454 "\n"
1455 " PieSeries {\n"
1456 " id: pieSeries\n"
1457 " PieSlice { label: \"First slice\"; value: 25 }\n"
1458 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1459 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1460 " }\n"
1461 "}\n" ) );
1462 break;
1463 }
1464 case 3:
1465 {
1466 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1467 "import QtCharts 2.0\n"
1468 "\n"
1469 "ChartView {\n"
1470 " title: \"Bar series\"\n"
1471 " width: 600\n"
1472 " height:400\n"
1473 " legend.alignment: Qt.AlignBottom\n"
1474 " antialiasing: true\n"
1475 " ValueAxis{\n"
1476 " id: valueAxisY\n"
1477 " min: 0\n"
1478 " max: 15\n"
1479 " }\n"
1480 "\n"
1481 " BarSeries {\n"
1482 " id: mySeries\n"
1483 " axisY: valueAxisY\n"
1484 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1485 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1486 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1487 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1488 " }\n"
1489 "}\n" ) );
1490 break;
1491 }
1492 default:
1493 break;
1494 }
1495 } );
1496
1497 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1498 expressionWidget->setButtonVisible( false );
1499 expressionWidget->registerExpressionContextGenerator( this );
1500 expressionWidget->setLayer( mLayer );
1501 QToolButton *addFieldButton = new QToolButton();
1502 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1503
1504 QToolButton *editExpressionButton = new QToolButton();
1505 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1506 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1507
1508 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1509 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1510 if ( !expression.isEmpty() )
1511 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1512 } );
1513
1514 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1515 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1516 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1518 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1519
1520 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1521 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1522 {
1523 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1524 if ( !expression.isEmpty() )
1525 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1526 }
1527 } );
1528
1529 layout->addWidget( new QLabel( tr( "Title" ) ) );
1530 layout->addWidget( title );
1531 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1532 qmlCodeBox->setLayout( new QVBoxLayout );
1533 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1534 QWidget *expressionWidgetBox = new QWidget();
1535 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1536 expressionWidgetBox->setLayout( new QHBoxLayout );
1537 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1538 expressionWidgetBox->layout()->addWidget( expressionWidget );
1539 expressionWidgetBox->layout()->addWidget( addFieldButton );
1540 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1541 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1542 layout->addWidget( qmlCodeBox );
1543 layout->addWidget( qmlCode );
1544 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1545 qmlPreviewBox->setMinimumWidth( 200 );
1546 qmlPreviewBox->setWidget( qmlWrapper->widget() );
1547 //emit to load preview for the first time
1548 emit qmlCode->editingTimeout();
1549 qmlSplitter->addWidget( qmlPreviewBox );
1550 qmlSplitter->setChildrenCollapsible( false );
1551 qmlSplitter->setHandleWidth( 6 );
1552 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
1553
1554 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1555
1556 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1557 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1558 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1559 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1560 } );
1561
1562 mainLayout->addWidget( buttonBox );
1563
1564 if ( dlg.exec() )
1565 {
1567 qmlEdCfg.qmlCode = qmlCode->text();
1568 itemData.setName( title->text() );
1569 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1570 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1571
1572 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1573 item->setText( 0, title->text() );
1574 }
1575 }
1576 break;
1577
1579 {
1580 if ( mType == QgsAttributesDnDTree::Type::Drag )
1581 return;
1582 QDialog dlg;
1583 dlg.setObjectName( "HTML Form Configuration Widget" );
1585 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1586
1587 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1588 QSplitter *htmlSplitter = new QSplitter();
1589 QWidget *htmlConfigWiget = new QWidget();
1590 QVBoxLayout *layout = new QVBoxLayout( htmlConfigWiget );
1591 layout->setContentsMargins( 0, 0, 0, 0 );
1592 mainLayout->addWidget( htmlSplitter );
1593 htmlSplitter->addWidget( htmlConfigWiget );
1594 htmlSplitter->setChildrenCollapsible( false );
1595 htmlSplitter->setHandleWidth( 6 );
1596 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1597 layout->addWidget( baseWidget );
1598
1599 QLineEdit *title = new QLineEdit( itemData.name() );
1600
1601 //htmlCode
1602 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML();
1603 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1604 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1605
1606 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1607 QgsFeature previewFeature;
1608 mLayer->getFeatures().nextFeature( previewFeature );
1609
1610 //update preview on text change
1611 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [=] {
1612 htmlWrapper->setHtmlCode( htmlCode->text() );
1613 htmlWrapper->reinitWidget();
1614 htmlWrapper->setFeature( previewFeature );
1615 } );
1616
1617 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1618 expressionWidget->setButtonVisible( false );
1619 expressionWidget->registerExpressionContextGenerator( this );
1620 expressionWidget->setLayer( mLayer );
1621 QToolButton *addFieldButton = new QToolButton();
1622 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1623
1624 QToolButton *editExpressionButton = new QToolButton();
1625 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1626 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1627
1628 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1629 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1630 if ( !expression.isEmpty() )
1631 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1632 } );
1633
1634 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1635 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1636 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1638 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1639
1640 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1641 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1642 {
1643 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1644 if ( !expression.isEmpty() )
1645 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1646 }
1647 } );
1648
1649 layout->addWidget( new QLabel( tr( "Title" ) ) );
1650 layout->addWidget( title );
1651 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1652 layout->addWidget( expressionWidgetBox );
1653 expressionWidgetBox->setLayout( new QHBoxLayout );
1654 expressionWidgetBox->layout()->addWidget( expressionWidget );
1655 expressionWidgetBox->layout()->addWidget( addFieldButton );
1656 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1657 layout->addWidget( htmlCode );
1658 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1659 htmlPreviewBox->setLayout( new QGridLayout );
1660 htmlPreviewBox->setMinimumWidth( 200 );
1661 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1662 //emit to load preview for the first time
1663 emit htmlCode->textChanged();
1664 htmlSplitter->addWidget( htmlPreviewBox );
1665 htmlSplitter->setChildrenCollapsible( false );
1666 htmlSplitter->setHandleWidth( 6 );
1667 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1668
1669 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1670
1671 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1672 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1673 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1674 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1675 } );
1676
1677 mainLayout->addWidget( buttonBox );
1678
1679 if ( dlg.exec() )
1680 {
1682 htmlEdCfg.htmlCode = htmlCode->text();
1683 itemData.setName( title->text() );
1684 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1685 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1686
1687 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1688 item->setText( 0, title->text() );
1689 }
1690 break;
1691 }
1692
1694 {
1695 if ( mType == QgsAttributesDnDTree::Type::Drag )
1696 return;
1697 QDialog dlg;
1698 dlg.setObjectName( "Text Form Configuration Widget" );
1700 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1701
1702 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1703 QSplitter *textSplitter = new QSplitter();
1704 QWidget *textConfigWiget = new QWidget();
1705 QVBoxLayout *layout = new QVBoxLayout( textConfigWiget );
1706 layout->setContentsMargins( 0, 0, 0, 0 );
1707 mainLayout->addWidget( textSplitter );
1708 textSplitter->addWidget( textConfigWiget );
1709 layout->addWidget( baseWidget );
1710
1711 QLineEdit *title = new QLineEdit( itemData.name() );
1712
1714 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1715 text->setText( itemData.textElementEditorConfiguration().text );
1716
1717 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1718 QgsFeature previewFeature;
1719 mLayer->getFeatures().nextFeature( previewFeature );
1720
1721 //update preview on text change
1722 connect( text, &QgsCodeEditorExpression::textChanged, this, [=] {
1723 textWrapper->setText( text->text() );
1724 textWrapper->reinitWidget();
1725 textWrapper->setFeature( previewFeature );
1726 } );
1727
1728 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1729 expressionWidget->setButtonVisible( false );
1730 expressionWidget->registerExpressionContextGenerator( this );
1731 expressionWidget->setLayer( mLayer );
1732 QToolButton *addFieldButton = new QToolButton();
1733 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1734
1735 QToolButton *editExpressionButton = new QToolButton();
1736 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1737 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1738
1739 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1740 QString expression = expressionWidget->expression().trimmed();
1741 if ( !expression.isEmpty() )
1742 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1743 } );
1744 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1745 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1746
1748 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1749
1750 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1751 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1752 {
1753 QString expression = exprDlg.expressionText().trimmed();
1754 if ( !expression.isEmpty() )
1755 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1756 }
1757 } );
1758
1759 layout->addWidget( new QLabel( tr( "Title" ) ) );
1760 layout->addWidget( title );
1761 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1762 layout->addWidget( expressionWidgetBox );
1763 expressionWidgetBox->setLayout( new QHBoxLayout );
1764 expressionWidgetBox->layout()->addWidget( expressionWidget );
1765 expressionWidgetBox->layout()->addWidget( addFieldButton );
1766 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1767 layout->addWidget( text );
1768 QScrollArea *textPreviewBox = new QgsScrollArea();
1769 textPreviewBox->setLayout( new QGridLayout );
1770 textPreviewBox->setMinimumWidth( 200 );
1771 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1772 //emit to load preview for the first time
1773 emit text->textChanged();
1774 textSplitter->addWidget( textPreviewBox );
1775 textSplitter->setChildrenCollapsible( false );
1776 textSplitter->setHandleWidth( 6 );
1777 textSplitter->setSizes( QList<int>() << 1 << 1 );
1778
1779 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1780
1781 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1782 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1783 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1784 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1785 } );
1786
1787 mainLayout->addWidget( buttonBox );
1788
1789 if ( dlg.exec() )
1790 {
1792 textEdCfg.text = text->text();
1793 itemData.setName( title->text() );
1794 itemData.setTextElementEditorConfiguration( textEdCfg );
1795 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1796
1797 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1798 item->setText( 0, title->text() );
1799 }
1800 break;
1801 }
1802
1804 {
1805 if ( mType == QgsAttributesDnDTree::Type::Drag )
1806 return;
1807 QDialog dlg;
1808 dlg.setObjectName( "Spacer Form Configuration Widget" );
1810 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1811
1812 QVBoxLayout *mainLayout = new QVBoxLayout();
1813 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1814 QLineEdit *title = new QLineEdit( itemData.name() );
1815 mainLayout->addWidget( title );
1816
1817 QHBoxLayout *cbLayout = new QHBoxLayout();
1818 mainLayout->addLayout( cbLayout );
1819 dlg.setLayout( mainLayout );
1820 QCheckBox *cb = new QCheckBox { &dlg };
1821 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1822 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1823 cbLayout->addWidget( cb );
1824
1825 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1826
1827 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1828 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1829 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1830 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1831 } );
1832
1833 mainLayout->addWidget( buttonBox );
1834
1835 if ( dlg.exec() )
1836 {
1838 spacerEdCfg.drawLine = cb->isChecked();
1839 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1840 itemData.setShowLabel( false );
1841 itemData.setName( title->text() );
1842 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1843 item->setText( 0, title->text() );
1844 }
1845
1846 break;
1847 }
1848 }
1849}
1850
1852{
1853 QgsExpressionContext expContext;
1856
1857 if ( mLayer )
1858 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1859
1861 return expContext;
1862}
1863
1865{
1866 return mType;
1867}
1868
1870{
1871 mType = value;
1872}
1873
1875{
1876 QTreeWidgetItemIterator it( this );
1877 while ( *it )
1878 {
1880 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1881 {
1882 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1883 {
1884 // the selection is already good
1885 }
1886 else
1887 {
1888 clearSelection();
1889 ( *it )->setSelected( true );
1890 }
1891 return;
1892 }
1893 ++it;
1894 }
1895 clearSelection();
1896}
1897
1898
1899/*
1900 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1901 */
1902
1903QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1904{
1905 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1906 return stream;
1907}
1908
1909QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1910{
1911 QString name;
1912 QString displayName;
1913 quint32 type;
1914
1915 stream >> type >> name >> displayName;
1916
1918 data.setName( name );
1919 data.setDisplayName( displayName );
1920
1921 return stream;
1922}
1923
1928
1933
1938
1940{
1941 mLabelStyle = labelStyle;
1942}
1943
1945{
1946 return mShowLabel;
1947}
1948
1950{
1951 mShowLabel = showLabel;
1952}
1953
1958
1960{
1961 mVisibilityExpression = visibilityExpression;
1962}
1963
1968
1970{
1971 mCollapsedExpression = collapsedExpression;
1972}
1973
1978
1980{
1981 mRelationEditorConfiguration = relationEditorConfiguration;
1982}
1983
1988
1990{
1991 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1992}
1993
1994
1999
2001{
2002 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
2003}
2004
2009
2011{
2012 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
2013}
2014
2016{
2017 return mBackgroundColor;
2018}
2019
2021{
2022 mBackgroundColor = backgroundColor;
2023}
2024
2029
2031{
2032 mTextElementEditorConfiguration = textElementEditorConfiguration;
2033}
2034
2035void QgsAttributesFormProperties::updatedFields()
2036{
2037 // Store configuration to insure changes made are kept after refreshing the list
2038 QMap<QString, FieldConfig> fieldConfigs;
2039 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2040 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2041 {
2042 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2043 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2044 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
2045 fieldConfigs[fieldName] = cfg;
2046 }
2047
2049
2050 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2051 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2052 {
2053 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2054 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2055 if ( fieldConfigs.contains( fieldName ) )
2056 {
2057 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
2058 }
2059 }
2060}
2061
2062void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
2063{
2064 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2065 return;
2066
2067 QPoint globalPos = mAvailableWidgetsTree->viewport()->mapToGlobal( point );
2068
2069 QTreeWidgetItem *item = mAvailableWidgetsTree->itemAt( point );
2070 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2071 if ( itemData.type() == DnDTreeItemData::Field )
2072 {
2073 const QClipboard *clipboard = QApplication::clipboard();
2074 const bool pasteEnabled = clipboard->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) );
2075 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
2076 mAvailableWidgetsTreeContextMenu->popup( globalPos );
2077 }
2078}
2079
2080void QgsAttributesFormProperties::copyWidgetConfiguration()
2081{
2082 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2083 return;
2084
2085 const QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2086 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2087 if ( itemData.type() != DnDTreeItemData::Field )
2088 return;
2089
2090 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2091 const int index = mLayer->fields().indexOf( fieldName );
2092
2093 if ( index < 0 )
2094 return;
2095
2096 const QgsField field = mLayer->fields().field( index );
2097
2098 // We won't copy field aliases nor comments
2099 QDomDocument doc;
2100 QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) );
2101 documentElement.setAttribute( QStringLiteral( "name" ), field.name() );
2102
2103 // Editor widget setup
2104 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
2105
2106 QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) );
2107 documentElement.appendChild( editWidgetElement );
2108 editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() );
2109 QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) );
2110
2111 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
2112 editWidgetElement.appendChild( editWidgetConfigElement );
2113
2114 // Split policy
2115 QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) );
2116 splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) );
2117 documentElement.appendChild( splitPolicyElement );
2118
2119 // Duplicate policy
2120 QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) );
2121 duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
2122 documentElement.appendChild( duplicatePolicyElement );
2123
2124 // Merge policy
2125 QDomElement mergePolicyElement = doc.createElement( QStringLiteral( "mergePolicy" ) );
2126 mergePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.mergePolicy() ) );
2127 documentElement.appendChild( mergePolicyElement );
2128
2129 // Default expressions
2130 QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) );
2131 defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() );
2132 defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2133 documentElement.appendChild( defaultElem );
2134
2135 // Constraints
2136 QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) );
2137 constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() );
2138 constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
2139 constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
2140 constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
2141 documentElement.appendChild( constraintElem );
2142
2143 // Constraint expressions
2144 QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) );
2145 constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() );
2146 constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() );
2147 documentElement.appendChild( constraintExpressionElem );
2148
2149 // Widget general settings
2150 QDomElement widgetGeneralSettingsElem = doc.createElement( QStringLiteral( "widgetGeneralSettings" ) );
2151 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "editable" ), !mLayer->editFormConfig().readOnly( index ) );
2152 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "reuse_last_values" ), mLayer->editFormConfig().labelOnTop( index ) );
2153 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "label_on_top" ), mLayer->editFormConfig().reuseLastValue( index ) );
2154 documentElement.appendChild( widgetGeneralSettingsElem );
2155
2156 // Widget display section
2158 {
2159 // Go for the corresponding form layout item and extract its display settings
2160 if ( mFormLayoutTree->selectedItems().count() != 1 )
2161 return;
2162
2163 const QTreeWidgetItem *itemLayout = mFormLayoutTree->selectedItems().at( 0 );
2164 const DnDTreeItemData itemDataLayout = itemLayout->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2165
2166 QDomElement displayElement = doc.createElement( QStringLiteral( "widgetDisplay" ) );
2167 displayElement.setAttribute( QStringLiteral( "showLabel" ), itemDataLayout.showLabel() );
2168 displayElement.setAttribute( QStringLiteral( "horizontalStretch" ), itemDataLayout.horizontalStretch() );
2169 displayElement.setAttribute( QStringLiteral( "verticalStretch" ), itemDataLayout.verticalStretch() );
2170 displayElement.appendChild( itemDataLayout.labelStyle().writeXml( doc ) );
2171 documentElement.appendChild( displayElement );
2172 }
2173
2174 doc.appendChild( documentElement );
2175
2176 QMimeData *mimeData = new QMimeData;
2177 mimeData->setData( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ), doc.toByteArray() );
2178 QClipboard *clipboard = QApplication::clipboard();
2179 clipboard->setMimeData( mimeData );
2180}
2181
2182void QgsAttributesFormProperties::pasteWidgetConfiguration()
2183{
2184 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2185 return;
2186
2187 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2188
2189 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2190 const int fieldIndex = mLayer->fields().indexOf( fieldName );
2191
2192 if ( fieldIndex < 0 )
2193 return;
2194
2195 // Get base config from target item and ovewrite settings when possible
2196 FieldConfig config = item->data( 0, FieldConfigRole ).value<FieldConfig>();
2197
2198 QDomDocument doc;
2199 QClipboard *clipboard = QApplication::clipboard();
2200 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) ) ) )
2201 {
2202 QDomElement docElem = doc.documentElement();
2203 if ( docElem.tagName() != QLatin1String( "FormWidgetClipboard" ) )
2204 return;
2205
2206 // When pasting, the target item has already been selected and
2207 // has triggered attribute type dialog loading. Therefore, we'll
2208 // only overwrite GUI settings instead of destroying and recreating
2209 // the whole dialog.
2210
2211 // Editor widget configuration
2212 const QDomElement fieldWidgetElement = docElem.firstChildElement( QStringLiteral( "editWidget" ) );
2213 if ( !fieldWidgetElement.isNull() )
2214 {
2215 const QString widgetType = fieldWidgetElement.attribute( QStringLiteral( "type" ) );
2216
2217 // Only paste if source editor widget type is supported by target field
2218 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
2219 if ( factory->supportsField( mLayer, fieldIndex ) )
2220 {
2221 const QDomElement configElement = fieldWidgetElement.firstChildElement( QStringLiteral( "config" ) );
2222 if ( !configElement.isNull() )
2223 {
2224 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
2225 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
2226 QgsReadWriteContext context;
2227 // translate widged configuration strings
2228 if ( widgetType == QStringLiteral( "ValueRelation" ) )
2229 {
2230 optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() );
2231 }
2232 if ( widgetType == QStringLiteral( "ValueMap" ) )
2233 {
2234 if ( optionsMap[QStringLiteral( "map" )].canConvert<QList<QVariant>>() )
2235 {
2236 QList<QVariant> translatedValueList;
2237 const QList<QVariant> valueList = optionsMap[QStringLiteral( "map" )].toList();
2238 for ( int i = 0, row = 0; i < valueList.count(); i++, row++ )
2239 {
2240 QMap<QString, QVariant> translatedValueMap;
2241 QString translatedKey = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuemapdescriptions" ).arg( mLayer->id(), fieldName ), valueList[i].toMap().constBegin().key() );
2242 translatedValueMap.insert( translatedKey, valueList[i].toMap().constBegin().value() );
2243 translatedValueList.append( translatedValueMap );
2244 }
2245 optionsMap.insert( QStringLiteral( "map" ), translatedValueList );
2246 }
2247 }
2248 config.mEditorWidgetType = widgetType;
2249 config.mEditorWidgetConfig = optionsMap;
2250 }
2251 }
2252 else
2253 {
2254 mMessageBar->pushMessage( QString(), tr( "Unable to paste widget configuration. The target field (%1) does not support the %2 widget type." ).arg( fieldName, widgetType ), Qgis::MessageLevel::Warning );
2255 }
2256 }
2257
2258 // Split policy
2259 const QDomElement splitPolicyElement = docElem.firstChildElement( QStringLiteral( "splitPolicy" ) );
2260 if ( !splitPolicyElement.isNull() )
2261 {
2262 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainSplitPolicy::Duplicate );
2263 config.mSplitPolicy = policy;
2264 }
2265
2266 // Duplicate policy
2267 const QDomElement duplicatePolicyElement = docElem.firstChildElement( QStringLiteral( "duplicatePolicy" ) );
2268 if ( !duplicatePolicyElement.isNull() )
2269 {
2270 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDuplicatePolicy::Duplicate );
2271 config.mDuplicatePolicy = policy;
2272 }
2273
2274 // Merge policy
2275 const QDomElement mergePolicyElement = docElem.firstChildElement( QStringLiteral( "mergePolicy" ) );
2276 if ( !mergePolicyElement.isNull() )
2277 {
2278 const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainMergePolicy::DefaultValue );
2279 config.mMergePolicy = policy;
2280 }
2281
2282 // Default expressions
2283 const QDomElement defaultElement = docElem.firstChildElement( QStringLiteral( "default" ) );
2284 if ( !defaultElement.isNull() )
2285 {
2286 mAttributeTypeDialog->setDefaultValueExpression( defaultElement.attribute( QStringLiteral( "expression" ) ) );
2287 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( defaultElement.attribute( QStringLiteral( "applyOnUpdate" ) ).toInt() );
2288 }
2289
2290 // Constraints
2291 // take target field constraints as a basis
2292 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
2293 const QDomElement constraintElement = docElem.firstChildElement( QStringLiteral( "constraint" ) );
2294 if ( !constraintElement.isNull() )
2295 {
2296 const int intConstraints = constraintElement.attribute( QStringLiteral( "constraints" ), QStringLiteral( "0" ) ).toInt();
2297 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
2298
2299 // always keep provider constraints intact
2301 {
2302 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
2304 else
2306 }
2308 {
2309 if ( constraints & QgsFieldConstraints::ConstraintUnique )
2311 else
2313 }
2315 {
2318 else
2320 }
2321
2322 const int uniqueStrength = constraintElement.attribute( QStringLiteral( "unique_strength" ), QStringLiteral( "1" ) ).toInt();
2323 const int notNullStrength = constraintElement.attribute( QStringLiteral( "notnull_strength" ), QStringLiteral( "1" ) ).toInt();
2324 const int expStrength = constraintElement.attribute( QStringLiteral( "exp_strength" ), QStringLiteral( "1" ) ).toInt();
2325
2329 }
2330
2331 // Constraint expressions
2332 // always keep provider constraints intact
2334 {
2335 const QDomElement constraintExpressionElement = docElem.firstChildElement( QStringLiteral( "constraintExpression" ) );
2336 if ( !constraintExpressionElement.isNull() )
2337 {
2338 QString expression = constraintExpressionElement.attribute( QStringLiteral( "exp" ), QString() );
2339 QString description = constraintExpressionElement.attribute( QStringLiteral( "desc" ), QString() );
2340 fieldConstraints.setConstraintExpression( expression, description );
2341 }
2342 }
2343 config.mFieldConstraints = fieldConstraints;
2344
2345 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( QStringLiteral( "widgetGeneralSettings" ) );
2346 if ( !widgetGeneralSettingsElement.isNull() )
2347 {
2348 const int editable = widgetGeneralSettingsElement.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
2349 const int reuse = widgetGeneralSettingsElement.attribute( QStringLiteral( "reuse_last_values" ), QStringLiteral( "0" ) ).toInt();
2350 const int labelOnTop = widgetGeneralSettingsElement.attribute( QStringLiteral( "label_on_top" ), QStringLiteral( "0" ) ).toInt();
2351
2352 config.mEditable = editable;
2353 config.mReuseLastValues = reuse;
2354 config.mLabelOnTop = labelOnTop;
2355 }
2356
2357 loadAttributeTypeDialogFromConfiguration( config );
2358
2359 // Widget display section
2361 {
2362 const QDomElement displayElement = docElem.firstChildElement( QStringLiteral( "widgetDisplay" ) );
2363 if ( !displayElement.isNull() )
2364 {
2365 const int showLabel = displayElement.attribute( QStringLiteral( "showLabel" ), QStringLiteral( "0" ) ).toInt();
2366 const int horizontalStretch = displayElement.attribute( QStringLiteral( "horizontalStretch" ), QStringLiteral( "0" ) ).toInt();
2367 const int verticalStretch = displayElement.attribute( QStringLiteral( "verticalStretch" ), QStringLiteral( "0" ) ).toInt();
2369 style.readXml( displayElement );
2370
2371 // Update current GUI controls
2372 mAttributeWidgetEdit->setShowLabel( showLabel );
2373 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
2374 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
2375 mAttributeWidgetEdit->setLabelStyle( style );
2376 }
2377 }
2378 }
2379}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5263
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5293
@ On
Always suppress feature form.
@ Default
Use the application-wide setting.
@ Off
Never suppress feature form.
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5278
@ 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.
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:3774
@ DefaultValue
Use default field value.
@ Warning
Warning message.
Definition qgis.h:156
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:3757
@ Duplicate
Duplicate original value.
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:3794
@ Duplicate
Duplicate original value.
@ Action
A layer action element.
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element.
@ SpacerElement
A spacer element.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Returns a list of actions that are available in the given action scope.
QgsAction action(QUuid id) const
Gets an action by its id.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:37
Dialog to add a tab or group of attributes.
QPair< QString, QTreeWidgetItem * > ContainerPair
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This element will load a layer action onto the form.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
A container for attribute editors, used to group them visually in the attribute form if it is set to ...
virtual void addChildElement(QgsAttributeEditorElement *element)
Add a child element to this container.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
void setColumnCount(int columnCount)
Set the number of columns in this group.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
The visibility expression is used in the attribute form to show or hide this container based on an ex...
QgsOptionalExpression collapsedExpression() const
The collapsed expression is used in the attribute form to set the collapsed status of the group box c...
bool collapsed() const
For group box containers returns true if this group box is collapsed.
Qgis::AttributeEditorContainerType type() const
Returns the container type.
void setType(Qgis::AttributeEditorContainerType type)
Sets the container type.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
The collapsed expression is used in the attribute form to set the collapsed status of the group box o...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
Returns the background color of the container.
void setCollapsed(bool collapsed)
For group box containers sets if this group box is collapsed.
int columnCount() const
Gets the number of columns in this group.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color to backgroundColor.
An abstract base class for any elements of a drag and drop form.
void setHorizontalStretch(int stretch)
Sets the horizontal stretch factor for the element.
LabelStyle labelStyle() const
Returns the label style.
void setLabelStyle(const LabelStyle &labelStyle)
Sets the labelStyle.
Qgis::AttributeEditorType type() const
The type of this element.
int verticalStretch() const
Returns the vertical stretch factor for the element.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString name() const
Returns the name of this element.
void setVerticalStretch(int stretch)
Sets the vertical stretch factor for the element.
void setShowLabel(bool showLabel)
Controls if this element should be labeled with a title (field, relation or groupname).
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
This element will load a field's widget onto the form.
An attribute editor widget that will represent arbitrary HTML code.
QString htmlCode() const
The Html code that will be represented within this widget.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code that will be represented within this widget to htmlCode.
An attribute editor widget that will represent arbitrary QML code.
QString qmlCode() const
The QML code that will be represented within this widget.
void setQmlCode(const QString &qmlCode)
Sets the QML code that will be represented within this widget to qmlCode.
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.
void setRelationWidgetTypeId(const QString &relationWidgetTypeId)
Sets the relation widget type.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
QVariantMap relationEditorConfiguration() const
Returns the relation editor widget configuration.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
QString relationWidgetTypeId() const
Returns the current relation widget type id.
void setRelationEditorConfiguration(const QVariantMap &config)
Sets the relation editor configuration.
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
QString label() const
Determines the label of this element.
An attribute editor widget that will represent a spacer.
void setDrawLine(bool drawLine)
Sets a flag to define if the spacer element will contain an horizontal line.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
An attribute editor widget that will represent arbitrary text code.
void setText(const QString &text)
Sets the text that will be represented within this widget to text.
QString text() const
The Text that will be represented within this widget.
Overrides mime type handling to be able to work with the drag and drop attribute editor.
void setType(QgsAttributesDnDTree::Type value)
void dropEvent(QDropEvent *event) override
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QTreeWidgetItem * addContainer(QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type)
Adds a new container to parent.
QStringList mimeTypes() const override
QgsAttributesDnDTree(QgsVectorLayer *layer, QWidget *parent=nullptr)
QMimeData * mimeData(const QList< QTreeWidgetItem * > &items) const override
bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) override
void dragMoveEvent(QDragMoveEvent *event) override
Is called when mouse is moved over attributes tree before a drop event.
void selectFirstMatchingItem(const QgsAttributesFormProperties::DnDTreeItemData &data)
QTreeWidgetItem * addItem(QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index=-1, const QIcon &icon=QIcon())
Adds a new item to a parent.
A dialog for configuring the Python init code handling for attribute forms.
QString initFilePath() const
Returns the file path for the file containing the init code.
void setCodeSource(Qgis::AttributeFormPythonInitCodeSource source)
Sets the Python init code source.
void setInitFilePath(const QString &initFilePath)
Sets the file path for the file containing the init code.
void setInitFunction(const QString &initFunction)
Sets the name of the init function.
QString initFunction() const
Returns the name of the init function.
void setInitCode(const QString &initCode)
Sets the init code contents.
Qgis::AttributeFormPythonInitCodeSource codeSource() const
Returns the Python init code source.
QString initCode() const
Returns the init code contents.
A tree widget item containing drag-and-drop form designer elements.
void setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
Returns the QML editor configuration.
int verticalStretch() const
Returns the vertical stretch factor for the element.
void setTextElementEditorConfiguration(const TextElementEditorConfiguration &textElementEditorConfiguration)
Sets the editor configuration for text element to textElementEditorConfiguration.
SpacerElementEditorConfiguration spacerElementEditorConfiguration() const
Returns the spacer element configuration.
void setRelationEditorConfiguration(const RelationEditorConfiguration &relationEditorConfiguration)
Sets the relation editor configuration.
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
const QgsAttributeEditorElement::LabelStyle labelStyle() const
Returns the label style.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
Sets the optional visibilityExpression that dynamically controls the visibility status of a container...
QgsOptionalExpression collapsedExpression() const
Returns the optional expression that dynamically controls the collapsed status of a group box contain...
@ WidgetType
In the widget tree, the type of widget.
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
void setContainerType(Qgis::AttributeEditorContainerType type)
Sets the container type.
void setQmlElementEditorConfiguration(const QmlElementEditorConfiguration &qmlElementEditorConfiguration)
Sets the QML editor configuration.
TextElementEditorConfiguration textElementEditorConfiguration() const
Returns the editor configuration for text element.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
Sets the optional collapsedExpression that dynamically controls the collapsed status of a group box c...
void setHtmlElementEditorConfiguration(const HtmlElementEditorConfiguration &htmlElementEditorConfiguration)
Sets the HTML editor configuration.
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
Returns the HTML editor configuration.
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
bool collapsed() const
For group box containers returns if this group box is collapsed.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
QgsAttributeEditorElement * createAttributeEditorWidget(QTreeWidgetItem *item, QgsAttributeEditorElement *parent, bool isTopLevel=false)
Creates a new attribute editor element based on the definition stored in item.
QgsAttributeFormContainerEdit * mAttributeContainerEdit
QgsAttributesFormProperties(QgsVectorLayer *layer, QWidget *parent=nullptr)
QgsAttributeTypeDialog * mAttributeTypeDialog
void store()
Stores currently opened widget configuration.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsAttributeWidgetEdit * mAttributeWidgetEdit
void apply()
Applies the attribute from properties to the vector layer.
A HTML editor based on QScintilla2.
A text editor based on QScintilla2.
void setText(const QString &text) override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
void editingTimeout()
Emitted when either:
Provides a container for managing client side default values for fields.
Contains configuration settings for an editor form.
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.
void setInitFunction(const QString &function)
Set Python function for edit form initialization.
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.
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.
QString initFunction() const
Gets Python function for edit form initialization.
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.
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.
Every attribute editor widget needs a factory, which inherits this class.
bool supportsField(const QgsVectorLayer *vl, int fieldIdx) const
Check if this editor widget type supports a certain field.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
A generic dialog for building expression strings.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Stores information about constraints which may be present on a field.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
void setConstraintStrength(Constraint constraint, ConstraintStrength strength)
Sets the strength of a constraint.
void setConstraintExpression(const QString &expression, const QString &description=QString())
Set the constraint expression for the field.
@ ConstraintOriginProvider
Constraint was set at data provider.
@ ConstraintOriginLayer
Constraint was set by layer.
ConstraintStrength constraintStrength(Constraint constraint) const
Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint is not pres...
ConstraintOrigin constraintOrigin(Constraint constraint) const
Returns the origin of a field constraint, or ConstraintOriginNotSet if the constraint is not present ...
QString constraintExpression() const
Returns the constraint expression for the field, if set.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
void removeConstraint(Constraint constraint)
Removes a constraint from the field.
QString constraintDescription() const
Returns the descriptive name for the constraint expression.
void setConstraint(Constraint constraint, ConstraintOrigin origin=ConstraintOriginLayer)
Sets a constraint on the field.
QFlags< Constraint > Constraints
A widget for selection of layer fields or expression creation.
void setButtonVisible(bool visible)
Set the visibility of the button.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the field's split policy, which indicates how field values should be handled during a split o...
Definition qgsfield.cpp:761
QString alias
Definition qgsfield.h:63
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:771
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:64
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the field's merge policy, which indicates how field values should be handled during a merge o...
Definition qgsfield.cpp:781
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:95
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:210
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
Wraps a QQuickWidget to display HTML code.
void reinitWidget()
Clears the content and makes new initialization.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
void setFeature(const QgsFeature &feature) override
QString id
Definition qgsmaplayer.h:80
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
An expression with an additional enabled flag.
A relation where the referenced (parent) layer is calculated based on fields from the referencing (ch...
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
int count() const
Returns the number of properties contained within the collection.
Wraps a QQuickWidget to display QML code.
void setFeature(const QgsFeature &feature) override
void reinitWidget()
Clears the content and makes new initialization.
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QString name
Definition qgsrelation.h:50
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QString id
Definition qgsrelation.h:47
A QScrollArea subclass with improved scrolling behavior.
static const QgsSettingsEntryBool * settingsDigitizingDisableEnterAttributeValuesDialog
Settings entry digitizing disable enter attribute values dialog.
Stores settings for use within QGIS.
Definition qgssettings.h:66
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Wraps a label widget to display text.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
void setFeature(const QgsFeature &feature) override
Represents a vector layer which manages a vector based dataset.
QgsDefaultValue defaultValueDefinition(int index) const
Returns the definition of the expression used when calculating the default value for a field.
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
void setFieldMergePolicy(int index, Qgis::FieldDomainMergePolicy policy)
Sets a merge policy for the field with the specified index.
void setDefaultValueDefinition(int index, const QgsDefaultValue &definition)
Sets the definition of the expression to use when calculating the default value for a field.
void setEditFormConfig(const QgsEditFormConfig &editFormConfig)
Sets the editFormConfig (configuration) of the form used to represent this vector layer.
void setEditorWidgetSetup(int index, const QgsEditorWidgetSetup &setup)
Sets the editor widget setup for the field at the specified index.
void setConstraintExpression(int index, const QString &expression, const QString &description=QString())
Sets the constraint expression for the specified field index.
void setFieldDuplicatePolicy(int index, Qgis::FieldDuplicatePolicy policy)
Sets a duplicate policy for the field with the specified index.
QgsActionManager * actions()
Returns all layer actions defined on this layer.
void setFieldAlias(int index, const QString &aliasString)
Sets an alias (a display name) for attributes to display in dialogs.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig
void setFieldSplitPolicy(int index, Qgis::FieldDomainSplitPolicy policy)
Sets a split policy for the field with the specified index.
QWidget * widget()
Access the widget managed by this wrapper.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6496
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6477
QDataStream & operator>>(QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data)
QDataStream & operator<<(QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data)
#define QgsDebugError(str)
Definition qgslogger.h:40
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.
void readXml(const QDomNode &node)
Reads configuration from node.