QGIS API Documentation 4.3.0-Master (9ff14a2eeba)
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 &selected, const QItemSelection &deselected )
516{
517 if ( selected.isEmpty() && deselected.isEmpty() )
518 {
519 // No-op notification, emitted e.g. by QItemSelectionModel when rows are inserted
520 return;
521 }
522
523 // when the selection changes in the main tree, sync the DnD layout
524 // block both selection handlers while syncing
525 disconnect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
526 disconnect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
527
528 QModelIndex index;
529 if ( mFormLayoutView->selectionModel()->selectedRows( 0 ).count() == 1 )
530 {
531 // Go to the form layout view and store the single-selected index, as
532 // it will be used to store its current settings before being deselected
533 index = mFormLayoutView->firstSelectedIndex();
534 }
535
536 loadAttributeSpecificEditor( mAvailableWidgetsView, mFormLayoutView, index );
537 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
538 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
539}
540
541void QgsAttributesFormProperties::onFormLayoutSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
542{
543 if ( selected.isEmpty() && deselected.isEmpty() )
544 {
545 // No-op notification, emitted e.g. by QItemSelectionModel when rows are inserted
546 return;
547 }
548
549 // when the selection changes in the DnD layout, sync the main tree
550 // block both selection handlers while syncing
551 disconnect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
552 disconnect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
553
554 QModelIndex index;
555 if ( deselected.indexes().count() == 1 )
556 {
557 index = mFormLayoutProxyModel->mapToSource( deselected.indexes().at( 0 ) );
558 }
559 else if ( deselected.indexes().count() == 0 && mFormLayoutView->selectionModel()->selectedIndexes().count() == 2 )
560 {
561 // There was 1 selected, it was not deselected, but instead a new item was added to selection
562 index = mFormLayoutView->firstSelectedIndex();
563 }
564
565 loadAttributeSpecificEditor( mFormLayoutView, mAvailableWidgetsView, index );
566 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
567 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
568}
569
570void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesFormBaseView *emitter, QgsAttributesFormBaseView *receiver, QModelIndex &deselectedFormLayoutIndex )
571{
572 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
573
575 {
576 storeAttributeWidgetEdit( deselectedFormLayoutIndex );
577 storeAttributeContainerEdit( deselectedFormLayoutIndex );
578 }
580 {
581 storeAttributeTypeDialog();
582 }
583
584 clearAttributeTypeFrame();
585
586 if ( emitter->selectionModel()->selectedRows( 0 ).count() != 1 )
587 {
588 receiver->clearSelection();
589 }
590 else
591 {
592 const QModelIndex index = emitter->firstSelectedIndex();
593 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
594 switch ( indexType )
595 {
597 {
600 {
601 loadAttributeWidgetEdit();
602 }
603 else
604 {
605 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
606 }
607 break;
608 }
610 {
613 {
614 loadAttributeWidgetEdit();
615 }
616 loadAttributeTypeDialog();
617 break;
618 }
620 {
621 receiver->clearSelection();
622 loadAttributeContainerEdit();
623 break;
624 }
626 {
628 const QgsAction action { mLayer->actions()->action( index.data( QgsAttributesFormModel::ItemIdRole ).toString() ) };
629 loadInfoWidget( action.html() );
630 break;
631 }
636 {
638 {
639 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
640 }
641 else
642 {
643 loadInfoWidget( tr( "This configuration is available with double-click in the Form Layout panel" ) );
644 }
645 receiver->clearSelection();
646 break;
647 }
649 {
650 receiver->clearSelection();
651 break;
652 }
653 }
654 }
655}
656
657void QgsAttributesFormProperties::clearAttributeTypeFrame()
658{
660 {
661 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
662 mAttributeWidgetEdit->deleteLater();
663 mAttributeWidgetEdit = nullptr;
664 }
666 {
667 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
668 mAttributeTypeDialog->deleteLater();
669 mAttributeTypeDialog = nullptr;
670 }
672 {
673 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
674 mAttributeContainerEdit->deleteLater();
675 mAttributeContainerEdit = nullptr;
676 }
677 if ( mInfoTextWidget )
678 {
679 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
680 mInfoTextWidget->deleteLater();
681 mInfoTextWidget = nullptr;
682 }
683}
684
685void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
686{
687 Q_UNUSED( checked )
688 for ( int i = 0; i < mFormLayoutProxyModel->rowCount(); ++i )
689 {
690 QModelIndex index = mFormLayoutProxyModel->index( i, 0 );
691 mFormLayoutView->selectionModel()->select( index, QItemSelectionModel::Toggle );
692 }
693}
694
695void QgsAttributesFormProperties::toggleShowAliases( bool checked )
696{
697 settingShowAliases->setValue( checked );
698 mAvailableWidgetsModel->setShowAliases( checked );
699 mFormLayoutModel->setShowAliases( checked );
700}
701
702void QgsAttributesFormProperties::addContainer()
703{
704 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList = mFormLayoutModel->listOfContainers();
705
706 QModelIndex currentItem;
707 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
708 currentItem = mFormLayoutView->firstSelectedIndex();
709
710 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
711
712 if ( !dialog.exec() )
713 return;
714
715 const QString name = dialog.name();
716 QModelIndex parentContainerItem = dialog.parentContainerItem();
717
718 mFormLayoutModel->addContainer( parentContainerItem, name, dialog.columnCount(), dialog.containerType() );
719 if ( parentContainerItem.isValid() )
720 mFormLayoutView->setExpanded( parentContainerItem, true );
721}
722
723void QgsAttributesFormProperties::removeTabOrGroupButton()
724{
725 // Take a snapshot of the selected rows in the proxy model, and map them to the source model.
726 // This is necessary because removing rows from the source model will invalidate the proxy model's indexes.
727 const QModelIndexList selectedRows = mFormLayoutView->selectionModel()->selectedRows();
728
729 QList< QPersistentModelIndex > sourceIndexes;
730 sourceIndexes.reserve( selectedRows.size() );
731 for ( const QModelIndex &proxyIndex : selectedRows )
732 sourceIndexes << QPersistentModelIndex( mFormLayoutProxyModel->mapToSource( proxyIndex ) );
733
734 // Clear the selection up front so that no selection handler fires while rows are being removed
735 mFormLayoutView->selectionModel()->clearSelection();
736
737 for ( const QPersistentModelIndex &sourceIndex : std::as_const( sourceIndexes ) )
738 {
739 if ( sourceIndex.isValid() )
740 mFormLayoutModel->removeRow( sourceIndex.row(), sourceIndex.parent() );
741 }
742}
743
744void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
745{
746 // Refresh the right-hand side panel: first, save selection to recover it later
747 const QItemSelection selection = mAvailableWidgetsView->selectionModel()->selection();
748 if ( selection.count() > 0 )
749 {
750 mAvailableWidgetsView->selectionModel()->clear();
751 }
752
753 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
754 {
755 mFormLayoutView->selectionModel()->clear(); // Get rid of e.g., container selection
756 }
757
758 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
759 switch ( layout )
760 {
762 mFormLayoutWidget->setVisible( false );
763 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
764 mUiFileFrame->setVisible( false );
765 mAddContainerButton->setVisible( false );
766 mRemoveLayoutItemButton->setVisible( false );
767 mInvertSelectionButton->setVisible( false );
768
769 setAvailableWidgetsIndicatorProvidersEnabled( true );
770 setFormLayoutIndicatorProvidersEnabled( false );
771 break;
772
774 mFormLayoutWidget->setVisible( true );
775 mTreeViewHorizontalSpacer->changeSize( 6, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
776 mUiFileFrame->setVisible( false );
777 mAddContainerButton->setVisible( true );
778 mRemoveLayoutItemButton->setVisible( true );
779 mInvertSelectionButton->setVisible( true );
780
781 setAvailableWidgetsIndicatorProvidersEnabled( false );
782 setFormLayoutIndicatorProvidersEnabled( true );
783 break;
784
786 // ui file
787 mFormLayoutWidget->setVisible( false );
788 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
789 mUiFileFrame->setVisible( true );
790 mAddContainerButton->setVisible( false );
791 mRemoveLayoutItemButton->setVisible( false );
792 mInvertSelectionButton->setVisible( false );
793
794 setAvailableWidgetsIndicatorProvidersEnabled( true );
795 setFormLayoutIndicatorProvidersEnabled( false );
796 break;
797 }
798
799 // Get the selection back so that we refresh the right-hand side panel
800 if ( selection.count() > 0 )
801 {
802 mAvailableWidgetsView->selectionModel()->select( selection, QItemSelectionModel::Select );
803 }
804}
805
806void QgsAttributesFormProperties::mTbInitCode_clicked()
807{
808 QgsAttributesFormInitCode attributesFormInitCode;
809
810 attributesFormInitCode.setCodeSource( mInitCodeSource );
811 attributesFormInitCode.setInitCode( mInitCode );
812 attributesFormInitCode.setInitFilePath( mInitFilePath );
813 attributesFormInitCode.setInitFunction( mInitFunction );
814
815 if ( !attributesFormInitCode.exec() )
816 return;
817
818 mInitCodeSource = attributesFormInitCode.codeSource();
819 mInitCode = attributesFormInitCode.initCode();
820 mInitFilePath = attributesFormInitCode.initFilePath();
821 mInitFunction = attributesFormInitCode.initFunction();
822}
823
824void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
825{
826 QgsSettings myQSettings;
827 const QString lastUsedDir = myQSettings.value( u"style/lastUIDir"_s, QDir::homePath() ).toString();
828 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
829
830 if ( uifilename.isNull() )
831 return;
832
833 const QFileInfo fi( uifilename );
834 myQSettings.setValue( u"style/lastUIDir"_s, fi.path() );
835 mEditFormLineEdit->setText( uifilename );
836}
837
839{
840 storeAttributeWidgetEdit();
841 storeAttributeContainerEdit();
842 storeAttributeTypeDialog();
843}
844
846{
847 mBlockUpdates++;
848 applyToLayer( mLayer );
849 mBlockUpdates--;
850}
851
852void QgsAttributesFormProperties::applyToLayer( QgsVectorLayer *layer )
853{
854 store();
855 QgsEditFormConfig editFormConfig = layer->editFormConfig();
856
857 const QModelIndex fieldContainer = mAvailableWidgetsModel->fieldContainer();
858 QModelIndex index;
859
860 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainer ); i++ )
861 {
862 index = mAvailableWidgetsModel->index( i, 0, fieldContainer );
864
865 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
866 const int idx = layer->fields().indexOf( fieldName );
867
868 //continue in case field does not exist anymore
869 if ( idx < 0 )
870 continue;
871
872 editFormConfig.setReadOnly( idx, !cfg.mEditable );
873 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
874 editFormConfig.setReuseLastValuePolicy( idx, cfg.mReuseLastValuePolicy );
875
876 if ( cfg.mDataDefinedProperties.count() > 0 )
877 {
878 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
879 }
880
881 layer->setEditorWidgetSetup( idx, QgsEditorWidgetSetup( cfg.mEditorWidgetType, cfg.mEditorWidgetConfig ) );
882
883 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
884 layer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
886 {
888 }
889 else
890 {
892 }
894 {
896 }
897 else
898 {
900 }
902 {
904 }
905 else
906 {
908 }
909
910 layer->setFieldAlias( idx, cfg.mAlias );
911 layer->setFieldCustomComment( idx, cfg.mCustomComment );
912 layer->setFieldSplitPolicy( idx, cfg.mSplitPolicy );
913 layer->setFieldDuplicatePolicy( idx, cfg.mDuplicatePolicy );
914 layer->setFieldMergePolicy( idx, cfg.mMergePolicy );
915
916 layer->setDefaultValueDefinition( idx, QgsDefaultValue( cfg.mDefaultValueExpression, cfg.mApplyDefaultValueOnUpdate ) );
917 }
918
919 // // tabs and groups
920 editFormConfig.clearTabs();
921
922 for ( int t = 0; t < mFormLayoutModel->rowCount(); t++ )
923 {
924 QModelIndex index = mFormLayoutModel->index( t, 0 );
925 QgsAttributeEditorElement *editorElement { mFormLayoutModel->createAttributeEditorWidget( index, nullptr ) };
926 if ( editorElement )
927 editFormConfig.addTab( editorElement );
928 }
929
930 editFormConfig.setUiForm( mEditFormLineEdit->text() );
931
932 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
933
934 editFormConfig.setInitCodeSource( mInitCodeSource );
935 editFormConfig.setInitFunction( mInitFunction );
936 editFormConfig.setInitFilePath( mInitFilePath );
937 editFormConfig.setInitCode( mInitCode );
938
939 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
940
941 // write the legacy config of relation widgets to support settings read by the API
942 const QModelIndex relationContainer = mAvailableWidgetsModel->relationContainer();
943
944 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( relationContainer ); i++ )
945 {
946 const QModelIndex relationIndex = mAvailableWidgetsModel->index( i, 0, relationContainer );
947
948 const QgsAttributesFormData::AttributeFormItemData itemData = relationIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
949 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( relationIndex.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
950 const QString indexId = relationIndex.data( QgsAttributesFormModel::ItemIdRole ).toString();
951
952 const QModelIndex layoutIndex = mFormLayoutModel->firstRecursiveMatchingModelIndex( indexType, indexId );
953 if ( layoutIndex.isValid() )
954 {
955 QVariantMap config;
956
957 const QgsAttributesFormData::AttributeFormItemData tabIndexData = layoutIndex.data( QgsAttributesFormModel::ItemDataRole ).value<QgsAttributesFormData::AttributeFormItemData>();
958 config[u"nm-rel"_s] = tabIndexData.relationEditorConfiguration().nmRelationId;
959 config[u"force-suppress-popup"_s] = tabIndexData.relationEditorConfiguration().forceSuppressFormPopup;
960
961 editFormConfig.setWidgetConfig( indexId, config );
962 break;
963 }
964 }
965
966 layer->setEditFormConfig( editFormConfig );
967}
968
969void QgsAttributesFormProperties::updatedFields()
970{
971 // Store configuration to insure changes made are kept after refreshing the list
972 QMap<QString, QgsAttributesFormData::FieldConfig> fieldConfigs;
973
974 const QModelIndex fieldContainerBefore = mAvailableWidgetsModel->fieldContainer();
975 QModelIndex index;
976
977 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerBefore ); i++ )
978 {
979 index = mAvailableWidgetsModel->index( i, 0, fieldContainerBefore );
980 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
981 const QgsAttributesFormData::FieldConfig config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
982 fieldConfigs[fieldName] = config;
983 }
984
986
987 const QModelIndex fieldContainerAfter = mAvailableWidgetsModel->fieldContainer();
988
989 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerAfter ); i++ )
990 {
991 index = mAvailableWidgetsModel->index( i, 0, fieldContainerAfter );
992 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
993
994 if ( fieldConfigs.contains( fieldName ) )
995 {
996 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName], QgsAttributesFormModel::ItemFieldConfigRole );
997 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName].mAlias, QgsAttributesFormModel::ItemDisplayRole );
998 }
999 }
1000}
1001
1002void QgsAttributesFormProperties::updateFilteredItems( const QString &filterText )
1003{
1004 const int availableWidgetsPreviousSelectionCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
1005 const int formLayoutPreviousSelectionCount = mFormLayoutView->selectionModel()->selectedRows().count();
1006
1007 static_cast< QgsAttributesAvailableWidgetsView *>( mAvailableWidgetsView )->setFilterText( filterText );
1008 mAvailableWidgetsView->expandAll();
1009
1010 static_cast< QgsAttributesFormLayoutView *>( mFormLayoutView )->setFilterText( filterText );
1011 mFormLayoutView->expandAll();
1012
1013 // If there was no previous selection leave as is, since
1014 // after a filter change no new selection may be added (only lost).
1015 if ( !( availableWidgetsPreviousSelectionCount == 0 && formLayoutPreviousSelectionCount == 0 ) )
1016 {
1017 const int selectedAvailableWidgetItemCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
1018 const int selectedFormLayoutItemCount = mFormLayoutView->selectionModel()->selectedRows().count();
1019
1020 if ( selectedAvailableWidgetItemCount == 0 && selectedFormLayoutItemCount == 0 )
1021 {
1022 // Clear right-hand side panel since all selected items have been filtered out
1023 clearAttributeTypeFrame();
1024 }
1025 }
1026}
1027
1028void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
1029{
1030 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1031 return;
1032
1033 QPoint globalPos = mAvailableWidgetsView->viewport()->mapToGlobal( point );
1034
1035 const QModelIndex index = mAvailableWidgetsView->indexAt( point );
1036 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1037 if ( itemType == QgsAttributesFormData::Field )
1038 {
1039 const QClipboard *clipboard = QApplication::clipboard();
1040 const QMimeData *mimeData = clipboard->mimeData();
1041 if ( !mimeData )
1042 return;
1043
1044 const bool pasteEnabled = mimeData->hasFormat( u"application/x-qgsattributetabledesignerelementclipboard"_s );
1045 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
1046 mAvailableWidgetsContextMenu->popup( globalPos );
1047 }
1048}
1049
1050void QgsAttributesFormProperties::copyWidgetConfiguration()
1051{
1052 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1053 return;
1054
1055 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1056 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1057
1058 if ( itemType != QgsAttributesFormData::Field )
1059 return;
1060
1061 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1062 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1063
1064 if ( fieldIndex < 0 )
1065 return;
1066
1067 const QgsField field = mLayer->fields().field( fieldIndex );
1068
1069 // We'll copy everything but field aliases or comments
1070 QDomDocument doc;
1071 QDomElement documentElement = doc.createElement( u"FormWidgetClipboard"_s );
1072 documentElement.setAttribute( u"name"_s, field.name() );
1073
1074 // Editor widget setup
1075 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
1076
1077 QDomElement editWidgetElement = doc.createElement( u"editWidget"_s );
1078 documentElement.appendChild( editWidgetElement );
1079 editWidgetElement.setAttribute( u"type"_s, widgetSetup.type() );
1080 QDomElement editWidgetConfigElement = doc.createElement( u"config"_s );
1081
1082 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
1083 editWidgetElement.appendChild( editWidgetConfigElement );
1084
1085 // Split policy
1086 QDomElement splitPolicyElement = doc.createElement( u"splitPolicy"_s );
1087 splitPolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.splitPolicy() ) );
1088 documentElement.appendChild( splitPolicyElement );
1089
1090 // Duplicate policy
1091 QDomElement duplicatePolicyElement = doc.createElement( u"duplicatePolicy"_s );
1092 duplicatePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.duplicatePolicy() ) );
1093 documentElement.appendChild( duplicatePolicyElement );
1094
1095 // Merge policy
1096 QDomElement mergePolicyElement = doc.createElement( u"mergePolicy"_s );
1097 mergePolicyElement.setAttribute( u"policy"_s, qgsEnumValueToKey( field.mergePolicy() ) );
1098 documentElement.appendChild( mergePolicyElement );
1099
1100 // Default expressions
1101 QDomElement defaultElem = doc.createElement( u"default"_s );
1102 defaultElem.setAttribute( u"expression"_s, field.defaultValueDefinition().expression() );
1103 defaultElem.setAttribute( u"applyOnUpdate"_s, field.defaultValueDefinition().applyOnUpdate() ? u"1"_s : u"0"_s );
1104 documentElement.appendChild( defaultElem );
1105
1106 // Constraints
1107 QDomElement constraintElem = doc.createElement( u"constraint"_s );
1108 constraintElem.setAttribute( u"constraints"_s, field.constraints().constraints() );
1109 constraintElem.setAttribute( u"unique_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
1110 constraintElem.setAttribute( u"notnull_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
1111 constraintElem.setAttribute( u"exp_strength"_s, field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
1112 documentElement.appendChild( constraintElem );
1113
1114 // Constraint expressions
1115 QDomElement constraintExpressionElem = doc.createElement( u"constraintExpression"_s );
1116 constraintExpressionElem.setAttribute( u"exp"_s, field.constraints().constraintExpression() );
1117 constraintExpressionElem.setAttribute( u"desc"_s, field.constraints().constraintDescription() );
1118 documentElement.appendChild( constraintExpressionElem );
1119
1120 // Widget general settings
1122 {
1123 QDomElement widgetGeneralSettingsElem = doc.createElement( u"widgetGeneralSettings"_s );
1124 widgetGeneralSettingsElem.setAttribute( u"editable"_s, mAttributeTypeDialog->fieldEditable() );
1125 widgetGeneralSettingsElem.setAttribute( u"label_on_top"_s, mAttributeTypeDialog->labelOnTop() );
1126 widgetGeneralSettingsElem.setAttribute( u"reuse_last_value_policy"_s, qgsEnumValueToKey( mAttributeTypeDialog->reuseLastValuePolicy() ) );
1127 documentElement.appendChild( widgetGeneralSettingsElem );
1128 }
1129
1130 // Widget display section
1132 {
1133 // Go for the corresponding form layout item and extract its display settings
1134 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
1135 return;
1136
1137 const QModelIndex indexLayout = mFormLayoutView->firstSelectedIndex();
1138 const auto layoutData = indexLayout.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
1139
1140 QDomElement displayElement = doc.createElement( u"widgetDisplay"_s );
1141 displayElement.setAttribute( u"showLabel"_s, layoutData.showLabel() );
1142 displayElement.setAttribute( u"horizontalStretch"_s, layoutData.horizontalStretch() );
1143 displayElement.setAttribute( u"verticalStretch"_s, layoutData.verticalStretch() );
1144 displayElement.appendChild( layoutData.labelStyle().writeXml( doc ) );
1145 documentElement.appendChild( displayElement );
1146 }
1147
1148 doc.appendChild( documentElement );
1149
1150 QMimeData *mimeData = new QMimeData;
1151 mimeData->setData( u"application/x-qgsattributetabledesignerelementclipboard"_s, doc.toByteArray() );
1152 QClipboard *clipboard = QApplication::clipboard();
1153 clipboard->setMimeData( mimeData );
1154}
1155
1156void QgsAttributesFormProperties::pasteWidgetConfiguration()
1157{
1158 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1159 return;
1160
1161 QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1162
1163 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1164 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1165
1166 if ( fieldIndex < 0 )
1167 return;
1168
1169 // Get base config from target item and ovewrite settings when possible
1170 auto config = index.data( QgsAttributesFormModel::ItemFieldConfigRole ).value< QgsAttributesFormData::FieldConfig >();
1171
1172 QDomDocument doc;
1173 QClipboard *clipboard = QApplication::clipboard();
1174 const QMimeData *mimeData = clipboard->mimeData();
1175 if ( !mimeData )
1176 return;
1177
1178 if ( doc.setContent( mimeData->data( u"application/x-qgsattributetabledesignerelementclipboard"_s ) ) )
1179 {
1180 QgsReadWriteContext context;
1181 QDomElement docElem = doc.documentElement();
1182 if ( docElem.tagName() != "FormWidgetClipboard"_L1 )
1183 return;
1184
1185 // When pasting, the target item has already been selected and
1186 // has triggered attribute type dialog loading. Therefore, we'll
1187 // only overwrite GUI settings instead of destroying and recreating
1188 // the whole dialog.
1189
1190 // Editor widget configuration
1191 const QDomElement fieldWidgetElement = docElem.firstChildElement( u"editWidget"_s );
1192 if ( !fieldWidgetElement.isNull() )
1193 {
1194 const QString widgetType = fieldWidgetElement.attribute( u"type"_s );
1195
1196 // Only paste if source editor widget type is supported by target field
1197 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
1198 if ( factory->supportsField( mLayer, fieldIndex ) )
1199 {
1200 const QDomElement configElement = fieldWidgetElement.firstChildElement( u"config"_s );
1201 if ( !configElement.isNull() )
1202 {
1203 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
1204 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
1205 config.mEditorWidgetType = widgetType;
1206 config.mEditorWidgetConfig = optionsMap;
1207 }
1208 }
1209 else
1210 {
1211 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 );
1212 }
1213 }
1214
1215 // Split policy
1216 const QDomElement splitPolicyElement = docElem.firstChildElement( u"splitPolicy"_s );
1217 if ( !splitPolicyElement.isNull() )
1218 {
1219 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainSplitPolicy::Duplicate );
1220 config.mSplitPolicy = policy;
1221 }
1222
1223 // Duplicate policy
1224 const QDomElement duplicatePolicyElement = docElem.firstChildElement( u"duplicatePolicy"_s );
1225 if ( !duplicatePolicyElement.isNull() )
1226 {
1227 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( u"policy"_s ), Qgis::FieldDuplicatePolicy::Duplicate );
1228 config.mDuplicatePolicy = policy;
1229 }
1230
1231 // Merge policy
1232 const QDomElement mergePolicyElement = docElem.firstChildElement( u"mergePolicy"_s );
1233 if ( !mergePolicyElement.isNull() )
1234 {
1235 const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( u"policy"_s ), Qgis::FieldDomainMergePolicy::DefaultValue );
1236 config.mMergePolicy = policy;
1237 }
1238
1239 // Default expressions
1240 const QDomElement defaultElement = docElem.firstChildElement( u"default"_s );
1241 if ( !defaultElement.isNull() )
1242 {
1243 config.mDefaultValueExpression = defaultElement.attribute( u"expression"_s );
1244 config.mApplyDefaultValueOnUpdate = defaultElement.attribute( u"applyOnUpdate"_s ).toInt();
1245 }
1246
1247 // Constraints
1248 // take target field constraints as a basis
1249 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
1250 const QDomElement constraintElement = docElem.firstChildElement( u"constraint"_s );
1251 if ( !constraintElement.isNull() )
1252 {
1253 const int intConstraints = constraintElement.attribute( u"constraints"_s, u"0"_s ).toInt();
1254 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
1255
1256 // always keep provider constraints intact
1258 {
1259 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
1261 else
1263 }
1265 {
1266 if ( constraints & QgsFieldConstraints::ConstraintUnique )
1268 else
1270 }
1272 {
1275 else
1277 }
1278
1279 const int uniqueStrength = constraintElement.attribute( u"unique_strength"_s, u"1"_s ).toInt();
1280 const int notNullStrength = constraintElement.attribute( u"notnull_strength"_s, u"1"_s ).toInt();
1281 const int expStrength = constraintElement.attribute( u"exp_strength"_s, u"1"_s ).toInt();
1282
1286 }
1287
1288 // Constraint expressions
1289 // always keep provider constraints intact
1291 {
1292 const QDomElement constraintExpressionElement = docElem.firstChildElement( u"constraintExpression"_s );
1293 if ( !constraintExpressionElement.isNull() )
1294 {
1295 QString expression = constraintExpressionElement.attribute( u"exp"_s, QString() );
1296 QString description = constraintExpressionElement.attribute( u"desc"_s, QString() );
1297 fieldConstraints.setConstraintExpression( expression, description );
1298 }
1299 }
1300 config.mFieldConstraints = fieldConstraints;
1301
1302 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( u"widgetGeneralSettings"_s );
1303 if ( !widgetGeneralSettingsElement.isNull() )
1304 {
1305 const int editable = widgetGeneralSettingsElement.attribute( u"editable"_s, u"0"_s ).toInt();
1307 if ( widgetGeneralSettingsElement.hasAttribute( u"reuse_last_values"_s ) )
1308 {
1309 reusePolicy = widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s, u"0"_s ).toInt() == 1 ? Qgis::AttributeFormReuseLastValuePolicy::AllowedDefaultOn
1311 }
1312 else
1313 {
1314 reusePolicy = qgsEnumKeyToValue( widgetGeneralSettingsElement.attribute( u"reuse_last_values"_s ), Qgis::AttributeFormReuseLastValuePolicy::NotAllowed );
1315 }
1316 const int labelOnTop = widgetGeneralSettingsElement.attribute( u"label_on_top"_s, u"0"_s ).toInt();
1317
1318 config.mEditable = editable;
1319 config.mReuseLastValuePolicy = reusePolicy;
1320 config.mLabelOnTop = labelOnTop;
1321 }
1322
1323 loadAttributeTypeDialogFromConfiguration( config );
1324
1325 // Widget display section
1327 {
1328 const QDomElement displayElement = docElem.firstChildElement( u"widgetDisplay"_s );
1329 if ( !displayElement.isNull() )
1330 {
1331 const int showLabel = displayElement.attribute( u"showLabel"_s, u"0"_s ).toInt();
1332 const int horizontalStretch = displayElement.attribute( u"horizontalStretch"_s, u"0"_s ).toInt();
1333 const int verticalStretch = displayElement.attribute( u"verticalStretch"_s, u"0"_s ).toInt();
1334 QgsAttributeEditorElement::LabelStyle style;
1335 style.readXml( displayElement );
1336
1337 // Update current GUI controls
1338 mAttributeWidgetEdit->setShowLabel( showLabel );
1339 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
1340 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
1341 mAttributeWidgetEdit->setLabelStyle( style );
1342 }
1343 }
1344 }
1345}
1346
1347void QgsAttributesFormProperties::setAvailableWidgetsIndicatorProvidersEnabled( bool enabled )
1348{
1349 // Only enable if the provider is disabled and only disable if it's enabled
1350 if ( enabled && !mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1351 {
1352 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1353 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1354 }
1355 else if ( !enabled && mDefaultValueIndicatorProviderAvailableWidgets->isEnabled() )
1356 {
1357 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderAvailableWidgets, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1358 mDefaultValueIndicatorProviderAvailableWidgets->setEnabled( enabled );
1359 }
1360
1361 if ( enabled && !mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1362 {
1363 connect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1364 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1365 }
1366 else if ( !enabled && mConstraintIndicatorProviderAvailableWidgets->isEnabled() )
1367 {
1368 disconnect( mAvailableWidgetsModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderAvailableWidgets, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1369 mConstraintIndicatorProviderAvailableWidgets->setEnabled( enabled );
1370 }
1371}
1372
1373void QgsAttributesFormProperties::setFormLayoutIndicatorProvidersEnabled( bool enabled )
1374{
1375 // Only enable if the provider is disabled and only disable if it's enabled
1376 if ( enabled && !mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1377 {
1378 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1379 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1380 }
1381 else if ( !enabled && mDefaultValueIndicatorProviderFormLayout->isEnabled() )
1382 {
1383 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mDefaultValueIndicatorProviderFormLayout, &QgsFieldDefaultValueIndicatorProvider::updateItemIndicator );
1384 mDefaultValueIndicatorProviderFormLayout->setEnabled( enabled );
1385 }
1386
1387 if ( enabled && !mConstraintIndicatorProviderFormLayout->isEnabled() )
1388 {
1389 connect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1390 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1391 }
1392 else if ( !enabled && mConstraintIndicatorProviderFormLayout->isEnabled() )
1393 {
1394 disconnect( mFormLayoutModel, &QgsAttributesFormModel::fieldConfigDataChanged, mConstraintIndicatorProviderFormLayout, &QgsFieldConstraintIndicatorProvider::updateItemIndicator );
1395 mConstraintIndicatorProviderFormLayout->setEnabled( enabled );
1396 }
1397}
1398
1399void QgsAttributesFormProperties::previewForm()
1400{
1401 if ( !mSourceFieldsProperties )
1402 {
1403 return;
1404 }
1405
1406 auto projectDirtyBlocker = std::make_unique<QgsProjectDirtyBlocker>( QgsProject::instance() );
1407
1408
1409 QgsFields fields;
1410 QList<QPair<QgsField, QString>> expressionFields;
1411
1412 for ( int i = 0; i < mLayer->fields().size(); i++ )
1413 {
1414 if ( mLayer->fields().fieldOrigin( i ) == Qgis::FieldOrigin::Expression )
1415 {
1416 expressionFields << qMakePair( mLayer->fields().at( i ), mLayer->expressionField( i ) );
1417 }
1418 else
1419 {
1420 fields.append( mLayer->fields().at( i ) );
1421 }
1422 }
1423
1424 std::unique_ptr<QgsVectorLayer> vlayer;
1425 vlayer.reset( QgsMemoryProviderUtils::createMemoryLayer( "preview"_L1, fields, mLayer->wkbType(), mLayer->crs() ) );
1426 for ( const QPair<QgsField, QString> &expressionField : std::as_const( expressionFields ) )
1427 {
1428 vlayer->addExpressionField( expressionField.second, expressionField.first );
1429 }
1430
1431 mSourceFieldsProperties->applyToLayer( vlayer.get() );
1432 applyToLayer( vlayer.get() );
1433
1434 QgsFeature feature = QgsVectorLayerUtils::createFeature( vlayer.get() );
1435 QgsAttributeDialog form( vlayer.get(), &feature, false, this, true );
1437 form.exec();
1438
1439 projectDirtyBlocker.reset();
1440}
AttributeFormReuseLastValuePolicy
Attribute form policy for reusing last entered values.
Definition qgis.h:6179
@ AllowedDefaultOn
Reuse of last values allowed and enabled by default.
Definition qgis.h:6181
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:6180
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:6150
@ On
Always suppress feature form.
Definition qgis.h:6152
@ Default
Use the application-wide setting.
Definition qgis.h:6151
@ Off
Never suppress feature form.
Definition qgis.h:6153
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:6135
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
Definition qgis.h:6137
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
Definition qgis.h:6136
@ UiFile
Load a .ui file for the layout. Needs to be configured.
Definition qgis.h:6138
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:4121
@ DefaultValue
Use default field value.
Definition qgis.h:4122
@ Warning
Warning message.
Definition qgis.h:162
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:4104
@ Duplicate
Duplicate original value.
Definition qgis.h:4106
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:4141
@ Duplicate
Duplicate original value.
Definition qgis.h:4143
@ Expression
Field is calculated from an expression.
Definition qgis.h:1843
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:769
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:779
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:789
QgsFieldConstraints constraints
Definition qgsfield.h:68
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:754
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.
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:7656
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7637
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