QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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
48 : QWidget( parent )
49 , mLayer( layer )
50{
51 if ( !layer )
52 return;
53
54 setupUi( this );
55
56 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
57 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
58 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
59
60 // available widgets tree
61 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
63 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
64 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
65 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
66 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
67 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
69
70 // form layout tree
71 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
73 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
74 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
75 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
76 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
78
79 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
80 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
81 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
82 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
83 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
84 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
85 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
86 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
87
88 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
89 if ( !mBlockUpdates )
90 updatedFields();
91 } );
92}
93
103
105{
106 mAvailableWidgetsTree->clear();
107 mAvailableWidgetsTree->setSortingEnabled( false );
108 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
109 mAvailableWidgetsTree->setAcceptDrops( false );
110 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
111
112 //load Fields
113
114 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
115 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
116
117 const QgsFields fields = mLayer->fields();
118 for ( int i = 0; i < fields.size(); ++i )
119 {
120 const QgsField field = fields.at( i );
121 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
122 itemData.setShowLabel( true );
123
124 FieldConfig cfg( mLayer, i );
125
126 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
127
128 item->setData( 0, FieldConfigRole, cfg );
129 item->setData( 0, FieldNameRole, field.name() );
130
131 QString tooltip;
132 if ( !field.alias().isEmpty() )
133 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
134 else
135 tooltip = field.name();
136 item->setToolTip( 0, tooltip );
137 }
138 catitem->setExpanded( true );
139
140 //load Relations
141 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
142 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
143
144 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
145
146 for ( const QgsRelation &relation : relations )
147 {
148 QString name;
149 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
150 if ( polymorphicRelation.isValid() )
151 {
152 name = QStringLiteral( "%1 (%2)" ).arg( relation.name(), polymorphicRelation.name() );
153 }
154 else
155 {
156 name = relation.name();
157 }
158 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), name );
159 itemData.setShowLabel( true );
160 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
161 item->setData( 0, FieldNameRole, relation.id() );
162 }
163 catitem->setExpanded( true );
164
165 // Form actions
166 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
167 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
168
169 const QList<QgsAction> actions { mLayer->actions()->actions() };
170
171 for ( const auto &action : std::as_const( actions ) )
172 {
173 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
174 {
175 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
176 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
177 itemData.setShowLabel( true );
178 mAvailableWidgetsTree->addItem( catitem, itemData );
179 }
180 }
181
182 // QML/HTML widget
183 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
184 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
185
186 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
187 itemData.setShowLabel( true );
188 mAvailableWidgetsTree->addItem( catitem, itemData );
189
190 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
191 itemDataHtml.setShowLabel( true );
192 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
193
194 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
195 itemDataText.setShowLabel( true );
196 mAvailableWidgetsTree->addItem( catitem, itemDataText );
197
198 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
199 itemDataSpacer.setShowLabel( false );
200 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
201
202 catitem->setExpanded( true );
203}
204
206{
207 // tabs and groups info
208 mFormLayoutTree->clear();
209 mFormLayoutTree->setSortingEnabled( false );
210 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
211 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
212 mFormLayoutTree->setAcceptDrops( true );
213 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
214
215 const auto constTabs = mLayer->editFormConfig().tabs();
216 for ( QgsAttributeEditorElement *wdg : constTabs )
217 {
218 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
219 }
220}
221
222
224{
226 {
227 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
228 }
229 else
230 {
231 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
232 }
233 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
234 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
235
236 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
237}
238
245
247{
248 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
249
250 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
251
253 mEditFormLineEdit->setText( cfg.uiForm() );
254}
255
257{
259
260 mInitCodeSource = cfg.initCodeSource();
261 mInitFunction = cfg.initFunction();
262 mInitFilePath = cfg.initFilePath();
263 mInitCode = cfg.initCode();
264
265 if ( mInitCode.isEmpty() )
266 {
267 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
268 "QGIS forms can have a Python function that is called when the form is\n"
269 "opened.\n"
270 "\n"
271 "Use this function to add extra logic to your forms.\n"
272 "\n"
273 "Enter the name of the function in the \"Python Init function\"\n"
274 "field.\n"
275 "An example follows:\n"
276 "\"\"\"\n"
277 "from qgis.PyQt.QtWidgets import QWidget\n\n"
278 "def my_form_open(dialog, layer, feature):\n"
279 " geom = feature.geometry()\n"
280 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
281 }
282}
283
284void QgsAttributesFormProperties::loadAttributeTypeDialog()
285{
286 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
287 return;
288
289 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
290
291 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
292 const QString fieldName = item->data( 0, FieldNameRole ).toString();
293 const int index = mLayer->fields().indexOf( fieldName );
294
295 if ( index < 0 )
296 return;
297
298 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
299
300 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
301
302 mAttributeTypeDialog->setAlias( cfg.mAlias );
303 mAttributeTypeDialog->setDataDefinedProperties( cfg.mDataDefinedProperties );
304 mAttributeTypeDialog->setComment( cfg.mComment );
305 mAttributeTypeDialog->setFieldEditable( cfg.mEditable );
306 mAttributeTypeDialog->setLabelOnTop( cfg.mLabelOnTop );
307 mAttributeTypeDialog->setReuseLastValues( cfg.mReuseLastValues );
312 mAttributeTypeDialog->setSplitPolicy( cfg.mSplitPolicy );
313 mAttributeTypeDialog->setDuplicatePolicy( cfg.mDuplicatePolicy );
314
317 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
319 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
321 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
322 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
323
324 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
325 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
327 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
328 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
329
330 mAttributeTypeDialog->setEditorWidgetConfig( cfg.mEditorWidgetConfig );
331 mAttributeTypeDialog->setEditorWidgetType( cfg.mEditorWidgetType );
332
333 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
334 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
335
336 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
337}
338
339
340void QgsAttributesFormProperties::storeAttributeTypeDialog()
341{
343 return;
344
345 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
346 return;
347
348 FieldConfig cfg;
349
350 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
351 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
352 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
353 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
354 cfg.mAlias = mAttributeTypeDialog->alias();
355 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
356
357 QgsFieldConstraints constraints;
358 if ( mAttributeTypeDialog->notNull() )
359 {
361 }
362 else if ( mAttributeTypeDialog->notNullFromProvider() )
363 {
365 }
366
367 if ( mAttributeTypeDialog->unique() )
368 {
370 }
371 else if ( mAttributeTypeDialog->uniqueFromProvider() )
372 {
374 }
375
376 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
377 {
379 }
380
381 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
382
386
387 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
388 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
389 cfg.mFieldConstraints = constraints;
390 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
391 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
392 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
393 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
394
395 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
396 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
397
398 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
399
400 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
401 {
402 QTreeWidgetItem *item = *itemIt;
403 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
404 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
405 }
406}
407
408void QgsAttributesFormProperties::storeAttributeWidgetEdit()
409{
411 return;
412
413 mAttributeWidgetEdit->updateItemData();
414}
415
416void QgsAttributesFormProperties::loadAttributeWidgetEdit()
417{
418 if ( mFormLayoutTree->selectedItems().count() != 1 )
419 return;
420
421 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
422 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
423 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
424 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
425}
426
427void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
428{
429 mInfoTextWidget = new QLabel( infoText );
430 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
431 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
432}
433
434void QgsAttributesFormProperties::storeAttributeContainerEdit()
435{
437 return;
438
439 mAttributeContainerEdit->updateItemData();
440}
441
442void QgsAttributesFormProperties::loadAttributeContainerEdit()
443{
444 if ( mFormLayoutTree->selectedItems().count() != 1 )
445 return;
446
447 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
448 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
449 mAttributeContainerEdit->registerExpressionContextGenerator( this );
450 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
451 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
452 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
453}
454
455QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
456{
457 auto setCommonProperties = [widgetDef]( DnDTreeItemData &itemData ) {
458 itemData.setShowLabel( widgetDef->showLabel() );
459 itemData.setLabelStyle( widgetDef->labelStyle() );
460 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
461 itemData.setVerticalStretch( widgetDef->verticalStretch() );
462 };
463
464 QTreeWidgetItem *newWidget = nullptr;
465 switch ( widgetDef->type() )
466 {
468 {
469 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
470 setCommonProperties( itemData );
471 newWidget = tree->addItem( parent, itemData );
472 break;
473 }
474
476 {
477 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
478 const QgsAction action { actionEditor->action( mLayer ) };
479 if ( action.isValid() )
480 {
481 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
482 setCommonProperties( itemData );
483 newWidget = tree->addItem( parent, itemData );
484 }
485 else
486 {
487 QgsDebugError( QStringLiteral( "Invalid form action" ) );
488 }
489 break;
490 }
491
493 {
494 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
495 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
496 setCommonProperties( itemData );
497
498 RelationEditorConfiguration relEdConfig;
499 // relEdConfig.buttons = relationEditor->visibleButtons();
500 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
501 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
502 relEdConfig.nmRelationId = relationEditor->nmRelationId();
503 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
504 relEdConfig.label = relationEditor->label();
505 itemData.setRelationEditorConfiguration( relEdConfig );
506 newWidget = tree->addItem( parent, itemData );
507 break;
508 }
509
511 {
512 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
513
514 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
515 if ( !container )
516 break;
517
518 itemData.setColumnCount( container->columnCount() );
519 itemData.setContainerType( container->type() );
520 itemData.setBackgroundColor( container->backgroundColor() );
521 itemData.setVisibilityExpression( container->visibilityExpression() );
522 itemData.setCollapsedExpression( container->collapsedExpression() );
523 itemData.setCollapsed( container->collapsed() );
524
525 setCommonProperties( itemData );
526
527 newWidget = tree->addItem( parent, itemData );
528
529 const QList<QgsAttributeEditorElement *> children = container->children();
530 for ( QgsAttributeEditorElement *wdg : children )
531 {
532 loadAttributeEditorTreeItem( wdg, newWidget, tree );
533 }
534 break;
535 }
536
538 {
539 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
540 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
541 QmlElementEditorConfiguration qmlEdConfig;
542 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
543 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
544 setCommonProperties( itemData );
545 newWidget = tree->addItem( parent, itemData );
546 break;
547 }
548
550 {
551 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
552 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
553 HtmlElementEditorConfiguration htmlEdConfig;
554 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
555 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
556 setCommonProperties( itemData );
557 newWidget = tree->addItem( parent, itemData );
558 break;
559 }
560
562 {
563 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
564 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
565 TextElementEditorConfiguration textEdConfig;
566 textEdConfig.text = textElementEditor->text();
567 itemData.setTextElementEditorConfiguration( textEdConfig );
568 setCommonProperties( itemData );
569 newWidget = tree->addItem( parent, itemData );
570 break;
571 }
572
574 {
575 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
576 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
577 SpacerElementEditorConfiguration spacerEdConfig;
578 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
579 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
580 setCommonProperties( itemData );
581 itemData.setShowLabel( false );
582 newWidget = tree->addItem( parent, itemData );
583 break;
584 }
585
587 {
588 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
589 break;
590 }
591 }
592
593 if ( newWidget )
594 newWidget->setExpanded( true );
595
596 return newWidget;
597}
598
599
600void QgsAttributesFormProperties::onAttributeSelectionChanged()
601{
602 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
603 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
604 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
605}
606
607void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
608{
609 // when the selection changes in the DnD layout, sync the main tree
610 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
611 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
612 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
613}
614
615void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
616{
617 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
618
620 storeAttributeWidgetEdit();
621
622 clearAttributeTypeFrame();
623
624 if ( emitter->selectedItems().count() != 1 )
625 {
626 receiver->clearSelection();
627 }
628 else
629 {
630 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
631 switch ( itemData.type() )
632 {
634 {
635 receiver->selectFirstMatchingItem( itemData );
637 {
638 loadAttributeWidgetEdit();
639 }
640 else
641 {
642 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
643 }
644 break;
645 }
647 {
648 receiver->selectFirstMatchingItem( itemData );
650 loadAttributeWidgetEdit();
651 loadAttributeTypeDialog();
652 break;
653 }
655 {
656 receiver->clearSelection();
657 loadAttributeContainerEdit();
658 break;
659 }
661 {
662 receiver->selectFirstMatchingItem( itemData );
663 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
664 loadInfoWidget( action.html() );
665 break;
666 }
671 {
673 {
674 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
675 }
676 else
677 {
678 loadInfoWidget( tr( "This configuration is available with double-click" ) );
679 }
680 receiver->clearSelection();
681 break;
682 }
684 {
685 receiver->clearSelection();
686 break;
687 }
688 }
689 }
690}
691
692void QgsAttributesFormProperties::clearAttributeTypeFrame()
693{
695 {
696 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
697 mAttributeWidgetEdit->deleteLater();
698 mAttributeWidgetEdit = nullptr;
699 }
701 {
702 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
703 mAttributeTypeDialog->deleteLater();
704 mAttributeTypeDialog = nullptr;
705 }
707 {
708 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
709 mAttributeContainerEdit->deleteLater();
710 mAttributeContainerEdit = nullptr;
711 }
712 if ( mInfoTextWidget )
713 {
714 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
715 mInfoTextWidget->deleteLater();
716 mInfoTextWidget = nullptr;
717 }
718}
719
720void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
721{
722 Q_UNUSED( checked )
723 const auto selectedItemList { mFormLayoutTree->selectedItems() };
724 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
725 for ( int i = 0; i < rootItem->childCount(); ++i )
726 {
727 rootItem->child( i )->setSelected( !selectedItemList.contains( rootItem->child( i ) ) );
728 }
729}
730
731void QgsAttributesFormProperties::addContainer()
732{
733 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
734
735 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
736 {
737 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
738 if ( itemData.type() == DnDTreeItemData::Container )
739 {
740 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
741 }
742 }
743 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
744 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
745
746 if ( !dialog.exec() )
747 return;
748
749 const QString name = dialog.name();
750 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
751 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(), name, dialog.columnCount(), dialog.containerType() );
752}
753
754void QgsAttributesFormProperties::removeTabOrGroupButton()
755{
756 // deleting an item may delete any number of nested child items -- so we delete
757 // them one at a time and then see if there's any selection left
758 while ( true )
759 {
760 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
761 if ( items.empty() )
762 break;
763
764 delete items.at( 0 );
765 }
766}
767
769{
770 QgsAttributeEditorElement *widgetDef = nullptr;
771
772 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
773
774 switch ( itemData.type() )
775 {
776 //indexed here?
778 {
779 const int idx = mLayer->fields().lookupField( itemData.name() );
780 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
781 break;
782 }
783
785 {
786 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
787 widgetDef = new QgsAttributeEditorAction( action, parent );
788 break;
789 }
790
792 {
793 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
794
795 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
797 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
798 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
799 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
800 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
801 relDef->setLabel( relationEditorConfig.label );
802 widgetDef = relDef;
803 break;
804 }
805
807 {
808 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
809 container->setColumnCount( itemData.columnCount() );
810 // only top-level containers can be tabs
812 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
813 {
814 // a top container found which isn't at the top level -- reset it to a group box instead
816 }
817 container->setType( type );
818 container->setCollapsed( itemData.collapsed() );
819 container->setCollapsedExpression( itemData.collapsedExpression() );
820 container->setVisibilityExpression( itemData.visibilityExpression() );
821 container->setBackgroundColor( itemData.backgroundColor() );
822
823 for ( int t = 0; t < item->childCount(); t++ )
824 {
825 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
826 if ( element )
827 container->addChildElement( element );
828 }
829
830 widgetDef = container;
831 break;
832 }
833
835 {
836 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
837 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
838 widgetDef = element;
839 break;
840 }
841
843 {
844 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
846 widgetDef = element;
847 break;
848 }
849
851 {
852 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
853 element->setText( itemData.textElementEditorConfiguration().text );
854 widgetDef = element;
855 break;
856 }
857
859 {
860 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
862 widgetDef = element;
863 break;
864 }
865
867 break;
868 }
869
870 if ( widgetDef )
871 {
872 widgetDef->setShowLabel( itemData.showLabel() );
873 widgetDef->setLabelStyle( itemData.labelStyle() );
874 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
875 widgetDef->setVerticalStretch( itemData.verticalStretch() );
876 }
877
878 return widgetDef;
879}
880
881void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
882{
883 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
884 switch ( layout )
885 {
887 mFormLayoutWidget->setVisible( false );
888 mUiFileFrame->setVisible( false );
889 mAddTabOrGroupButton->setVisible( false );
890 mRemoveTabOrGroupButton->setVisible( false );
891 mInvertSelectionButton->setVisible( false );
892 break;
893
895 mFormLayoutWidget->setVisible( true );
896 mUiFileFrame->setVisible( false );
897 mAddTabOrGroupButton->setVisible( true );
898 mRemoveTabOrGroupButton->setVisible( true );
899 mInvertSelectionButton->setVisible( true );
900 break;
901
903 // ui file
904 mFormLayoutWidget->setVisible( false );
905 mUiFileFrame->setVisible( true );
906 mAddTabOrGroupButton->setVisible( false );
907 mRemoveTabOrGroupButton->setVisible( false );
908 mInvertSelectionButton->setVisible( false );
909 break;
910 }
911}
912
913void QgsAttributesFormProperties::mTbInitCode_clicked()
914{
915 QgsAttributesFormInitCode attributesFormInitCode;
916
917 attributesFormInitCode.setCodeSource( mInitCodeSource );
918 attributesFormInitCode.setInitCode( mInitCode );
919 attributesFormInitCode.setInitFilePath( mInitFilePath );
920 attributesFormInitCode.setInitFunction( mInitFunction );
921
922 if ( !attributesFormInitCode.exec() )
923 return;
924
925 mInitCodeSource = attributesFormInitCode.codeSource();
926 mInitCode = attributesFormInitCode.initCode();
927 mInitFilePath = attributesFormInitCode.initFilePath();
928 mInitFunction = attributesFormInitCode.initFunction();
929}
930
931void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
932{
933 QgsSettings myQSettings;
934 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
935 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
936
937 if ( uifilename.isNull() )
938 return;
939
940 const QFileInfo fi( uifilename );
941 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
942 mEditFormLineEdit->setText( uifilename );
943}
944
946{
947 storeAttributeWidgetEdit();
948 storeAttributeContainerEdit();
949 storeAttributeTypeDialog();
950}
951
953{
954 mBlockUpdates++;
955 storeAttributeWidgetEdit();
956 storeAttributeContainerEdit();
957 storeAttributeTypeDialog();
958
959 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
960
961 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
962
963 for ( int i = 0; i < fieldContainer->childCount(); i++ )
964 {
965 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
966 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
967
968 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
969 const int idx = mLayer->fields().indexOf( fieldName );
970
971 //continue in case field does not exist anymore
972 if ( idx < 0 )
973 continue;
974
975 editFormConfig.setReadOnly( idx, !cfg.mEditable );
976 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
977 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
978
979 if ( cfg.mDataDefinedProperties.count() > 0 )
980 {
981 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
982 }
983
985
986 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
987 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
989 {
991 }
992 else
993 {
995 }
997 {
999 }
1000 else
1001 {
1003 }
1005 {
1007 }
1008 else
1009 {
1011 }
1012
1013 mLayer->setFieldAlias( idx, cfg.mAlias );
1016 }
1017
1018 // tabs and groups
1019 editFormConfig.clearTabs();
1020 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1021 {
1022 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1023 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1024 if ( editorElement )
1025 editFormConfig.addTab( editorElement );
1026 }
1027
1028 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1029
1030 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
1031
1032 editFormConfig.setInitCodeSource( mInitCodeSource );
1033 editFormConfig.setInitFunction( mInitFunction );
1034 editFormConfig.setInitFilePath( mInitFilePath );
1035 editFormConfig.setInitCode( mInitCode );
1036
1037 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
1038
1039 // write the legacy config of relation widgets to support settings read by the API
1040 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1041
1042 for ( int i = 0; i < relationContainer->childCount(); i++ )
1043 {
1044 QTreeWidgetItem *relationItem = relationContainer->child( i );
1045 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1046
1047 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1048 {
1049 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1050 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1051
1052 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1053 {
1054 QVariantMap cfg;
1055
1056 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1057 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1058
1059 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1060 break;
1061 }
1062 }
1063 }
1064
1065 mLayer->setEditFormConfig( editFormConfig );
1066 mBlockUpdates--;
1067}
1068
1069
1070/*
1071 * FieldConfig implementation
1072 */
1074{
1075 mAlias = layer->fields().at( idx ).alias();
1077 mComment = layer->fields().at( idx ).comment();
1078 mEditable = !layer->editFormConfig().readOnly( idx );
1079 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1081 mFieldConstraints = layer->fields().at( idx ).constraints();
1082 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1083 mEditorWidgetType = setup.type();
1084 mEditorWidgetConfig = setup.config();
1085 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1086 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1087}
1088
1089QgsAttributesFormProperties::FieldConfig::operator QVariant()
1090{
1091 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1092}
1093
1094/*
1095 * RelationEditorConfiguration implementation
1096 */
1097
1098QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1099{
1100 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1101}
1102
1103/*
1104 * DnDTree implementation
1105 */
1106
1107QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1108{
1109 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1110 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1111 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1113 itemData.setColumnCount( columnCount );
1114 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1115 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1116 parent->addChild( newItem );
1117 newItem->setExpanded( true );
1118 return newItem;
1119}
1120
1122 : QTreeWidget( parent )
1123 , mLayer( layer )
1124{
1125 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1126}
1127
1128QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon )
1129{
1130 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1131
1132 switch ( data.type() )
1133 {
1141 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1142 break;
1143
1146 {
1147 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1148 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1149 }
1150 break;
1151 }
1152
1153 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) );
1154 newItem->setText( 0, data.displayName() );
1155 newItem->setIcon( 0, icon );
1156
1158 {
1159 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( data.name() );
1160 if ( !relation.isValid() || relation.referencedLayer() != mLayer )
1161 {
1162 newItem->setText( 0, tr( "Invalid relation" ) );
1163 newItem->setForeground( 0, QColor( 255, 0, 0 ) );
1164 }
1165 }
1166
1167 if ( index < 0 )
1168 parent->addChild( newItem );
1169 else
1170 parent->insertChild( index, newItem );
1171
1172 return newItem;
1173}
1174
1180void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1181{
1182 const QMimeData *data = event->mimeData();
1183
1184 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1185 {
1187
1188 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1189 QDataStream stream( &itemData, QIODevice::ReadOnly );
1190 stream >> itemElement;
1191
1192 // Inner drag and drop actions are always MoveAction
1193 if ( event->source() == this )
1194 {
1195 event->setDropAction( Qt::MoveAction );
1196 }
1197 }
1198 else
1199 {
1200 event->ignore();
1201 }
1202
1203 QTreeWidget::dragMoveEvent( event );
1204}
1205
1206
1207bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1208{
1209 bool bDropSuccessful = false;
1210
1211 if ( action == Qt::IgnoreAction )
1212 {
1213 bDropSuccessful = true;
1214 }
1215 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1216 {
1217 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1218 QDataStream stream( &itemData, QIODevice::ReadOnly );
1220
1221 while ( !stream.atEnd() )
1222 {
1223 stream >> itemElement;
1224
1225 QTreeWidgetItem *newItem;
1226
1227 if ( parent )
1228 {
1229 newItem = addItem( parent, itemElement, index++ );
1230 bDropSuccessful = true;
1231 }
1232 else
1233 {
1234 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1235 bDropSuccessful = true;
1236 }
1237
1239 {
1240 onItemDoubleClicked( newItem, 0 );
1241 }
1242
1244 {
1245 onItemDoubleClicked( newItem, 0 );
1246 }
1247
1249 {
1250 onItemDoubleClicked( newItem, 0 );
1251 }
1252
1254 {
1255 onItemDoubleClicked( newItem, 0 );
1256 }
1257
1258 clearSelection();
1259 newItem->setSelected( true );
1260 }
1261 }
1262
1263 return bDropSuccessful;
1264}
1265
1266void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1267{
1268 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1269 return;
1270
1271 if ( event->source() == this )
1272 {
1273 event->setDropAction( Qt::MoveAction );
1274 }
1275
1276 QTreeWidget::dropEvent( event );
1277}
1278
1280{
1281 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1282}
1283
1284#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1285QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1286#else
1287QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1288#endif
1289{
1290 if ( items.count() <= 0 )
1291 return nullptr;
1292
1293 const QStringList types = mimeTypes();
1294
1295 if ( types.isEmpty() )
1296 return nullptr;
1297
1298 QMimeData *data = new QMimeData();
1299 const QString format = types.at( 0 );
1300 QByteArray encoded;
1301 QDataStream stream( &encoded, QIODevice::WriteOnly );
1302
1303 const auto constItems = items;
1304 for ( const QTreeWidgetItem *item : constItems )
1305 {
1306 if ( item )
1307 {
1308 // Relevant information is always in the DnDTreeRole of the first column
1310 stream << itemData;
1311 }
1312 }
1313
1314 data->setData( format, encoded );
1315
1316 return data;
1317}
1318
1319void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1320{
1321 Q_UNUSED( column )
1322
1324
1325 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1326
1327 QFormLayout *baseLayout = new QFormLayout();
1328 baseData->setLayout( baseLayout );
1329 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1330 showLabelCheckbox->setChecked( itemData.showLabel() );
1331 baseLayout->addRow( showLabelCheckbox );
1332 QWidget *baseWidget = new QWidget();
1333 baseWidget->setLayout( baseLayout );
1334
1335 switch ( itemData.type() )
1336 {
1342 break;
1343
1345 {
1346 if ( mType == QgsAttributesDnDTree::Type::Drag )
1347 return;
1348
1349 QDialog dlg;
1350 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1351
1352 QVBoxLayout *mainLayout = new QVBoxLayout();
1353 QHBoxLayout *qmlLayout = new QHBoxLayout();
1354 QVBoxLayout *layout = new QVBoxLayout();
1355 mainLayout->addLayout( qmlLayout );
1356 qmlLayout->addLayout( layout );
1357 dlg.setLayout( mainLayout );
1358 layout->addWidget( baseWidget );
1359
1360 QLineEdit *title = new QLineEdit( itemData.name() );
1361
1362 //qmlCode
1363 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1364 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1365
1366 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1367 QgsFeature previewFeature;
1368 mLayer->getFeatures().nextFeature( previewFeature );
1369
1370 //update preview on text change
1371 connect( qmlCode, &QsciScintilla::textChanged, this, [=] {
1372 qmlWrapper->setQmlCode( qmlCode->text() );
1373 qmlWrapper->reinitWidget();
1374 qmlWrapper->setFeature( previewFeature );
1375 } );
1376
1377 //templates
1378 QComboBox *qmlObjectTemplate = new QComboBox();
1379 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1380 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1381 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1382 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1383 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [=]( int index ) {
1384 qmlCode->clear();
1385 switch ( index )
1386 {
1387 case 0:
1388 {
1389 qmlCode->setText( QString() );
1390 break;
1391 }
1392 case 1:
1393 {
1394 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1395 "\n"
1396 "Rectangle {\n"
1397 " width: 100\n"
1398 " height: 100\n"
1399 " color: \"steelblue\"\n"
1400 " Text{ text: \"A rectangle\" }\n"
1401 "}\n" ) );
1402 break;
1403 }
1404 case 2:
1405 {
1406 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1407 "import QtCharts 2.0\n"
1408 "\n"
1409 "ChartView {\n"
1410 " width: 400\n"
1411 " height: 400\n"
1412 "\n"
1413 " PieSeries {\n"
1414 " id: pieSeries\n"
1415 " PieSlice { label: \"First slice\"; value: 25 }\n"
1416 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1417 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1418 " }\n"
1419 "}\n" ) );
1420 break;
1421 }
1422 case 3:
1423 {
1424 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1425 "import QtCharts 2.0\n"
1426 "\n"
1427 "ChartView {\n"
1428 " title: \"Bar series\"\n"
1429 " width: 600\n"
1430 " height:400\n"
1431 " legend.alignment: Qt.AlignBottom\n"
1432 " antialiasing: true\n"
1433 " ValueAxis{\n"
1434 " id: valueAxisY\n"
1435 " min: 0\n"
1436 " max: 15\n"
1437 " }\n"
1438 "\n"
1439 " BarSeries {\n"
1440 " id: mySeries\n"
1441 " axisY: valueAxisY\n"
1442 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1443 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1444 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1445 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1446 " }\n"
1447 "}\n" ) );
1448 break;
1449 }
1450 default:
1451 break;
1452 }
1453 } );
1454
1455 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1456 expressionWidget->setButtonVisible( false );
1457 expressionWidget->registerExpressionContextGenerator( this );
1458 expressionWidget->setLayer( mLayer );
1459 QToolButton *addFieldButton = new QToolButton();
1460 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1461
1462 QToolButton *editExpressionButton = new QToolButton();
1463 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1464 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1465
1466 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1467 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1468 if ( !expression.isEmpty() )
1469 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1470 } );
1471
1472 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1473 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1474 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1476 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1477
1478 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1479 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1480 {
1481 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1482 if ( !expression.isEmpty() )
1483 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1484 }
1485 } );
1486
1487 layout->addWidget( new QLabel( tr( "Title" ) ) );
1488 layout->addWidget( title );
1489 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1490 qmlCodeBox->setLayout( new QVBoxLayout );
1491 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1492 QWidget *expressionWidgetBox = new QWidget();
1493 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1494 expressionWidgetBox->setLayout( new QHBoxLayout );
1495 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1496 expressionWidgetBox->layout()->addWidget( expressionWidget );
1497 expressionWidgetBox->layout()->addWidget( addFieldButton );
1498 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1499 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1500 layout->addWidget( qmlCodeBox );
1501 layout->addWidget( qmlCode );
1502 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1503 qmlPreviewBox->setLayout( new QGridLayout );
1504 qmlPreviewBox->setMinimumWidth( 400 );
1505 qmlPreviewBox->layout()->addWidget( qmlWrapper->widget() );
1506 //emit to load preview for the first time
1507 emit qmlCode->textChanged();
1508 qmlLayout->addWidget( qmlPreviewBox );
1509
1510 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1511
1512 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1513 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1514
1515 mainLayout->addWidget( buttonBox );
1516
1517 if ( dlg.exec() )
1518 {
1520 qmlEdCfg.qmlCode = qmlCode->text();
1521 itemData.setName( title->text() );
1522 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1523 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1524
1525 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1526 item->setText( 0, title->text() );
1527 }
1528 }
1529 break;
1530
1532 {
1533 if ( mType == QgsAttributesDnDTree::Type::Drag )
1534 return;
1535 QDialog dlg;
1536 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1537
1538 QVBoxLayout *mainLayout = new QVBoxLayout();
1539 QHBoxLayout *htmlLayout = new QHBoxLayout();
1540 QVBoxLayout *layout = new QVBoxLayout();
1541 mainLayout->addLayout( htmlLayout );
1542 htmlLayout->addLayout( layout );
1543 dlg.setLayout( mainLayout );
1544 layout->addWidget( baseWidget );
1545
1546 QLineEdit *title = new QLineEdit( itemData.name() );
1547
1548 //htmlCode
1549 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML();
1550 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1551 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1552
1553 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1554 QgsFeature previewFeature;
1555 mLayer->getFeatures().nextFeature( previewFeature );
1556
1557 //update preview on text change
1558 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [=] {
1559 htmlWrapper->setHtmlCode( htmlCode->text() );
1560 htmlWrapper->reinitWidget();
1561 htmlWrapper->setFeature( previewFeature );
1562 } );
1563
1564 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1565 expressionWidget->setButtonVisible( false );
1566 expressionWidget->registerExpressionContextGenerator( this );
1567 expressionWidget->setLayer( mLayer );
1568 QToolButton *addFieldButton = new QToolButton();
1569 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1570
1571 QToolButton *editExpressionButton = new QToolButton();
1572 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1573 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1574
1575 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1576 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1577 if ( !expression.isEmpty() )
1578 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1579 } );
1580
1581 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1582 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1583 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1585 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1586
1587 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1588 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1589 {
1590 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1591 if ( !expression.isEmpty() )
1592 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1593 }
1594 } );
1595
1596 layout->addWidget( new QLabel( tr( "Title" ) ) );
1597 layout->addWidget( title );
1598 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1599 layout->addWidget( expressionWidgetBox );
1600 expressionWidgetBox->setLayout( new QHBoxLayout );
1601 expressionWidgetBox->layout()->addWidget( expressionWidget );
1602 expressionWidgetBox->layout()->addWidget( addFieldButton );
1603 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1604 layout->addWidget( htmlCode );
1605 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1606 htmlPreviewBox->setLayout( new QGridLayout );
1607 htmlPreviewBox->setMinimumWidth( 400 );
1608 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1609 //emit to load preview for the first time
1610 emit htmlCode->textChanged();
1611 htmlLayout->addWidget( htmlPreviewBox );
1612
1613 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1614
1615 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1616 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1617
1618 mainLayout->addWidget( buttonBox );
1619
1620 if ( dlg.exec() )
1621 {
1623 htmlEdCfg.htmlCode = htmlCode->text();
1624 itemData.setName( title->text() );
1625 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1626 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1627
1628 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1629 item->setText( 0, title->text() );
1630 }
1631 break;
1632 }
1633
1635 {
1636 if ( mType == QgsAttributesDnDTree::Type::Drag )
1637 return;
1638 QDialog dlg;
1639 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1640
1641 QVBoxLayout *mainLayout = new QVBoxLayout();
1642 QHBoxLayout *textLayout = new QHBoxLayout();
1643 QVBoxLayout *layout = new QVBoxLayout();
1644 mainLayout->addLayout( textLayout );
1645 textLayout->addLayout( layout );
1646 dlg.setLayout( mainLayout );
1647 layout->addWidget( baseWidget );
1648
1649 QLineEdit *title = new QLineEdit( itemData.name() );
1650
1652 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1653 text->setText( itemData.textElementEditorConfiguration().text );
1654
1655 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1656 QgsFeature previewFeature;
1657 mLayer->getFeatures().nextFeature( previewFeature );
1658
1659 //update preview on text change
1660 connect( text, &QgsCodeEditorExpression::textChanged, this, [=] {
1661 textWrapper->setText( text->text() );
1662 textWrapper->reinitWidget();
1663 textWrapper->setFeature( previewFeature );
1664 } );
1665
1666 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1667 expressionWidget->setButtonVisible( false );
1668 expressionWidget->registerExpressionContextGenerator( this );
1669 expressionWidget->setLayer( mLayer );
1670 QToolButton *addFieldButton = new QToolButton();
1671 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1672
1673 QToolButton *editExpressionButton = new QToolButton();
1674 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1675 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1676
1677 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1678 QString expression = expressionWidget->expression().trimmed();
1679 if ( !expression.isEmpty() )
1680 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1681 } );
1682 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1683 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1684
1686 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1687
1688 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1689 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1690 {
1691 QString expression = exprDlg.expressionText().trimmed();
1692 if ( !expression.isEmpty() )
1693 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1694 }
1695 } );
1696
1697 layout->addWidget( new QLabel( tr( "Title" ) ) );
1698 layout->addWidget( title );
1699 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1700 layout->addWidget( expressionWidgetBox );
1701 expressionWidgetBox->setLayout( new QHBoxLayout );
1702 expressionWidgetBox->layout()->addWidget( expressionWidget );
1703 expressionWidgetBox->layout()->addWidget( addFieldButton );
1704 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1705 layout->addWidget( text );
1706 QScrollArea *textPreviewBox = new QgsScrollArea();
1707 textPreviewBox->setLayout( new QGridLayout );
1708 textPreviewBox->setMinimumWidth( 400 );
1709 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1710 //emit to load preview for the first time
1711 emit text->textChanged();
1712 textLayout->addWidget( textPreviewBox );
1713
1714 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1715
1716 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1717 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1718
1719 mainLayout->addWidget( buttonBox );
1720
1721 if ( dlg.exec() )
1722 {
1724 textEdCfg.text = text->text();
1725 itemData.setName( title->text() );
1726 itemData.setTextElementEditorConfiguration( textEdCfg );
1727 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1728
1729 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1730 item->setText( 0, title->text() );
1731 }
1732 break;
1733 }
1734
1736 {
1737 if ( mType == QgsAttributesDnDTree::Type::Drag )
1738 return;
1739 QDialog dlg;
1740 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1741
1742 QVBoxLayout *mainLayout = new QVBoxLayout();
1743 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1744 QLineEdit *title = new QLineEdit( itemData.name() );
1745 mainLayout->addWidget( title );
1746
1747 QHBoxLayout *cbLayout = new QHBoxLayout();
1748 mainLayout->addLayout( cbLayout );
1749 dlg.setLayout( mainLayout );
1750 QCheckBox *cb = new QCheckBox { &dlg };
1751 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1752 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1753 cbLayout->addWidget( cb );
1754
1755
1756 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1757
1758 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1759 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1760
1761 mainLayout->addWidget( buttonBox );
1762
1763 if ( dlg.exec() )
1764 {
1766 spacerEdCfg.drawLine = cb->isChecked();
1767 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1768 itemData.setShowLabel( false );
1769 itemData.setName( title->text() );
1770 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1771 item->setText( 0, title->text() );
1772 }
1773
1774 break;
1775 }
1776 }
1777}
1778
1780{
1781 QgsExpressionContext expContext;
1784
1785 if ( mLayer )
1786 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1787
1789 return expContext;
1790}
1791
1793{
1794 return mType;
1795}
1796
1798{
1799 mType = value;
1800}
1801
1803{
1804 QTreeWidgetItemIterator it( this );
1805 while ( *it )
1806 {
1808 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1809 {
1810 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1811 {
1812 // the selection is already good
1813 }
1814 else
1815 {
1816 clearSelection();
1817 ( *it )->setSelected( true );
1818 }
1819 return;
1820 }
1821 ++it;
1822 }
1823 clearSelection();
1824}
1825
1826
1827/*
1828 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1829 */
1830
1831QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1832{
1833 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1834 return stream;
1835}
1836
1837QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1838{
1839 QString name;
1840 QString displayName;
1841 quint32 type;
1842
1843 stream >> type >> name >> displayName;
1844
1846 data.setName( name );
1847 data.setDisplayName( displayName );
1848
1849 return stream;
1850}
1851
1856
1861
1866
1868{
1869 mLabelStyle = labelStyle;
1870}
1871
1873{
1874 return mShowLabel;
1875}
1876
1878{
1879 mShowLabel = showLabel;
1880}
1881
1886
1888{
1889 mVisibilityExpression = visibilityExpression;
1890}
1891
1896
1898{
1899 mCollapsedExpression = collapsedExpression;
1900}
1901
1906
1908{
1909 mRelationEditorConfiguration = relationEditorConfiguration;
1910}
1911
1916
1918{
1919 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1920}
1921
1922
1927
1929{
1930 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1931}
1932
1937
1939{
1940 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
1941}
1942
1944{
1945 return mBackgroundColor;
1946}
1947
1949{
1950 mBackgroundColor = backgroundColor;
1951}
1952
1957
1959{
1960 mTextElementEditorConfiguration = textElementEditorConfiguration;
1961}
1962
1963void QgsAttributesFormProperties::updatedFields()
1964{
1965 // Store configuration to insure changes made are kept after refreshing the list
1966 QMap<QString, FieldConfig> fieldConfigs;
1967 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1968 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1969 {
1970 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1971 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1972 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1973 fieldConfigs[fieldName] = cfg;
1974 }
1975
1977
1978 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1979 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1980 {
1981 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1982 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1983 if ( fieldConfigs.contains( fieldName ) )
1984 {
1985 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
1986 }
1987 }
1988}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5144
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5174
@ 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:5159
@ 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.
@ 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.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
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.
This is 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.
This class 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.
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 insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
The QgsDefaultValue class 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.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
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.
@ 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.
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().
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
The QgsFieldExpressionWidget class creates a widget to choose fields and edit expressions It contains...
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:755
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:765
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
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
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
An expression with an additional enabled flag.
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
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
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.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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 data sets.
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 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.
QDataStream & operator>>(QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data)
QDataStream & operator<<(QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data)
#define QgsDebugError(str)
Definition qgslogger.h:38
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.