QGIS API Documentation 3.41.0-Master (cea29feecf2)
Loading...
Searching...
No Matches
qgsvaluerelationwidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvaluerelationwidgetwrapper.cpp
3 --------------------------------------
4 Date : 5.1.2014
5 Copyright : (C) 2014 Matthias Kuhn
6 Email : matthias at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsvaluerelationwidgetwrapper.cpp"
18
19#include "qgis.h"
20#include "qgsfields.h"
21#include "qgsproject.h"
22#include "qgsvectorlayer.h"
23#include "qgsfilterlineedit.h"
25#include "qgsattributeform.h"
27
28#include <QComboBox>
29#include <QLineEdit>
30#include <QStringListModel>
31#include <QCompleter>
32#include <QTimer>
33#include <QVBoxLayout>
34#include <QHeaderView>
35#include <QKeyEvent>
36#include <QStandardItemModel>
37
38#include <nlohmann/json.hpp>
39using namespace nlohmann;
40
42QgsFilteredTableWidget::QgsFilteredTableWidget( QWidget *parent, bool showSearch, bool displayGroupName )
43 : QWidget( parent )
44 , mDisplayGroupName( displayGroupName )
45{
46 mSearchWidget = new QgsFilterLineEdit( this );
47 mSearchWidget->setShowSearchIcon( true );
48 mSearchWidget->setShowClearButton( true );
49 mTableWidget = new QTableWidget( this );
50 mTableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
51 mTableWidget->horizontalHeader()->setVisible( false );
52 mTableWidget->verticalHeader()->setSectionResizeMode( QHeaderView::Stretch );
53 mTableWidget->verticalHeader()->setVisible( false );
54 mTableWidget->setShowGrid( false );
55 mTableWidget->setEditTriggers( QAbstractItemView::NoEditTriggers );
56 mTableWidget->setSelectionMode( QAbstractItemView::NoSelection );
57 QVBoxLayout *layout = new QVBoxLayout();
58 layout->addWidget( mSearchWidget );
59 layout->addWidget( mTableWidget );
60 layout->setContentsMargins( 0, 0, 0, 0 );
61 layout->setSpacing( 0 );
62 if ( showSearch )
63 {
64 mTableWidget->setFocusProxy( mSearchWidget );
65 connect( mSearchWidget, &QgsFilterLineEdit::textChanged, this, &QgsFilteredTableWidget::filterStringChanged );
66 installEventFilter( this );
67 }
68 else
69 {
70 mSearchWidget->setVisible( false );
71 }
72 setLayout( layout );
73 connect( mTableWidget, &QTableWidget::itemChanged, this, &QgsFilteredTableWidget::itemChanged_p );
74}
75
76bool QgsFilteredTableWidget::eventFilter( QObject *watched, QEvent *event )
77{
78 Q_UNUSED( watched )
79 if ( event->type() == QEvent::KeyPress )
80 {
81 QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
82 if ( keyEvent->key() == Qt::Key_Escape && !mSearchWidget->text().isEmpty() )
83 {
84 mSearchWidget->clear();
85 return true;
86 }
87 }
88 return false;
89}
90
91void QgsFilteredTableWidget::filterStringChanged( const QString &filterString )
92{
93 auto signalBlockedTableWidget = whileBlocking( mTableWidget );
94 Q_UNUSED( signalBlockedTableWidget )
95
96 mTableWidget->clearContents();
97 if ( !mCache.isEmpty() )
98 {
99 QVariantList groups;
100 groups << QVariant();
101 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
102 {
103 if ( !groups.contains( pair.first.group ) )
104 {
105 groups << pair.first.group;
106 }
107 }
108 const int groupsCount = mDisplayGroupName ? groups.count() : groups.count() - 1;
109
110 const int rCount = std::max( 1, ( int ) std::ceil( ( float ) ( mCache.count() + groupsCount ) / ( float ) mColumnCount ) );
111 mTableWidget->setRowCount( rCount );
112
113 int row = 0;
114 int column = 0;
115 QVariant currentGroup;
116 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
117 {
118 if ( column == mColumnCount )
119 {
120 row++;
121 column = 0;
122 }
123 if ( currentGroup != pair.first.group )
124 {
125 currentGroup = pair.first.group;
126 if ( mDisplayGroupName || !( row == 0 && column == 0 ) )
127 {
128 QTableWidgetItem *item = new QTableWidgetItem( mDisplayGroupName ? pair.first.group.toString() : QString() );
129 item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
130 mTableWidget->setItem( row, column, item );
131 column++;
132 if ( column == mColumnCount )
133 {
134 row++;
135 column = 0;
136 }
137 }
138 }
139 if ( pair.first.value.contains( filterString, Qt::CaseInsensitive ) )
140 {
141 QTableWidgetItem *item = new QTableWidgetItem( pair.first.value );
142 item->setData( Qt::UserRole, pair.first.key );
143 item->setData( Qt::ToolTipRole, pair.first.description );
144 item->setCheckState( pair.second );
145 item->setFlags( mEnabledTable ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
146 mTableWidget->setItem( row, column, item );
147 column++;
148 }
149 }
150 mTableWidget->setRowCount( row + 1 );
151 }
152}
153
154QStringList QgsFilteredTableWidget::selection() const
155{
156 QStringList sel;
157 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
158 {
159 if ( pair.second == Qt::Checked )
160 sel.append( pair.first.key.toString() );
161 }
162 return sel;
163}
164
165void QgsFilteredTableWidget::checkItems( const QStringList &checked )
166{
167 for ( QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : mCache )
168 {
169 const bool isChecked = checked.contains( pair.first.key.toString() );
170 pair.second = isChecked ? Qt::Checked : Qt::Unchecked;
171 }
172
173 filterStringChanged( mSearchWidget->text() );
174}
175
176void QgsFilteredTableWidget::populate( QgsValueRelationFieldFormatter::ValueRelationCache cache )
177{
178 mCache.clear();
179 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &element : std::as_const( cache ) )
180 {
181 mCache.append( qMakePair( element, Qt::Unchecked ) );
182 }
183 filterStringChanged( mSearchWidget->text() );
184}
185
186void QgsFilteredTableWidget::setIndeterminateState()
187{
188 for ( int rowIndex = 0; rowIndex < mTableWidget->rowCount(); rowIndex++ )
189 {
190 for ( int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex )
191 {
192 if ( item( rowIndex, columnIndex ) )
193 {
194 whileBlocking( mTableWidget )->item( rowIndex, columnIndex )->setCheckState( Qt::PartiallyChecked );
195 }
196 else
197 {
198 break;
199 }
200 }
201 }
202}
203
204void QgsFilteredTableWidget::setEnabledTable( const bool enabled )
205{
206 if ( mEnabledTable == enabled )
207 return;
208
209 mEnabledTable = enabled;
210 if ( !enabled )
211 mSearchWidget->clear();
212
213 filterStringChanged( mSearchWidget->text() );
214}
215
216void QgsFilteredTableWidget::setColumnCount( const int count )
217{
218 mColumnCount = count;
219 mTableWidget->setColumnCount( count );
220}
221
222void QgsFilteredTableWidget::itemChanged_p( QTableWidgetItem *item )
223{
224 for ( QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : mCache )
225 {
226 if ( pair.first.key == item->data( Qt::UserRole ) )
227 pair.second = item->checkState();
228 }
229 emit itemChanged( item );
230}
232
233
234QgsValueRelationWidgetWrapper::QgsValueRelationWidgetWrapper( QgsVectorLayer *layer, int fieldIdx, QWidget *editor, QWidget *parent )
235 : QgsEditorWidgetWrapper( layer, fieldIdx, editor, parent )
236{
237}
238
240{
241 QVariant v;
242
243 if ( mComboBox )
244 {
245 int cbxIdx = mComboBox->currentIndex();
246 if ( cbxIdx > -1 )
247 {
248 v = mComboBox->currentData();
249 if ( QgsVariantUtils::isNull( v ) )
251 }
252 }
253 else if ( mTableWidget )
254 {
255 QStringList selection = mTableWidget->selection();
256
257 // If there is no selection and allow NULL is not checked return NULL.
258 if ( selection.isEmpty() && !config( QStringLiteral( "AllowNull" ) ).toBool() )
259 {
260 return QgsVariantUtils::createNullVariant( QMetaType::Type::QVariantList );
261 }
262
263 QVariantList vl;
264 //store as QVariantList because the field type supports data structure
265 for ( const QString &s : std::as_const( selection ) )
266 {
267 // Convert to proper type
268 const QMetaType::Type type { fkType() };
269 switch ( type )
270 {
271 case QMetaType::Type::Int:
272 vl.push_back( s.toInt() );
273 break;
274 case QMetaType::Type::LongLong:
275 vl.push_back( s.toLongLong() );
276 break;
277 default:
278 vl.push_back( s );
279 break;
280 }
281 }
282
283 if ( layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantMap || layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
284 {
285 v = vl;
286 }
287 else
288 {
289 //make string
291 }
292 }
293 else if ( mLineEdit )
294 {
295 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( mCache ) )
296 {
297 if ( item.value == mLineEdit->text() )
298 {
299 v = item.key;
300 break;
301 }
302 }
303 }
304
305 return v;
306}
307
309{
310 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
311 if ( form )
313
314 mExpression = config().value( QStringLiteral( "FilterExpression" ) ).toString();
315
316 const bool allowMulti = config( QStringLiteral( "AllowMulti" ) ).toBool();
317 const bool useCompleter = config( QStringLiteral( "UseCompleter" ) ).toBool();
318 if ( allowMulti )
319 {
320 const bool displayGroupName = config( QStringLiteral( "DisplayGroupName" ) ).toBool();
321 return new QgsFilteredTableWidget( parent, useCompleter, displayGroupName );
322 }
323 else if ( useCompleter )
324 {
325 return new QgsFilterLineEdit( parent );
326 }
327 else
328 {
329 QgsToolTipComboBox *combo = new QgsToolTipComboBox( parent );
330 combo->setMinimumContentsLength( 1 );
331 combo->setSizeAdjustPolicy( QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon );
332 return combo;
333 }
334}
335
337{
338 mComboBox = qobject_cast<QComboBox *>( editor );
339 mTableWidget = qobject_cast<QgsFilteredTableWidget *>( editor );
340 mLineEdit = qobject_cast<QLineEdit *>( editor );
341
342 // Read current initial form values from the editor context
344
345 if ( mComboBox )
346 {
347 mComboBox->view()->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
348 connect( mComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, static_cast<void ( QgsEditorWidgetWrapper::* )()>( &QgsEditorWidgetWrapper::emitValueChanged ), Qt::UniqueConnection );
349 }
350 else if ( mTableWidget )
351 {
352 connect( mTableWidget, &QgsFilteredTableWidget::itemChanged, this, static_cast<void ( QgsEditorWidgetWrapper::* )()>( &QgsEditorWidgetWrapper::emitValueChanged ), Qt::UniqueConnection );
353 }
354 else if ( mLineEdit )
355 {
356 if ( QgsFilterLineEdit *filterLineEdit = qobject_cast<QgsFilterLineEdit *>( editor ) )
357 {
358 connect( filterLineEdit, &QgsFilterLineEdit::valueChanged, this, [=]( const QString & ) {
359 if ( mSubWidgetSignalBlocking == 0 )
361 } );
362 }
363 else
364 {
365 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsValueRelationWidgetWrapper::emitValueChangedInternal, Qt::UniqueConnection );
366 }
367 }
368}
369
371{
372 return mTableWidget || mLineEdit || mComboBox;
373}
374
375void QgsValueRelationWidgetWrapper::updateValues( const QVariant &value, const QVariantList & )
376{
377 updateValue( value, true );
378}
379
380void QgsValueRelationWidgetWrapper::updateValue( const QVariant &value, bool forceComboInsertion )
381{
382 if ( mTableWidget )
383 {
384 QStringList checkList;
385
386 if ( layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantMap || layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
387 {
388 checkList = value.toStringList();
389 }
390 else
391 {
393 }
394
395 mTableWidget->checkItems( checkList );
396 }
397 else if ( mComboBox )
398 {
399 // findData fails to tell a 0 from a NULL
400 // See: "Value relation, value 0 = NULL" - https://github.com/qgis/QGIS/issues/27803
401 int idx = -1; // default to not found
402 for ( int i = 0; i < mComboBox->count(); i++ )
403 {
404 QVariant v( mComboBox->itemData( i ) );
405 if ( qgsVariantEqual( v, value ) )
406 {
407 idx = i;
408 break;
409 }
410 }
411
412 if ( idx == -1 )
413 {
414 // if value doesn't exist, we show it in '(...)' (just like value map widget)
415 if ( QgsVariantUtils::isNull( value ) || !forceComboInsertion )
416 {
417 mComboBox->setCurrentIndex( -1 );
418 }
419 else
420 {
421 mComboBox->addItem( value.toString().prepend( '(' ).append( ')' ), value );
422 mComboBox->setCurrentIndex( mComboBox->findData( value ) );
423 }
424 }
425 else
426 {
427 mComboBox->setCurrentIndex( idx );
428 }
429 }
430 else if ( mLineEdit )
431 {
432 mSubWidgetSignalBlocking++;
433 mLineEdit->clear();
434 bool wasFound { false };
435 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &i : std::as_const( mCache ) )
436 {
437 if ( i.key == value )
438 {
439 mLineEdit->setText( i.value );
440 wasFound = true;
441 break;
442 }
443 }
444 // Value could not be found
445 if ( !wasFound )
446 {
447 mLineEdit->setText( tr( "(no selection)" ) );
448 }
449 mSubWidgetSignalBlocking--;
450 }
451}
452
453void QgsValueRelationWidgetWrapper::widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged )
454{
455 // Do nothing if the value has not changed except for multi edit mode
456 // In multi edit mode feature is not updated (so attributeChanged is false) until user validate it but we need to update the
457 // value relation which could have an expression depending on another modified field
458 if ( attributeChanged || context().attributeFormMode() == QgsAttributeEditorContext::Mode::MultiEditMode )
459 {
460 QVariant oldValue( value() );
461 setFormFeatureAttribute( attribute, newValue );
462 // Update combos if the value used in the filter expression has changed
464 && QgsValueRelationFieldFormatter::expressionFormAttributes( mExpression ).contains( attribute ) )
465 {
466 populate();
467 // Restore value
468 updateValue( oldValue, false );
469 // If the value has changed as a result of another widget's value change,
470 // we need to emit the signal to make sure other dependent widgets are
471 // updated.
472 QgsFields formFields( formFeature().fields() );
473
474 // Also check for fields in the layer in case this is a multi-edit form
475 // and there is not form feature set
476 if ( formFields.count() == 0 && layer() )
477 {
478 formFields = layer()->fields();
479 }
480
481 if ( oldValue != value() && fieldIdx() < formFields.count() )
482 {
483 QString attributeName( formFields.names().at( fieldIdx() ) );
484 setFormFeatureAttribute( attributeName, value() );
486 }
487 }
488 }
489}
490
491
493{
494 setFormFeature( feature );
495 whileBlocking( this )->populate();
496 whileBlocking( this )->setValue( feature.attribute( fieldIdx() ) );
497
498 // As we block any signals, possible depending widgets will not being updated
499 // so we force emit signal once and for all
501
502 // A bit of logic to set the default value if AllowNull is false and this is a new feature
503 // Note that this needs to be here after the cache has been created/updated by populate()
504 // and signals unblocked (we want this to propagate to the feature itself)
505 if ( context().attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode
506 && !formFeature().attribute( fieldIdx() ).isValid()
507 && !mCache.isEmpty()
508 && !config( QStringLiteral( "AllowNull" ) ).toBool() )
509 {
510 // This is deferred because at the time the feature is set in one widget it is not
511 // set in the next, which is typically the "down" in a drill-down
512 QTimer::singleShot( 0, this, [this] {
513 if ( !mCache.isEmpty() )
514 {
515 updateValues( formFeature().attribute( fieldIdx() ).isValid() ? formFeature().attribute( fieldIdx() ) : mCache.at( 0 ).key );
516 }
517 } );
518 }
519}
520
521int QgsValueRelationWidgetWrapper::columnCount() const
522{
523 return std::max( 1, config( QStringLiteral( "NofColumns" ) ).toInt() );
524}
525
526
527QMetaType::Type QgsValueRelationWidgetWrapper::fkType() const
528{
530 if ( layer )
531 {
532 QgsFields fields = layer->fields();
533 int idx { fields.lookupField( config().value( QStringLiteral( "Key" ) ).toString() ) };
534 if ( idx >= 0 )
535 {
536 return fields.at( idx ).type();
537 }
538 }
539 return QMetaType::Type::UnknownType;
540}
541
542void QgsValueRelationWidgetWrapper::populate()
543{
544 // Initialize, note that signals are blocked, to avoid double signals on new features
546 {
547 if ( context().parentFormFeature().isValid() )
548 {
549 mCache = QgsValueRelationFieldFormatter::createCache( config(), formFeature(), context().parentFormFeature() );
550 }
551 else
552 {
554 }
555 }
556 else if ( mCache.empty() )
557 {
559 }
560
561 if ( mComboBox )
562 {
563 mComboBox->blockSignals( true );
564 mComboBox->clear();
565 const bool allowNull = config( QStringLiteral( "AllowNull" ) ).toBool();
566 if ( allowNull )
567 {
568 mComboBox->addItem( tr( "(no selection)" ), QgsVariantUtils::createNullVariant( field().type() ) );
569 }
570
571 if ( !mCache.isEmpty() )
572 {
573 QVariant currentGroup;
574 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mComboBox->model() );
575 const bool displayGroupName = config( QStringLiteral( "DisplayGroupName" ) ).toBool();
576 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &element : std::as_const( mCache ) )
577 {
578 if ( currentGroup != element.group )
579 {
580 if ( mComboBox->count() > ( allowNull ? 1 : 0 ) )
581 {
582 mComboBox->insertSeparator( mComboBox->count() );
583 }
584 if ( displayGroupName )
585 {
586 mComboBox->addItem( element.group.toString() );
587 QStandardItem *item = model->item( mComboBox->count() - 1 );
588 item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
589 }
590 currentGroup = element.group;
591 }
592
593 mComboBox->addItem( element.value, element.key );
594
595 if ( !element.description.isEmpty() )
596 {
597 mComboBox->setItemData( mComboBox->count() - 1, element.description, Qt::ToolTipRole );
598 }
599 }
600 }
601 mComboBox->blockSignals( false );
602 }
603 else if ( mTableWidget )
604 {
605 mTableWidget->setColumnCount( columnCount() );
606 mTableWidget->populate( mCache );
607 }
608 else if ( mLineEdit )
609 {
610 QStringList values;
611 values.reserve( mCache.size() );
612 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &i : std::as_const( mCache ) )
613 {
614 values << i.value;
615 }
616 QStringListModel *m = new QStringListModel( values, mLineEdit );
617 QCompleter *completer = new QCompleter( m, mLineEdit );
618
619 const Qt::MatchFlags completerMatchFlags { config().contains( QStringLiteral( "CompleterMatchFlags" ) ) ? static_cast<Qt::MatchFlags>( config().value( QStringLiteral( "CompleterMatchFlags" ), Qt::MatchFlag::MatchStartsWith ).toInt() ) : Qt::MatchFlag::MatchStartsWith };
620
621 if ( completerMatchFlags.testFlag( Qt::MatchFlag::MatchContains ) )
622 {
623 completer->setFilterMode( Qt::MatchFlag::MatchContains );
624 }
625 else
626 {
627 completer->setFilterMode( Qt::MatchFlag::MatchStartsWith );
628 }
629 completer->setCaseSensitivity( Qt::CaseInsensitive );
630 mLineEdit->setCompleter( completer );
631 }
632}
633
635{
636 if ( mTableWidget )
637 {
638 mTableWidget->setIndeterminateState();
639 }
640 else if ( mComboBox )
641 {
642 whileBlocking( mComboBox )->setCurrentIndex( -1 );
643 }
644 else if ( mLineEdit )
645 {
646 whileBlocking( mLineEdit )->clear();
647 }
648}
649
651{
652 if ( mEnabled == enabled )
653 return;
654
655 mEnabled = enabled;
656
657 if ( mTableWidget )
658 {
659 mTableWidget->setEnabledTable( enabled );
660 }
661 else
663}
664
665void QgsValueRelationWidgetWrapper::parentFormValueChanged( const QString &attribute, const QVariant &value )
666{
667 // Update the parent feature in the context ( which means to replace the whole context :/ )
669 QgsFeature feature { context().parentFormFeature() };
670 feature.setAttribute( attribute, value );
671 ctx.setParentFormFeature( feature );
672 setContext( ctx );
673
674 // Check if the change might affect the filter expression and the cache needs updates
676 && ( config( QStringLiteral( "Value" ) ).toString() == attribute || config( QStringLiteral( "Key" ) ).toString() == attribute || !QgsValueRelationFieldFormatter::expressionParentFormVariables( mExpression ).isEmpty() || QgsValueRelationFieldFormatter::expressionParentFormAttributes( mExpression ).contains( attribute ) ) )
677 {
678 populate();
679 }
680}
681
682void QgsValueRelationWidgetWrapper::emitValueChangedInternal( const QString &value )
683{
685 emit valueChanged( value );
687 emit valuesChanged( value );
688}
This class contains context information for attribute editor widgets.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
@ MultiEditMode
Multi edit mode, for editing fields of multiple features at once.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
Manages an editor widget Widget and wrapper share the same parent.
QgsFeature formFeature() const
The feature currently being edited, in its current state.
Q_DECL_DEPRECATED void valueChanged(const QVariant &value)
Emit this signal, whenever the value changed.
void setFormFeature(const QgsFeature &feature)
Set the feature currently being edited to feature.
int fieldIdx() const
Access the field index.
void setEnabled(bool enabled) override
Is used to enable or disable the edit functionality of the managed widget.
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
bool setFormFeatureAttribute(const QString &attributeName, const QVariant &attributeValue)
Update the feature currently being edited by changing its attribute attributeName to attributeValue.
void emitValueChanged()
Will call the value() method to determine the emitted value.
QgsField field() const
Access the field.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
QMetaType::Type type
Definition qgsfield.h:60
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QStringList names
Definition qgsfields.h:51
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
void valueChanged(const QString &value)
Same as textChanged() but with support for null values.
static QString buildArray(const QVariantList &list)
Build a postgres array like formatted list in a string from a QVariantList.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QComboBox subclass which features a tooltip when mouse hovering the combobox.
static QgsVectorLayer * resolveLayer(const QVariantMap &config, const QgsProject *project)
Returns the (possibly nullptr) layer from the widget's config and project.
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
static bool expressionRequiresParentFormScope(const QString &expression)
Check if the expression requires a parent form scope (i.e.
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
static QSet< QString > expressionParentFormVariables(const QString &expression)
Returns a list of variables required by the parent form's form context expression.
static QSet< QString > expressionParentFormAttributes(const QString &expression)
Returns a list of attributes required by the parent form's form context expression.
static QStringList valueToStringList(const QVariant &value)
Utility to convert a list or a string representation of an (hstore style: {1,2...}) list in value to ...
static QSet< QString > expressionFormAttributes(const QString &expression)
Returns a list of attributes required by the form context expression.
QVector< QgsValueRelationFieldFormatter::ValueRelationItem > ValueRelationCache
QVariant value() const override
Will be used to access the widget's value.
bool valid() const override
Returns true if the widget has been properly initialized.
void showIndeterminateState() override
Sets the widget to display in an indeterminate "mixed value" state.
void parentFormValueChanged(const QString &attribute, const QVariant &value) override
QgsValueRelationWidgetWrapper(QgsVectorLayer *layer, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
Constructor for QgsValueRelationWidgetWrapper.
void setEnabled(bool enabled) override
Is used to enable or disable the edit functionality of the managed widget.
void widgetValueChanged(const QString &attribute, const QVariant &newValue, bool attributeChanged)
Will be called when a value in the current edited form or table row changes.
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
void setFeature(const QgsFeature &feature) override
Will be called when the feature changes.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Represents a vector layer which manages a vector based data sets.
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
QVariantMap config() const
Returns the whole config.
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, two NULL values are always treated a...
Definition qgis.cpp:248
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6601
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6600
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5928