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