QGIS API Documentation 3.99.0-Master (a26b91b364d)
qgsattributesformproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformproperties.cpp
3 ---------------------
4 begin : August 2017
5 copyright : (C) 2017 by David Signer
6 email : david at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsactionmanager.h"
17#include "qgsaddtaborgroup.h"
19#include "moc_qgsattributesformproperties.cpp"
20#include "qgsattributetypedialog.h"
21#include "qgsattributeformcontaineredit.h"
22#include "qgsattributewidgetedit.h"
24#include "qgsqmlwidgetwrapper.h"
26#include "qgsapplication.h"
27#include "qgscodeeditor.h"
28#include "qgscodeeditorhtml.h"
32#include "qgsgui.h"
35#include "qgsfieldcombobox.h"
36#include "qgsexpressionfinder.h"
38#include "qgshelp.h"
39#include "qgsxmlutils.h"
40
41#ifdef ENABLE_MODELTEST
42#include "modeltest.h"
43#endif
44
45const QgsSettingsEntryBool *QgsAttributesFormProperties::settingShowAliases = new QgsSettingsEntryBool( QStringLiteral( "show-aliases" ), sTreeAttributesForm, false, QStringLiteral( "Whether to show aliases (true) or names (false) in both the Available Widgets and the Form Layout panels." ) );
46
48 : QWidget( parent )
49 , mLayer( layer )
50{
51 if ( !layer )
52 return;
53
54 setupUi( this );
55
56 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
57 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
58 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
59 mShowAliasesButton->setChecked( settingShowAliases->value() );
60
61 // available widgets tree
62 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
64 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsView );
65 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
66 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
67 mAvailableWidgetsView->setContextMenuPolicy( Qt::CustomContextMenu );
68
69 mAvailableWidgetsModel = new QgsAttributesAvailableWidgetsModel( mLayer, QgsProject().instance(), this );
70 mAvailableWidgetsProxyModel = new QgsAttributesFormProxyModel( this );
71 mAvailableWidgetsProxyModel->setAttributesFormSourceModel( mAvailableWidgetsModel );
72 mAvailableWidgetsProxyModel->setRecursiveFilteringEnabled( true );
73 mAvailableWidgetsView->setModel( mAvailableWidgetsProxyModel );
74
75#ifdef ENABLE_MODELTEST
76 new ModelTest( mAvailableWidgetsProxyModel, this );
77#endif
78
79 // form layout tree
80 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
82 formLayoutWidgetLayout->addWidget( mFormLayoutView );
83 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
84 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
85
86 mFormLayoutModel = new QgsAttributesFormLayoutModel( mLayer, QgsProject().instance(), this );
87 mFormLayoutProxyModel = new QgsAttributesFormProxyModel( this );
88 mFormLayoutProxyModel->setAttributesFormSourceModel( mFormLayoutModel );
89 mFormLayoutProxyModel->setRecursiveFilteringEnabled( true );
90 mFormLayoutView->setModel( mFormLayoutProxyModel );
91
92#ifdef ENABLE_MODELTEST
93 new ModelTest( mFormLayoutProxyModel, this );
94#endif
95
96 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
97 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
98
99 connect( mAvailableWidgetsView, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
100
101 connect( mAddContainerButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
102 connect( mRemoveLayoutItemButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
103 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
104 connect( mShowAliasesButton, &QAbstractButton::toggled, this, &QgsAttributesFormProperties::toggleShowAliases );
105 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
106 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
107 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
108
109 connect( mSearchLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsAttributesFormProperties::updateFilteredItems );
110
111 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
112 if ( !mBlockUpdates )
113 updatedFields();
114 } );
115
116 // Context menu and children actions
117 mAvailableWidgetsContextMenu = new QMenu( this );
118 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
119 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
120
121 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
122 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
123
124 mAvailableWidgetsContextMenu->addAction( mActionCopyWidgetConfiguration );
125 mAvailableWidgetsContextMenu->addAction( mActionPasteWidgetConfiguration );
126
127 mMessageBar = new QgsMessageBar();
128 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
129 gridLayout->addWidget( mMessageBar, 0, 0 );
130
131 // Assign initial size to splitter widgets. By doing so, we can
132 // show an eventual horizontal scrollbar in the right-hand side panel
133 splitter->setSizes( { widget->minimumSizeHint().width(), 600 } );
134}
135
145
147{
148 mAvailableWidgetsView->setSortingEnabled( false );
149 mAvailableWidgetsView->setSelectionBehavior( QAbstractItemView::SelectRows );
150 mAvailableWidgetsView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
151 mAvailableWidgetsView->setAcceptDrops( false );
152 mAvailableWidgetsView->setDragDropMode( QAbstractItemView::DragDropMode::DragOnly );
153
154 mAvailableWidgetsModel->populate();
155 mAvailableWidgetsModel->setShowAliases( settingShowAliases->value() );
156 mAvailableWidgetsView->expandAll();
157}
158
160{
161 // tabs and groups info
162 mFormLayoutView->setSortingEnabled( false );
163 mFormLayoutView->setSelectionBehavior( QAbstractItemView::SelectRows );
164 mFormLayoutView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
165 mFormLayoutView->setAcceptDrops( true );
166 mFormLayoutView->setDragDropMode( QAbstractItemView::DragDropMode::DragDrop );
167 mFormLayoutView->setDefaultDropAction( Qt::MoveAction );
168
169 mFormLayoutView->selectionModel()->clear();
170 mFormLayoutModel->populate();
171 mFormLayoutModel->setShowAliases( settingShowAliases->value() );
172 mFormLayoutView->expandAll();
173}
174
175
177{
179 {
180 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
181 }
182 else
183 {
184 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
185 }
186 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
187 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
188
189 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
190}
191
192void QgsAttributesFormProperties::initAvailableWidgetsActions( const QList< QgsAction > actions )
193{
194 mAvailableWidgetsModel->populateLayerActions( actions );
195}
196
203
205{
206 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
207
208 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
209
211 mEditFormLineEdit->setText( cfg.uiForm() );
212}
213
215{
217
218 mInitCodeSource = cfg.initCodeSource();
219 mInitFunction = cfg.initFunction();
220 mInitFilePath = cfg.initFilePath();
221 mInitCode = cfg.initCode();
222
223 if ( mInitCode.isEmpty() )
224 {
225 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
226 "QGIS forms can have a Python function that is called when the form is\n"
227 "opened.\n"
228 "\n"
229 "Use this function to add extra logic to your forms.\n"
230 "\n"
231 "Enter the name of the function in the \"Python Init function\"\n"
232 "field.\n"
233 "An example follows:\n"
234 "\"\"\"\n"
235 "from qgis.PyQt.QtWidgets import QWidget\n\n"
236 "def my_form_open(dialog, layer, feature):\n"
237 " geom = feature.geometry()\n"
238 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
239 }
240}
241
242void QgsAttributesFormProperties::loadAttributeTypeDialog()
243{
244 if ( mAvailableWidgetsView->selectionModel()->selectedRows( 0 ).count() != 1 )
245 return;
246
247 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
248
250 const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesFormModel::ItemNameRole ).toString();
251 const int fieldIndex = mLayer->fields().indexOf( fieldName );
252
253 if ( fieldIndex < 0 )
254 return;
255
256 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, fieldIndex, mAttributeTypeFrame );
257
258 loadAttributeTypeDialogFromConfiguration( cfg );
259
260 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( fieldIndex ).expression() );
261 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( fieldIndex ).applyOnUpdate() );
262
263 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
264 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
265
266 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
267}
268
269void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const QgsAttributesFormData::FieldConfig &config )
270{
271 const QgsFieldConstraints constraints = config.mFieldConstraints;
272
273 mAttributeTypeDialog->setAlias( config.mAlias );
274 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
275 mAttributeTypeDialog->setComment( config.mComment );
276 mAttributeTypeDialog->setFieldEditable( config.mEditable );
277 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
278 mAttributeTypeDialog->setReuseLastValues( config.mReuseLastValues );
283 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
284 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
285 mAttributeTypeDialog->setMergePolicy( config.mMergePolicy );
286
289 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
291 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
293 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
294 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
295
296 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
297 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
299
300 // Make sure the widget is refreshed, even if
301 // the new widget type matches the current one
302 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
303 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
304}
305
306void QgsAttributesFormProperties::storeAttributeTypeDialog()
307{
309 return;
310
311 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
312 return;
313
315
316 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
317 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
318 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
319 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
320 cfg.mAlias = mAttributeTypeDialog->alias();
321 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
322
323 QgsFieldConstraints constraints;
324 if ( mAttributeTypeDialog->notNull() )
325 {
327 }
328 else if ( mAttributeTypeDialog->notNullFromProvider() )
329 {
331 }
332
333 if ( mAttributeTypeDialog->unique() )
334 {
336 }
337 else if ( mAttributeTypeDialog->uniqueFromProvider() )
338 {
340 }
341
342 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
343 {
345 }
346
347 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
348
352
353 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
354 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
355 cfg.mFieldConstraints = constraints;
356 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
357 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
358 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
359 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
360 cfg.mMergePolicy = mAttributeTypeDialog->mergePolicy();
361
362 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
363 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
364
365 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
366
367 QModelIndex index = mAvailableWidgetsModel->fieldModelIndex( fieldName );
368 if ( index.isValid() )
369 {
370 mAvailableWidgetsModel->setData( index, QVariant::fromValue<QgsAttributesFormData::FieldConfig>( cfg ), QgsAttributesFormModel::ItemFieldConfigRole );
371 mAvailableWidgetsModel->setData( index, mAttributeTypeDialog->alias(), QgsAttributesFormModel::ItemDisplayRole );
372 }
373
374 // Save alias to each matching field item in Form Layout model
375 mFormLayoutModel->updateAliasForFieldItems( fieldName, mAttributeTypeDialog->alias() );
376}
377
378void QgsAttributesFormProperties::storeAttributeWidgetEdit()
379{
381 return;
382
383 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
384 return;
385
386 QModelIndex index = mFormLayoutView->firstSelectedIndex();
387 storeAttributeWidgetEdit( index );
388}
389
390void QgsAttributesFormProperties::storeAttributeWidgetEdit( const QModelIndex &index )
391{
393 return;
394
395 if ( !index.isValid() )
396 return;
397
399 mAttributeWidgetEdit->updateItemData( itemData );
400
402 {
403 QgsAttributesFormData::RelationEditorConfiguration config = mAttributeWidgetEdit->updatedRelationConfiguration();
404 itemData.setRelationEditorConfiguration( config );
405 mFormLayoutModel->setData( index, config.label, QgsAttributesFormLayoutModel::ItemDisplayRole );
406 }
407 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
408}
409
410void QgsAttributesFormProperties::loadAttributeWidgetEdit()
411{
412 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
413 return;
414
415 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
417 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( itemData, this );
419 {
420 mAttributeWidgetEdit->setRelationSpecificWidget( itemData.relationEditorConfiguration(), currentIndex.data( QgsAttributesFormModel::ItemIdRole ).toString() );
421 }
422 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
423 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
424}
425
426void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
427{
428 mInfoTextWidget = new QLabel( infoText );
429 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
430 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
431}
432
433void QgsAttributesFormProperties::storeAttributeContainerEdit()
434{
436 return;
437
438 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
439 return;
440
441 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
442 storeAttributeContainerEdit( currentIndex );
443}
444
445void QgsAttributesFormProperties::storeAttributeContainerEdit( const QModelIndex &index )
446{
448 return;
449
450 if ( !index.isValid() )
451 return;
452
454 QString containerName;
455
456 mAttributeContainerEdit->updateItemData( itemData, containerName );
457 mFormLayoutModel->setData( index, itemData, QgsAttributesFormLayoutModel::ItemDataRole );
458 mFormLayoutModel->setData( index, containerName, QgsAttributesFormLayoutModel::ItemNameRole );
459}
460
461void QgsAttributesFormProperties::loadAttributeContainerEdit()
462{
463 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
464 return;
465
466 const QModelIndex currentIndex = mFormLayoutView->firstSelectedIndex();
468 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( itemData, mLayer, this );
469 mAttributeContainerEdit->setTitle( currentIndex.data( QgsAttributesFormModel::ItemNameRole ).toString() );
470 mAttributeContainerEdit->setUpContainerTypeComboBox( !currentIndex.parent().isValid(), itemData.containerType() );
471
472 mAttributeContainerEdit->registerExpressionContextGenerator( this );
473 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
474 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
475 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
476}
477
478void QgsAttributesFormProperties::onAttributeSelectionChanged( const QItemSelection &, const QItemSelection & )
479{
480 disconnect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
481
482 QModelIndex index;
483 if ( mFormLayoutView->selectionModel()->selectedRows( 0 ).count() == 1 )
484 {
485 // Go to the form layout view and store the single-selected index, as
486 // it will be used to store its current settings before being deselected
488 }
489
490 loadAttributeSpecificEditor( mAvailableWidgetsView, mFormLayoutView, index );
491 connect( mFormLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
492}
493
494void QgsAttributesFormProperties::onFormLayoutSelectionChanged( const QItemSelection &, const QItemSelection &deselected )
495{
496 // when the selection changes in the DnD layout, sync the main tree
497 disconnect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
498 QModelIndex index;
499 if ( deselected.indexes().count() == 1 )
500 {
501 index = mFormLayoutProxyModel->mapToSource( deselected.indexes().at( 0 ) );
502 }
503 else if ( deselected.indexes().count() == 0 && mFormLayoutView->selectionModel()->selectedIndexes().count() == 2 )
504 {
505 // There was 1 selected, it was not deselected, but instead a new item was added to selection
507 }
508
509 loadAttributeSpecificEditor( mFormLayoutView, mAvailableWidgetsView, index );
510 connect( mAvailableWidgetsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
511}
512
513void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesFormBaseView *emitter, QgsAttributesFormBaseView *receiver, QModelIndex &deselectedFormLayoutIndex )
514{
515 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
516
518 {
519 storeAttributeWidgetEdit( deselectedFormLayoutIndex );
520 storeAttributeContainerEdit( deselectedFormLayoutIndex );
521 }
523 {
524 storeAttributeTypeDialog();
525 }
526
527 clearAttributeTypeFrame();
528
529 if ( emitter->selectionModel()->selectedRows( 0 ).count() != 1 )
530 {
531 receiver->clearSelection();
532 }
533 else
534 {
535 const QModelIndex index = emitter->firstSelectedIndex();
536 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
537 switch ( indexType )
538 {
540 {
543 {
544 loadAttributeWidgetEdit();
545 }
546 else
547 {
548 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
549 }
550 break;
551 }
553 {
556 {
557 loadAttributeWidgetEdit();
558 }
559 loadAttributeTypeDialog();
560 break;
561 }
563 {
564 receiver->clearSelection();
565 loadAttributeContainerEdit();
566 break;
567 }
569 {
571 const QgsAction action { mLayer->actions()->action( index.data( QgsAttributesFormModel::ItemIdRole ).toString() ) };
572 loadInfoWidget( action.html() );
573 break;
574 }
579 {
581 {
582 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
583 }
584 else
585 {
586 loadInfoWidget( tr( "This configuration is available with double-click in the Form Layout panel" ) );
587 }
588 receiver->clearSelection();
589 break;
590 }
592 {
593 receiver->clearSelection();
594 break;
595 }
596 }
597 }
598}
599
600void QgsAttributesFormProperties::clearAttributeTypeFrame()
601{
603 {
604 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
605 mAttributeWidgetEdit->deleteLater();
606 mAttributeWidgetEdit = nullptr;
607 }
609 {
610 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
611 mAttributeTypeDialog->deleteLater();
612 mAttributeTypeDialog = nullptr;
613 }
615 {
616 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
617 mAttributeContainerEdit->deleteLater();
618 mAttributeContainerEdit = nullptr;
619 }
620 if ( mInfoTextWidget )
621 {
622 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
623 mInfoTextWidget->deleteLater();
624 mInfoTextWidget = nullptr;
625 }
626}
627
628void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
629{
630 Q_UNUSED( checked )
631 for ( int i = 0; i < mFormLayoutProxyModel->rowCount(); ++i )
632 {
633 QModelIndex index = mFormLayoutProxyModel->index( i, 0 );
634 mFormLayoutView->selectionModel()->select( index, QItemSelectionModel::Toggle );
635 }
636}
637
638void QgsAttributesFormProperties::toggleShowAliases( bool checked )
639{
640 settingShowAliases->setValue( checked );
641 mAvailableWidgetsModel->setShowAliases( checked );
642 mFormLayoutModel->setShowAliases( checked );
643}
644
645void QgsAttributesFormProperties::addContainer()
646{
647 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList = mFormLayoutModel->listOfContainers();
648
649 QModelIndex currentItem;
650 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
651 currentItem = mFormLayoutView->firstSelectedIndex();
652
653 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
654
655 if ( !dialog.exec() )
656 return;
657
658 const QString name = dialog.name();
659 QModelIndex parentContainerItem = dialog.parentContainerItem();
660
661 mFormLayoutModel->addContainer( parentContainerItem, name, dialog.columnCount(), dialog.containerType() );
662 if ( parentContainerItem.isValid() )
663 mFormLayoutView->setExpanded( parentContainerItem, true );
664}
665
666void QgsAttributesFormProperties::removeTabOrGroupButton()
667{
668 // deleting an item may delete any number of nested child items -- so we delete
669 // them one at a time and then see if there's any selection left
670 while ( true )
671 {
672 const QModelIndexList items = mFormLayoutView->selectionModel()->selectedRows();
673 if ( items.empty() )
674 break;
675
676 const QModelIndex item = mFormLayoutProxyModel->mapToSource( items.at( 0 ) );
677 mFormLayoutModel->removeRow( item.row(), item.parent() );
678 }
679}
680
681void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
682{
683 // Refresh the right panel. Save selection to recover it later.
684 const QItemSelection selection = mAvailableWidgetsView->selectionModel()->selection();
685 if ( selection.count() > 0 )
686 {
687 mAvailableWidgetsView->selectionModel()->clear();
688 }
689
690 if ( mFormLayoutView->selectionModel()->selectedRows().count() > 0 )
691 {
692 mFormLayoutView->selectionModel()->clear(); // Get rid of e.g., container selection
693 }
694
695 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
696 switch ( layout )
697 {
699 mFormLayoutWidget->setVisible( false );
700 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
701 mUiFileFrame->setVisible( false );
702 mAddContainerButton->setVisible( false );
703 mRemoveLayoutItemButton->setVisible( false );
704 mInvertSelectionButton->setVisible( false );
705 break;
706
708 mFormLayoutWidget->setVisible( true );
709 mTreeViewHorizontalSpacer->changeSize( 6, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
710 mUiFileFrame->setVisible( false );
711 mAddContainerButton->setVisible( true );
712 mRemoveLayoutItemButton->setVisible( true );
713 mInvertSelectionButton->setVisible( true );
714 break;
715
717 // ui file
718 mFormLayoutWidget->setVisible( false );
719 mTreeViewHorizontalSpacer->changeSize( 0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed );
720 mUiFileFrame->setVisible( true );
721 mAddContainerButton->setVisible( false );
722 mRemoveLayoutItemButton->setVisible( false );
723 mInvertSelectionButton->setVisible( false );
724 break;
725 }
726
727 // Get the selection back so that we refresh the right panel
728 if ( selection.count() > 0 )
729 {
730 mAvailableWidgetsView->selectionModel()->select( selection, QItemSelectionModel::Select );
731 }
732}
733
734void QgsAttributesFormProperties::mTbInitCode_clicked()
735{
736 QgsAttributesFormInitCode attributesFormInitCode;
737
738 attributesFormInitCode.setCodeSource( mInitCodeSource );
739 attributesFormInitCode.setInitCode( mInitCode );
740 attributesFormInitCode.setInitFilePath( mInitFilePath );
741 attributesFormInitCode.setInitFunction( mInitFunction );
742
743 if ( !attributesFormInitCode.exec() )
744 return;
745
746 mInitCodeSource = attributesFormInitCode.codeSource();
747 mInitCode = attributesFormInitCode.initCode();
748 mInitFilePath = attributesFormInitCode.initFilePath();
749 mInitFunction = attributesFormInitCode.initFunction();
750}
751
752void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
753{
754 QgsSettings myQSettings;
755 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
756 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
757
758 if ( uifilename.isNull() )
759 return;
760
761 const QFileInfo fi( uifilename );
762 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
763 mEditFormLineEdit->setText( uifilename );
764}
765
767{
768 storeAttributeWidgetEdit();
769 storeAttributeContainerEdit();
770 storeAttributeTypeDialog();
771}
772
774{
775 mBlockUpdates++;
776 store();
777
778 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
779
780 const QModelIndex fieldContainer = mAvailableWidgetsModel->fieldContainer();
781 QModelIndex index;
782
783 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainer ); i++ )
784 {
785 index = mAvailableWidgetsModel->index( i, 0, fieldContainer );
787
788 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
789 const int idx = mLayer->fields().indexOf( fieldName );
790
791 //continue in case field does not exist anymore
792 if ( idx < 0 )
793 continue;
794
795 editFormConfig.setReadOnly( idx, !cfg.mEditable );
796 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
797 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
798
799 if ( cfg.mDataDefinedProperties.count() > 0 )
800 {
801 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
802 }
803
805
806 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
807 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
809 {
811 }
812 else
813 {
815 }
817 {
819 }
820 else
821 {
823 }
825 {
827 }
828 else
829 {
831 }
832
833 mLayer->setFieldAlias( idx, cfg.mAlias );
837 }
838
839 // // tabs and groups
840 editFormConfig.clearTabs();
841
842 for ( int t = 0; t < mFormLayoutModel->rowCount(); t++ )
843 {
844 QModelIndex index = mFormLayoutModel->index( t, 0 );
845 QgsAttributeEditorElement *editorElement { mFormLayoutModel->createAttributeEditorWidget( index, nullptr ) };
846 if ( editorElement )
847 editFormConfig.addTab( editorElement );
848 }
849
850 editFormConfig.setUiForm( mEditFormLineEdit->text() );
851
852 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
853
854 editFormConfig.setInitCodeSource( mInitCodeSource );
855 editFormConfig.setInitFunction( mInitFunction );
856 editFormConfig.setInitFilePath( mInitFilePath );
857 editFormConfig.setInitCode( mInitCode );
858
859 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
860
861 // write the legacy config of relation widgets to support settings read by the API
862 const QModelIndex relationContainer = mAvailableWidgetsModel->relationContainer();
863
864 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( relationContainer ); i++ )
865 {
866 const QModelIndex relationIndex = mAvailableWidgetsModel->index( i, 0, relationContainer );
867
869 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( relationIndex.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
870 const QString indexId = relationIndex.data( QgsAttributesFormModel::ItemIdRole ).toString();
871
872 const QModelIndex layoutIndex = mFormLayoutModel->firstRecursiveMatchingModelIndex( indexType, indexId );
873 if ( layoutIndex.isValid() )
874 {
875 QVariantMap config;
876
878 config[QStringLiteral( "nm-rel" )] = tabIndexData.relationEditorConfiguration().nmRelationId;
879 config[QStringLiteral( "force-suppress-popup" )] = tabIndexData.relationEditorConfiguration().forceSuppressFormPopup;
880
881 editFormConfig.setWidgetConfig( indexId, config );
882 break;
883 }
884 }
885
886 mLayer->setEditFormConfig( editFormConfig );
887 mBlockUpdates--;
888}
889
890
892 : QTreeView( parent )
893 , mLayer( layer )
894{
895}
896
898{
899 if ( selectionModel()->selectedRows( 0 ).count() == 0 )
900 return QModelIndex();
901
902 return mModel->mapToSource( selectionModel()->selectedRows( 0 ).at( 0 ) );
903}
904
917
919{
920 // To be used with Relations, fields and actions
921 const auto *model = static_cast< QgsAttributesFormModel * >( mModel->sourceModel() );
922 QModelIndex index = mModel->mapFromSource( model->firstRecursiveMatchingModelIndex( itemType, itemId ) );
923
924 if ( index.isValid() )
925 {
926 // TODO: compare with eventual single selected index, if they match, avoid calling next line
927 selectionModel()->setCurrentIndex( index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
928 }
929 else
930 {
931 selectionModel()->clearSelection();
932 }
933}
934
936{
937 mModel->setFilterText( text );
938}
939
940
945
946void QgsAttributesAvailableWidgetsView::setModel( QAbstractItemModel *model )
947{
948 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
949 if ( !mModel )
950 return;
951
952 QTreeView::setModel( mModel );
953}
954
959
960
962 : QgsAttributesFormBaseView( layer, parent )
963{
964 connect( this, &QTreeView::doubleClicked, this, &QgsAttributesFormLayoutView::onItemDoubleClicked );
965}
966
967void QgsAttributesFormLayoutView::setModel( QAbstractItemModel *model )
968{
969 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
970 if ( !mModel )
971 return;
972
973 QTreeView::setModel( mModel );
974
975 const auto *formLayoutModel = static_cast< QgsAttributesFormLayoutModel * >( mModel->sourceModel() );
976 connect( formLayoutModel, &QgsAttributesFormLayoutModel::externalItemDropped, this, &QgsAttributesFormLayoutView::handleExternalDroppedItem );
977 connect( formLayoutModel, &QgsAttributesFormLayoutModel::internalItemDropped, this, &QgsAttributesFormLayoutView::handleInternalDroppedItem );
978}
979
980
981void QgsAttributesFormLayoutView::handleExternalDroppedItem( QModelIndex &index )
982{
983 selectionModel()->setCurrentIndex( mModel->mapFromSource( index ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
984
985 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
986
987 if ( itemType == QgsAttributesFormData::QmlWidget
991 {
992 onItemDoubleClicked( mModel->mapFromSource( index ) );
993 }
994}
995
996void QgsAttributesFormLayoutView::handleInternalDroppedItem( QModelIndex &index )
997{
998 selectionModel()->clearCurrentIndex();
999 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1000 if ( itemType == QgsAttributesFormData::Container )
1001 {
1002 expandRecursively( mModel->mapFromSource( index ) );
1003 }
1004}
1005
1007{
1008 const QMimeData *data = event->mimeData();
1009
1010 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributesformavailablewidgetsrelement" ) )
1011 || data->hasFormat( QStringLiteral( "application/x-qgsattributesformlayoutelement" ) ) )
1012 {
1013 // Inner drag and drop actions are always MoveAction
1014 if ( event->source() == this )
1015 {
1016 event->setDropAction( Qt::MoveAction );
1017 }
1018 }
1019 else
1020 {
1021 event->ignore();
1022 }
1023
1024 QTreeView::dragEnterEvent( event );
1025}
1026
1032{
1033 const QMimeData *data = event->mimeData();
1034
1035 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributesformavailablewidgetsrelement" ) )
1036 || data->hasFormat( QStringLiteral( "application/x-qgsattributesformlayoutelement" ) ) )
1037 {
1038 // Inner drag and drop actions are always MoveAction
1039 if ( event->source() == this )
1040 {
1041 event->setDropAction( Qt::MoveAction );
1042 }
1043 }
1044 else
1045 {
1046 event->ignore();
1047 }
1048
1049 QTreeView::dragMoveEvent( event );
1050}
1051
1053{
1054 if ( !( event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributesformavailablewidgetsrelement" ) )
1055 || event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributesformlayoutelement" ) ) ) )
1056 return;
1057
1058 if ( event->source() == this )
1059 {
1060 event->setDropAction( Qt::MoveAction );
1061 }
1062
1063 QTreeView::dropEvent( event );
1064}
1065
1066void QgsAttributesFormLayoutView::onItemDoubleClicked( const QModelIndex &index )
1067{
1068 QModelIndex sourceIndex = mModel->mapToSource( index );
1070 const auto itemType = static_cast<QgsAttributesFormData::AttributesFormItemType>( sourceIndex.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1071 const QString itemName = sourceIndex.data( QgsAttributesFormModel::ItemNameRole ).toString();
1072
1073 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1074
1075 QFormLayout *baseLayout = new QFormLayout();
1076 baseData->setLayout( baseLayout );
1077 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1078 showLabelCheckbox->setChecked( itemData.showLabel() );
1079 baseLayout->addRow( showLabelCheckbox );
1080 QWidget *baseWidget = new QWidget();
1081 baseWidget->setLayout( baseLayout );
1082
1083 switch ( itemType )
1084 {
1090 break;
1091
1093 {
1094 QDialog dlg;
1095 dlg.setObjectName( "QML Form Configuration Widget" );
1097 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1098
1099 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1100 QSplitter *qmlSplitter = new QSplitter();
1101 QWidget *qmlConfigWiget = new QWidget();
1102 QVBoxLayout *layout = new QVBoxLayout( qmlConfigWiget );
1103 layout->setContentsMargins( 0, 0, 0, 0 );
1104 mainLayout->addWidget( qmlSplitter );
1105 qmlSplitter->addWidget( qmlConfigWiget );
1106 layout->addWidget( baseWidget );
1107
1108 QLineEdit *title = new QLineEdit( itemName );
1109
1110 //qmlCode
1111 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1112 qmlCode->setEditingTimeoutInterval( 250 );
1113 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1114
1115 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1116 QgsFeature previewFeature;
1117 mLayer->getFeatures().nextFeature( previewFeature );
1118
1119 //update preview on text change
1120 connect( qmlCode, &QgsCodeEditor::editingTimeout, this, [qmlWrapper, qmlCode, previewFeature] {
1121 qmlWrapper->setQmlCode( qmlCode->text() );
1122 qmlWrapper->reinitWidget();
1123 qmlWrapper->setFeature( previewFeature );
1124 } );
1125
1126 //templates
1127 QComboBox *qmlObjectTemplate = new QComboBox();
1128 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1129 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1130 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1131 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1132 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [qmlCode]( int index ) {
1133 qmlCode->clear();
1134 switch ( index )
1135 {
1136 case 0:
1137 {
1138 qmlCode->setText( QString() );
1139 break;
1140 }
1141 case 1:
1142 {
1143 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1144 "\n"
1145 "Rectangle {\n"
1146 " width: 100\n"
1147 " height: 100\n"
1148 " color: \"steelblue\"\n"
1149 " Text{ text: \"A rectangle\" }\n"
1150 "}\n" ) );
1151 break;
1152 }
1153 case 2:
1154 {
1155 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1156 "import QtCharts 2.0\n"
1157 "\n"
1158 "ChartView {\n"
1159 " width: 400\n"
1160 " height: 400\n"
1161 "\n"
1162 " PieSeries {\n"
1163 " id: pieSeries\n"
1164 " PieSlice { label: \"First slice\"; value: 25 }\n"
1165 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1166 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1167 " }\n"
1168 "}\n" ) );
1169 break;
1170 }
1171 case 3:
1172 {
1173 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1174 "import QtCharts 2.0\n"
1175 "\n"
1176 "ChartView {\n"
1177 " title: \"Bar series\"\n"
1178 " width: 600\n"
1179 " height:400\n"
1180 " legend.alignment: Qt.AlignBottom\n"
1181 " antialiasing: true\n"
1182 " ValueAxis{\n"
1183 " id: valueAxisY\n"
1184 " min: 0\n"
1185 " max: 15\n"
1186 " }\n"
1187 "\n"
1188 " BarSeries {\n"
1189 " id: mySeries\n"
1190 " axisY: valueAxisY\n"
1191 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1192 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1193 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1194 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1195 " }\n"
1196 "}\n" ) );
1197 break;
1198 }
1199 default:
1200 break;
1201 }
1202 } );
1203
1204 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1205 expressionWidget->setButtonVisible( false );
1206 expressionWidget->registerExpressionContextGenerator( this );
1207 expressionWidget->setLayer( mLayer );
1208 QToolButton *addFieldButton = new QToolButton();
1209 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1210
1211 QToolButton *editExpressionButton = new QToolButton();
1212 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1213 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1214
1215 connect( addFieldButton, &QAbstractButton::clicked, this, [expressionWidget, qmlCode] {
1216 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1217 if ( !expression.isEmpty() )
1218 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1219 } );
1220
1221 connect( editExpressionButton, &QAbstractButton::clicked, this, [this, qmlCode] {
1222 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1223 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1225 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1226
1227 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1228 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1229 {
1230 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1231 if ( !expression.isEmpty() )
1232 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1233 }
1234 } );
1235
1236 layout->addWidget( new QLabel( tr( "Title" ) ) );
1237 layout->addWidget( title );
1238 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1239 qmlCodeBox->setLayout( new QVBoxLayout );
1240 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1241 QWidget *expressionWidgetBox = new QWidget();
1242 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1243 expressionWidgetBox->setLayout( new QHBoxLayout );
1244 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1245 expressionWidgetBox->layout()->addWidget( expressionWidget );
1246 expressionWidgetBox->layout()->addWidget( addFieldButton );
1247 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1248 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1249 layout->addWidget( qmlCodeBox );
1250 layout->addWidget( qmlCode );
1251 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1252 qmlPreviewBox->setMinimumWidth( 200 );
1253 qmlPreviewBox->setWidget( qmlWrapper->widget() );
1254 //emit to load preview for the first time
1255 emit qmlCode->editingTimeout();
1256 qmlSplitter->addWidget( qmlPreviewBox );
1257 qmlSplitter->setChildrenCollapsible( false );
1258 qmlSplitter->setHandleWidth( 6 );
1259 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
1260
1261 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1262
1263 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1264 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1265 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
1266 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1267 } );
1268
1269 mainLayout->addWidget( buttonBox );
1270
1271 if ( dlg.exec() )
1272 {
1274 qmlEdCfg.qmlCode = qmlCode->text();
1275 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1276 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1277
1278 mModel->sourceModel()->setData( sourceIndex, itemData, QgsAttributesFormModel::ItemDataRole );
1279 mModel->sourceModel()->setData( sourceIndex, title->text(), QgsAttributesFormModel::ItemNameRole );
1280 }
1281 }
1282 break;
1283
1285 {
1286 QDialog dlg;
1287 dlg.setObjectName( "HTML Form Configuration Widget" );
1289 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1290
1291 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1292 QSplitter *htmlSplitter = new QSplitter();
1293 QWidget *htmlConfigWiget = new QWidget();
1294 QVBoxLayout *layout = new QVBoxLayout( htmlConfigWiget );
1295 layout->setContentsMargins( 0, 0, 0, 0 );
1296 mainLayout->addWidget( htmlSplitter );
1297 htmlSplitter->addWidget( htmlConfigWiget );
1298 htmlSplitter->setChildrenCollapsible( false );
1299 htmlSplitter->setHandleWidth( 6 );
1300 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1301 layout->addWidget( baseWidget );
1302
1303 QLineEdit *title = new QLineEdit( itemName );
1304
1305 //htmlCode
1306 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML();
1307 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1308 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1309
1310 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1311 QgsFeature previewFeature;
1312 mLayer->getFeatures().nextFeature( previewFeature );
1313
1314 //update preview on text change
1315 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [htmlWrapper, htmlCode, previewFeature] {
1316 htmlWrapper->setHtmlCode( htmlCode->text() );
1317 htmlWrapper->reinitWidget();
1318 htmlWrapper->setFeature( previewFeature );
1319 } );
1320
1321 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1322 expressionWidget->setButtonVisible( false );
1323 expressionWidget->registerExpressionContextGenerator( this );
1324 expressionWidget->setLayer( mLayer );
1325 QToolButton *addFieldButton = new QToolButton();
1326 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1327
1328 QToolButton *editExpressionButton = new QToolButton();
1329 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1330 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1331
1332 connect( addFieldButton, &QAbstractButton::clicked, this, [expressionWidget, htmlCode] {
1333 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1334 if ( !expression.isEmpty() )
1335 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1336 } );
1337
1338 connect( editExpressionButton, &QAbstractButton::clicked, this, [this, htmlCode] {
1339 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1340 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1342 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1343
1344 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1345 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1346 {
1347 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1348 if ( !expression.isEmpty() )
1349 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1350 }
1351 } );
1352
1353 layout->addWidget( new QLabel( tr( "Title" ) ) );
1354 layout->addWidget( title );
1355 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1356 layout->addWidget( expressionWidgetBox );
1357 expressionWidgetBox->setLayout( new QHBoxLayout );
1358 expressionWidgetBox->layout()->addWidget( expressionWidget );
1359 expressionWidgetBox->layout()->addWidget( addFieldButton );
1360 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1361 layout->addWidget( htmlCode );
1362 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1363 htmlPreviewBox->setLayout( new QGridLayout );
1364 htmlPreviewBox->setMinimumWidth( 200 );
1365 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1366 //emit to load preview for the first time
1367 emit htmlCode->textChanged();
1368 htmlSplitter->addWidget( htmlPreviewBox );
1369 htmlSplitter->setChildrenCollapsible( false );
1370 htmlSplitter->setHandleWidth( 6 );
1371 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1372
1373 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1374
1375 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1376 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1377 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
1378 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1379 } );
1380
1381 mainLayout->addWidget( buttonBox );
1382
1383 if ( dlg.exec() )
1384 {
1386 htmlEdCfg.htmlCode = htmlCode->text();
1387 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1388 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1389
1390 mModel->sourceModel()->setData( sourceIndex, itemData, QgsAttributesFormModel::ItemDataRole );
1391 mModel->sourceModel()->setData( sourceIndex, title->text(), QgsAttributesFormModel::ItemNameRole );
1392 }
1393 break;
1394 }
1395
1397 {
1398 QDialog dlg;
1399 dlg.setObjectName( "Text Form Configuration Widget" );
1401 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1402
1403 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1404 QSplitter *textSplitter = new QSplitter();
1405 QWidget *textConfigWiget = new QWidget();
1406 QVBoxLayout *layout = new QVBoxLayout( textConfigWiget );
1407 layout->setContentsMargins( 0, 0, 0, 0 );
1408 mainLayout->addWidget( textSplitter );
1409 textSplitter->addWidget( textConfigWiget );
1410 layout->addWidget( baseWidget );
1411
1412 QLineEdit *title = new QLineEdit( itemName );
1413
1415 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1416 text->setText( itemData.textElementEditorConfiguration().text );
1417
1418 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1419 QgsFeature previewFeature;
1420 mLayer->getFeatures().nextFeature( previewFeature );
1421
1422 //update preview on text change
1423 connect( text, &QgsCodeEditorExpression::textChanged, this, [textWrapper, previewFeature, text] {
1424 textWrapper->setText( text->text() );
1425 textWrapper->reinitWidget();
1426 textWrapper->setFeature( previewFeature );
1427 } );
1428
1429 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1430 expressionWidget->setButtonVisible( false );
1431 expressionWidget->registerExpressionContextGenerator( this );
1432 expressionWidget->setLayer( mLayer );
1433 QToolButton *addFieldButton = new QToolButton();
1434 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1435
1436 QToolButton *editExpressionButton = new QToolButton();
1437 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1438 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1439
1440 connect( addFieldButton, &QAbstractButton::clicked, this, [expressionWidget, text] {
1441 QString expression = expressionWidget->expression().trimmed();
1442 if ( !expression.isEmpty() )
1443 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1444 } );
1445 connect( editExpressionButton, &QAbstractButton::clicked, this, [this, text] {
1446 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1447
1449 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1450
1451 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1452 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1453 {
1454 QString expression = exprDlg.expressionText().trimmed();
1455 if ( !expression.isEmpty() )
1456 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1457 }
1458 } );
1459
1460 layout->addWidget( new QLabel( tr( "Title" ) ) );
1461 layout->addWidget( title );
1462 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1463 layout->addWidget( expressionWidgetBox );
1464 expressionWidgetBox->setLayout( new QHBoxLayout );
1465 expressionWidgetBox->layout()->addWidget( expressionWidget );
1466 expressionWidgetBox->layout()->addWidget( addFieldButton );
1467 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1468 layout->addWidget( text );
1469 QScrollArea *textPreviewBox = new QgsScrollArea();
1470 textPreviewBox->setLayout( new QGridLayout );
1471 textPreviewBox->setMinimumWidth( 200 );
1472 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1473 //emit to load preview for the first time
1474 emit text->textChanged();
1475 textSplitter->addWidget( textPreviewBox );
1476 textSplitter->setChildrenCollapsible( false );
1477 textSplitter->setHandleWidth( 6 );
1478 textSplitter->setSizes( QList<int>() << 1 << 1 );
1479
1480 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1481
1482 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1483 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1484 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
1485 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1486 } );
1487
1488 mainLayout->addWidget( buttonBox );
1489
1490 if ( dlg.exec() )
1491 {
1493 textEdCfg.text = text->text();
1494 itemData.setTextElementEditorConfiguration( textEdCfg );
1495 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1496
1497 mModel->sourceModel()->setData( sourceIndex, itemData, QgsAttributesFormModel::ItemDataRole );
1498 mModel->sourceModel()->setData( sourceIndex, title->text(), QgsAttributesFormModel::ItemNameRole );
1499 }
1500 break;
1501 }
1502
1504 {
1505 QDialog dlg;
1506 dlg.setObjectName( "Spacer Form Configuration Widget" );
1508 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1509
1510 QVBoxLayout *mainLayout = new QVBoxLayout();
1511 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1512 QLineEdit *title = new QLineEdit( itemName );
1513 mainLayout->addWidget( title );
1514
1515 QHBoxLayout *cbLayout = new QHBoxLayout();
1516 mainLayout->addLayout( cbLayout );
1517 dlg.setLayout( mainLayout );
1518 QCheckBox *cb = new QCheckBox { &dlg };
1519 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1520 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1521 cbLayout->addWidget( cb );
1522
1523 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1524
1525 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1526 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1527 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
1528 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1529 } );
1530
1531 mainLayout->addWidget( buttonBox );
1532
1533 if ( dlg.exec() )
1534 {
1536 spacerEdCfg.drawLine = cb->isChecked();
1537 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1538 itemData.setShowLabel( false );
1539
1540 mModel->sourceModel()->setData( sourceIndex, itemData, QgsAttributesFormModel::ItemDataRole );
1541 mModel->sourceModel()->setData( sourceIndex, title->text(), QgsAttributesFormModel::ItemNameRole );
1542 }
1543
1544 break;
1545 }
1546 }
1547}
1548
1549
1550void QgsAttributesFormProperties::updatedFields()
1551{
1552 // Store configuration to insure changes made are kept after refreshing the list
1553 QMap<QString, QgsAttributesFormData::FieldConfig> fieldConfigs;
1554
1555 const QModelIndex fieldContainerBefore = mAvailableWidgetsModel->fieldContainer();
1556 QModelIndex index;
1557
1558 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerBefore ); i++ )
1559 {
1560 index = mAvailableWidgetsModel->index( i, 0, fieldContainerBefore );
1561 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1563 fieldConfigs[fieldName] = config;
1564 }
1565
1567
1568 const QModelIndex fieldContainerAfter = mAvailableWidgetsModel->fieldContainer();
1569
1570 for ( int i = 0; i < mAvailableWidgetsModel->rowCount( fieldContainerAfter ); i++ )
1571 {
1572 index = mAvailableWidgetsModel->index( i, 0, fieldContainerAfter );
1573 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1574
1575 if ( fieldConfigs.contains( fieldName ) )
1576 {
1577 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName], QgsAttributesFormModel::ItemFieldConfigRole );
1578 mAvailableWidgetsModel->setData( index, fieldConfigs[fieldName].mAlias, QgsAttributesFormModel::ItemDisplayRole );
1579 }
1580 }
1581}
1582
1583void QgsAttributesFormProperties::updateFilteredItems( const QString &filterText )
1584{
1585 const int availableWidgetsPreviousSelectionCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
1586 const int formLayoutPreviousSelectionCount = mFormLayoutView->selectionModel()->selectedRows().count();
1587
1588 static_cast< QgsAttributesAvailableWidgetsView *>( mAvailableWidgetsView )->setFilterText( filterText );
1589 mAvailableWidgetsView->expandAll();
1590
1591 static_cast< QgsAttributesFormLayoutView *>( mFormLayoutView )->setFilterText( filterText );
1592 mFormLayoutView->expandAll();
1593
1594 // If there was no previous selection leave as is, since
1595 // after a filter change no new selection may be added (only lost).
1596 if ( !( availableWidgetsPreviousSelectionCount == 0 && formLayoutPreviousSelectionCount == 0 ) )
1597 {
1598 const int selectedAvailableWidgetItemCount = mAvailableWidgetsView->selectionModel()->selectedRows().count();
1599 const int selectedFormLayoutItemCount = mFormLayoutView->selectionModel()->selectedRows().count();
1600
1601 if ( selectedAvailableWidgetItemCount == 0 && selectedFormLayoutItemCount == 0 )
1602 {
1603 // Clear right-hand side panel since all selected items have been filtered out
1604 clearAttributeTypeFrame();
1605 }
1606 }
1607}
1608
1609void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
1610{
1611 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1612 return;
1613
1614 QPoint globalPos = mAvailableWidgetsView->viewport()->mapToGlobal( point );
1615
1616 const QModelIndex index = mAvailableWidgetsView->indexAt( point );
1617 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1618 if ( itemType == QgsAttributesFormData::Field )
1619 {
1620 const QClipboard *clipboard = QApplication::clipboard();
1621 const QMimeData *mimeData = clipboard->mimeData();
1622 if ( !mimeData )
1623 return;
1624
1625 const bool pasteEnabled = mimeData->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) );
1626 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
1627 mAvailableWidgetsContextMenu->popup( globalPos );
1628 }
1629}
1630
1631void QgsAttributesFormProperties::copyWidgetConfiguration()
1632{
1633 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1634 return;
1635
1636 const QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1637 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1638
1639 if ( itemType != QgsAttributesFormData::Field )
1640 return;
1641
1642 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1643 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1644
1645 if ( fieldIndex < 0 )
1646 return;
1647
1648 const QgsField field = mLayer->fields().field( fieldIndex );
1649
1650 // We'll copy everything but field aliases or comments
1651 QDomDocument doc;
1652 QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) );
1653 documentElement.setAttribute( QStringLiteral( "name" ), field.name() );
1654
1655 // Editor widget setup
1656 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
1657
1658 QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) );
1659 documentElement.appendChild( editWidgetElement );
1660 editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() );
1661 QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) );
1662
1663 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
1664 editWidgetElement.appendChild( editWidgetConfigElement );
1665
1666 // Split policy
1667 QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) );
1668 splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) );
1669 documentElement.appendChild( splitPolicyElement );
1670
1671 // Duplicate policy
1672 QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) );
1673 duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
1674 documentElement.appendChild( duplicatePolicyElement );
1675
1676 // Merge policy
1677 QDomElement mergePolicyElement = doc.createElement( QStringLiteral( "mergePolicy" ) );
1678 mergePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.mergePolicy() ) );
1679 documentElement.appendChild( mergePolicyElement );
1680
1681 // Default expressions
1682 QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) );
1683 defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() );
1684 defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1685 documentElement.appendChild( defaultElem );
1686
1687 // Constraints
1688 QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) );
1689 constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() );
1690 constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
1691 constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
1692 constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
1693 documentElement.appendChild( constraintElem );
1694
1695 // Constraint expressions
1696 QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) );
1697 constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() );
1698 constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() );
1699 documentElement.appendChild( constraintExpressionElem );
1700
1701 // Widget general settings
1703 {
1704 QDomElement widgetGeneralSettingsElem = doc.createElement( QStringLiteral( "widgetGeneralSettings" ) );
1705 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "editable" ), mAttributeTypeDialog->fieldEditable() );
1706 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "reuse_last_values" ), mAttributeTypeDialog->labelOnTop() );
1707 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "label_on_top" ), mAttributeTypeDialog->reuseLastValues() );
1708 documentElement.appendChild( widgetGeneralSettingsElem );
1709 }
1710
1711 // Widget display section
1713 {
1714 // Go for the corresponding form layout item and extract its display settings
1715 if ( mFormLayoutView->selectionModel()->selectedRows().count() != 1 )
1716 return;
1717
1718 const QModelIndex indexLayout = mFormLayoutView->firstSelectedIndex();
1719 const auto layoutData = indexLayout.data( QgsAttributesFormModel::ItemDataRole ).value< QgsAttributesFormData::AttributeFormItemData >();
1720
1721 QDomElement displayElement = doc.createElement( QStringLiteral( "widgetDisplay" ) );
1722 displayElement.setAttribute( QStringLiteral( "showLabel" ), layoutData.showLabel() );
1723 displayElement.setAttribute( QStringLiteral( "horizontalStretch" ), layoutData.horizontalStretch() );
1724 displayElement.setAttribute( QStringLiteral( "verticalStretch" ), layoutData.verticalStretch() );
1725 displayElement.appendChild( layoutData.labelStyle().writeXml( doc ) );
1726 documentElement.appendChild( displayElement );
1727 }
1728
1729 doc.appendChild( documentElement );
1730
1731 QMimeData *mimeData = new QMimeData;
1732 mimeData->setData( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ), doc.toByteArray() );
1733 QClipboard *clipboard = QApplication::clipboard();
1734 clipboard->setMimeData( mimeData );
1735}
1736
1737void QgsAttributesFormProperties::pasteWidgetConfiguration()
1738{
1739 if ( mAvailableWidgetsView->selectionModel()->selectedRows().count() != 1 )
1740 return;
1741
1742 QModelIndex index = mAvailableWidgetsView->firstSelectedIndex();
1743
1744 const QString fieldName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1745 const int fieldIndex = mLayer->fields().indexOf( fieldName );
1746
1747 if ( fieldIndex < 0 )
1748 return;
1749
1750 // Get base config from target item and ovewrite settings when possible
1752
1753 QDomDocument doc;
1754 QClipboard *clipboard = QApplication::clipboard();
1755 const QMimeData *mimeData = clipboard->mimeData();
1756 if ( !mimeData )
1757 return;
1758
1759 if ( doc.setContent( mimeData->data( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) ) ) )
1760 {
1761 QDomElement docElem = doc.documentElement();
1762 if ( docElem.tagName() != QLatin1String( "FormWidgetClipboard" ) )
1763 return;
1764
1765 // When pasting, the target item has already been selected and
1766 // has triggered attribute type dialog loading. Therefore, we'll
1767 // only overwrite GUI settings instead of destroying and recreating
1768 // the whole dialog.
1769
1770 // Editor widget configuration
1771 const QDomElement fieldWidgetElement = docElem.firstChildElement( QStringLiteral( "editWidget" ) );
1772 if ( !fieldWidgetElement.isNull() )
1773 {
1774 const QString widgetType = fieldWidgetElement.attribute( QStringLiteral( "type" ) );
1775
1776 // Only paste if source editor widget type is supported by target field
1777 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
1778 if ( factory->supportsField( mLayer, fieldIndex ) )
1779 {
1780 const QDomElement configElement = fieldWidgetElement.firstChildElement( QStringLiteral( "config" ) );
1781 if ( !configElement.isNull() )
1782 {
1783 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
1784 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
1785 QgsReadWriteContext context;
1786 // translate widget configuration strings
1787 if ( widgetType == QStringLiteral( "ValueRelation" ) )
1788 {
1789 optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() );
1790 }
1791 if ( widgetType == QStringLiteral( "ValueMap" ) )
1792 {
1793 if ( optionsMap[QStringLiteral( "map" )].canConvert<QList<QVariant>>() )
1794 {
1795 QList<QVariant> translatedValueList;
1796 const QList<QVariant> valueList = optionsMap[QStringLiteral( "map" )].toList();
1797 for ( int i = 0, row = 0; i < valueList.count(); i++, row++ )
1798 {
1799 QMap<QString, QVariant> translatedValueMap;
1800 QString translatedKey = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuemapdescriptions" ).arg( mLayer->id(), fieldName ), valueList[i].toMap().constBegin().key() );
1801 translatedValueMap.insert( translatedKey, valueList[i].toMap().constBegin().value() );
1802 translatedValueList.append( translatedValueMap );
1803 }
1804 optionsMap.insert( QStringLiteral( "map" ), translatedValueList );
1805 }
1806 }
1807 config.mEditorWidgetType = widgetType;
1808 config.mEditorWidgetConfig = optionsMap;
1809 }
1810 }
1811 else
1812 {
1813 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 );
1814 }
1815 }
1816
1817 // Split policy
1818 const QDomElement splitPolicyElement = docElem.firstChildElement( QStringLiteral( "splitPolicy" ) );
1819 if ( !splitPolicyElement.isNull() )
1820 {
1821 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainSplitPolicy::Duplicate );
1822 config.mSplitPolicy = policy;
1823 }
1824
1825 // Duplicate policy
1826 const QDomElement duplicatePolicyElement = docElem.firstChildElement( QStringLiteral( "duplicatePolicy" ) );
1827 if ( !duplicatePolicyElement.isNull() )
1828 {
1829 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDuplicatePolicy::Duplicate );
1830 config.mDuplicatePolicy = policy;
1831 }
1832
1833 // Merge policy
1834 const QDomElement mergePolicyElement = docElem.firstChildElement( QStringLiteral( "mergePolicy" ) );
1835 if ( !mergePolicyElement.isNull() )
1836 {
1837 const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainMergePolicy::DefaultValue );
1838 config.mMergePolicy = policy;
1839 }
1840
1841 // Default expressions
1842 const QDomElement defaultElement = docElem.firstChildElement( QStringLiteral( "default" ) );
1843 if ( !defaultElement.isNull() )
1844 {
1845 mAttributeTypeDialog->setDefaultValueExpression( defaultElement.attribute( QStringLiteral( "expression" ) ) );
1846 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( defaultElement.attribute( QStringLiteral( "applyOnUpdate" ) ).toInt() );
1847 }
1848
1849 // Constraints
1850 // take target field constraints as a basis
1851 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
1852 const QDomElement constraintElement = docElem.firstChildElement( QStringLiteral( "constraint" ) );
1853 if ( !constraintElement.isNull() )
1854 {
1855 const int intConstraints = constraintElement.attribute( QStringLiteral( "constraints" ), QStringLiteral( "0" ) ).toInt();
1856 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
1857
1858 // always keep provider constraints intact
1860 {
1861 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
1863 else
1865 }
1867 {
1868 if ( constraints & QgsFieldConstraints::ConstraintUnique )
1870 else
1872 }
1874 {
1877 else
1879 }
1880
1881 const int uniqueStrength = constraintElement.attribute( QStringLiteral( "unique_strength" ), QStringLiteral( "1" ) ).toInt();
1882 const int notNullStrength = constraintElement.attribute( QStringLiteral( "notnull_strength" ), QStringLiteral( "1" ) ).toInt();
1883 const int expStrength = constraintElement.attribute( QStringLiteral( "exp_strength" ), QStringLiteral( "1" ) ).toInt();
1884
1888 }
1889
1890 // Constraint expressions
1891 // always keep provider constraints intact
1893 {
1894 const QDomElement constraintExpressionElement = docElem.firstChildElement( QStringLiteral( "constraintExpression" ) );
1895 if ( !constraintExpressionElement.isNull() )
1896 {
1897 QString expression = constraintExpressionElement.attribute( QStringLiteral( "exp" ), QString() );
1898 QString description = constraintExpressionElement.attribute( QStringLiteral( "desc" ), QString() );
1899 fieldConstraints.setConstraintExpression( expression, description );
1900 }
1901 }
1902 config.mFieldConstraints = fieldConstraints;
1903
1904 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( QStringLiteral( "widgetGeneralSettings" ) );
1905 if ( !widgetGeneralSettingsElement.isNull() )
1906 {
1907 const int editable = widgetGeneralSettingsElement.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1908 const int reuse = widgetGeneralSettingsElement.attribute( QStringLiteral( "reuse_last_values" ), QStringLiteral( "0" ) ).toInt();
1909 const int labelOnTop = widgetGeneralSettingsElement.attribute( QStringLiteral( "label_on_top" ), QStringLiteral( "0" ) ).toInt();
1910
1911 config.mEditable = editable;
1912 config.mReuseLastValues = reuse;
1913 config.mLabelOnTop = labelOnTop;
1914 }
1915
1916 loadAttributeTypeDialogFromConfiguration( config );
1917
1918 // Widget display section
1920 {
1921 const QDomElement displayElement = docElem.firstChildElement( QStringLiteral( "widgetDisplay" ) );
1922 if ( !displayElement.isNull() )
1923 {
1924 const int showLabel = displayElement.attribute( QStringLiteral( "showLabel" ), QStringLiteral( "0" ) ).toInt();
1925 const int horizontalStretch = displayElement.attribute( QStringLiteral( "horizontalStretch" ), QStringLiteral( "0" ) ).toInt();
1926 const int verticalStretch = displayElement.attribute( QStringLiteral( "verticalStretch" ), QStringLiteral( "0" ) ).toInt();
1928 style.readXml( displayElement );
1929
1930 // Update current GUI controls
1931 mAttributeWidgetEdit->setShowLabel( showLabel );
1932 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
1933 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
1934 mAttributeWidgetEdit->setLabelStyle( style );
1935 }
1936 }
1937 }
1938}
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5379
@ On
Always suppress feature form.
@ Default
Use the application-wide setting.
@ Off
Never suppress feature form.
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5364
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
@ UiFile
Load a .ui file for the layout. Needs to be configured.
FieldDomainMergePolicy
Merge policy for field domains.
Definition qgis.h:3824
@ DefaultValue
Use default field value.
@ Warning
Warning message.
Definition qgis.h:156
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:3807
@ Duplicate
Duplicate original value.
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:3844
@ Duplicate
Duplicate original value.
QgsAction action(QUuid id) const
Gets an action by its id.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:37
Dialog to add a container for attribute widgets.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
An abstract base class for any elements of a drag and drop form.
Manages available widgets when configuring attributes forms.
QModelIndex fieldModelIndex(const QString &fieldName) const
Returns the model index that corresponds to the field with the given fieldName.
void populateLayerActions(const QList< QgsAction > actions)
Refresh layer actions in the model to keep an updated action list.
QModelIndex fieldContainer() const
Returns the field container in this model, expected to be placed at the first top-level row.
QModelIndex relationContainer() const
Returns the relation container in this model, expected to be placed at the second top-level row.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Graphical representation for the available widgets while configuring attributes forms.
void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsAttributesFormProxyModel is an acceptable model.
QgsAttributesAvailableWidgetsView(QgsVectorLayer *layer, QWidget *parent=nullptr)
Constructor for QgsAttributesAvailableWidgetsView, with the given parent.
QgsAttributesAvailableWidgetsModel * availableWidgetsModel() const
Access the underlying QgsAttributesAvailableWidgetsModel source model.
Graphical representation for the attribute drag and drop editor.
void setFilterText(const QString &text)
Sets the filter text to the underlying proxy model.
QgsAttributesFormProxyModel * mModel
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsAttributesFormBaseView(QgsVectorLayer *layer, QWidget *parent=nullptr)
Constructor for QgsAttributesFormBaseView, with the given parent.
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.
Main class to store and transfer editor data contained in a QgsAttributesFormModel.
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
Returns the HTML editor configuration.
TextElementEditorConfiguration textElementEditorConfiguration() const
Returns the editor configuration for text element.
SpacerElementEditorConfiguration spacerElementEditorConfiguration() const
Returns the spacer element configuration.
void setHtmlElementEditorConfiguration(const HtmlElementEditorConfiguration &htmlElementEditorConfiguration)
Sets the HTML editor configuration.
bool showLabel() const
Returns whether the widget's label is to be shown.
void setShowLabel(bool showLabel)
Sets whether the label for the widget should be shown.
void setQmlElementEditorConfiguration(const QmlElementEditorConfiguration &qmlElementEditorConfiguration)
Sets the QML editor configuration.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
Returns the QML editor configuration.
void setTextElementEditorConfiguration(const TextElementEditorConfiguration &textElementEditorConfiguration)
Sets the editor configuration for text element to textElementEditorConfiguration.
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,.
A dialog for configuring the Python init code handling for attribute forms.
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.
void externalItemDropped(QModelIndex &index)
Informs that items were inserted (via drop) in the model from another model.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
void updateAliasForFieldItems(const QString &fieldName, const QString &fieldAlias)
Updates the aliases of all matching fields in the model.
bool removeRow(int row, const QModelIndex &parent=QModelIndex())
Removes the index located at row within the given parent.
void addContainer(QModelIndex &parent, const QString &name, int columnCount, Qgis::AttributeEditorContainerType type)
Adds a new container to parent.
QList< QgsAddAttributeFormContainerDialog::ContainerPair > listOfContainers() const
Returns a list of containers stored in the model, structured as pairs (name, container model index).
QgsAttributeEditorElement * createAttributeEditorWidget(const QModelIndex &index, QgsAttributeEditorElement *parent) const
Creates a new attribute editor element based on the definition stored in a form layout model index.
void internalItemDropped(QModelIndex &index)
Informs that items were moved (via drop) in the model from the same model.
Graphical representation for the form layout while configuring attributes forms.
void dropEvent(QDropEvent *event) override
void dragEnterEvent(QDragEnterEvent *event) override
QgsAttributesFormLayoutView(QgsVectorLayer *layer, QWidget *parent=nullptr)
Constructor for QgsAttributesFormLayoutView, with the given parent.
void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsAttributesFormProxyModel is an acceptable model.
void dragMoveEvent(QDragMoveEvent *event) override
Is called when mouse is moved over attributes tree before a drop event.
Abstract class for tree models allowing for configuration of 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.
void setShowAliases(bool show)
Sets whether field aliases should be preferred over field names as item text.
QModelIndex firstRecursiveMatchingModelIndex(const QgsAttributesFormData::AttributesFormItemType &itemType, const QString &itemId) const
Returns the first model index that matches the given itemType and itemId, recursively.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QgsAttributesFormBaseView * mFormLayoutView
void initAvailableWidgetsActions(const QList< QgsAction > actions)
Refresh layer actions in the Available Widgets view.
QgsAttributeFormContainerEdit * mAttributeContainerEdit
static const QgsSettingsEntryBool * settingShowAliases
QgsAttributesFormProperties(QgsVectorLayer *layer, QWidget *parent=nullptr)
QgsAttributesFormBaseView * mAvailableWidgetsView
QgsAttributeTypeDialog * mAttributeTypeDialog
void store()
Stores currently opened widget configuration.
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 setFilterText(const QString &filterText=QString())
Sets the filter text.
void setAttributesFormSourceModel(QgsAttributesFormModel *model)
Sets the source model for the proxy model.
A HTML editor based on QScintilla2.
A text editor based on QScintilla2.
void setText(const QString &text) override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
void editingTimeout()
Emitted when either:
Provides a container for managing client side default values for fields.
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.
Qgis::AttributeFormSuppression suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting)
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 setReuseLastValue(int index, bool reuse)
Sets whether the widget at the given index will remember the previously entered value from this QGIS ...
void setUiForm(const QString &ui)
Set path to the .ui form.
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.
Qgis::AttributeFormLayout layout() const
Gets the active layout style for the attribute editor for this layer.
Every attribute editor widget needs a factory, which inherits this class.
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.
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
A generic dialog for building expression strings.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Stores information about constraints which may be present on a field.
ConstraintStrength
Strength of constraints.
@ 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
A widget for selection of layer fields or expression creation.
void setButtonVisible(bool visible)
Set the visibility of the button.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
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:761
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:771
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:64
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:781
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:95
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:210
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
Wraps a QQuickWidget to display HTML code.
void reinitWidget()
Clears the content and makes new initialization.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
void setFeature(const QgsFeature &feature) override
QString id
Definition qgsmaplayer.h:81
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
int count() const
Returns the number of properties contained within the collection.
Wraps a QQuickWidget to display QML code.
void setFeature(const QgsFeature &feature) override
void reinitWidget()
Clears the content and makes new initialization.
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
A QScrollArea subclass with improved scrolling behavior.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
A boolean settings entry.
static const QgsSettingsEntryBool * settingsDigitizingDisableEnterAttributeValuesDialog
Settings entry digitizing disable enter attribute values dialog.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Wraps a label widget to display text.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
void setFeature(const QgsFeature &feature) override
Represents a vector layer which manages a vector based dataset.
QgsDefaultValue defaultValueDefinition(int index) const
Returns the definition of the expression used when calculating the default value for a field.
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
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.
QgsActionManager * actions()
Returns all layer actions defined on this layer.
void setFieldAlias(int index, const QString &aliasString)
Sets an alias (a display name) for attributes to display in dialogs.
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.
QWidget * widget()
Access the widget managed by this wrapper.
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:6602
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6583
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.
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