QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsfeaturefilterwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfeaturefilterwidget.cpp
3 --------------------------------------
4 Date : 20.9.2019
5 Copyright : (C) 2019 Julien Cabieces
6 Email : julien dot cabieces at oslandia dot com
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//
19// W A R N I N G
20// -------------
21//
22// This file is not part of the QGIS API. It exists purely as an
23// implementation detail. This header file may change from version to
24// version without notice, or even be removed.
25//
26
27#include "qgsapplication.h"
28#include "qgsdialog.h"
29#include "qgsdualview.h"
35#include "qgsgui.h"
36#include "qgsmessagebar.h"
39
40#include <QMenu>
41#include <QString>
42
43using namespace Qt::StringLiterals;
44
45QgsFeatureFilterWidget::QgsFeatureFilterWidget( QWidget *parent )
46 : QWidget( parent )
47{
48 setupUi( this );
49
50 // Initialize filter gui elements
51 mFilterColumnsMenu = new QMenu( this );
52 mActionFilterColumnsMenu->setMenu( mFilterColumnsMenu );
53 mStoredFilterExpressionMenu = new QMenu( this );
54 mActionStoredFilterExpressions->setMenu( mStoredFilterExpressionMenu );
55
56 // Set filter icon in a couple of places
57 mActionShowAllFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.svg" ) );
58 mActionAdvancedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionFilter2.svg" ) );
59 mActionSelectedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableSelected.svg" ) );
60 mActionInvalidFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableInvalid.svg" ) );
61 mActionVisibleFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableVisible.svg" ) );
62 mActionEditedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableEdited.svg" ) );
63
64
65 // Set button to store or delete stored filter expressions
66 mStoreFilterExpressionButton->setDefaultAction( mActionHandleStoreFilterExpression );
67 connect( mActionSaveAsStoredFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::saveAsStoredFilterExpression );
68 connect( mActionEditStoredFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::editStoredFilterExpression );
69 connect( mActionHandleStoreFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::handleStoreFilterExpression );
70 mApplyFilterButton->setDefaultAction( mActionApplyFilter );
71
72 // Connect filter signals
73 connect( mActionAdvancedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterExpressionBuilder );
74 connect( mActionShowAllFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterShowAll );
75 connect( mActionSelectedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterSelected );
76 connect( mActionInvalidFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterInvalid );
77 connect( mActionVisibleFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterVisible );
78 connect( mActionEditedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterEdited );
79 connect( mFilterQuery, &QLineEdit::returnPressed, this, &QgsFeatureFilterWidget::filterQueryAccepted );
80 connect( mActionApplyFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterQueryAccepted );
81 connect( mFilterQuery, &QLineEdit::textChanged, this, &QgsFeatureFilterWidget::onFilterQueryTextChanged );
82}
83
84void QgsFeatureFilterWidget::init( QgsVectorLayer *layer, const QgsAttributeEditorContext &context, QgsDualView *mainView, QgsMessageBar *messageBar, int )
85{
86 mMainView = mainView;
87 mLayer = layer;
88 mEditorContext = context;
89 mMessageBar = messageBar;
90
91 connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsFeatureFilterWidget::columnBoxInit );
92 connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsFeatureFilterWidget::columnBoxInit );
93
94 //set delay on entering text
95 mFilterQueryTimer.setSingleShot( true );
96 connect( &mFilterQueryTimer, &QTimer::timeout, this, &QgsFeatureFilterWidget::updateCurrentStoredFilterExpression );
97
98 columnBoxInit();
99 storedFilterExpressionBoxInit();
100 storeExpressionButtonInit();
101}
102
103void QgsFeatureFilterWidget::filterShowAll()
104{
105 mFilterButton->setDefaultAction( mActionShowAllFilter );
106 mFilterButton->setPopupMode( QToolButton::InstantPopup );
107 mFilterQuery->setVisible( false );
108 mFilterQuery->setText( QString() );
109 if ( mCurrentSearchWidgetWrapper )
110 {
111 mCurrentSearchWidgetWrapper->widget()->setVisible( false );
112 }
113 mApplyFilterButton->setVisible( false );
114 mStoreFilterExpressionButton->setVisible( false );
115 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowAll );
116}
117
118void QgsFeatureFilterWidget::filterSelected()
119{
120 mFilterButton->setDefaultAction( mActionSelectedFilter );
121 mFilterButton->setPopupMode( QToolButton::InstantPopup );
122 mFilterQuery->setVisible( false );
123 mApplyFilterButton->setVisible( false );
124 mStoreFilterExpressionButton->setVisible( false );
125 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowSelected );
126}
127
128void QgsFeatureFilterWidget::filterVisible()
129{
130 if ( !mLayer->isSpatial() )
131 {
132 filterShowAll();
133 return;
134 }
135
136 mFilterButton->setDefaultAction( mActionVisibleFilter );
137 mFilterButton->setPopupMode( QToolButton::InstantPopup );
138 mFilterQuery->setVisible( false );
139 mApplyFilterButton->setVisible( false );
140 mStoreFilterExpressionButton->setVisible( false );
141 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowVisible );
142}
143
144void QgsFeatureFilterWidget::filterInvalid()
145{
146 mFilterButton->setDefaultAction( mActionInvalidFilter );
147 mFilterButton->setPopupMode( QToolButton::InstantPopup );
148 mFilterQuery->setVisible( false );
149 mApplyFilterButton->setVisible( false );
150 mStoreFilterExpressionButton->setVisible( false );
152 mMainView->filterFeatures( u"is_feature_valid() = false"_s, context );
153 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowInvalid );
154}
155
156void QgsFeatureFilterWidget::filterEdited()
157{
158 mFilterButton->setDefaultAction( mActionEditedFilter );
159 mFilterButton->setPopupMode( QToolButton::InstantPopup );
160 mFilterQuery->setVisible( false );
161 mApplyFilterButton->setVisible( false );
162 mStoreFilterExpressionButton->setVisible( false );
163 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowEdited );
164}
165
166
167void QgsFeatureFilterWidget::filterQueryAccepted()
168{
169 if ( ( mFilterQuery->isVisible() && mFilterQuery->text().isEmpty() ) || ( mCurrentSearchWidgetWrapper && mCurrentSearchWidgetWrapper->widget()->isVisible() && mCurrentSearchWidgetWrapper->expression().isEmpty() ) )
170 {
171 filterShowAll();
172 return;
173 }
174 filterQueryChanged( mFilterQuery->text() );
175}
176
177void QgsFeatureFilterWidget::filterQueryChanged( const QString &query )
178{
179 QString str;
180 if ( mFilterButton->defaultAction() == mActionAdvancedFilter )
181 {
182 str = query;
183 }
184 else if ( mCurrentSearchWidgetWrapper )
185 {
186 str = mCurrentSearchWidgetWrapper->expression();
187 }
188
189 setFilterExpression( str );
190}
191
192void QgsFeatureFilterWidget::columnBoxInit()
193{
194 const auto constActions = mFilterColumnsMenu->actions();
195 for ( QAction *a : constActions )
196 {
197 mFilterColumnsMenu->removeAction( a );
198 mFilterButton->removeAction( a );
199 delete a;
200 }
201
202 mFilterButton->addAction( mActionShowAllFilter );
203 mFilterButton->addAction( mActionSelectedFilter );
204 if ( mLayer->isSpatial() )
205 {
206 mFilterButton->addAction( mActionVisibleFilter );
207 }
208 mFilterButton->addAction( mActionInvalidFilter );
209 mFilterButton->addAction( mActionEditedFilter );
210 mFilterButton->addAction( mActionFilterColumnsMenu );
211 mFilterButton->addAction( mActionAdvancedFilter );
212 mFilterButton->addAction( mActionStoredFilterExpressions );
213
214 const QList<QgsField> fields = mLayer->fields().toList();
215
216 const auto constFields = fields;
217 for ( const QgsField &field : constFields )
218 {
219 const int idx = mLayer->fields().lookupField( field.name() );
220 if ( idx < 0 )
221 continue;
222
223 if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != "Hidden"_L1 )
224 {
225 const QIcon icon = mLayer->fields().iconForField( idx );
226 const QString alias = mLayer->attributeDisplayName( idx );
227
228 // Generate action for the filter popup button
229 QAction *filterAction = new QAction( icon, alias, mFilterButton );
230 filterAction->setData( field.name() );
231
232 connect( filterAction, &QAction::triggered, this, [this, filterAction] { filterColumnChanged( filterAction ); } );
233 mFilterColumnsMenu->addAction( filterAction );
234 }
235 }
236}
237
238void QgsFeatureFilterWidget::handleStoreFilterExpression()
239{
240 if ( !mActionHandleStoreFilterExpression->isChecked() )
241 {
242 mLayer->storedExpressionManager()->removeStoredExpression( mActionHandleStoreFilterExpression->data().toString() );
243 }
244 else
245 {
246 mLayer->storedExpressionManager()->addStoredExpression( mFilterQuery->text(), mFilterQuery->text() );
247 }
248
249 updateCurrentStoredFilterExpression();
250 storedFilterExpressionBoxInit();
251}
252
253void QgsFeatureFilterWidget::storedFilterExpressionBoxInit()
254{
255 const auto constActions = mStoredFilterExpressionMenu->actions();
256 for ( QAction *a : constActions )
257 {
258 mStoredFilterExpressionMenu->removeAction( a );
259 delete a;
260 }
261
262 const QList<QgsStoredExpression> storedExpressions = mLayer->storedExpressionManager()->storedExpressions();
263 for ( const QgsStoredExpression &storedExpression : storedExpressions )
264 {
265 QAction *storedExpressionAction = new QAction( storedExpression.name, mFilterButton );
266 connect( storedExpressionAction, &QAction::triggered, this, [this, storedExpression]() {
267 setFilterExpression( storedExpression.expression, QgsAttributeForm::ReplaceFilter, true );
268 } );
269 mStoredFilterExpressionMenu->addAction( storedExpressionAction );
270 }
271}
272
273void QgsFeatureFilterWidget::storeExpressionButtonInit()
274{
275 if ( mActionHandleStoreFilterExpression->isChecked() )
276 {
277 mActionHandleStoreFilterExpression->setToolTip( tr( "Delete stored expression" ) );
278 mActionHandleStoreFilterExpression->setText( tr( "Delete Stored Expression" ) );
279 mActionHandleStoreFilterExpression->setIcon( QgsApplication::getThemeIcon( u"mActionHandleStoreFilterExpressionChecked.svg"_s ) );
280 mStoreFilterExpressionButton->removeAction( mActionSaveAsStoredFilterExpression );
281 mStoreFilterExpressionButton->addAction( mActionEditStoredFilterExpression );
282 }
283 else
284 {
285 mActionHandleStoreFilterExpression->setToolTip( tr( "Save expression with the text as name" ) );
286 mActionHandleStoreFilterExpression->setText( tr( "Save Expression" ) );
287 mActionHandleStoreFilterExpression->setIcon( QgsApplication::getThemeIcon( u"mActionHandleStoreFilterExpressionUnchecked.svg"_s ) );
288 mStoreFilterExpressionButton->addAction( mActionSaveAsStoredFilterExpression );
289 mStoreFilterExpressionButton->removeAction( mActionEditStoredFilterExpression );
290 }
291}
292
293
294void QgsFeatureFilterWidget::filterColumnChanged( QAction *filterAction )
295{
296 mFilterButton->setDefaultAction( filterAction );
297 mFilterButton->setPopupMode( QToolButton::InstantPopup );
298 // replace the search line edit with a search widget that is suited to the selected field
299 // delete previous widget
300 if ( mCurrentSearchWidgetWrapper )
301 {
302 mCurrentSearchWidgetWrapper->widget()->setVisible( false );
303 delete mCurrentSearchWidgetWrapper;
304 }
305 const QString fieldName = mFilterButton->defaultAction()->data().toString();
306 // get the search widget
307 const int fldIdx = mLayer->fields().lookupField( fieldName );
308 if ( fldIdx < 0 )
309 return;
310 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName );
311 mCurrentSearchWidgetWrapper = QgsGui::editorWidgetRegistry()->createSearchWidget( setup.type(), mLayer, fldIdx, setup.config(), mFilterContainer, mEditorContext );
312 if ( mCurrentSearchWidgetWrapper->applyDirectly() )
313 {
314 connect( mCurrentSearchWidgetWrapper, &QgsSearchWidgetWrapper::expressionChanged, this, &QgsFeatureFilterWidget::filterQueryChanged );
315 mApplyFilterButton->setVisible( false );
316 mStoreFilterExpressionButton->setVisible( false );
317 }
318 else
319 {
320 connect( mCurrentSearchWidgetWrapper, &QgsSearchWidgetWrapper::expressionChanged, this, &QgsFeatureFilterWidget::filterQueryAccepted );
321 mApplyFilterButton->setVisible( true );
322 mStoreFilterExpressionButton->setVisible( true );
323 }
324
325 replaceSearchWidget( mFilterQuery, mCurrentSearchWidgetWrapper->widget() );
326}
327
328void QgsFeatureFilterWidget::filterExpressionBuilder()
329{
330 // Show expression builder
332
333 QgsExpressionBuilderDialog dlg( mLayer, mFilterQuery->text(), this, u"generic"_s, context );
334 dlg.setWindowTitle( tr( "Expression Based Filter" ) );
335
336 QgsDistanceArea myDa;
337 myDa.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
338 myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
339 dlg.setGeomCalculator( myDa );
340
341 if ( dlg.exec() == QDialog::Accepted )
342 {
343 setFilterExpression( dlg.expressionText(), QgsAttributeForm::ReplaceFilter, true );
344 }
345}
346
347void QgsFeatureFilterWidget::saveAsStoredFilterExpression()
348{
349 QgsDialog *dlg = new QgsDialog( this, Qt::WindowFlags(), QDialogButtonBox::Save | QDialogButtonBox::Cancel );
350 dlg->setWindowTitle( tr( "Save Expression As" ) );
351 QVBoxLayout *layout = dlg->layout();
352 dlg->resize( std::max( 400, this->width() / 2 ), dlg->height() );
353
354 QLabel *nameLabel = new QLabel( tr( "Name" ), dlg );
355 QLineEdit *nameEdit = new QLineEdit( dlg );
356 layout->addWidget( nameLabel );
357 layout->addWidget( nameEdit );
358 nameEdit->setFocus();
359
360 if ( dlg->exec() == QDialog::Accepted )
361 {
362 mLayer->storedExpressionManager()->addStoredExpression( nameEdit->text(), mFilterQuery->text() );
363
364 updateCurrentStoredFilterExpression();
365 storedFilterExpressionBoxInit();
366 }
367}
368
369void QgsFeatureFilterWidget::editStoredFilterExpression()
370{
371 QgsDialog *dlg = new QgsDialog( this, Qt::WindowFlags(), QDialogButtonBox::Save | QDialogButtonBox::Cancel );
372 dlg->setWindowTitle( tr( "Edit expression" ) );
373 QVBoxLayout *layout = dlg->layout();
374 dlg->resize( std::max( 400, this->width() / 2 ), dlg->height() );
375
376 QLabel *nameLabel = new QLabel( tr( "Name" ), dlg );
377 QLineEdit *nameEdit = new QLineEdit( mLayer->storedExpressionManager()->storedExpression( mActionHandleStoreFilterExpression->data().toString() ).name, dlg );
378 QLabel *expressionLabel = new QLabel( tr( "Expression" ), dlg );
379 QgsExpressionLineEdit *expressionEdit = new QgsExpressionLineEdit( dlg );
380 expressionEdit->setExpression( mLayer->storedExpressionManager()->storedExpression( mActionHandleStoreFilterExpression->data().toString() ).expression );
381
382 layout->addWidget( nameLabel );
383 layout->addWidget( nameEdit );
384 layout->addWidget( expressionLabel );
385 layout->addWidget( expressionEdit );
386 nameEdit->setFocus();
387
388 if ( dlg->exec() == QDialog::Accepted )
389 {
390 //update stored expression
391 mLayer->storedExpressionManager()->updateStoredExpression( mActionHandleStoreFilterExpression->data().toString(), nameEdit->text(), expressionEdit->expression(), QgsStoredExpression::Category::FilterExpression );
392
393 //update text
394 mFilterQuery->setValue( expressionEdit->expression() );
395
396 storedFilterExpressionBoxInit();
397 }
398}
399
400void QgsFeatureFilterWidget::updateCurrentStoredFilterExpression()
401{
402 const QgsStoredExpression currentStoredExpression = mLayer->storedExpressionManager()->findStoredExpressionByExpression( mFilterQuery->value() );
403
404 //set checked when it's an existing stored expression
405 mActionHandleStoreFilterExpression->setChecked( !currentStoredExpression.id.isNull() );
406
407 mActionHandleStoreFilterExpression->setData( currentStoredExpression.id );
408 mActionEditStoredFilterExpression->setData( currentStoredExpression.id );
409
410 //update bookmark button
411 storeExpressionButtonInit();
412}
413
414void QgsFeatureFilterWidget::setFilterExpression( const QString &filterString, QgsAttributeForm::FilterType type, bool alwaysShowFilter )
415{
416 QString filter;
417 if ( !mFilterQuery->text().isEmpty() && !filterString.isEmpty() )
418 {
419 switch ( type )
420 {
422 filter = filterString;
423 break;
424
426 filter = u"(%1) AND (%2)"_s.arg( mFilterQuery->text(), filterString );
427 break;
428
430 filter = u"(%1) OR (%2)"_s.arg( mFilterQuery->text(), filterString );
431 break;
432 }
433 }
434 else if ( !filterString.isEmpty() )
435 {
436 filter = filterString;
437 }
438 else
439 {
440 filterShowAll();
441 return;
442 }
443
444 mFilterQuery->setText( filter );
445
446 if ( alwaysShowFilter || !mCurrentSearchWidgetWrapper || !mCurrentSearchWidgetWrapper->applyDirectly() )
447 {
448 mFilterButton->setDefaultAction( mActionAdvancedFilter );
449 mFilterButton->setPopupMode( QToolButton::MenuButtonPopup );
450 mFilterQuery->setVisible( true );
451 mApplyFilterButton->setVisible( true );
452 mStoreFilterExpressionButton->setVisible( true );
453 if ( mCurrentSearchWidgetWrapper )
454 {
455 // replace search widget widget with the normal filter query line edit
456 replaceSearchWidget( mCurrentSearchWidgetWrapper->widget(), mFilterQuery );
457 }
458 }
459
460 // parse search string and build parsed tree
461 QgsExpression filterExpression( filter );
462 if ( filterExpression.hasParserError() )
463 {
464 mMessageBar->pushMessage( tr( "Parsing error" ), filterExpression.parserErrorString(), Qgis::MessageLevel::Warning );
465 return;
466 }
467
469
470 if ( !filterExpression.prepare( &context ) )
471 {
472 mMessageBar->pushMessage( tr( "Evaluation error" ), filterExpression.evalErrorString(), Qgis::MessageLevel::Warning );
473 }
474
475 mMainView->filterFeatures( filterExpression, context );
476 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowFilteredList );
477}
478
479void QgsFeatureFilterWidget::replaceSearchWidget( QWidget *oldw, QWidget *neww )
480{
481 mFilterLayout->removeWidget( oldw );
482 oldw->setVisible( false );
483 mFilterLayout->addWidget( neww, 0, 0 );
484 neww->setVisible( true );
485 neww->setFocus();
486}
487
488void QgsFeatureFilterWidget::onFilterQueryTextChanged( const QString &value )
489{
490 Q_UNUSED( value );
491 mFilterQueryTimer.start( 300 );
492}
493
@ Warning
Warning message.
Definition qgis.h:161
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Contains context information for attribute editor widgets.
FilterType
Filter types.
@ ReplaceFilter
Filter should replace any existing filter.
@ FilterOr
Filter should be combined using "OR".
@ FilterAnd
Filter should be combined using "AND".
@ ShowFilteredList
Show only features whose ids are on the filter list. {.
@ ShowVisible
Show only visible features (depends on the map canvas).
@ ShowSelected
Show only selected features.
@ ShowInvalid
Show only features not respecting constraints.
@ ShowEdited
Show only features which have unsaved changes.
A generic dialog with layout and button box.
Definition qgsdialog.h:34
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
Definition qgsdialog.h:43
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition qgsdualview.h:47
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QVariantMap &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
A generic dialog for building expression strings.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A widget which includes a line edit for entering expressions together with a button to open the expre...
QString expression() const
Returns the current expression shown in the widget.
void setExpression(const QString &expression)
Sets the current expression to show in the widget.
Handles parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:109
A bar for displaying non-blocking messages to the user.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void expressionChanged(const QString &exp)
Emitted whenever the expression changes.
Stored expression containing name, content (expression text) and a category tag.
@ FilterExpression
Expressions to filter features.
QString id
generated uuid used for identification
Represents a vector layer which manages a vector based dataset.
void attributeAdded(int idx)
Will be emitted, when a new attribute has been added to this vector layer.
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.