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