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