QGIS API Documentation 4.1.0-Master (009143bf4b4)
Loading...
Searching...
No Matches
qgsattributesformproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformproperties.cpp
3 ---------------------
4 begin : August 2017
5 copyright : (C) 2017 by David Signer
6 email : david at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsactionmanager.h"
19#include "qgsaddtaborgroup.h"
20#include "qgsapplication.h"
21#include "qgsattributedialog.h"
23#include "qgsattributeformcontaineredit.h"
27#include "qgsattributetypedialog.h"
28#include "qgsattributewidgetedit.h"
29#include "qgscodeeditor.h"
32#include "qgsfieldcombobox.h"
33#include "qgsgui.h"
37#include "qgsvectorlayerutils.h"
38#include "qgsxmlutils.h"
39
40#include <QString>
41
42#include "moc_qgsattributesformproperties.cpp"
43
44using namespace Qt::StringLiterals;
45
46#ifdef ENABLE_MODELTEST
47#include "modeltest.h"
48#endif
49
51 = new QgsSettingsEntryBool( u"show-aliases"_s, sTreeAttributesForm, false, u"Whether to show aliases (true) or names (false) in both the Available Widgets and the Form Layout panels."_s );
52
54 : QWidget( parent )
55 , mLayer( layer )
56 , mSourceFieldsProperties( sourceFieldsProperties )
57{
58 if ( !layer )
59 return;
60
61 setupUi( this );
62
63 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
64 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
65 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
66 mShowAliasesButton->setChecked( settingShowAliases->value() );
67
68 // available widgets tree
69 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
71 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsView );
72 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
73 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
74 mAvailableWidgetsView->setContextMenuPolicy( Qt::CustomContextMenu );
75
76 // we need a custom item delegate in order to draw indicators
77 mAvailableWidgetsView->setItemDelegate( new QgsAttributesFormTreeViewItemDelegate( mAvailableWidgetsView ) );
78 mAvailableWidgetsView->setStyle( new QgsAttributesFormTreeViewProxyStyle( mAvailableWidgetsView ) );
79
80 mAvailableWidgetsModel = new QgsAttributesAvailableWidgetsModel( mLayer, QgsProject().instance(), this );
81 mAvailableWidgetsProxyModel = new QgsAttributesFormProxyModel( this );
82 mAvailableWidgetsProxyModel->setAttributesFormSourceModel( mAvailableWidgetsModel );
83 mAvailableWidgetsProxyModel->setRecursiveFilteringEnabled( true );
84 mAvailableWidgetsView->setModel( mAvailableWidgetsProxyModel );
85
86 mConstraintIndicatorProviderAvailableWidgets = new QgsFieldConstraintIndicatorProvider( mAvailableWidgetsView ); // gets parented to the available widgets view
87 mDefaultValueIndicatorProviderAvailableWidgets = new QgsFieldDefaultValueIndicatorProvider( mAvailableWidgetsView ); // gets parented to the available widgets view
88
89#ifdef ENABLE_MODELTEST
90 new ModelTest( mAvailableWidgetsProxyModel, this );
91#endif
92
93 // form layout tree
94 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
96 formLayoutWidgetLayout->addWidget( mFormLayoutView );
97 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
98 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
99
100 // we need a custom item delegate in order to draw indicators
101 mFormLayoutView->setItemDelegate( new QgsAttributesFormTreeViewItemDelegate( mFormLayoutView ) );
102 mFormLayoutView->setStyle( new QgsAttributesFormTreeViewProxyStyle( mFormLayoutView ) );
103
104 mFormLayoutModel = new QgsAttributesFormLayoutModel( mLayer, QgsProject().instance(), this );
105 mFormLayoutProxyModel = new QgsAttributesFormProxyModel( this );
106 mFormLayoutProxyModel->setAttributesFormSourceModel( mFormLayoutModel );
107 mFormLayoutProxyModel->setRecursiveFilteringEnabled( true );
108 mFormLayoutView->setModel( mFormLayoutProxyModel );
109
110 mConstraintIndicatorProviderFormLayout = new QgsFieldConstraintIndicatorProvider( mFormLayoutView ); // gets parented to the form layout view
111 mDefaultValueIndicatorProviderFormLayout = new QgsFieldDefaultValueIndicatorProvider( mFormLayoutView ); // gets parented to the form layout view
112
113#ifdef ENABLE_MODELTEST
114 new ModelTest( mFormLayoutProxyModel, this );
115#endif
116
117 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
118 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
119
120 connect( mAvailableWidgetsView, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
121
122 connect( mAddContainerButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
123 connect( mRemoveLayoutItemButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
124 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
125 connect( mShowAliasesButton, &QAbstractButton::toggled, this, &QgsAttributesFormProperties::toggleShowAliases );
126 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
127 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
128 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
129
130 connect( mSearchLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsAttributesFormProperties::updateFilteredItems );
131
132 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
133 if ( !mBlockUpdates )
134 updatedFields();
135 } );
136
137 // Context menu and children actions
138 mAvailableWidgetsContextMenu = new QMenu( this );
139 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
140 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
141
142 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
143 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
144
145 mAvailableWidgetsContextMenu->addAction( mActionCopyWidgetConfiguration );
146 mAvailableWidgetsContextMenu->addAction( mActionPasteWidgetConfiguration );
147
148 mMessageBar = new QgsMessageBar();
149 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
150 gridLayout->addWidget( mMessageBar, 0, 0 );
151
152 // Assign initial size to splitter widgets. By doing so, we can
153 // show an eventual horizontal scrollbar in the right-hand side panel
154 splitter->setSizes( { widget->minimumSizeHint().width(), 600 } );
155
156 if ( mSourceFieldsProperties )
157 {
158 connect( mFormPreviewButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::previewForm );
159 }
160 else
161 {
162 mFormPreviewButton->setVisible( false );
163 }
164}
165
175
177{
178 mAvailableWidgetsView->setSortingEnabled( false );
179 mAvailableWidgetsView->setSelectionBehavior( QAbstractItemView::SelectRows );
180 mAvailableWidgetsView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
181 mAvailableWidgetsView->setAcceptDrops( false );
182 mAvailableWidgetsView->setDragDropMode( QAbstractItemView::DragDropMode::DragOnly );
183
184 mAvailableWidgetsModel->populate();
185 mAvailableWidgetsModel->setShowAliases( settingShowAliases->value() );
186 mAvailableWidgetsView->expandAll();
187}
188
190{
191 // tabs and groups info
192 mFormLayoutView->setSortingEnabled( false );
193 mFormLayoutView->setSelectionBehavior( QAbstractItemView::SelectRows );
194 mFormLayoutView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
195 mFormLayoutView->setAcceptDrops( true );
196 mFormLayoutView->setDragDropMode( QAbstractItemView::DragDropMode::DragDrop );
197 mFormLayoutView->setDefaultDropAction( Qt::MoveAction );
198
199 mFormLayoutView->selectionModel()->clear();
200 mFormLayoutModel->populate();
201 mFormLayoutModel->setShowAliases( settingShowAliases->value() );
202 mFormLayoutView->expandAll();
203}
204
205
207{
209 {
210 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
211 }
212 else
213 {
214 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
215 }
216 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
217 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
218
219 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
220}
221
222void QgsAttributesFormProperties::initAvailableWidgetsActions( const QList< QgsAction > actions )
223{
224 mAvailableWidgetsModel->populateLayerActions( actions );
225}
226
233
235{
236 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
237
238 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
239
240 const QgsEditFormConfig cfg = mLayer->editFormConfig();
241 mEditFormLineEdit->setText( cfg.uiForm() );
242}
243
245{
246 const QgsEditFormConfig cfg = mLayer->editFormConfig();
247
248 mInitCodeSource = cfg.initCodeSource();
249 mInitFunction = cfg.initFunction();
250 mInitFilePath = cfg.initFilePath();
251 mInitCode = cfg.initCode();
252
253 if ( mInitCode.isEmpty() )
254 {
255 mInitCode.append( tr(
256 "# -*- coding: utf-8 -*-\n\"\"\"\n"
257 "QGIS forms can have a Python function that is called when the form is\n"
258 "opened.\n"
259 "\n"
260 "Use this function to add extra logic to your forms.\n"
261 "\n"
262 "Enter the name of the function in the \"Python Init function\"\n"
263 "field.\n"
264 "An example follows:\n"
265 "\"\"\"\n"
266 "from qgis.PyQt.QtWidgets import QWidget\n\n"
267 "def my_form_open(dialog, layer, feature):\n"
268 " geom = feature.geometry()\n"
269 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n"
270 ) );
271 }
272}
273
274void QgsAttributesFormProperties::loadAttributeTypeDialog()
275{
276 if ( mAvailableWidgetsView->selectionModel()->selectedRows( 0 ).count() != 1 )
277 return;
278
279 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
280
282 const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesFormModel::ItemNameRole ).toString();
283 const int fieldIndex = mLayer->fields().indexOf( fieldName );
284
285 if ( fieldIndex < 0 )
286 return;
287
288 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, fieldIndex, mAttributeTypeFrame );
289
290 loadAttributeTypeDialogFromConfiguration( cfg );
291
292 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
293 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
294
295 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
296}
297
298void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const QgsAttributesFormData::FieldConfig &config )
299{
300 const QgsFieldConstraints constraints = config.mFieldConstraints;
301
302 mAttributeTypeDialog->setAlias( config.mAlias );
303 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
304 mAttributeTypeDialog->setComment( config.mComment );
305 mAttributeTypeDialog->setCustomComment( config.mCustomComment );
306 mAttributeTypeDialog->setFieldEditable( config.mEditable );
307 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
308 mAttributeTypeDialog->setReuseLastValuePolicy( config.mReuseLastValuePolicy );
313 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
314 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
315 mAttributeTypeDialog->setMergePolicy( config.mMergePolicy );
316 mAttributeTypeDialog->setDefaultValueExpression( config.mDefaultValueExpression );
317 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( config.mApplyDefaultValueOnUpdate );
318
321 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
323 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
325 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
326 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
327
328 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
329 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
331
332 // Make sure the widget is refreshed, even if
333 // the new widget type matches the current one
334 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
335 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
336}
337
338void QgsAttributesFormProperties::storeAttributeTypeDialog()
339{
341 return;
342
343 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
344 return;
345
346 QgsAttributesFormData::FieldConfig cfg;
347
348 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
349 cfg.mCustomComment = mAttributeTypeDialog->customComment();
350 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
351 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
352 cfg.mReuseLastValuePolicy = mAttributeTypeDialog->reuseLastValuePolicy();
353 cfg.mAlias = mAttributeTypeDialog->alias();
354 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
355
356 QgsFieldConstraints constraints;
357 if ( mAttributeTypeDialog->notNull() )
358 {
360 }
361 else if ( mAttributeTypeDialog->notNullFromProvider() )
362 {
364 }
365
366 if ( mAttributeTypeDialog->unique() )
367 {
369 }
370 else if ( mAttributeTypeDialog->uniqueFromProvider() )
371 {
373 }
374
375 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
376 {
378 }
379
380 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
381
385
386 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
387 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
388 cfg.mFieldConstraints = constraints;
389 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
390 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
391 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
392 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
393 cfg.mMergePolicy = mAttributeTypeDialog->mergePolicy();
394
395 cfg.mApplyDefaultValueOnUpdate = mAttributeTypeDialog->applyDefaultValueOnUpdate();
396 cfg.mDefaultValueExpression = mAttributeTypeDialog->defaultValueExpression();
397
398 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
399 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
400
401 QModelIndex index = mAvailableWidgetsModel->fieldModelIndex( fieldName );
402 if ( index.isValid() )
403 {
404 mAvailableWidgetsModel->setData( index, QVariant::fromValue<QgsAttributesFormData::FieldConfig>( cfg ), QgsAttributesFormModel::ItemFieldConfigRole );
405 mAvailableWidgetsModel->setData( index, mAttributeTypeDialog->alias(), QgsAttributesFormModel::ItemDisplayRole );
406 }
407
408 // Save field config to each matching field item in Form Layout model
409 mFormLayoutModel->updateFieldConfigForFieldItems( fieldName, cfg );
410
411 // Save alias to each matching field item in Form Layout model
412 mFormLayoutModel->updateAliasForFieldItems( fieldName, mAttributeTypeDialog->alias() );
413}
414
415void QgsAttributesFormProperties::storeAttributeWidgetEdit()
416{
418 return;
419
420 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
421 return;
422
423 QModelIndex index = mFormLayoutView->firstSelectedIndex();
424 storeAttributeWidgetEdit( index );
425}
426
427void QgsAttributesFormProperties::storeAttributeWidgetEdit( const QModelIndex &index )
428{
430 return;
431
432 if ( !index.isValid() )
433 return;
434
435 auto itemData = index.data( QgsAttributesFormLayoutModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
436 mAttributeWidgetEdit->updateItemData( itemData );
437
439 {
440 QgsAttributesFormData::RelationEditorConfiguration config = mAttributeWidgetEdit->updatedRelationConfiguration();
441 itemData.setRelationEditorConfiguration( config );
442 mFormLayoutModel->setData( index, config.label, QgsAttributesFormLayoutModel::ItemDisplayRole );
443 }
444 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
445}
446
447void QgsAttributesFormProperties::loadAttributeWidgetEdit()
448{
449 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
450 return;
451
452 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
453 const QgsAttributesFormData::AttributeFormItemData itemData = currentIndex.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
454 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( itemData, this );
456 {
457 mAttributeWidgetEdit->setRelationSpecificWidget( itemData.relationEditorConfiguration(), currentIndex.data( QgsAttributesFormModel::ItemIdRole ).toString() );
458 }
459 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
460 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
461}
462
463void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
464{
465 mInfoTextWidget = new QLabel( infoText );
466 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
467 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
468}
469
470void QgsAttributesFormProperties::storeAttributeContainerEdit()
471{
473 return;
474
475 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
476 return;
477
478 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
479 storeAttributeContainerEdit( currentIndex );
480}
481
482void QgsAttributesFormProperties::storeAttributeContainerEdit( const QModelIndex &index )
483{
485 return;
486
487 if ( !index.isValid() )
488 return;
489
490 auto itemData = index.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
491 QString containerName;
492
493 mAttributeContainerEdit->updateItemData( itemData, containerName );
494 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
495 mFormLayoutModel->setData( index, containerName, QgsAttributesFormLayoutModel::ItemNameRole );
496}
497
498void QgsAttributesFormProperties::loadAttributeContainerEdit()
499{
500 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
501 return;
502
503 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
504 const QgsAttributesFormData::AttributeFormItemData itemData = currentIndex.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
505 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( itemData, mLayer, this );
506 mAttributeContainerEdit->setTitle( currentIndex.data( QgsAttributesFormModel::ItemNameRole ).toString() );
507 mAttributeContainerEdit->setUpContainerTypeComboBox( !currentIndex.parent().isValid(), itemData.containerType() );
508
509 mAttributeContainerEdit->registerExpressionContextGenerator( this );
510 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
511 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
512 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
513}
514
515void QgsAttributesFormProperties::onAttributeSelectionChanged( const QItemSelection &, const QItemSelection & )
516{
517 disconnect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
518
519 QModelIndex index;
520 if ( mFormLayoutView->selectionModel()->selectedRows( 0 ).count() == 1 )
521 {
522 // Go to the form layout view and store the single-selected index, as
523 // it will be used to store its current settings before being deselected
524 index = mFormLayoutView->firstSelectedIndex();
525 }
526
527 loadAttributeSpecificEditor( mAvailableWidgetsView, mFormLayoutView, index );
528 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
529}
530
531void QgsAttributesFormProperties::onFormLayoutSelectionChanged( const QItemSelection &, const QItemSelection &deselected )
532{
533 // when the selection changes in the DnD layout, sync the main tree
534 disconnect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
535 QModelIndex index;
536 if ( deselected.indexes().count() == 1 )
537 {
538 index = mFormLayoutProxyModel->mapToSource( deselected.indexes().at( 0 ) );
539 }
540 else if ( deselected.indexes().count() == 0 && mFormLayoutView->selectionModel()->selectedIndexes().count() == 2 )
541 {
542 // There was 1 selected, it was not deselected, but instead a new item was added to selection
543 index = mFormLayoutView->firstSelectedIndex();
544 }
545
546 loadAttributeSpecificEditor( mFormLayoutView, mAvailableWidgetsView, index );
547 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
548}
549
550void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesFormBaseView *emitter, QgsAttributesFormBaseView *receiver, QModelIndex &deselectedFormLayoutIndex )
551{
552 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
553
555 {
556 storeAttributeWidgetEdit( deselectedFormLayoutIndex );
557 storeAttributeContainerEdit( deselectedFormLayoutIndex );
558 }
560 {
561 storeAttributeTypeDialog();
562 }
563
564 clearAttributeTypeFrame();
565
566 if ( emitter->selectionModel()->selectedRows( 0 ).count() != 1 )
567 {
568 receiver->clearSelection();
569 }
570 else
571 {
572 const QModelIndex index = emitter->firstSelectedIndex();
573 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
574 switch ( indexType )
575 {
577 {
580 {
581 loadAttributeWidgetEdit();
582 }
583 else
584 {
585 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
586 }
587 break;
588 }
590 {
593 {
594 loadAttributeWidgetEdit();
595 }
596 loadAttributeTypeDialog();
597 break;
598 }
600 {
601 receiver->clearSelection();
602 loadAttributeContainerEdit();
603 break;
604 }
606 {
608 const QgsAction action { mLayer->actions()->action( index.data( QgsAttributesFormModel::ItemIdRole ).toString() ) };
609 loadInfoWidget( action.html() );
610 break;
611 }
616 {
618 {
619 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
620 }
621 else
622 {
623 loadInfoWidget( tr( "This configuration is available with double-click in the Form Layout panel" ) );
624 }
625 receiver->clearSelection();
626 break;
627 }
629 {
630 receiver->clearSelection();
631 break;
632 }
633 }
634 }
635}
636
637void QgsAttributesFormProperties::clearAttributeTypeFrame()
638{
640 {
641 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
642 mAttributeWidgetEdit->deleteLater();
643 mAttributeWidgetEdit = nullptr;
644 }
646 {
647 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
648 mAttributeTypeDialog->deleteLater();
649 mAttributeTypeDialog = nullptr;
650 }
652 {
653 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
654 mAttributeContainerEdit->deleteLater();
655 mAttributeContainerEdit = nullptr;
656 }
657 if ( mInfoTextWidget )
658 {
659 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
660 mInfoTextWidget->deleteLater();
661 mInfoTextWidget = nullptr;
662 }
663}
664
665void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
666{
667 Q_UNUSED( checked )
668 for ( int i = 0; i < mFormLayoutProxyModel->rowCount(); ++i )
669 {
670 QModelIndex index = mFormLayoutProxyModel->index( i, 0 );
671 mFormLayoutView->selectionModel()->select( index, QItemSelectionModel::Toggle );
672 }
673}
674
675void QgsAttributesFormProperties::toggleShowAliases( bool checked )
676{
677 settingShowAliases->setValue( checked );
678 mAvailableWidgetsModel->setShowAliases( checked );
679 mFormLayoutModel->setShowAliases( checked );
680}
681
682void QgsAttributesFormProperties::addContainer()
683{
684 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList = mFormLayoutModel->listOfContainers();
685
686 QModelIndex currentItem;
687 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
688 currentItem = mFormLayoutView->firstSelectedIndex();
689
690 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
691
692 if ( !dialog.exec() )
693 return;
694
695 const QString name = dialog.name();
696 QModelIndex parentContainerItem = dialog.parentContainerItem();
697
698 mFormLayoutModel->addContainer( parentContainerItem, name, dialog.columnCount(), dialog.containerType() );
699 if ( parentContainerItem.isValid() )
700 mFormLayoutView->setExpanded( parentContainerItem, true );
701}
702
703void QgsAttributesFormProperties::removeTabOrGroupButton()
704{
705 // deleting an item may delete any number of nested child items -- so we delete
706 // them one at a time and then see if there's any selection left
707 while ( true )
708 {
709 const QModelIndexList items = mFormLayoutView->selectionModel()->selectedRows();
710 if ( items.empty() )
711 break;
712
713 const QModelIndex item = mFormLayoutProxyModel->mapToSource( items.at( 0 ) );
714 mFormLayoutModel->removeRow( item.row(), item.parent() );
715 }
716}
717
718void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
719{
720 // Refresh the right-hand side panel: first, save selection to recover it later
721 const QItemSelection selection = mAvailableWidgetsView->selectionModel()->selection();
722 if ( selection.count() > 0 )
723 {
724 mAvailableWidgetsView->selectionModel()->clear();
725 }
726
727 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
728 {
729 mFormLayoutView->selectionModel()->clear(); // Get rid of e.g., container selection
730 }
731
732 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
733 switch ( layout )
734 {
736 mFormLayoutWidget->setVisible( false );
737 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
738 mUiFileFrame->setVisible( false );
739 mAddContainerButton->setVisible( false );
740 mRemoveLayoutItemButton->setVisible( false );
741 mInvertSelectionButton->setVisible( false );
742
743 setAvailableWidgetsIndicatorProvidersEnabled( true );
744 setFormLayoutIndicatorProvidersEnabled( false );
745 break;
746
748 mFormLayoutWidget->setVisible( true );
749 mTreeViewHorizontalSpacer->changeSize( 6, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
750 mUiFileFrame->setVisible( false );
751 mAddContainerButton->setVisible( true );
752 mRemoveLayoutItemButton->setVisible( true );
753 mInvertSelectionButton->setVisible( true );
754
755 setAvailableWidgetsIndicatorProvidersEnabled( false );
756 setFormLayoutIndicatorProvidersEnabled( true );
757 break;
758
760 // ui file
761 mFormLayoutWidget->setVisible( false );
762 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
763 mUiFileFrame->setVisible( true );
764 mAddContainerButton->setVisible( false );
765 mRemoveLayoutItemButton->setVisible( false );
766 mInvertSelectionButton->setVisible( false );
767
768 setAvailableWidgetsIndicatorProvidersEnabled( true );
769 setFormLayoutIndicatorProvidersEnabled( false );
770 break;
771 }
772
773 // Get the selection back so that we refresh the right-hand side panel
774 if ( selection.count() > 0 )
775 {
776 mAvailableWidgetsView->selectionModel()->select( selection, QItemSelectionModel::Select );
777 }
778}
779
780void QgsAttributesFormProperties::mTbInitCode_clicked()
781{
782 QgsAttributesFormInitCode attributesFormInitCode;
783
784 attributesFormInitCode.setCodeSource( mInitCodeSource );
785 attributesFormInitCode.setInitCode( mInitCode );
786 attributesFormInitCode.setInitFilePath( mInitFilePath );
787 attributesFormInitCode.setInitFunction( mInitFunction );
788
789 if ( !attributesFormInitCode.exec() )
790 return;
791
792 mInitCodeSource = attributesFormInitCode.codeSource();
793 mInitCode = attributesFormInitCode.initCode();
794 mInitFilePath = attributesFormInitCode.initFilePath();
795 mInitFunction = attributesFormInitCode.initFunction();
796}
797
798void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
799{
800 QgsSettings myQSettings;
801 const QString lastUsedDir = myQSettings.value( u"style/lastUIDir"_s, QDir::homePath() ).toString();
802 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
803
804 if ( uifilename.isNull() )
805 return;
806
807 const QFileInfo fi( uifilename );
808 myQSettings.setValue( u"style/lastUIDir"_s, fi.path() );
809 mEditFormLineEdit->setText( uifilename );
810}
811
813{
814 storeAttributeWidgetEdit();
815 storeAttributeContainerEdit();
816 storeAttributeTypeDialog();
817}
818
820{
821 mBlockUpdates++;
822 applyToLayer( mLayer );
823 mBlockUpdates--;
824}
825
826void QgsAttributesFormProperties::applyToLayer( QgsVectorLayer *layer )
827{
828 store();
829 QgsEditFormConfig editFormConfig = layer->editFormConfig();
830
831 const QModelIndex fieldContainer = mAvailableWidgetsModel->fieldContainer();
832 QModelIndex index;
833
834 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainer ); i++ )
835 {
836 index = mAvailableWidgetsModel->index( i, 0, fieldContainer );
838
839 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
840 const int idx = layer->fields().indexOf( fieldName );
841
842 //continue in case field does not exist anymore
843 if ( idx < 0 )
844 continue;
845
846 editFormConfig.setReadOnly( idx, !cfg.mEditable );
847 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
848 editFormConfig.setReuseLastValuePolicy( idx, cfg.mReuseLastValuePolicy );
849
850 if ( cfg.mDataDefinedProperties.count() > 0 )
851 {
852 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
853 }
854
855 layer->setEditorWidgetSetup( idx, QgsEditorWidgetSetup( cfg.mEditorWidgetType, cfg.mEditorWidgetConfig ) );
856
857 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
858 layer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
860 {
862 }
863 else
864 {
866 }
868 {
870 }
871 else
872 {
874 }
876 {
878 }
879 else
880 {
882 }
883
884 layer->setFieldAlias( idx, cfg.mAlias );
885 layer->setFieldCustomComment( idx, cfg.mCustomComment );
886 layer->setFieldSplitPolicy( idx, cfg.mSplitPolicy );
887 layer->setFieldDuplicatePolicy( idx, cfg.mDuplicatePolicy );
888 layer->setFieldMergePolicy( idx, cfg.mMergePolicy );
889
890 layer->setDefaultValueDefinition( idx, QgsDefaultValue( cfg.mDefaultValueExpression, cfg.mApplyDefaultValueOnUpdate ) );
891 }
892
893 // // tabs and groups
894 editFormConfig.clearTabs();
895
896 for ( int t = 0; t < mFormLayoutModel->rowCount(); t++ )
897 {
898 QModelIndex index = mFormLayoutModel->index( t, 0 );
899 QgsAttributeEditorElement *editorElement { mFormLayoutModel->createAttributeEditorWidget( index, nullptr ) };
900 if ( editorElement )
901 editFormConfig.addTab( editorElement );
902 }
903
904 editFormConfig.setUiForm( mEditFormLineEdit->text() );
905
906 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
907
908 editFormConfig.setInitCodeSource( mInitCodeSource );
909 editFormConfig.setInitFunction( mInitFunction );
910 editFormConfig.setInitFilePath( mInitFilePath );
911 editFormConfig.setInitCode( mInitCode );
912
913 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
914
915 // write the legacy config of relation widgets to support settings read by the API
916 const QModelIndex relationContainer = mAvailableWidgetsModel->relationContainer();
917
918 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( relationContainer ); i++ )
919 {
920 const QModelIndex relationIndex = mAvailableWidgetsModel->index( i, 0, relationContainer );
921
922 const QgsAttributesFormData::AttributeFormItemData itemData = relationIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
923 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( relationIndex.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
924 const QString indexId = relationIndex.data( QgsAttributesFormModel::ItemIdRole ).toString();
925
926 const QModelIndex layoutIndex = mFormLayoutModel->firstRecursiveMatchingModelIndex( indexType, indexId );
927 if ( layoutIndex.isValid() )
928 {
929 QVariantMap config;
930
931 const QgsAttributesFormData::AttributeFormItemData tabIndexData = layoutIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
932 config[u"nm-rel"_s] = tabIndexData.relationEditorConfiguration().nmRelationId;
933 config[u"force-suppress-popup"_s] = tabIndexData.relationEditorConfiguration().forceSuppressFormPopup;
934
935 editFormConfig.setWidgetConfig( indexId, config );
936 break;
937 }
938 }
939
940 layer->setEditFormConfig( editFormConfig );
941}
942
943void QgsAttributesFormProperties::updatedFields()
944{
945 // Store configuration to insure changes made are kept after refreshing the list
946 QMap<QString, QgsAttributesFormData::FieldConfig> fieldConfigs;
947
948 const QModelIndex fieldContainerBefore = mAvailableWidgetsModel->fieldContainer();
949 QModelIndex index;
950
951 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerBefore ); i++ )
952 {
953 index = mAvailableWidgetsModel->index( i, 0, fieldContainerBefore );
954 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
955 const QgsAttributesFormData::FieldConfig config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
956 fieldConfigs[fieldName] = config;
957 }
958
960
961 const QModelIndex fieldContainerAfter = mAvailableWidgetsModel->fieldContainer();
962
963 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerAfter ); i++ )
964 {
965 index = mAvailableWidgetsModel->index( i, 0, fieldContainerAfter );
966 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
967
968 if ( fieldConfigs.contains( fieldName ) )
969 {
970 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName], QgsAttributesFormModel::ItemFieldConfigRole );
971 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName].mAlias, QgsAttributesFormModel::ItemDisplayRole );
972 }
973 }
974}
975
976void QgsAttributesFormProperties::updateFilteredItems( const QString &filterText )
977{
978 const int availableWidgetsPreviousSelectionCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
979 const int formLayoutPreviousSelectionCount = mFormLayoutView->selectionModel()->selectedRows().count();
980
981 static_cast< QgsAttributesAvailableWidgetsView *>( mAvailableWidgetsView )->setFilterText( filterText );
982 mAvailableWidgetsView->expandAll();
983
984 static_cast< QgsAttributesFormLayoutView *>( mFormLayoutView )->setFilterText( filterText );
985 mFormLayoutView->expandAll();
986
987 // If there was no previous selection leave as is, since
988 // after a filter change no new selection may be added (only lost).
989 if ( !( availableWidgetsPreviousSelectionCount == 0 && formLayoutPreviousSelectionCount == 0 ) )
990 {
991 const int selectedAvailableWidgetItemCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
992 const int selectedFormLayoutItemCount = mFormLayoutView->selectionModel()->selectedRows().count();
993
994 if ( selectedAvailableWidgetItemCount == 0 && selectedFormLayoutItemCount == 0 )
995 {
996 // Clear right-hand side panel since all selected items have been filtered out
997 clearAttributeTypeFrame();
998 }
999 }
1000}
1001
1002void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
1003{
1004 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1005 return;
1006
1007 QPoint globalPos = mAvailableWidgetsView->viewport()->mapToGlobal( point );
1008
1009 const QModelIndex index = mAvailableWidgetsView->indexAt( point );
1010 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1011 if ( itemType == QgsAttributesFormData::Field )
1012 {
1013 const QClipboard *clipboard = QApplication::clipboard();
1014 const QMimeData *mimeData = clipboard->mimeData();
1015 if ( !mimeData )
1016 return;
1017
1018 const bool pasteEnabled = mimeData->hasFormat( u"application/x-qgsattributetabledesignerelementclipboard"_s );
1019 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
1020 mAvailableWidgetsContextMenu->popup( globalPos );
1021 }
1022}
1023
1024void QgsAttributesFormProperties::copyWidgetConfiguration()
1025{
1026 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1027 return;
1028
1029 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1030 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1031
1032 if ( itemType != QgsAttributesFormData::Field )
1033 return;
1034
1035 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1036 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1037
1038 if ( fieldIndex < 0 )
1039 return;
1040
1041 const QgsField field = mLayer->fields().field( fieldIndex );
1042
1043 // We'll copy everything but field aliases or comments
1044 QDomDocument doc;
1045 QDomElement documentElement = doc.createElement( u"FormWidgetClipboard"_s );
1046 documentElement.setAttribute( u"name"_s, field.name() );
1047
1048 // Editor widget setup
1049 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
1050
1051 QDomElement editWidgetElement = doc.createElement( u"editWidget"_s );
1052 documentElement.appendChild( editWidgetElement );
1053 editWidgetElement.setAttribute( u"type"_s, widgetSetup.type() );
1054 QDomElement editWidgetConfigElement = doc.createElement( u"config"_s );
1055
1056 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
1057 editWidgetElement.appendChild( editWidgetConfigElement );
1058
1059 // Split policy
1060 QDomElement splitPolicyElement = doc.createElement( u"splitPolicy"_s );
1061 splitPolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.splitPolicy() ) );
1062 documentElement.appendChild( splitPolicyElement );
1063
1064 // Duplicate policy
1065 QDomElement duplicatePolicyElement = doc.createElement( u"duplicatePolicy"_s );
1066 duplicatePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.duplicatePolicy() ) );
1067 documentElement.appendChild( duplicatePolicyElement );
1068
1069 // Merge policy
1070 QDomElement mergePolicyElement = doc.createElement( u"mergePolicy"_s );
1071 mergePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.mergePolicy() ) );
1072 documentElement.appendChild( mergePolicyElement );
1073
1074 // Default expressions
1075 QDomElement defaultElem = doc.createElement( u"default"_s );
1076 defaultElem.setAttribute( u"expression"_s, field.defaultValueDefinition().expression() );
1077 defaultElem.setAttribute( u"applyOnUpdate"_s, field.defaultValueDefinition().applyOnUpdate() ? u"1"_s : u"0"_s );
1078 documentElement.appendChild( defaultElem );
1079
1080 // Constraints
1081 QDomElement constraintElem = doc.createElement( u"constraint"_s );
1082 constraintElem.setAttribute( u"constraints"_s, field.constraints().constraints() );
1083 constraintElem.setAttribute( u"unique_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
1084 constraintElem.setAttribute( u"notnull_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
1085 constraintElem.setAttribute( u"exp_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
1086 documentElement.appendChild( constraintElem );
1087
1088 // Constraint expressions
1089 QDomElement constraintExpressionElem = doc.createElement( u"constraintExpression"_s );
1090 constraintExpressionElem.setAttribute( u"exp"_s, field.constraints().constraintExpression() );
1091 constraintExpressionElem.setAttribute( u"desc"_s, field.constraints().constraintDescription() );
1092 documentElement.appendChild( constraintExpressionElem );
1093
1094 // Widget general settings
1096 {
1097 QDomElement widgetGeneralSettingsElem = doc.createElement( u"widgetGeneralSettings"_s );
1098 widgetGeneralSettingsElem.setAttribute( u"editable"_s, mAttributeTypeDialog->fieldEditable() );
1099 widgetGeneralSettingsElem.setAttribute( u"label_on_top"_s, mAttributeTypeDialog->labelOnTop() );
1100 widgetGeneralSettingsElem.setAttribute( u"reuse_last_value_policy"_s, qgsEnumValueToKey( mAttributeTypeDialog->reuseLastValuePolicy() ) );
1101 documentElement.appendChild( widgetGeneralSettingsElem );
1102 }
1103
1104 // Widget display section
1106 {
1107 // Go for the corresponding form layout item and extract its display settings
1108 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
1109 return;
1110
1111 const QModelIndex indexLayout = mFormLayoutView->firstSelectedIndex();
1112 const auto layoutData = indexLayout.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
1113
1114 QDomElement displayElement = doc.createElement( u"widgetDisplay"_s );
1115 displayElement.setAttribute( u"showLabel"_s, layoutData.showLabel() );
1116 displayElement.setAttribute( u"horizontalStretch"_s, layoutData.horizontalStretch() );
1117 displayElement.setAttribute( u"verticalStretch"_s, layoutData.verticalStretch() );
1118 displayElement.appendChild( layoutData.labelStyle().writeXml( doc ) );
1119 documentElement.appendChild( displayElement );
1120 }
1121
1122 doc.appendChild( documentElement );
1123
1124 QMimeData *mimeData = new QMimeData;
1125 mimeData->setData( u"application/x-qgsattributetabledesignerelementclipboard"_s, doc.toByteArray() );
1126 QClipboard *clipboard = QApplication::clipboard();
1127 clipboard->setMimeData( mimeData );
1128}
1129
1130void QgsAttributesFormProperties::pasteWidgetConfiguration()
1131{
1132 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1133 return;
1134
1135 QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1136
1137 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1138 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1139
1140 if ( fieldIndex < 0 )
1141 return;
1142
1143 // Get base config from target item and ovewrite settings when possible
1144 auto config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
1145
1146 QDomDocument doc;
1147 QClipboard *clipboard = QApplication::clipboard();
1148 const QMimeData *mimeData = clipboard->mimeData();
1149 if ( !mimeData )
1150 return;
1151
1152 if ( doc.setContent( mimeData->data( u"application/x-qgsattributetabledesignerelementclipboard"_s ) ) )
1153 {
1154 QgsReadWriteContext context;
1155 QDomElement docElem = doc.documentElement();
1156 if ( docElem.tagName() != "FormWidgetClipboard"_L1 )
1157 return;
1158
1159 // When pasting, the target item has already been selected and
1160 // has triggered attribute type dialog loading. Therefore, we'll
1161 // only overwrite GUI settings instead of destroying and recreating
1162 // the whole dialog.
1163
1164 // Editor widget configuration
1165 const QDomElement fieldWidgetElement = docElem.firstChildElement( u"editWidget"_s );
1166 if ( !fieldWidgetElement.isNull() )
1167 {
1168 const QString widgetType = fieldWidgetElement.attribute( u"type"_s );
1169
1170 // Only paste if source editor widget type is supported by target field
1171 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
1172 if ( factory->supportsField( mLayer, fieldIndex ) )
1173 {
1174 const QDomElement configElement = fieldWidgetElement.firstChildElement( u"config"_s );
1175 if ( !configElement.isNull() )
1176 {
1177 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
1178 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
1179 config.mEditorWidgetType = widgetType;
1180 config.mEditorWidgetConfig = optionsMap;
1181 }
1182 }
1183 else
1184 {
1185 mMessageBar->pushMessage( QString(), tr( "Unable to paste widget configuration. The target field (%1) does not support the %2 widget type." ).arg( fieldName, widgetType ), Qgis::MessageLevel::Warning );
1186 }
1187 }
1188
1189 // Split policy
1190 const QDomElement splitPolicyElement = docElem.firstChildElement( u"splitPolicy"_s );
1191 if ( !splitPolicyElement.isNull() )
1192 {
1193 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainSplitPolicy::Duplicate );
1194 config.mSplitPolicy = policy;
1195 }
1196
1197 // Duplicate policy
1198 const QDomElement duplicatePolicyElement = docElem.firstChildElement( u"duplicatePolicy"_s );
1199 if ( !duplicatePolicyElement.isNull() )
1200 {
1201 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( u"policy"_s ), Qgis::FieldDuplicatePolicy::Duplicate );
1202 config.mDuplicatePolicy = policy;
1203 }
1204
1205 // Merge policy
1206 const QDomElement mergePolicyElement = docElem.firstChildElement( u"mergePolicy"_s );
1207 if ( !mergePolicyElement.isNull() )
1208 {
1209 const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainMergePolicy::DefaultValue );
1210 config.mMergePolicy = policy;
1211 }
1212
1213 // Default expressions
1214 const QDomElement defaultElement = docElem.firstChildElement( u"default"_s );
1215 if ( !defaultElement.isNull() )
1216 {
1217 config.mDefaultValueExpression = defaultElement.attribute( u"expression"_s );
1218 config.mApplyDefaultValueOnUpdate = defaultElement.attribute( u"applyOnUpdate"_s ).toInt();
1219 }
1220
1221 // Constraints
1222 // take target field constraints as a basis
1223 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
1224 const QDomElement constraintElement = docElem.firstChildElement( u"constraint"_s );
1225 if ( !constraintElement.isNull() )
1226 {
1227 const int intConstraints = constraintElement.attribute( u"constraints"_s, u"0"_s ).toInt();
1228 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
1229
1230 // always keep provider constraints intact
1232 {
1233 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
1235 else
1237 }
1239 {
1240 if ( constraints & QgsFieldConstraints::ConstraintUnique )
1242 else
1244 }
1246 {
1249 else
1251 }
1252
1253 const int uniqueStrength = constraintElement.attribute( u"unique_strength"_s, u"1"_s ).toInt();
1254 const int notNullStrength = constraintElement.attribute( u"notnull_strength"_s, u"1"_s ).toInt();
1255 const int expStrength = constraintElement.attribute( u"exp_strength"_s, u"1"_s ).toInt();
1256
1260 }
1261
1262 // Constraint expressions
1263 // always keep provider constraints intact
1265 {
1266 const QDomElement constraintExpressionElement = docElem.firstChildElement( u"constraintExpression"_s );
1267 if ( !constraintExpressionElement.isNull() )
1268 {
1269 QString expression = constraintExpressionElement.attribute( u"exp"_s, QString() );
1270 QString description = constraintExpressionElement.attribute( u"desc"_s, QString() );
1271 fieldConstraints.setConstraintExpression( expression, description );
1272 }
1273 }
1274 config.mFieldConstraints = fieldConstraints;
1275
1276 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( u"widgetGeneralSettings"_s );
1277 if ( !widgetGeneralSettingsElement.isNull() )
1278 {
1279 const int editable = widgetGeneralSettingsElement.attribute( u"editable"_s, u"0"_s ).toInt();
1281 if ( widgetGeneralSettingsElement.hasAttribute( u"reuse_last_values"_s ) )
1282 {
1283 reusePolicy = widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s, u"0"_s ).toInt() == 1 ? Qgis::AttributeFormReuseLastValuePolicy::AllowedDefaultOn
1285 }
1286 else
1287 {
1288 reusePolicy = qgsEnumKeyToValue( widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s ), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed );
1289 }
1290 const int labelOnTop = widgetGeneralSettingsElement.attribute( u"label_on_top"_s, u"0"_s ).toInt();
1291
1292 config.mEditable = editable;
1293 config.mReuseLastValuePolicy = reusePolicy;
1294 config.mLabelOnTop = labelOnTop;
1295 }
1296
1297 loadAttributeTypeDialogFromConfiguration( config );
1298
1299 // Widget display section
1301 {
1302 const QDomElement displayElement = docElem.firstChildElement( u"widgetDisplay"_s );
1303 if ( !displayElement.isNull() )
1304 {
1305 const int showLabel = displayElement.attribute( u"showLabel"_s, u"0"_s ).toInt();
1306 const int horizontalStretch = displayElement.attribute( u"horizontalStretch"_s, u"0"_s ).toInt();
1307 const int verticalStretch = displayElement.attribute( u"verticalStretch"_s, u"0"_s ).toInt();
1308 QgsAttributeEditorElement::LabelStyle style;
1309 style.readXml( displayElement );
1310
1311 // Update current GUI controls
1312 mAttributeWidgetEdit->setShowLabel( showLabel );
1313 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
1314 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
1315 mAttributeWidgetEdit->setLabelStyle( style );
1316 }
1317 }
1318 }
1319}
1320
1321void QgsAttributesFormProperties::setAvailableWidgetsIndicatorProvidersEnabled( bool enabled )
1322{
1323 // Only enable if the provider is disabled and only disable if it's enabled
1324 if ( enabled && !mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1325 {
1326 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1327 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1328 }
1329 else if ( !enabled && mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1330 {
1331 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1332 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1333 }
1334
1335 if ( enabled && !mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1336 {
1337 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1338 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1339 }
1340 else if ( !enabled && mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1341 {
1342 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1343 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1344 }
1345}
1346
1347void QgsAttributesFormProperties::setFormLayoutIndicatorProvidersEnabled( bool enabled )
1348{
1349 // Only enable if the provider is disabled and only disable if it's enabled
1350 if ( enabled && !mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1351 {
1352 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1353 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1354 }
1355 else if ( !enabled && mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1356 {
1357 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1358 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1359 }
1360
1361 if ( enabled && !mConstraintIndicatorProviderFormLayout->isEnabled() )
1362 {
1363 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1364 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1365 }
1366 else if ( !enabled && mConstraintIndicatorProviderFormLayout->isEnabled() )
1367 {
1368 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1369 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1370 }
1371}
1372
1373void QgsAttributesFormProperties::previewForm()
1374{
1375 if ( !mSourceFieldsProperties )
1376 {
1377 return;
1378 }
1379
1380 auto projectDirtyBlocker = std::make_unique<QgsProjectDirtyBlocker>( QgsProject::instance() );
1381
1382
1383 QgsFields fields;
1384 QList<QPair<QgsField, QString>> expressionFields;
1385
1386 for ( int i = 0; i < mLayer->fields().size(); i++ )
1387 {
1388 if ( mLayer->fields().fieldOrigin( i ) == Qgis::FieldOrigin::Expression )
1389 {
1390 expressionFields << qMakePair( mLayer->fields().at( i ), mLayer->expressionField( i ) );
1391 }
1392 else
1393 {
1394 fields.append( mLayer->fields().at( i ) );
1395 }
1396 }
1397
1398 std::unique_ptr<QgsVectorLayer> vlayer;
1399 vlayer.reset( QgsMemoryProviderUtils::createMemoryLayer( "preview"_L1, fields, mLayer->wkbType(), mLayer->crs() ) );
1400 for ( const QPair<QgsField, QString> &expressionField : std::as_const( expressionFields ) )
1401 {
1402 vlayer->addExpressionField( expressionField.second, expressionField.first );
1403 }
1404
1405 mSourceFieldsProperties->applyToLayer( vlayer.get() );
1406 applyToLayer( vlayer.get() );
1407
1408 QgsFeature feature = QgsVectorLayerUtils::createFeature( vlayer.get() );
1409 QgsAttributeDialog form( vlayer.get(), &feature, false, this, true );
1411 form.exec();
1412
1413 projectDirtyBlocker.reset();
1414}
AttributeFormReuseLastValuePolicy
Attribute form policy for reusing last entered values.
Definition qgis.h:6015
@ AllowedDefaultOn
Reuse of last values allowed and enabled by default.
Definition qgis.h:6017
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:6016
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5986
@ On
Always suppress feature form.
Definition qgis.h:5988
@ Default
Use the application-wide setting.
Definition qgis.h:5987
@ Off
Never suppress feature form.
Definition qgis.h:5989
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5971
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
Definition qgis.h:5973
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
Definition qgis.h:5972
@ UiFile
Load a .ui file for the layout. Needs to be configured.
Definition qgis.h:5974
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:4093
@ DefaultValue
Use default field value.
Definition qgis.h:4094
@ Warning
Warning message.
Definition qgis.h:162
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:4076
@ Duplicate
Duplicate original value.
Definition qgis.h:4078
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:4113
@ Duplicate
Duplicate original value.
Definition qgis.h:4115
@ Expression
Field is calculated from an expression.
Definition qgis.h:1829
QString html() const
Returns an HTML table with the basic information about this action.
@ PreviewMode
Preview mode, for previewing attribute configurations.
Manages available widgets when configuring attributes forms.
QModelIndex fieldContainer() const
Returns the field container in this model, expected to be placed at the first top-level row.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Graphical representation for the available widgets while configuring attributes forms.
Graphical representation for the attribute drag and drop editor.
void selectFirstMatchingItem(const QgsAttributesFormData::AttributesFormItemType &itemType, const QString &itemId)
Selects the first item that matches a itemType and a itemId.
QModelIndex firstSelectedIndex() const
Returns the source model index corresponding to the first selected row.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
AttributesFormItemType
Custom item types.
@ Container
Container for the form, which may be tab, group or row.
@ Relation
Relation between two vector layers.
@ Field
Vector layer field.
@ SpacerWidget
Spacer widget type,.
@ WidgetType
In the available widgets tree, the type of widget.
@ TextWidget
Text widget type,.
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.
Manages form layouts when configuring attributes forms via drag and drop designer.
Graphical representation for the form layout while configuring attributes forms.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
@ ItemFieldConfigRole
Prior to QGIS 3.44, this was available as FieldConfigRole.
@ ItemDisplayRole
Display text for the item.
@ ItemNameRole
Prior to QGIS 3.44, this was available as FieldNameRole.
@ ItemDataRole
Prior to QGIS 3.44, this was available as DnDTreeRole.
@ ItemIdRole
Items may have ids to ease comparison. Used by Relations, fields, actions and containers.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void fieldConfigDataChanged(QgsAttributesFormItem *item)
Notifies other objects that the field config data has changed in the item.
QgsAttributesFormBaseView * mFormLayoutView
void initAvailableWidgetsActions(const QList< QgsAction > actions)
Refresh layer actions in the Available Widgets view.
QgsAttributeFormContainerEdit * mAttributeContainerEdit
static const QgsSettingsEntryBool * settingShowAliases
QgsAttributesFormBaseView * mAvailableWidgetsView
QgsAttributeTypeDialog * mAttributeTypeDialog
void store()
Stores currently opened widget configuration.
QgsAttributesFormProperties(QgsVectorLayer *layer, QWidget *parent=nullptr, QgsSourceFieldsProperties *sourceFieldsProperties=nullptr)
The QgsAttributesFormProperties constructor.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsAttributeWidgetEdit * mAttributeWidgetEdit
void apply()
Applies the attribute from properties to the vector layer.
void initFormLayoutView()
Initializes the form layout tree view, repopulating the underlying model.
void initAvailableWidgetsView()
Initializes the available widgets tree view, repopulating the underlying model.
Proxy model to filter items in the tree views of the drag and drop designer.
void updateItemIndicator(QgsAttributesFormItem *item)
Updates the state of a the indicator for the given item.
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,...
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.
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.
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.
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.
void setReuseLastValuePolicy(int index, Qgis::AttributeFormReuseLastValuePolicy policy)
Sets the reuse of last value policy for an attribute index.
bool supportsField(const QgsVectorLayer *vl, int fieldIdx) const
Check if this editor widget type supports a certain field.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Provides field constraint indicators for attribute form views.
ConstraintStrength
Strength of constraints.
@ 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.
@ ConstraintOriginLayer
Constraint was set by layer.
ConstraintStrength constraintStrength(Constraint constraint) const
Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint is not pres...
ConstraintOrigin constraintOrigin(Constraint constraint) const
Returns the origin of a field constraint, or ConstraintOriginNotSet if the constraint is not present ...
QString constraintExpression() const
Returns the constraint expression for the field, if set.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
void removeConstraint(Constraint constraint)
Removes a constraint from the field.
QString constraintDescription() const
Returns the descriptive name for the constraint expression.
void setConstraint(Constraint constraint, ConstraintOrigin origin=ConstraintOriginLayer)
Sets a constraint on the field.
QFlags< Constraint > Constraints
Provides default value indicators for attribute form views.
QString name
Definition qgsfield.h:65
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:764
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:774
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:67
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the field's merge policy, which indicates how field values should be handled during a merge o...
Definition qgsfield.cpp:784
QgsFieldConstraints constraints
Definition qgsfield.h:68
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:749
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:109
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
A bar for displaying non-blocking messages to the user.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
static QgsProject * instance()
Returns the QgsProject singleton instance.
int count() const
Returns the number of properties contained within the collection.
A boolean settings entry.
static const QgsSettingsEntryBool * settingsDigitizingDisableEnterAttributeValuesDialog
Settings entry digitizing disable enter attribute values dialog.
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.
A widget which displays information about vector layer fields, and allows some configuration of them.
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
Represents a vector layer which manages a vector based dataset.
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
void setFieldMergePolicy(int index, Qgis::FieldDomainMergePolicy policy)
Sets a merge policy for the field with the specified index.
void setDefaultValueDefinition(int index, const QgsDefaultValue &definition)
Sets the definition of the expression to use when calculating the default value for a field.
void setEditFormConfig(const QgsEditFormConfig &editFormConfig)
Sets the editFormConfig (configuration) of the form used to represent this vector layer.
void setEditorWidgetSetup(int index, const QgsEditorWidgetSetup &setup)
Sets the editor widget setup for the field at the specified index.
void setConstraintExpression(int index, const QString &expression, const QString &description=QString())
Sets the constraint expression for the specified field index.
void setFieldDuplicatePolicy(int index, Qgis::FieldDuplicatePolicy policy)
Sets a duplicate policy for the field with the specified index.
Q_INVOKABLE void setFieldCustomComment(int index, const QString &customCommentString)
Sets the custom comment for the field.
Q_INVOKABLE void setFieldAlias(int index, const QString &aliasString)
Sets an alias (a display name) for attributes to display in dialogs.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig
void setFieldSplitPolicy(int index, Qgis::FieldDomainSplitPolicy policy)
Sets a split policy for the field with the specified index.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7309
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7290
void readXml(const QDomNode &node)
Reads configuration from node.
Holds the configuration for a field.
Qgis::FieldDuplicatePolicy mDuplicatePolicy
QMap< QString, QVariant > mEditorWidgetConfig
Qgis::FieldDomainSplitPolicy mSplitPolicy
Qgis::FieldDomainMergePolicy mMergePolicy
Qgis::AttributeFormReuseLastValuePolicy mReuseLastValuePolicy