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