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