QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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
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 ) )
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 QgsVariantUtils::createNullVariant( QMetaType::Type::QVariantList );
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 QMetaType::Type type { fkType() };
274 switch ( type )
275 {
276 case QMetaType::Type::Int:
277 vl.push_back( s.toInt() );
278 break;
279 case QMetaType::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() == QMetaType::Type::QVariantMap ||
289 layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
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() == QMetaType::Type::QVariantMap ||
390 layer()->fields().at( fieldIdx() ).type() == QMetaType::Type::QVariantList )
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 // Do nothing if the value has not changed except for multi edit mode
460 // In multi edit mode feature is not updated (so attributeChanged is false) until user validate it but we need to update the
461 // value relation which could have an expression depending on another modified field
462 if ( attributeChanged || context().attributeFormMode() == QgsAttributeEditorContext::Mode::MultiEditMode )
463 {
464 QVariant oldValue( value( ) );
465 setFormFeatureAttribute( attribute, newValue );
466 // Update combos if the value used in the filter expression has changed
468 && QgsValueRelationFieldFormatter::expressionFormAttributes( mExpression ).contains( attribute ) )
469 {
470 populate();
471 // Restore value
472 updateValues( value( ) );
473 // If the value has changed as a result of another widget's value change,
474 // we need to emit the signal to make sure other dependent widgets are
475 // updated.
476 QgsFields formFields( formFeature().fields() );
477
478 // Also check for fields in the layer in case this is a multi-edit form
479 // and there is not form feature set
480 if ( formFields.count() == 0 && layer() )
481 {
482 formFields = layer()->fields();
483 }
484
485 if ( oldValue != value() && fieldIdx() < formFields.count() )
486 {
487 QString attributeName( formFields.names().at( fieldIdx() ) );
488 setFormFeatureAttribute( attributeName, value( ) );
490 }
491 }
492 }
493}
494
495
497{
498 setFormFeature( feature );
499 whileBlocking( this )->populate();
500 whileBlocking( this )->setValue( feature.attribute( fieldIdx() ) );
501
502 // As we block any signals, possible depending widgets will not being updated
503 // so we force emit signal once and for all
505
506 // A bit of logic to set the default value if AllowNull is false and this is a new feature
507 // Note that this needs to be here after the cache has been created/updated by populate()
508 // and signals unblocked (we want this to propagate to the feature itself)
509 if ( context().attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode
510 && ! formFeature().attribute( fieldIdx() ).isValid()
511 && ! mCache.isEmpty()
512 && ! config( QStringLiteral( "AllowNull" ) ).toBool( ) )
513 {
514 // This is deferred because at the time the feature is set in one widget it is not
515 // set in the next, which is typically the "down" in a drill-down
516 QTimer::singleShot( 0, this, [ this ]
517 {
518 if ( ! mCache.isEmpty() )
519 {
520 updateValues( formFeature().attribute( fieldIdx() ).isValid() ? formFeature().attribute( fieldIdx() ) : mCache.at( 0 ).key );
521 }
522 } );
523 }
524}
525
526int QgsValueRelationWidgetWrapper::columnCount() const
527{
528 return std::max( 1, config( QStringLiteral( "NofColumns" ) ).toInt() );
529}
530
531
532QMetaType::Type QgsValueRelationWidgetWrapper::fkType() const
533{
535 if ( layer )
536 {
537 QgsFields fields = layer->fields();
538 int idx { fields.lookupField( config().value( QStringLiteral( "Key" ) ).toString() ) };
539 if ( idx >= 0 )
540 {
541 return fields.at( idx ).type();
542 }
543 }
544 return QMetaType::Type::UnknownType;
545}
546
547void QgsValueRelationWidgetWrapper::populate()
548{
549 // Initialize, note that signals are blocked, to avoid double signals on new features
552 {
553 if ( context().parentFormFeature().isValid() )
554 {
555 mCache = QgsValueRelationFieldFormatter::createCache( config(), formFeature(), context().parentFormFeature() );
556 }
557 else
558 {
560 }
561 }
562 else if ( mCache.empty() )
563 {
565 }
566
567 if ( mComboBox )
568 {
569 mComboBox->blockSignals( true );
570 mComboBox->clear();
571 const bool allowNull = config( QStringLiteral( "AllowNull" ) ).toBool();
572 if ( allowNull )
573 {
574 mComboBox->addItem( tr( "(no selection)" ), QgsVariantUtils::createNullVariant( field().type( ) ) );
575 }
576
577 if ( !mCache.isEmpty() )
578 {
579 QVariant currentGroup;
580 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mComboBox->model() );
581 const bool displayGroupName = config( QStringLiteral( "DisplayGroupName" ) ).toBool();
582 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &element : std::as_const( mCache ) )
583 {
584 if ( currentGroup != element.group )
585 {
586 if ( mComboBox->count() > ( allowNull ? 1 : 0 ) )
587 {
588 mComboBox->insertSeparator( mComboBox->count() );
589 }
590 if ( displayGroupName )
591 {
592 mComboBox->addItem( element.group.toString() );
593 QStandardItem *item = model->item( mComboBox->count() - 1 );
594 item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
595 }
596 currentGroup = element.group;
597 }
598
599 mComboBox->addItem( element.value, element.key );
600
601 if ( !element.description.isEmpty() )
602 {
603 mComboBox->setItemData( mComboBox->count() - 1, element.description, Qt::ToolTipRole );
604 }
605 }
606 }
607 mComboBox->blockSignals( false );
608 }
609 else if ( mTableWidget )
610 {
611 mTableWidget->setColumnCount( columnCount() );
612 mTableWidget->populate( mCache );
613 }
614 else if ( mLineEdit )
615 {
616 QStringList values;
617 values.reserve( mCache.size() );
618 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &i : std::as_const( mCache ) )
619 {
620 values << i.value;
621 }
622 QStringListModel *m = new QStringListModel( values, mLineEdit );
623 QCompleter *completer = new QCompleter( m, mLineEdit );
624
625 const Qt::MatchFlags completerMatchFlags { config().contains( QStringLiteral( "CompleterMatchFlags" ) ) ? static_cast<Qt::MatchFlags>( config().value( QStringLiteral( "CompleterMatchFlags" ), Qt::MatchFlag::MatchStartsWith ).toInt( ) ) : Qt::MatchFlag::MatchStartsWith };
626
627 if ( completerMatchFlags.testFlag( Qt::MatchFlag::MatchContains ) )
628 {
629 completer->setFilterMode( Qt::MatchFlag::MatchContains );
630 }
631 else
632 {
633 completer->setFilterMode( Qt::MatchFlag::MatchStartsWith );
634 }
635 completer->setCaseSensitivity( Qt::CaseInsensitive );
636 mLineEdit->setCompleter( completer );
637 }
638}
639
641{
642 if ( mTableWidget )
643 {
644 mTableWidget->setIndeterminateState();
645 }
646 else if ( mComboBox )
647 {
648 whileBlocking( mComboBox )->setCurrentIndex( -1 );
649 }
650 else if ( mLineEdit )
651 {
652 whileBlocking( mLineEdit )->clear();
653 }
654}
655
657{
658 if ( mEnabled == enabled )
659 return;
660
661 mEnabled = enabled;
662
663 if ( mTableWidget )
664 {
665 mTableWidget->setEnabledTable( enabled );
666 }
667 else
669}
670
671void QgsValueRelationWidgetWrapper::parentFormValueChanged( const QString &attribute, const QVariant &value )
672{
673
674 // Update the parent feature in the context ( which means to replace the whole context :/ )
676 QgsFeature feature { context().parentFormFeature() };
677 feature.setAttribute( attribute, value );
678 ctx.setParentFormFeature( feature );
679 setContext( ctx );
680
681 // Check if the change might affect the filter expression and the cache needs updates
683 && ( config( QStringLiteral( "Value" ) ).toString() == attribute ||
684 config( QStringLiteral( "Key" ) ).toString() == attribute ||
686 QgsValueRelationFieldFormatter::expressionParentFormAttributes( mExpression ).contains( attribute ) ) )
687 {
688 populate();
689 }
690
691}
692
693void QgsValueRelationWidgetWrapper::emitValueChangedInternal( const QString &value )
694{
696 emit valueChanged( value );
698 emit valuesChanged( value );
699}
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:247
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6494
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6493
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5821