QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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"
23#include "qgsvectorlayer.h"
24#include "qgsfilterlineedit.h"
25#include "qgsfeatureiterator.h"
27#include "qgsattributeform.h"
28#include "qgsattributes.h"
29#include "qgsjsonutils.h"
31#include "qgsapplication.h"
32
33#include <QComboBox>
34#include <QLineEdit>
35#include <QStringListModel>
36#include <QCompleter>
37#include <QTimer>
38#include <QVBoxLayout>
39#include <QHeaderView>
40#include <QKeyEvent>
41#include <QStandardItemModel>
42
43#include <nlohmann/json.hpp>
44using namespace nlohmann;
45
47QgsFilteredTableWidget::QgsFilteredTableWidget( QWidget *parent, bool showSearch, bool displayGroupName )
48 : QWidget( parent )
49 , mDisplayGroupName( displayGroupName )
50{
51 mSearchWidget = new QgsFilterLineEdit( this );
52 mSearchWidget->setShowSearchIcon( true );
53 mSearchWidget->setShowClearButton( true );
54 mTableWidget = new QTableWidget( this );
55 mTableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
56 mTableWidget->horizontalHeader()->setVisible( false );
57 mTableWidget->verticalHeader()->setSectionResizeMode( QHeaderView::Stretch );
58 mTableWidget->verticalHeader()->setVisible( false );
59 mTableWidget->setShowGrid( false );
60 mTableWidget->setEditTriggers( QAbstractItemView::NoEditTriggers );
61 mTableWidget->setSelectionMode( QAbstractItemView::NoSelection );
62 QVBoxLayout *layout = new QVBoxLayout();
63 layout->addWidget( mSearchWidget );
64 layout->addWidget( mTableWidget );
65 layout->setContentsMargins( 0, 0, 0, 0 );
66 layout->setSpacing( 0 );
67 if ( showSearch )
68 {
69 mTableWidget->setFocusProxy( mSearchWidget );
70 connect( mSearchWidget, &QgsFilterLineEdit::textChanged, this, &QgsFilteredTableWidget::filterStringChanged );
71 installEventFilter( this );
72 }
73 else
74 {
75 mSearchWidget->setVisible( false );
76 }
77 setLayout( layout );
78 connect( mTableWidget, &QTableWidget::itemChanged, this, &QgsFilteredTableWidget::itemChanged_p );
79}
80
81bool QgsFilteredTableWidget::eventFilter( QObject *watched, QEvent *event )
82{
83 Q_UNUSED( watched )
84 if ( event->type() == QEvent::KeyPress )
85 {
86 QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
87 if ( keyEvent->key() == Qt::Key_Escape &&
88 !mSearchWidget->text().isEmpty() )
89 {
90 mSearchWidget->clear();
91 return true;
92 }
93 }
94 return false;
95}
96
97void QgsFilteredTableWidget::filterStringChanged( const QString &filterString )
98{
99 auto signalBlockedTableWidget = whileBlocking( mTableWidget );
100 Q_UNUSED( signalBlockedTableWidget )
101
102 mTableWidget->clearContents();
103 if ( !mCache.isEmpty() )
104 {
105 QVariantList groups;
106 groups << QVariant();
107 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
108 {
109 if ( !groups.contains( pair.first.group ) )
110 {
111 groups << pair.first.group;
112 }
113 }
114 const int groupsCount = mDisplayGroupName ? groups.count() : groups.count() - 1;
115
116 const int rCount = std::max( 1, ( int ) std::ceil( ( float )( mCache.count() + groupsCount ) / ( float ) mColumnCount ) );
117 mTableWidget->setRowCount( rCount );
118
119 int row = 0;
120 int column = 0;
121 QVariant currentGroup;
122 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
123 {
124 if ( column == mColumnCount )
125 {
126 row++;
127 column = 0;
128 }
129 if ( currentGroup != pair.first.group )
130 {
131 currentGroup = pair.first.group;
132 if ( mDisplayGroupName || !( row == 0 && column == 0 ) )
133 {
134 QTableWidgetItem *item = new QTableWidgetItem( mDisplayGroupName ? pair.first.group.toString() : QString() );
135 item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
136 mTableWidget->setItem( row, column, item );
137 column++;
138 if ( column == mColumnCount )
139 {
140 row++;
141 column = 0;
142 }
143 }
144 }
145 if ( pair.first.value.contains( filterString, Qt::CaseInsensitive ) )
146 {
147 QTableWidgetItem *item = new QTableWidgetItem( pair.first.value );
148 item->setData( Qt::UserRole, pair.first.key );
149 item->setData( Qt::ToolTipRole, pair.first.description );
150 item->setCheckState( pair.second );
151 item->setFlags( mEnabledTable ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
152 mTableWidget->setItem( row, column, item );
153 column++;
154 }
155 }
156 mTableWidget->setRowCount( row + 1 );
157 }
158}
159
160QStringList QgsFilteredTableWidget::selection() const
161{
162 QStringList sel;
163 for ( const QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : std::as_const( mCache ) )
164 {
165 if ( pair.second == Qt::Checked )
166 sel.append( pair.first.key.toString() );
167 }
168 return sel;
169}
170
171void QgsFilteredTableWidget::checkItems( const QStringList &checked )
172{
173 for ( QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : mCache )
174 {
175 const bool isChecked = checked.contains( pair.first.key.toString() );
176 pair.second = isChecked ? Qt::Checked : Qt::Unchecked;
177 }
178
179 filterStringChanged( mSearchWidget->text() );
180}
181
182void QgsFilteredTableWidget::populate( QgsValueRelationFieldFormatter::ValueRelationCache cache )
183{
184 mCache.clear();
185 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &element : std::as_const( cache ) )
186 {
187 mCache.append( qMakePair( element, Qt::Unchecked ) );
188 }
189 filterStringChanged( mSearchWidget->text() );
190}
191
192void QgsFilteredTableWidget::setIndeterminateState()
193{
194 for ( int rowIndex = 0; rowIndex < mTableWidget->rowCount(); rowIndex++ )
195 {
196 for ( int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex )
197 {
198 if ( item( rowIndex, columnIndex ) )
199 {
200 whileBlocking( mTableWidget )->item( rowIndex, columnIndex )->setCheckState( Qt::PartiallyChecked );
201 }
202 else
203 {
204 break;
205 }
206 }
207 }
208}
209
210void QgsFilteredTableWidget::setEnabledTable( const bool enabled )
211{
212 if ( mEnabledTable == enabled )
213 return;
214
215 mEnabledTable = enabled;
216 if ( !enabled )
217 mSearchWidget->clear();
218
219 filterStringChanged( mSearchWidget->text() );
220}
221
222void QgsFilteredTableWidget::setColumnCount( const int count )
223{
224 mColumnCount = count;
225 mTableWidget->setColumnCount( count );
226}
227
228void QgsFilteredTableWidget::itemChanged_p( QTableWidgetItem *item )
229{
230 for ( QPair<QgsValueRelationFieldFormatter::ValueRelationItem, Qt::CheckState> &pair : mCache )
231 {
232 if ( pair.first.key == item->data( Qt::UserRole ) )
233 pair.second = item->checkState();
234 }
235 emit itemChanged( item );
236}
238
239
240QgsValueRelationWidgetWrapper::QgsValueRelationWidgetWrapper( QgsVectorLayer *layer, int fieldIdx, QWidget *editor, QWidget *parent )
241 : QgsEditorWidgetWrapper( layer, fieldIdx, editor, parent )
242{
243}
244
246{
247 QVariant v;
248
249 if ( mComboBox )
250 {
251 int cbxIdx = mComboBox->currentIndex();
252 if ( cbxIdx > -1 )
253 {
254 v = mComboBox->currentData();
255 if ( QgsVariantUtils::isNull( v ) )
257 }
258 }
259 else if ( mTableWidget )
260 {
261 QStringList selection = mTableWidget->selection();
262
263 // If there is no selection and allow NULL is not checked return NULL.
264 if ( selection.isEmpty() && ! config( QStringLiteral( "AllowNull" ) ).toBool( ) )
265 {
266 return QgsVariantUtils::createNullVariant( QMetaType::Type::QVariantList );
267 }
268
269 QVariantList vl;
270 //store as QVariantList because the field type supports data structure
271 for ( const QString &s : std::as_const( selection ) )
272 {
273 // Convert to proper type
274 const QMetaType::Type type { fkType() };
275 switch ( type )
276 {
277 case QMetaType::Type::Int:
278 vl.push_back( s.toInt() );
279 break;
280 case QMetaType::Type::LongLong:
281 vl.push_back( s.toLongLong() );
282 break;
283 default:
284 vl.push_back( s );
285 break;
286 }
287 }
288
289 if ( layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantMap ||
290 layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
291 {
292 v = vl;
293 }
294 else
295 {
296 //make string
298 }
299 }
300 else if ( mLineEdit )
301 {
302 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( mCache ) )
303 {
304 if ( item.value == mLineEdit->text() )
305 {
306 v = item.key;
307 break;
308 }
309 }
310 }
311
312 return v;
313}
314
316{
317 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
318 if ( form )
320
321 mExpression = config().value( QStringLiteral( "FilterExpression" ) ).toString();
322
323 const bool allowMulti = config( QStringLiteral( "AllowMulti" ) ).toBool();
324 const bool useCompleter = config( QStringLiteral( "UseCompleter" ) ).toBool();
325 if ( allowMulti )
326 {
327 const bool displayGroupName = config( QStringLiteral( "DisplayGroupName" ) ).toBool();
328 return new QgsFilteredTableWidget( parent, useCompleter, displayGroupName );
329 }
330 else if ( useCompleter )
331 {
332 return new QgsFilterLineEdit( parent );
333 }
334 else
335 {
336 QgsToolTipComboBox *combo = new QgsToolTipComboBox( parent );
337 combo->setMinimumContentsLength( 1 );
338 combo->setSizeAdjustPolicy( QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon );
339 return combo;
340 }
341}
342
344{
345 mComboBox = qobject_cast<QComboBox *>( editor );
346 mTableWidget = qobject_cast<QgsFilteredTableWidget *>( editor );
347 mLineEdit = qobject_cast<QLineEdit *>( editor );
348
349 // Read current initial form values from the editor context
351
352 if ( mComboBox )
353 {
354 mComboBox->view()->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
355 connect( mComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ),
356 this, static_cast<void ( QgsEditorWidgetWrapper::* )()>( &QgsEditorWidgetWrapper::emitValueChanged ), Qt::UniqueConnection );
357 }
358 else if ( mTableWidget )
359 {
360 connect( mTableWidget, &QgsFilteredTableWidget::itemChanged, this, static_cast<void ( QgsEditorWidgetWrapper::* )()>( &QgsEditorWidgetWrapper::emitValueChanged ), Qt::UniqueConnection );
361 }
362 else if ( mLineEdit )
363 {
364 if ( QgsFilterLineEdit *filterLineEdit = qobject_cast<QgsFilterLineEdit *>( editor ) )
365 {
366 connect( filterLineEdit, &QgsFilterLineEdit::valueChanged, this, [ = ]( const QString & )
367 {
368 if ( mSubWidgetSignalBlocking == 0 )
370 } );
371 }
372 else
373 {
374 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsValueRelationWidgetWrapper::emitValueChangedInternal, Qt::UniqueConnection );
375 }
376 }
377}
378
380{
381 return mTableWidget || mLineEdit || mComboBox;
382}
383
384void QgsValueRelationWidgetWrapper::updateValues( const QVariant &value, const QVariantList & )
385{
386 if ( mTableWidget )
387 {
388 QStringList checkList;
389
390 if ( layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantMap ||
391 layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
392 {
393 checkList = value.toStringList();
394 }
395 else
396 {
398 }
399
400 mTableWidget->checkItems( checkList );
401 }
402 else if ( mComboBox )
403 {
404 // findData fails to tell a 0 from a NULL
405 // See: "Value relation, value 0 = NULL" - https://github.com/qgis/QGIS/issues/27803
406 int idx = -1; // default to not found
407 for ( int i = 0; i < mComboBox->count(); i++ )
408 {
409 QVariant v( mComboBox->itemData( i ) );
410 if ( qgsVariantEqual( v, value ) )
411 {
412 idx = i;
413 break;
414 }
415 }
416
417 if ( idx == -1 )
418 {
419 // if value doesn't exist, we show it in '(...)' (just like value map widget)
421 {
422 mComboBox->setCurrentIndex( -1 );
423 }
424 else
425 {
426 mComboBox->addItem( value.toString().prepend( '(' ).append( ')' ), value );
427 mComboBox->setCurrentIndex( mComboBox->findData( value ) );
428 }
429 }
430 else
431 {
432 mComboBox->setCurrentIndex( idx );
433 }
434 }
435 else if ( mLineEdit )
436 {
437 mSubWidgetSignalBlocking ++;
438 mLineEdit->clear();
439 bool wasFound { false };
440 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &i : std::as_const( mCache ) )
441 {
442 if ( i.key == value )
443 {
444 mLineEdit->setText( i.value );
445 wasFound = true;
446 break;
447 }
448 }
449 // Value could not be found
450 if ( ! wasFound )
451 {
452 mLineEdit->setText( tr( "(no selection)" ) );
453 }
454 mSubWidgetSignalBlocking --;
455 }
456}
457
458void QgsValueRelationWidgetWrapper::widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged )
459{
460 // Do nothing if the value has not changed except for multi edit mode
461 // In multi edit mode feature is not updated (so attributeChanged is false) until user validate it but we need to update the
462 // value relation which could have an expression depending on another modified field
463 if ( attributeChanged || context().attributeFormMode() == QgsAttributeEditorContext::Mode::MultiEditMode )
464 {
465 QVariant oldValue( value( ) );
466 setFormFeatureAttribute( attribute, newValue );
467 // Update combos if the value used in the filter expression has changed
469 && QgsValueRelationFieldFormatter::expressionFormAttributes( mExpression ).contains( attribute ) )
470 {
471 populate();
472 // Restore value
473 updateValues( value( ) );
474 // If the value has changed as a result of another widget's value change,
475 // we need to emit the signal to make sure other dependent widgets are
476 // updated.
477 QgsFields formFields( formFeature().fields() );
478
479 // Also check for fields in the layer in case this is a multi-edit form
480 // and there is not form feature set
481 if ( formFields.count() == 0 && layer() )
482 {
483 formFields = layer()->fields();
484 }
485
486 if ( oldValue != value() && fieldIdx() < formFields.count() )
487 {
488 QString attributeName( formFields.names().at( fieldIdx() ) );
489 setFormFeatureAttribute( attributeName, value( ) );
491 }
492 }
493 }
494}
495
496
498{
499 setFormFeature( feature );
500 whileBlocking( this )->populate();
501 whileBlocking( this )->setValue( feature.attribute( fieldIdx() ) );
502
503 // As we block any signals, possible depending widgets will not being updated
504 // so we force emit signal once and for all
506
507 // A bit of logic to set the default value if AllowNull is false and this is a new feature
508 // Note that this needs to be here after the cache has been created/updated by populate()
509 // and signals unblocked (we want this to propagate to the feature itself)
510 if ( context().attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode
511 && ! formFeature().attribute( fieldIdx() ).isValid()
512 && ! mCache.isEmpty()
513 && ! config( QStringLiteral( "AllowNull" ) ).toBool( ) )
514 {
515 // This is deferred because at the time the feature is set in one widget it is not
516 // set in the next, which is typically the "down" in a drill-down
517 QTimer::singleShot( 0, this, [ this ]
518 {
519 if ( ! mCache.isEmpty() )
520 {
521 updateValues( formFeature().attribute( fieldIdx() ).isValid() ? formFeature().attribute( fieldIdx() ) : mCache.at( 0 ).key );
522 }
523 } );
524 }
525}
526
527int QgsValueRelationWidgetWrapper::columnCount() const
528{
529 return std::max( 1, config( QStringLiteral( "NofColumns" ) ).toInt() );
530}
531
532
533QMetaType::Type QgsValueRelationWidgetWrapper::fkType() const
534{
536 if ( layer )
537 {
538 QgsFields fields = layer->fields();
539 int idx { fields.lookupField( config().value( QStringLiteral( "Key" ) ).toString() ) };
540 if ( idx >= 0 )
541 {
542 return fields.at( idx ).type();
543 }
544 }
545 return QMetaType::Type::UnknownType;
546}
547
548void QgsValueRelationWidgetWrapper::populate()
549{
550 // Initialize, note that signals are blocked, to avoid double signals on new features
553 {
554 if ( context().parentFormFeature().isValid() )
555 {
556 mCache = QgsValueRelationFieldFormatter::createCache( config(), formFeature(), context().parentFormFeature() );
557 }
558 else
559 {
561 }
562 }
563 else if ( mCache.empty() )
564 {
566 }
567
568 if ( mComboBox )
569 {
570 mComboBox->blockSignals( true );
571 mComboBox->clear();
572 const bool allowNull = config( QStringLiteral( "AllowNull" ) ).toBool();
573 if ( allowNull )
574 {
575 mComboBox->addItem( tr( "(no selection)" ), QgsVariantUtils::createNullVariant( field().type( ) ) );
576 }
577
578 if ( !mCache.isEmpty() )
579 {
580 QVariant currentGroup;
581 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mComboBox->model() );
582 const bool displayGroupName = config( QStringLiteral( "DisplayGroupName" ) ).toBool();
583 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &element : std::as_const( mCache ) )
584 {
585 if ( currentGroup != element.group )
586 {
587 if ( mComboBox->count() > ( allowNull ? 1 : 0 ) )
588 {
589 mComboBox->insertSeparator( mComboBox->count() );
590 }
591 if ( displayGroupName )
592 {
593 mComboBox->addItem( element.group.toString() );
594 QStandardItem *item = model->item( mComboBox->count() - 1 );
595 item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
596 }
597 currentGroup = element.group;
598 }
599
600 mComboBox->addItem( element.value, element.key );
601
602 if ( !element.description.isEmpty() )
603 {
604 mComboBox->setItemData( mComboBox->count() - 1, element.description, Qt::ToolTipRole );
605 }
606 }
607 }
608 mComboBox->blockSignals( false );
609 }
610 else if ( mTableWidget )
611 {
612 mTableWidget->setColumnCount( columnCount() );
613 mTableWidget->populate( mCache );
614 }
615 else if ( mLineEdit )
616 {
617 QStringList values;
618 values.reserve( mCache.size() );
619 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &i : std::as_const( mCache ) )
620 {
621 values << i.value;
622 }
623 QStringListModel *m = new QStringListModel( values, mLineEdit );
624 QCompleter *completer = new QCompleter( m, mLineEdit );
625
626 const Qt::MatchFlags completerMatchFlags { config().contains( QStringLiteral( "CompleterMatchFlags" ) ) ? static_cast<Qt::MatchFlags>( config().value( QStringLiteral( "CompleterMatchFlags" ), Qt::MatchFlag::MatchStartsWith ).toInt( ) ) : Qt::MatchFlag::MatchStartsWith };
627
628 if ( completerMatchFlags.testFlag( Qt::MatchFlag::MatchContains ) )
629 {
630 completer->setFilterMode( Qt::MatchFlag::MatchContains );
631 }
632 else
633 {
634 completer->setFilterMode( Qt::MatchFlag::MatchStartsWith );
635 }
636 completer->setCaseSensitivity( Qt::CaseInsensitive );
637 mLineEdit->setCompleter( completer );
638 }
639}
640
642{
643 if ( mTableWidget )
644 {
645 mTableWidget->setIndeterminateState();
646 }
647 else if ( mComboBox )
648 {
649 whileBlocking( mComboBox )->setCurrentIndex( -1 );
650 }
651 else if ( mLineEdit )
652 {
653 whileBlocking( mLineEdit )->clear();
654 }
655}
656
658{
659 if ( mEnabled == enabled )
660 return;
661
662 mEnabled = enabled;
663
664 if ( mTableWidget )
665 {
666 mTableWidget->setEnabledTable( enabled );
667 }
668 else
670}
671
672void QgsValueRelationWidgetWrapper::parentFormValueChanged( const QString &attribute, const QVariant &value )
673{
674
675 // Update the parent feature in the context ( which means to replace the whole context :/ )
677 QgsFeature feature { context().parentFormFeature() };
678 feature.setAttribute( attribute, value );
679 ctx.setParentFormFeature( feature );
680 setContext( ctx );
681
682 // Check if the change might affect the filter expression and the cache needs updates
684 && ( config( QStringLiteral( "Value" ) ).toString() == attribute ||
685 config( QStringLiteral( "Key" ) ).toString() == attribute ||
687 QgsValueRelationFieldFormatter::expressionParentFormAttributes( mExpression ).contains( attribute ) ) )
688 {
689 populate();
690 }
691
692}
693
694void QgsValueRelationWidgetWrapper::emitValueChangedInternal( const QString &value )
695{
697 emit valueChanged( value );
699 emit valuesChanged( value );
700}
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:6535
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6534
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5862