QGIS API Documentation 3.99.0-Master (d270888f95f)
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
50const QgsSettingsEntryBool *QgsAttributesFormProperties::settingShowAliases = 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 );
51
53 : QWidget( parent )
54 , mLayer( layer )
55 , mSourceFieldsProperties( sourceFieldsProperties )
56{
57 if ( !layer )
58 return;
59
60 setupUi( this );
61
62 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
63 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
64 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
65 mShowAliasesButton->setChecked( settingShowAliases->value() );
66
67 // available widgets tree
68 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
70 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsView );
71 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
72 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
73 mAvailableWidgetsView->setContextMenuPolicy( Qt::CustomContextMenu );
74
75 // we need a custom item delegate in order to draw indicators
76 mAvailableWidgetsView->setItemDelegate( new QgsAttributesFormTreeViewItemDelegate( mAvailableWidgetsView ) );
77 mAvailableWidgetsView->setStyle( new QgsAttributesFormTreeViewProxyStyle( mAvailableWidgetsView ) );
78
79 mAvailableWidgetsModel = new QgsAttributesAvailableWidgetsModel( mLayer, QgsProject().instance(), this );
80 mAvailableWidgetsProxyModel = new QgsAttributesFormProxyModel( this );
81 mAvailableWidgetsProxyModel->setAttributesFormSourceModel( mAvailableWidgetsModel );
82 mAvailableWidgetsProxyModel->setRecursiveFilteringEnabled( true );
83 mAvailableWidgetsView->setModel( mAvailableWidgetsProxyModel );
84
85 mConstraintIndicatorProviderAvailableWidgets = new QgsFieldConstraintIndicatorProvider( mAvailableWidgetsView ); // gets parented to the available widgets view
86 mDefaultValueIndicatorProviderAvailableWidgets = new QgsFieldDefaultValueIndicatorProvider( mAvailableWidgetsView ); // gets parented to the available widgets view
87
88#ifdef ENABLE_MODELTEST
89 new ModelTest( mAvailableWidgetsProxyModel, this );
90#endif
91
92 // form layout tree
93 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
95 formLayoutWidgetLayout->addWidget( mFormLayoutView );
96 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
97 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
98
99 // we need a custom item delegate in order to draw indicators
100 mFormLayoutView->setItemDelegate( new QgsAttributesFormTreeViewItemDelegate( mFormLayoutView ) );
101 mFormLayoutView->setStyle( new QgsAttributesFormTreeViewProxyStyle( mFormLayoutView ) );
102
103 mFormLayoutModel = new QgsAttributesFormLayoutModel( mLayer, QgsProject().instance(), this );
104 mFormLayoutProxyModel = new QgsAttributesFormProxyModel( this );
105 mFormLayoutProxyModel->setAttributesFormSourceModel( mFormLayoutModel );
106 mFormLayoutProxyModel->setRecursiveFilteringEnabled( true );
107 mFormLayoutView->setModel( mFormLayoutProxyModel );
108
109 mConstraintIndicatorProviderFormLayout = new QgsFieldConstraintIndicatorProvider( mFormLayoutView ); // gets parented to the form layout view
110 mDefaultValueIndicatorProviderFormLayout = new QgsFieldDefaultValueIndicatorProvider( mFormLayoutView ); // gets parented to the form layout view
111
112#ifdef ENABLE_MODELTEST
113 new ModelTest( mFormLayoutProxyModel, this );
114#endif
115
116 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
117 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
118
119 connect( mAvailableWidgetsView, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
120
121 connect( mAddContainerButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
122 connect( mRemoveLayoutItemButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
123 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
124 connect( mShowAliasesButton, &QAbstractButton::toggled, this, &QgsAttributesFormProperties::toggleShowAliases );
125 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
126 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
127 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
128
129 connect( mSearchLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsAttributesFormProperties::updateFilteredItems );
130
131 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
132 if ( !mBlockUpdates )
133 updatedFields();
134 } );
135
136 // Context menu and children actions
137 mAvailableWidgetsContextMenu = new QMenu( this );
138 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
139 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
140
141 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
142 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
143
144 mAvailableWidgetsContextMenu->addAction( mActionCopyWidgetConfiguration );
145 mAvailableWidgetsContextMenu->addAction( mActionPasteWidgetConfiguration );
146
147 mMessageBar = new QgsMessageBar();
148 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
149 gridLayout->addWidget( mMessageBar, 0, 0 );
150
151 // Assign initial size to splitter widgets. By doing so, we can
152 // show an eventual horizontal scrollbar in the right-hand side panel
153 splitter->setSizes( { widget->minimumSizeHint().width(), 600 } );
154
155 if ( mSourceFieldsProperties )
156 {
157 connect( mFormPreviewButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::previewForm );
158 }
159 else
160 {
161 mFormPreviewButton->setVisible( false );
162 }
163}
164
174
176{
177 mAvailableWidgetsView->setSortingEnabled( false );
178 mAvailableWidgetsView->setSelectionBehavior( QAbstractItemView::SelectRows );
179 mAvailableWidgetsView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
180 mAvailableWidgetsView->setAcceptDrops( false );
181 mAvailableWidgetsView->setDragDropMode( QAbstractItemView::DragDropMode::DragOnly );
182
183 mAvailableWidgetsModel->populate();
184 mAvailableWidgetsModel->setShowAliases( settingShowAliases->value() );
185 mAvailableWidgetsView->expandAll();
186}
187
189{
190 // tabs and groups info
191 mFormLayoutView->setSortingEnabled( false );
192 mFormLayoutView->setSelectionBehavior( QAbstractItemView::SelectRows );
193 mFormLayoutView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
194 mFormLayoutView->setAcceptDrops( true );
195 mFormLayoutView->setDragDropMode( QAbstractItemView::DragDropMode::DragDrop );
196 mFormLayoutView->setDefaultDropAction( Qt::MoveAction );
197
198 mFormLayoutView->selectionModel()->clear();
199 mFormLayoutModel->populate();
200 mFormLayoutModel->setShowAliases( settingShowAliases->value() );
201 mFormLayoutView->expandAll();
202}
203
204
206{
208 {
209 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
210 }
211 else
212 {
213 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
214 }
215 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
216 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
217
218 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
219}
220
221void QgsAttributesFormProperties::initAvailableWidgetsActions( const QList< QgsAction > actions )
222{
223 mAvailableWidgetsModel->populateLayerActions( actions );
224}
225
232
234{
235 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
236
237 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
238
239 const QgsEditFormConfig cfg = mLayer->editFormConfig();
240 mEditFormLineEdit->setText( cfg.uiForm() );
241}
242
244{
245 const QgsEditFormConfig cfg = mLayer->editFormConfig();
246
247 mInitCodeSource = cfg.initCodeSource();
248 mInitFunction = cfg.initFunction();
249 mInitFilePath = cfg.initFilePath();
250 mInitCode = cfg.initCode();
251
252 if ( mInitCode.isEmpty() )
253 {
254 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
255 "QGIS forms can have a Python function that is called when the form is\n"
256 "opened.\n"
257 "\n"
258 "Use this function to add extra logic to your forms.\n"
259 "\n"
260 "Enter the name of the function in the \"Python Init function\"\n"
261 "field.\n"
262 "An example follows:\n"
263 "\"\"\"\n"
264 "from qgis.PyQt.QtWidgets import QWidget\n\n"
265 "def my_form_open(dialog, layer, feature):\n"
266 " geom = feature.geometry()\n"
267 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
268 }
269}
270
271void QgsAttributesFormProperties::loadAttributeTypeDialog()
272{
273 if ( mAvailableWidgetsView->selectionModel()->selectedRows( 0 ).count() != 1 )
274 return;
275
276 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
277
279 const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesFormModel::ItemNameRole ).toString();
280 const int fieldIndex = mLayer->fields().indexOf( fieldName );
281
282 if ( fieldIndex < 0 )
283 return;
284
285 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, fieldIndex, mAttributeTypeFrame );
286
287 loadAttributeTypeDialogFromConfiguration( cfg );
288
289 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
290 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
291
292 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
293}
294
295void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const QgsAttributesFormData::FieldConfig &config )
296{
297 const QgsFieldConstraints constraints = config.mFieldConstraints;
298
299 mAttributeTypeDialog->setAlias( config.mAlias );
300 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
301 mAttributeTypeDialog->setComment( config.mComment );
302 mAttributeTypeDialog->setFieldEditable( config.mEditable );
303 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
304 mAttributeTypeDialog->setReuseLastValuePolicy( config.mReuseLastValuePolicy );
309 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
310 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
311 mAttributeTypeDialog->setMergePolicy( config.mMergePolicy );
312 mAttributeTypeDialog->setDefaultValueExpression( config.mDefaultValueExpression );
313 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( config.mApplyDefaultValueOnUpdate );
314
317 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
319 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
321 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
322 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
323
324 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
325 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
327
328 // Make sure the widget is refreshed, even if
329 // the new widget type matches the current one
330 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
331 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
332}
333
334void QgsAttributesFormProperties::storeAttributeTypeDialog()
335{
337 return;
338
339 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
340 return;
341
342 QgsAttributesFormData::FieldConfig cfg;
343
344 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
345 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
346 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
347 cfg.mReuseLastValuePolicy = mAttributeTypeDialog->reuseLastValuePolicy();
348 cfg.mAlias = mAttributeTypeDialog->alias();
349 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
350
351 QgsFieldConstraints constraints;
352 if ( mAttributeTypeDialog->notNull() )
353 {
355 }
356 else if ( mAttributeTypeDialog->notNullFromProvider() )
357 {
359 }
360
361 if ( mAttributeTypeDialog->unique() )
362 {
364 }
365 else if ( mAttributeTypeDialog->uniqueFromProvider() )
366 {
368 }
369
370 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
371 {
373 }
374
375 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
376
380
381 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
382 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
383 cfg.mFieldConstraints = constraints;
384 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
385 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
386 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
387 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
388 cfg.mMergePolicy = mAttributeTypeDialog->mergePolicy();
389
390 cfg.mApplyDefaultValueOnUpdate = mAttributeTypeDialog->applyDefaultValueOnUpdate();
391 cfg.mDefaultValueExpression = mAttributeTypeDialog->defaultValueExpression();
392
393 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
394 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
395
396 QModelIndex index = mAvailableWidgetsModel->fieldModelIndex( fieldName );
397 if ( index.isValid() )
398 {
399 mAvailableWidgetsModel->setData( index, QVariant::fromValue<QgsAttributesFormData::FieldConfig>( cfg ), QgsAttributesFormModel::ItemFieldConfigRole );
400 mAvailableWidgetsModel->setData( index, mAttributeTypeDialog->alias(), QgsAttributesFormModel::ItemDisplayRole );
401 }
402
403 // Save field config to each matching field item in Form Layout model
404 mFormLayoutModel->updateFieldConfigForFieldItems( fieldName, cfg );
405
406 // Save alias to each matching field item in Form Layout model
407 mFormLayoutModel->updateAliasForFieldItems( fieldName, mAttributeTypeDialog->alias() );
408}
409
410void QgsAttributesFormProperties::storeAttributeWidgetEdit()
411{
413 return;
414
415 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
416 return;
417
418 QModelIndex index = mFormLayoutView->firstSelectedIndex();
419 storeAttributeWidgetEdit( index );
420}
421
422void QgsAttributesFormProperties::storeAttributeWidgetEdit( const QModelIndex &index )
423{
425 return;
426
427 if ( !index.isValid() )
428 return;
429
430 auto itemData = index.data( QgsAttributesFormLayoutModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
431 mAttributeWidgetEdit->updateItemData( itemData );
432
434 {
435 QgsAttributesFormData::RelationEditorConfiguration config = mAttributeWidgetEdit->updatedRelationConfiguration();
436 itemData.setRelationEditorConfiguration( config );
437 mFormLayoutModel->setData( index, config.label, QgsAttributesFormLayoutModel::ItemDisplayRole );
438 }
439 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
440}
441
442void QgsAttributesFormProperties::loadAttributeWidgetEdit()
443{
444 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
445 return;
446
447 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
448 const QgsAttributesFormData::AttributeFormItemData itemData = currentIndex.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
449 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( itemData, this );
451 {
452 mAttributeWidgetEdit->setRelationSpecificWidget( itemData.relationEditorConfiguration(), currentIndex.data( QgsAttributesFormModel::ItemIdRole ).toString() );
453 }
454 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
455 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
456}
457
458void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
459{
460 mInfoTextWidget = new QLabel( infoText );
461 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
462 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
463}
464
465void QgsAttributesFormProperties::storeAttributeContainerEdit()
466{
468 return;
469
470 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
471 return;
472
473 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
474 storeAttributeContainerEdit( currentIndex );
475}
476
477void QgsAttributesFormProperties::storeAttributeContainerEdit( const QModelIndex &index )
478{
480 return;
481
482 if ( !index.isValid() )
483 return;
484
485 auto itemData = index.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
486 QString containerName;
487
488 mAttributeContainerEdit->updateItemData( itemData, containerName );
489 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
490 mFormLayoutModel->setData( index, containerName, QgsAttributesFormLayoutModel::ItemNameRole );
491}
492
493void QgsAttributesFormProperties::loadAttributeContainerEdit()
494{
495 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
496 return;
497
498 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
499 const QgsAttributesFormData::AttributeFormItemData itemData = currentIndex.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
500 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( itemData, mLayer, this );
501 mAttributeContainerEdit->setTitle( currentIndex.data( QgsAttributesFormModel::ItemNameRole ).toString() );
502 mAttributeContainerEdit->setUpContainerTypeComboBox( !currentIndex.parent().isValid(), itemData.containerType() );
503
504 mAttributeContainerEdit->registerExpressionContextGenerator( this );
505 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
506 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
507 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
508}
509
510void QgsAttributesFormProperties::onAttributeSelectionChanged( const QItemSelection &, const QItemSelection & )
511{
512 disconnect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
513
514 QModelIndex index;
515 if ( mFormLayoutView->selectionModel()->selectedRows( 0 ).count() == 1 )
516 {
517 // Go to the form layout view and store the single-selected index, as
518 // it will be used to store its current settings before being deselected
519 index = mFormLayoutView->firstSelectedIndex();
520 }
521
522 loadAttributeSpecificEditor( mAvailableWidgetsView, mFormLayoutView, index );
523 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
524}
525
526void QgsAttributesFormProperties::onFormLayoutSelectionChanged( const QItemSelection &, const QItemSelection &deselected )
527{
528 // when the selection changes in the DnD layout, sync the main tree
529 disconnect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
530 QModelIndex index;
531 if ( deselected.indexes().count() == 1 )
532 {
533 index = mFormLayoutProxyModel->mapToSource( deselected.indexes().at( 0 ) );
534 }
535 else if ( deselected.indexes().count() == 0 && mFormLayoutView->selectionModel()->selectedIndexes().count() == 2 )
536 {
537 // There was 1 selected, it was not deselected, but instead a new item was added to selection
538 index = mFormLayoutView->firstSelectedIndex();
539 }
540
541 loadAttributeSpecificEditor( mFormLayoutView, mAvailableWidgetsView, index );
542 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
543}
544
545void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesFormBaseView *emitter, QgsAttributesFormBaseView *receiver, QModelIndex &deselectedFormLayoutIndex )
546{
547 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
548
550 {
551 storeAttributeWidgetEdit( deselectedFormLayoutIndex );
552 storeAttributeContainerEdit( deselectedFormLayoutIndex );
553 }
555 {
556 storeAttributeTypeDialog();
557 }
558
559 clearAttributeTypeFrame();
560
561 if ( emitter->selectionModel()->selectedRows( 0 ).count() != 1 )
562 {
563 receiver->clearSelection();
564 }
565 else
566 {
567 const QModelIndex index = emitter->firstSelectedIndex();
568 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
569 switch ( indexType )
570 {
572 {
575 {
576 loadAttributeWidgetEdit();
577 }
578 else
579 {
580 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
581 }
582 break;
583 }
585 {
588 {
589 loadAttributeWidgetEdit();
590 }
591 loadAttributeTypeDialog();
592 break;
593 }
595 {
596 receiver->clearSelection();
597 loadAttributeContainerEdit();
598 break;
599 }
601 {
603 const QgsAction action { mLayer->actions()->action( index.data( QgsAttributesFormModel::ItemIdRole ).toString() ) };
604 loadInfoWidget( action.html() );
605 break;
606 }
611 {
613 {
614 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
615 }
616 else
617 {
618 loadInfoWidget( tr( "This configuration is available with double-click in the Form Layout panel" ) );
619 }
620 receiver->clearSelection();
621 break;
622 }
624 {
625 receiver->clearSelection();
626 break;
627 }
628 }
629 }
630}
631
632void QgsAttributesFormProperties::clearAttributeTypeFrame()
633{
635 {
636 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
637 mAttributeWidgetEdit->deleteLater();
638 mAttributeWidgetEdit = nullptr;
639 }
641 {
642 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
643 mAttributeTypeDialog->deleteLater();
644 mAttributeTypeDialog = nullptr;
645 }
647 {
648 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
649 mAttributeContainerEdit->deleteLater();
650 mAttributeContainerEdit = nullptr;
651 }
652 if ( mInfoTextWidget )
653 {
654 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
655 mInfoTextWidget->deleteLater();
656 mInfoTextWidget = nullptr;
657 }
658}
659
660void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
661{
662 Q_UNUSED( checked )
663 for ( int i = 0; i < mFormLayoutProxyModel->rowCount(); ++i )
664 {
665 QModelIndex index = mFormLayoutProxyModel->index( i, 0 );
666 mFormLayoutView->selectionModel()->select( index, QItemSelectionModel::Toggle );
667 }
668}
669
670void QgsAttributesFormProperties::toggleShowAliases( bool checked )
671{
672 settingShowAliases->setValue( checked );
673 mAvailableWidgetsModel->setShowAliases( checked );
674 mFormLayoutModel->setShowAliases( checked );
675}
676
677void QgsAttributesFormProperties::addContainer()
678{
679 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList = mFormLayoutModel->listOfContainers();
680
681 QModelIndex currentItem;
682 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
683 currentItem = mFormLayoutView->firstSelectedIndex();
684
685 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
686
687 if ( !dialog.exec() )
688 return;
689
690 const QString name = dialog.name();
691 QModelIndex parentContainerItem = dialog.parentContainerItem();
692
693 mFormLayoutModel->addContainer( parentContainerItem, name, dialog.columnCount(), dialog.containerType() );
694 if ( parentContainerItem.isValid() )
695 mFormLayoutView->setExpanded( parentContainerItem, true );
696}
697
698void QgsAttributesFormProperties::removeTabOrGroupButton()
699{
700 // deleting an item may delete any number of nested child items -- so we delete
701 // them one at a time and then see if there's any selection left
702 while ( true )
703 {
704 const QModelIndexList items = mFormLayoutView->selectionModel()->selectedRows();
705 if ( items.empty() )
706 break;
707
708 const QModelIndex item = mFormLayoutProxyModel->mapToSource( items.at( 0 ) );
709 mFormLayoutModel->removeRow( item.row(), item.parent() );
710 }
711}
712
713void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
714{
715 // Refresh the right-hand side panel: first, save selection to recover it later
716 const QItemSelection selection = mAvailableWidgetsView->selectionModel()->selection();
717 if ( selection.count() > 0 )
718 {
719 mAvailableWidgetsView->selectionModel()->clear();
720 }
721
722 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
723 {
724 mFormLayoutView->selectionModel()->clear(); // Get rid of e.g., container selection
725 }
726
727 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
728 switch ( layout )
729 {
731 mFormLayoutWidget->setVisible( false );
732 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
733 mUiFileFrame->setVisible( false );
734 mAddContainerButton->setVisible( false );
735 mRemoveLayoutItemButton->setVisible( false );
736 mInvertSelectionButton->setVisible( false );
737
738 setAvailableWidgetsIndicatorProvidersEnabled( true );
739 setFormLayoutIndicatorProvidersEnabled( false );
740 break;
741
743 mFormLayoutWidget->setVisible( true );
744 mTreeViewHorizontalSpacer->changeSize( 6, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
745 mUiFileFrame->setVisible( false );
746 mAddContainerButton->setVisible( true );
747 mRemoveLayoutItemButton->setVisible( true );
748 mInvertSelectionButton->setVisible( true );
749
750 setAvailableWidgetsIndicatorProvidersEnabled( false );
751 setFormLayoutIndicatorProvidersEnabled( true );
752 break;
753
755 // ui file
756 mFormLayoutWidget->setVisible( false );
757 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
758 mUiFileFrame->setVisible( true );
759 mAddContainerButton->setVisible( false );
760 mRemoveLayoutItemButton->setVisible( false );
761 mInvertSelectionButton->setVisible( false );
762
763 setAvailableWidgetsIndicatorProvidersEnabled( true );
764 setFormLayoutIndicatorProvidersEnabled( false );
765 break;
766 }
767
768 // Get the selection back so that we refresh the right-hand side panel
769 if ( selection.count() > 0 )
770 {
771 mAvailableWidgetsView->selectionModel()->select( selection, QItemSelectionModel::Select );
772 }
773}
774
775void QgsAttributesFormProperties::mTbInitCode_clicked()
776{
777 QgsAttributesFormInitCode attributesFormInitCode;
778
779 attributesFormInitCode.setCodeSource( mInitCodeSource );
780 attributesFormInitCode.setInitCode( mInitCode );
781 attributesFormInitCode.setInitFilePath( mInitFilePath );
782 attributesFormInitCode.setInitFunction( mInitFunction );
783
784 if ( !attributesFormInitCode.exec() )
785 return;
786
787 mInitCodeSource = attributesFormInitCode.codeSource();
788 mInitCode = attributesFormInitCode.initCode();
789 mInitFilePath = attributesFormInitCode.initFilePath();
790 mInitFunction = attributesFormInitCode.initFunction();
791}
792
793void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
794{
795 QgsSettings myQSettings;
796 const QString lastUsedDir = myQSettings.value( u"style/lastUIDir"_s, QDir::homePath() ).toString();
797 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
798
799 if ( uifilename.isNull() )
800 return;
801
802 const QFileInfo fi( uifilename );
803 myQSettings.setValue( u"style/lastUIDir"_s, fi.path() );
804 mEditFormLineEdit->setText( uifilename );
805}
806
808{
809 storeAttributeWidgetEdit();
810 storeAttributeContainerEdit();
811 storeAttributeTypeDialog();
812}
813
815{
816 mBlockUpdates++;
817 applyToLayer( mLayer );
818 mBlockUpdates--;
819}
820
821void QgsAttributesFormProperties::applyToLayer( QgsVectorLayer *layer )
822{
823 store();
824 QgsEditFormConfig editFormConfig = layer->editFormConfig();
825
826 const QModelIndex fieldContainer = mAvailableWidgetsModel->fieldContainer();
827 QModelIndex index;
828
829 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainer ); i++ )
830 {
831 index = mAvailableWidgetsModel->index( i, 0, fieldContainer );
833
834 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
835 const int idx = layer->fields().indexOf( fieldName );
836
837 //continue in case field does not exist anymore
838 if ( idx < 0 )
839 continue;
840
841 editFormConfig.setReadOnly( idx, !cfg.mEditable );
842 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
843 editFormConfig.setReuseLastValuePolicy( idx, cfg.mReuseLastValuePolicy );
844
845 if ( cfg.mDataDefinedProperties.count() > 0 )
846 {
847 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
848 }
849
850 layer->setEditorWidgetSetup( idx, QgsEditorWidgetSetup( cfg.mEditorWidgetType, cfg.mEditorWidgetConfig ) );
851
852 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
853 layer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
855 {
857 }
858 else
859 {
861 }
863 {
865 }
866 else
867 {
869 }
871 {
873 }
874 else
875 {
877 }
878
879 layer->setFieldAlias( idx, cfg.mAlias );
880 layer->setFieldSplitPolicy( idx, cfg.mSplitPolicy );
881 layer->setFieldDuplicatePolicy( idx, cfg.mDuplicatePolicy );
882 layer->setFieldMergePolicy( idx, cfg.mMergePolicy );
883
884 layer->setDefaultValueDefinition( idx, QgsDefaultValue( cfg.mDefaultValueExpression, cfg.mApplyDefaultValueOnUpdate ) );
885 }
886
887 // // tabs and groups
888 editFormConfig.clearTabs();
889
890 for ( int t = 0; t < mFormLayoutModel->rowCount(); t++ )
891 {
892 QModelIndex index = mFormLayoutModel->index( t, 0 );
893 QgsAttributeEditorElement *editorElement { mFormLayoutModel->createAttributeEditorWidget( index, nullptr ) };
894 if ( editorElement )
895 editFormConfig.addTab( editorElement );
896 }
897
898 editFormConfig.setUiForm( mEditFormLineEdit->text() );
899
900 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
901
902 editFormConfig.setInitCodeSource( mInitCodeSource );
903 editFormConfig.setInitFunction( mInitFunction );
904 editFormConfig.setInitFilePath( mInitFilePath );
905 editFormConfig.setInitCode( mInitCode );
906
907 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
908
909 // write the legacy config of relation widgets to support settings read by the API
910 const QModelIndex relationContainer = mAvailableWidgetsModel->relationContainer();
911
912 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( relationContainer ); i++ )
913 {
914 const QModelIndex relationIndex = mAvailableWidgetsModel->index( i, 0, relationContainer );
915
916 const QgsAttributesFormData::AttributeFormItemData itemData = relationIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
917 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( relationIndex.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
918 const QString indexId = relationIndex.data( QgsAttributesFormModel::ItemIdRole ).toString();
919
920 const QModelIndex layoutIndex = mFormLayoutModel->firstRecursiveMatchingModelIndex( indexType, indexId );
921 if ( layoutIndex.isValid() )
922 {
923 QVariantMap config;
924
925 const QgsAttributesFormData::AttributeFormItemData tabIndexData = layoutIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
926 config[u"nm-rel"_s] = tabIndexData.relationEditorConfiguration().nmRelationId;
927 config[u"force-suppress-popup"_s] = tabIndexData.relationEditorConfiguration().forceSuppressFormPopup;
928
929 editFormConfig.setWidgetConfig( indexId, config );
930 break;
931 }
932 }
933
934 layer->setEditFormConfig( editFormConfig );
935}
936
937void QgsAttributesFormProperties::updatedFields()
938{
939 // Store configuration to insure changes made are kept after refreshing the list
940 QMap<QString, QgsAttributesFormData::FieldConfig> fieldConfigs;
941
942 const QModelIndex fieldContainerBefore = mAvailableWidgetsModel->fieldContainer();
943 QModelIndex index;
944
945 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerBefore ); i++ )
946 {
947 index = mAvailableWidgetsModel->index( i, 0, fieldContainerBefore );
948 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
949 const QgsAttributesFormData::FieldConfig config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
950 fieldConfigs[fieldName] = config;
951 }
952
954
955 const QModelIndex fieldContainerAfter = mAvailableWidgetsModel->fieldContainer();
956
957 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerAfter ); i++ )
958 {
959 index = mAvailableWidgetsModel->index( i, 0, fieldContainerAfter );
960 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
961
962 if ( fieldConfigs.contains( fieldName ) )
963 {
964 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName], QgsAttributesFormModel::ItemFieldConfigRole );
965 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName].mAlias, QgsAttributesFormModel::ItemDisplayRole );
966 }
967 }
968}
969
970void QgsAttributesFormProperties::updateFilteredItems( const QString &filterText )
971{
972 const int availableWidgetsPreviousSelectionCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
973 const int formLayoutPreviousSelectionCount = mFormLayoutView->selectionModel()->selectedRows().count();
974
975 static_cast< QgsAttributesAvailableWidgetsView *>( mAvailableWidgetsView )->setFilterText( filterText );
976 mAvailableWidgetsView->expandAll();
977
978 static_cast< QgsAttributesFormLayoutView *>( mFormLayoutView )->setFilterText( filterText );
979 mFormLayoutView->expandAll();
980
981 // If there was no previous selection leave as is, since
982 // after a filter change no new selection may be added (only lost).
983 if ( !( availableWidgetsPreviousSelectionCount == 0 && formLayoutPreviousSelectionCount == 0 ) )
984 {
985 const int selectedAvailableWidgetItemCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
986 const int selectedFormLayoutItemCount = mFormLayoutView->selectionModel()->selectedRows().count();
987
988 if ( selectedAvailableWidgetItemCount == 0 && selectedFormLayoutItemCount == 0 )
989 {
990 // Clear right-hand side panel since all selected items have been filtered out
991 clearAttributeTypeFrame();
992 }
993 }
994}
995
996void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
997{
998 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
999 return;
1000
1001 QPoint globalPos = mAvailableWidgetsView->viewport()->mapToGlobal( point );
1002
1003 const QModelIndex index = mAvailableWidgetsView->indexAt( point );
1004 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1005 if ( itemType == QgsAttributesFormData::Field )
1006 {
1007 const QClipboard *clipboard = QApplication::clipboard();
1008 const QMimeData *mimeData = clipboard->mimeData();
1009 if ( !mimeData )
1010 return;
1011
1012 const bool pasteEnabled = mimeData->hasFormat( u"application/x-qgsattributetabledesignerelementclipboard"_s );
1013 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
1014 mAvailableWidgetsContextMenu->popup( globalPos );
1015 }
1016}
1017
1018void QgsAttributesFormProperties::copyWidgetConfiguration()
1019{
1020 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1021 return;
1022
1023 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1024 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1025
1026 if ( itemType != QgsAttributesFormData::Field )
1027 return;
1028
1029 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1030 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1031
1032 if ( fieldIndex < 0 )
1033 return;
1034
1035 const QgsField field = mLayer->fields().field( fieldIndex );
1036
1037 // We'll copy everything but field aliases or comments
1038 QDomDocument doc;
1039 QDomElement documentElement = doc.createElement( u"FormWidgetClipboard"_s );
1040 documentElement.setAttribute( u"name"_s, field.name() );
1041
1042 // Editor widget setup
1043 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
1044
1045 QDomElement editWidgetElement = doc.createElement( u"editWidget"_s );
1046 documentElement.appendChild( editWidgetElement );
1047 editWidgetElement.setAttribute( u"type"_s, widgetSetup.type() );
1048 QDomElement editWidgetConfigElement = doc.createElement( u"config"_s );
1049
1050 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
1051 editWidgetElement.appendChild( editWidgetConfigElement );
1052
1053 // Split policy
1054 QDomElement splitPolicyElement = doc.createElement( u"splitPolicy"_s );
1055 splitPolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.splitPolicy() ) );
1056 documentElement.appendChild( splitPolicyElement );
1057
1058 // Duplicate policy
1059 QDomElement duplicatePolicyElement = doc.createElement( u"duplicatePolicy"_s );
1060 duplicatePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.duplicatePolicy() ) );
1061 documentElement.appendChild( duplicatePolicyElement );
1062
1063 // Merge policy
1064 QDomElement mergePolicyElement = doc.createElement( u"mergePolicy"_s );
1065 mergePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.mergePolicy() ) );
1066 documentElement.appendChild( mergePolicyElement );
1067
1068 // Default expressions
1069 QDomElement defaultElem = doc.createElement( u"default"_s );
1070 defaultElem.setAttribute( u"expression"_s, field.defaultValueDefinition().expression() );
1071 defaultElem.setAttribute( u"applyOnUpdate"_s, field.defaultValueDefinition().applyOnUpdate() ? u"1"_s : u"0"_s );
1072 documentElement.appendChild( defaultElem );
1073
1074 // Constraints
1075 QDomElement constraintElem = doc.createElement( u"constraint"_s );
1076 constraintElem.setAttribute( u"constraints"_s, field.constraints().constraints() );
1077 constraintElem.setAttribute( u"unique_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
1078 constraintElem.setAttribute( u"notnull_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
1079 constraintElem.setAttribute( u"exp_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
1080 documentElement.appendChild( constraintElem );
1081
1082 // Constraint expressions
1083 QDomElement constraintExpressionElem = doc.createElement( u"constraintExpression"_s );
1084 constraintExpressionElem.setAttribute( u"exp"_s, field.constraints().constraintExpression() );
1085 constraintExpressionElem.setAttribute( u"desc"_s, field.constraints().constraintDescription() );
1086 documentElement.appendChild( constraintExpressionElem );
1087
1088 // Widget general settings
1090 {
1091 QDomElement widgetGeneralSettingsElem = doc.createElement( u"widgetGeneralSettings"_s );
1092 widgetGeneralSettingsElem.setAttribute( u"editable"_s, mAttributeTypeDialog->fieldEditable() );
1093 widgetGeneralSettingsElem.setAttribute( u"label_on_top"_s, mAttributeTypeDialog->labelOnTop() );
1094 widgetGeneralSettingsElem.setAttribute( u"reuse_last_value_policy"_s, qgsEnumValueToKey( mAttributeTypeDialog->reuseLastValuePolicy() ) );
1095 documentElement.appendChild( widgetGeneralSettingsElem );
1096 }
1097
1098 // Widget display section
1100 {
1101 // Go for the corresponding form layout item and extract its display settings
1102 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
1103 return;
1104
1105 const QModelIndex indexLayout = mFormLayoutView->firstSelectedIndex();
1106 const auto layoutData = indexLayout.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
1107
1108 QDomElement displayElement = doc.createElement( u"widgetDisplay"_s );
1109 displayElement.setAttribute( u"showLabel"_s, layoutData.showLabel() );
1110 displayElement.setAttribute( u"horizontalStretch"_s, layoutData.horizontalStretch() );
1111 displayElement.setAttribute( u"verticalStretch"_s, layoutData.verticalStretch() );
1112 displayElement.appendChild( layoutData.labelStyle().writeXml( doc ) );
1113 documentElement.appendChild( displayElement );
1114 }
1115
1116 doc.appendChild( documentElement );
1117
1118 QMimeData *mimeData = new QMimeData;
1119 mimeData->setData( u"application/x-qgsattributetabledesignerelementclipboard"_s, doc.toByteArray() );
1120 QClipboard *clipboard = QApplication::clipboard();
1121 clipboard->setMimeData( mimeData );
1122}
1123
1124void QgsAttributesFormProperties::pasteWidgetConfiguration()
1125{
1126 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1127 return;
1128
1129 QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1130
1131 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1132 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1133
1134 if ( fieldIndex < 0 )
1135 return;
1136
1137 // Get base config from target item and ovewrite settings when possible
1138 auto config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
1139
1140 QDomDocument doc;
1141 QClipboard *clipboard = QApplication::clipboard();
1142 const QMimeData *mimeData = clipboard->mimeData();
1143 if ( !mimeData )
1144 return;
1145
1146 if ( doc.setContent( mimeData->data( u"application/x-qgsattributetabledesignerelementclipboard"_s ) ) )
1147 {
1148 QgsReadWriteContext context;
1149 QDomElement docElem = doc.documentElement();
1150 if ( docElem.tagName() != "FormWidgetClipboard"_L1 )
1151 return;
1152
1153 // When pasting, the target item has already been selected and
1154 // has triggered attribute type dialog loading. Therefore, we'll
1155 // only overwrite GUI settings instead of destroying and recreating
1156 // the whole dialog.
1157
1158 // Editor widget configuration
1159 const QDomElement fieldWidgetElement = docElem.firstChildElement( u"editWidget"_s );
1160 if ( !fieldWidgetElement.isNull() )
1161 {
1162 const QString widgetType = fieldWidgetElement.attribute( u"type"_s );
1163
1164 // Only paste if source editor widget type is supported by target field
1165 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
1166 if ( factory->supportsField( mLayer, fieldIndex ) )
1167 {
1168 const QDomElement configElement = fieldWidgetElement.firstChildElement( u"config"_s );
1169 if ( !configElement.isNull() )
1170 {
1171 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
1172 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
1173 config.mEditorWidgetType = widgetType;
1174 config.mEditorWidgetConfig = optionsMap;
1175 }
1176 }
1177 else
1178 {
1179 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 );
1180 }
1181 }
1182
1183 // Split policy
1184 const QDomElement splitPolicyElement = docElem.firstChildElement( u"splitPolicy"_s );
1185 if ( !splitPolicyElement.isNull() )
1186 {
1187 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainSplitPolicy::Duplicate );
1188 config.mSplitPolicy = policy;
1189 }
1190
1191 // Duplicate policy
1192 const QDomElement duplicatePolicyElement = docElem.firstChildElement( u"duplicatePolicy"_s );
1193 if ( !duplicatePolicyElement.isNull() )
1194 {
1195 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( u"policy"_s ), Qgis::FieldDuplicatePolicy::Duplicate );
1196 config.mDuplicatePolicy = policy;
1197 }
1198
1199 // Merge policy
1200 const QDomElement mergePolicyElement = docElem.firstChildElement( u"mergePolicy"_s );
1201 if ( !mergePolicyElement.isNull() )
1202 {
1203 const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainMergePolicy::DefaultValue );
1204 config.mMergePolicy = policy;
1205 }
1206
1207 // Default expressions
1208 const QDomElement defaultElement = docElem.firstChildElement( u"default"_s );
1209 if ( !defaultElement.isNull() )
1210 {
1211 config.mDefaultValueExpression = defaultElement.attribute( u"expression"_s );
1212 config.mApplyDefaultValueOnUpdate = defaultElement.attribute( u"applyOnUpdate"_s ).toInt();
1213 }
1214
1215 // Constraints
1216 // take target field constraints as a basis
1217 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
1218 const QDomElement constraintElement = docElem.firstChildElement( u"constraint"_s );
1219 if ( !constraintElement.isNull() )
1220 {
1221 const int intConstraints = constraintElement.attribute( u"constraints"_s, u"0"_s ).toInt();
1222 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
1223
1224 // always keep provider constraints intact
1226 {
1227 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
1229 else
1231 }
1233 {
1234 if ( constraints & QgsFieldConstraints::ConstraintUnique )
1236 else
1238 }
1240 {
1243 else
1245 }
1246
1247 const int uniqueStrength = constraintElement.attribute( u"unique_strength"_s, u"1"_s ).toInt();
1248 const int notNullStrength = constraintElement.attribute( u"notnull_strength"_s, u"1"_s ).toInt();
1249 const int expStrength = constraintElement.attribute( u"exp_strength"_s, u"1"_s ).toInt();
1250
1254 }
1255
1256 // Constraint expressions
1257 // always keep provider constraints intact
1259 {
1260 const QDomElement constraintExpressionElement = docElem.firstChildElement( u"constraintExpression"_s );
1261 if ( !constraintExpressionElement.isNull() )
1262 {
1263 QString expression = constraintExpressionElement.attribute( u"exp"_s, QString() );
1264 QString description = constraintExpressionElement.attribute( u"desc"_s, QString() );
1265 fieldConstraints.setConstraintExpression( expression, description );
1266 }
1267 }
1268 config.mFieldConstraints = fieldConstraints;
1269
1270 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( u"widgetGeneralSettings"_s );
1271 if ( !widgetGeneralSettingsElement.isNull() )
1272 {
1273 const int editable = widgetGeneralSettingsElement.attribute( u"editable"_s, u"0"_s ).toInt();
1275 if ( widgetGeneralSettingsElement.hasAttribute( u"reuse_last_values"_s ) )
1276 {
1277 reusePolicy = widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s, u"0"_s ).toInt() == 1 ? Qgis::AttributeFormReuseLastValuePolicy::AllowedDefaultOn : Qgis::AttributeFormReuseLastValuePolicy::NotAllowed;
1278 }
1279 else
1280 {
1281 reusePolicy = qgsEnumKeyToValue( widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s ), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed );
1282 }
1283 const int labelOnTop = widgetGeneralSettingsElement.attribute( u"label_on_top"_s, u"0"_s ).toInt();
1284
1285 config.mEditable = editable;
1286 config.mReuseLastValuePolicy = reusePolicy;
1287 config.mLabelOnTop = labelOnTop;
1288 }
1289
1290 loadAttributeTypeDialogFromConfiguration( config );
1291
1292 // Widget display section
1294 {
1295 const QDomElement displayElement = docElem.firstChildElement( u"widgetDisplay"_s );
1296 if ( !displayElement.isNull() )
1297 {
1298 const int showLabel = displayElement.attribute( u"showLabel"_s, u"0"_s ).toInt();
1299 const int horizontalStretch = displayElement.attribute( u"horizontalStretch"_s, u"0"_s ).toInt();
1300 const int verticalStretch = displayElement.attribute( u"verticalStretch"_s, u"0"_s ).toInt();
1301 QgsAttributeEditorElement::LabelStyle style;
1302 style.readXml( displayElement );
1303
1304 // Update current GUI controls
1305 mAttributeWidgetEdit->setShowLabel( showLabel );
1306 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
1307 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
1308 mAttributeWidgetEdit->setLabelStyle( style );
1309 }
1310 }
1311 }
1312}
1313
1314void QgsAttributesFormProperties::setAvailableWidgetsIndicatorProvidersEnabled( bool enabled )
1315{
1316 // Only enable if the provider is disabled and only disable if it's enabled
1317 if ( enabled && !mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1318 {
1319 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1320 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1321 }
1322 else if ( !enabled && mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1323 {
1324 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1325 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1326 }
1327
1328 if ( enabled && !mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1329 {
1330 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1331 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1332 }
1333 else if ( !enabled && mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1334 {
1335 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1336 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1337 }
1338}
1339
1340void QgsAttributesFormProperties::setFormLayoutIndicatorProvidersEnabled( bool enabled )
1341{
1342 // Only enable if the provider is disabled and only disable if it's enabled
1343 if ( enabled && !mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1344 {
1345 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1346 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1347 }
1348 else if ( !enabled && mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1349 {
1350 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1351 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1352 }
1353
1354 if ( enabled && !mConstraintIndicatorProviderFormLayout->isEnabled() )
1355 {
1356 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1357 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1358 }
1359 else if ( !enabled && mConstraintIndicatorProviderFormLayout->isEnabled() )
1360 {
1361 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1362 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1363 }
1364}
1365
1366void QgsAttributesFormProperties::previewForm()
1367{
1368 if ( !mSourceFieldsProperties )
1369 {
1370 return;
1371 }
1372
1373 auto projectDirtyBlocker = std::make_unique<QgsProjectDirtyBlocker>( QgsProject::instance() );
1374
1375
1376 QgsFields fields;
1377 QList<QPair<QgsField, QString>> expressionFields;
1378
1379 for ( int i = 0; i < mLayer->fields().size(); i++ )
1380 {
1381 if ( mLayer->fields().fieldOrigin( i ) == Qgis::FieldOrigin::Expression )
1382 {
1383 expressionFields << qMakePair( mLayer->fields().at( i ), mLayer->expressionField( i ) );
1384 }
1385 else
1386 {
1387 fields.append( mLayer->fields().at( i ) );
1388 }
1389 }
1390
1391 std::unique_ptr<QgsVectorLayer> vlayer;
1392 vlayer.reset( QgsMemoryProviderUtils::createMemoryLayer( "preview"_L1, fields, mLayer->wkbType(), mLayer->crs() ) );
1393 for ( const QPair<QgsField, QString> &expressionField : std::as_const( expressionFields ) )
1394 {
1395 vlayer->addExpressionField( expressionField.second, expressionField.first );
1396 }
1397
1398 mSourceFieldsProperties->applyToLayer( vlayer.get() );
1399 applyToLayer( vlayer.get() );
1400
1401 QgsFeature feature = QgsVectorLayerUtils::createFeature( vlayer.get() );
1402 QgsAttributeDialog form( vlayer.get(), &feature, false, this, true );
1404 form.exec();
1405
1406 projectDirtyBlocker.reset();
1407}
AttributeFormReuseLastValuePolicy
Attribute form policy for reusing last entered values.
Definition qgis.h:5795
@ AllowedDefaultOn
Reuse of last values allowed and enabled by default.
Definition qgis.h:5797
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:5796
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5766
@ On
Always suppress feature form.
Definition qgis.h:5768
@ Default
Use the application-wide setting.
Definition qgis.h:5767
@ Off
Never suppress feature form.
Definition qgis.h:5769
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5751
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
Definition qgis.h:5753
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
Definition qgis.h:5752
@ UiFile
Load a .ui file for the layout. Needs to be configured.
Definition qgis.h:5754
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:3982
@ DefaultValue
Use default field value.
Definition qgis.h:3983
@ Warning
Warning message.
Definition qgis.h:161
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:3965
@ Duplicate
Duplicate original value.
Definition qgis.h:3967
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:4002
@ Duplicate
Duplicate original value.
Definition qgis.h:4004
@ Expression
Field is calculated from an expression.
Definition qgis.h:1767
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:766
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:776
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:786
QgsFieldConstraints constraints
Definition qgsfield.h:68
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:751
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:76
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:112
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 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:7110
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
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