QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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
42QgsFeatureFilterWidget::QgsFeatureFilterWidget( QWidget *parent )
43 : QWidget( parent )
44{
45 setupUi( this );
46
47 // Initialize filter gui elements
48 mFilterColumnsMenu = new QMenu( this );
49 mActionFilterColumnsMenu->setMenu( mFilterColumnsMenu );
50 mStoredFilterExpressionMenu = new QMenu( this );
51 mActionStoredFilterExpressions->setMenu( mStoredFilterExpressionMenu );
52
53 // Set filter icon in a couple of places
54 mActionShowAllFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.svg" ) );
55 mActionAdvancedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionFilter2.svg" ) );
56 mActionSelectedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableSelected.svg" ) );
57 mActionInvalidFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableInvalid.svg" ) );
58 mActionVisibleFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableVisible.svg" ) );
59 mActionEditedFilter->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTableEdited.svg" ) );
60
61
62 // Set button to store or delete stored filter expressions
63 mStoreFilterExpressionButton->setDefaultAction( mActionHandleStoreFilterExpression );
64 connect( mActionSaveAsStoredFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::saveAsStoredFilterExpression );
65 connect( mActionEditStoredFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::editStoredFilterExpression );
66 connect( mActionHandleStoreFilterExpression, &QAction::triggered, this, &QgsFeatureFilterWidget::handleStoreFilterExpression );
67 mApplyFilterButton->setDefaultAction( mActionApplyFilter );
68
69 // Connect filter signals
70 connect( mActionAdvancedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterExpressionBuilder );
71 connect( mActionShowAllFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterShowAll );
72 connect( mActionSelectedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterSelected );
73 connect( mActionInvalidFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterInvalid );
74 connect( mActionVisibleFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterVisible );
75 connect( mActionEditedFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterEdited );
76 connect( mFilterQuery, &QLineEdit::returnPressed, this, &QgsFeatureFilterWidget::filterQueryAccepted );
77 connect( mActionApplyFilter, &QAction::triggered, this, &QgsFeatureFilterWidget::filterQueryAccepted );
78 connect( mFilterQuery, &QLineEdit::textChanged, this, &QgsFeatureFilterWidget::onFilterQueryTextChanged );
79}
80
81void QgsFeatureFilterWidget::init( QgsVectorLayer *layer, const QgsAttributeEditorContext &context, QgsDualView *mainView, QgsMessageBar *messageBar, int )
82{
83 mMainView = mainView;
84 mLayer = layer;
85 mEditorContext = context;
86 mMessageBar = messageBar;
87
88 connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsFeatureFilterWidget::columnBoxInit );
89 connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsFeatureFilterWidget::columnBoxInit );
90
91 //set delay on entering text
92 mFilterQueryTimer.setSingleShot( true );
93 connect( &mFilterQueryTimer, &QTimer::timeout, this, &QgsFeatureFilterWidget::updateCurrentStoredFilterExpression );
94
95 columnBoxInit();
96 storedFilterExpressionBoxInit();
97 storeExpressionButtonInit();
98}
99
100void QgsFeatureFilterWidget::filterShowAll()
101{
102 mFilterButton->setDefaultAction( mActionShowAllFilter );
103 mFilterButton->setPopupMode( QToolButton::InstantPopup );
104 mFilterQuery->setVisible( false );
105 mFilterQuery->setText( QString() );
106 if ( mCurrentSearchWidgetWrapper )
107 {
108 mCurrentSearchWidgetWrapper->widget()->setVisible( false );
109 }
110 mApplyFilterButton->setVisible( false );
111 mStoreFilterExpressionButton->setVisible( false );
112 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowAll );
113}
114
115void QgsFeatureFilterWidget::filterSelected()
116{
117 mFilterButton->setDefaultAction( mActionSelectedFilter );
118 mFilterButton->setPopupMode( QToolButton::InstantPopup );
119 mFilterQuery->setVisible( false );
120 mApplyFilterButton->setVisible( false );
121 mStoreFilterExpressionButton->setVisible( false );
122 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowSelected );
123}
124
125void QgsFeatureFilterWidget::filterVisible()
126{
127 if ( !mLayer->isSpatial() )
128 {
129 filterShowAll();
130 return;
131 }
132
133 mFilterButton->setDefaultAction( mActionVisibleFilter );
134 mFilterButton->setPopupMode( QToolButton::InstantPopup );
135 mFilterQuery->setVisible( false );
136 mApplyFilterButton->setVisible( false );
137 mStoreFilterExpressionButton->setVisible( false );
138 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowVisible );
139}
140
141void QgsFeatureFilterWidget::filterInvalid()
142{
143 mFilterButton->setDefaultAction( mActionInvalidFilter );
144 mFilterButton->setPopupMode( QToolButton::InstantPopup );
145 mFilterQuery->setVisible( false );
146 mApplyFilterButton->setVisible( false );
147 mStoreFilterExpressionButton->setVisible( false );
149 mMainView->filterFeatures( QStringLiteral( "is_feature_valid() = false" ), context );
150 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowInvalid );
151}
152
153void QgsFeatureFilterWidget::filterEdited()
154{
155 mFilterButton->setDefaultAction( mActionEditedFilter );
156 mFilterButton->setPopupMode( QToolButton::InstantPopup );
157 mFilterQuery->setVisible( false );
158 mApplyFilterButton->setVisible( false );
159 mStoreFilterExpressionButton->setVisible( false );
160 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowEdited );
161}
162
163
164void QgsFeatureFilterWidget::filterQueryAccepted()
165{
166 if ( ( mFilterQuery->isVisible() && mFilterQuery->text().isEmpty() ) || ( mCurrentSearchWidgetWrapper && mCurrentSearchWidgetWrapper->widget()->isVisible() && mCurrentSearchWidgetWrapper->expression().isEmpty() ) )
167 {
168 filterShowAll();
169 return;
170 }
171 filterQueryChanged( mFilterQuery->text() );
172}
173
174void QgsFeatureFilterWidget::filterQueryChanged( const QString &query )
175{
176 QString str;
177 if ( mFilterButton->defaultAction() == mActionAdvancedFilter )
178 {
179 str = query;
180 }
181 else if ( mCurrentSearchWidgetWrapper )
182 {
183 str = mCurrentSearchWidgetWrapper->expression();
184 }
185
186 setFilterExpression( str );
187}
188
189void QgsFeatureFilterWidget::columnBoxInit()
190{
191 const auto constActions = mFilterColumnsMenu->actions();
192 for ( QAction *a : constActions )
193 {
194 mFilterColumnsMenu->removeAction( a );
195 mFilterButton->removeAction( a );
196 delete a;
197 }
198
199 mFilterButton->addAction( mActionShowAllFilter );
200 mFilterButton->addAction( mActionSelectedFilter );
201 if ( mLayer->isSpatial() )
202 {
203 mFilterButton->addAction( mActionVisibleFilter );
204 }
205 mFilterButton->addAction( mActionInvalidFilter );
206 mFilterButton->addAction( mActionEditedFilter );
207 mFilterButton->addAction( mActionFilterColumnsMenu );
208 mFilterButton->addAction( mActionAdvancedFilter );
209 mFilterButton->addAction( mActionStoredFilterExpressions );
210
211 const QList<QgsField> fields = mLayer->fields().toList();
212
213 const auto constFields = fields;
214 for ( const QgsField &field : constFields )
215 {
216 const int idx = mLayer->fields().lookupField( field.name() );
217 if ( idx < 0 )
218 continue;
219
220 if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) )
221 {
222 const QIcon icon = mLayer->fields().iconForField( idx );
223 const QString alias = mLayer->attributeDisplayName( idx );
224
225 // Generate action for the filter popup button
226 QAction *filterAction = new QAction( icon, alias, mFilterButton );
227 filterAction->setData( field.name() );
228
229 connect( filterAction, &QAction::triggered, this, [this, filterAction] { filterColumnChanged( filterAction ); } );
230 mFilterColumnsMenu->addAction( filterAction );
231 }
232 }
233}
234
235void QgsFeatureFilterWidget::handleStoreFilterExpression()
236{
237 if ( !mActionHandleStoreFilterExpression->isChecked() )
238 {
239 mLayer->storedExpressionManager()->removeStoredExpression( mActionHandleStoreFilterExpression->data().toString() );
240 }
241 else
242 {
243 mLayer->storedExpressionManager()->addStoredExpression( mFilterQuery->text(), mFilterQuery->text() );
244 }
245
246 updateCurrentStoredFilterExpression();
247 storedFilterExpressionBoxInit();
248}
249
250void QgsFeatureFilterWidget::storedFilterExpressionBoxInit()
251{
252 const auto constActions = mStoredFilterExpressionMenu->actions();
253 for ( QAction *a : constActions )
254 {
255 mStoredFilterExpressionMenu->removeAction( a );
256 delete a;
257 }
258
259 const QList<QgsStoredExpression> storedExpressions = mLayer->storedExpressionManager()->storedExpressions();
260 for ( const QgsStoredExpression &storedExpression : storedExpressions )
261 {
262 QAction *storedExpressionAction = new QAction( storedExpression.name, mFilterButton );
263 connect( storedExpressionAction, &QAction::triggered, this, [this, storedExpression]() {
264 setFilterExpression( storedExpression.expression, QgsAttributeForm::ReplaceFilter, true );
265 } );
266 mStoredFilterExpressionMenu->addAction( storedExpressionAction );
267 }
268}
269
270void QgsFeatureFilterWidget::storeExpressionButtonInit()
271{
272 if ( mActionHandleStoreFilterExpression->isChecked() )
273 {
274 mActionHandleStoreFilterExpression->setToolTip( tr( "Delete stored expression" ) );
275 mActionHandleStoreFilterExpression->setText( tr( "Delete Stored Expression" ) );
276 mActionHandleStoreFilterExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionHandleStoreFilterExpressionChecked.svg" ) ) );
277 mStoreFilterExpressionButton->removeAction( mActionSaveAsStoredFilterExpression );
278 mStoreFilterExpressionButton->addAction( mActionEditStoredFilterExpression );
279 }
280 else
281 {
282 mActionHandleStoreFilterExpression->setToolTip( tr( "Save expression with the text as name" ) );
283 mActionHandleStoreFilterExpression->setText( tr( "Save Expression" ) );
284 mActionHandleStoreFilterExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionHandleStoreFilterExpressionUnchecked.svg" ) ) );
285 mStoreFilterExpressionButton->addAction( mActionSaveAsStoredFilterExpression );
286 mStoreFilterExpressionButton->removeAction( mActionEditStoredFilterExpression );
287 }
288}
289
290
291void QgsFeatureFilterWidget::filterColumnChanged( QAction *filterAction )
292{
293 mFilterButton->setDefaultAction( filterAction );
294 mFilterButton->setPopupMode( QToolButton::InstantPopup );
295 // replace the search line edit with a search widget that is suited to the selected field
296 // delete previous widget
297 if ( mCurrentSearchWidgetWrapper )
298 {
299 mCurrentSearchWidgetWrapper->widget()->setVisible( false );
300 delete mCurrentSearchWidgetWrapper;
301 }
302 const QString fieldName = mFilterButton->defaultAction()->data().toString();
303 // get the search widget
304 const int fldIdx = mLayer->fields().lookupField( fieldName );
305 if ( fldIdx < 0 )
306 return;
307 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName );
308 mCurrentSearchWidgetWrapper = QgsGui::editorWidgetRegistry()->createSearchWidget( setup.type(), mLayer, fldIdx, setup.config(), mFilterContainer, mEditorContext );
309 if ( mCurrentSearchWidgetWrapper->applyDirectly() )
310 {
311 connect( mCurrentSearchWidgetWrapper, &QgsSearchWidgetWrapper::expressionChanged, this, &QgsFeatureFilterWidget::filterQueryChanged );
312 mApplyFilterButton->setVisible( false );
313 mStoreFilterExpressionButton->setVisible( false );
314 }
315 else
316 {
317 connect( mCurrentSearchWidgetWrapper, &QgsSearchWidgetWrapper::expressionChanged, this, &QgsFeatureFilterWidget::filterQueryAccepted );
318 mApplyFilterButton->setVisible( true );
319 mStoreFilterExpressionButton->setVisible( true );
320 }
321
322 replaceSearchWidget( mFilterQuery, mCurrentSearchWidgetWrapper->widget() );
323}
324
325void QgsFeatureFilterWidget::filterExpressionBuilder()
326{
327 // Show expression builder
329
330 QgsExpressionBuilderDialog dlg( mLayer, mFilterQuery->text(), this, QStringLiteral( "generic" ), context );
331 dlg.setWindowTitle( tr( "Expression Based Filter" ) );
332
333 QgsDistanceArea myDa;
334 myDa.setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() );
335 myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
336 dlg.setGeomCalculator( myDa );
337
338 if ( dlg.exec() == QDialog::Accepted )
339 {
340 setFilterExpression( dlg.expressionText(), QgsAttributeForm::ReplaceFilter, true );
341 }
342}
343
344void QgsFeatureFilterWidget::saveAsStoredFilterExpression()
345{
346 QgsDialog *dlg = new QgsDialog( this, Qt::WindowFlags(), QDialogButtonBox::Save | QDialogButtonBox::Cancel );
347 dlg->setWindowTitle( tr( "Save Expression As" ) );
348 QVBoxLayout *layout = dlg->layout();
349 dlg->resize( std::max( 400, this->width() / 2 ), dlg->height() );
350
351 QLabel *nameLabel = new QLabel( tr( "Name" ), dlg );
352 QLineEdit *nameEdit = new QLineEdit( dlg );
353 layout->addWidget( nameLabel );
354 layout->addWidget( nameEdit );
355 nameEdit->setFocus();
356
357 if ( dlg->exec() == QDialog::Accepted )
358 {
359 mLayer->storedExpressionManager()->addStoredExpression( nameEdit->text(), mFilterQuery->text() );
360
361 updateCurrentStoredFilterExpression();
362 storedFilterExpressionBoxInit();
363 }
364}
365
366void QgsFeatureFilterWidget::editStoredFilterExpression()
367{
368 QgsDialog *dlg = new QgsDialog( this, Qt::WindowFlags(), QDialogButtonBox::Save | QDialogButtonBox::Cancel );
369 dlg->setWindowTitle( tr( "Edit expression" ) );
370 QVBoxLayout *layout = dlg->layout();
371 dlg->resize( std::max( 400, this->width() / 2 ), dlg->height() );
372
373 QLabel *nameLabel = new QLabel( tr( "Name" ), dlg );
374 QLineEdit *nameEdit = new QLineEdit( mLayer->storedExpressionManager()->storedExpression( mActionHandleStoreFilterExpression->data().toString() ).name, dlg );
375 QLabel *expressionLabel = new QLabel( tr( "Expression" ), dlg );
376 QgsExpressionLineEdit *expressionEdit = new QgsExpressionLineEdit( dlg );
377 expressionEdit->setExpression( mLayer->storedExpressionManager()->storedExpression( mActionHandleStoreFilterExpression->data().toString() ).expression );
378
379 layout->addWidget( nameLabel );
380 layout->addWidget( nameEdit );
381 layout->addWidget( expressionLabel );
382 layout->addWidget( expressionEdit );
383 nameEdit->setFocus();
384
385 if ( dlg->exec() == QDialog::Accepted )
386 {
387 //update stored expression
388 mLayer->storedExpressionManager()->updateStoredExpression( mActionHandleStoreFilterExpression->data().toString(), nameEdit->text(), expressionEdit->expression(), QgsStoredExpression::Category::FilterExpression );
389
390 //update text
391 mFilterQuery->setValue( expressionEdit->expression() );
392
393 storedFilterExpressionBoxInit();
394 }
395}
396
397void QgsFeatureFilterWidget::updateCurrentStoredFilterExpression()
398{
399 const QgsStoredExpression currentStoredExpression = mLayer->storedExpressionManager()->findStoredExpressionByExpression( mFilterQuery->value() );
400
401 //set checked when it's an existing stored expression
402 mActionHandleStoreFilterExpression->setChecked( !currentStoredExpression.id.isNull() );
403
404 mActionHandleStoreFilterExpression->setData( currentStoredExpression.id );
405 mActionEditStoredFilterExpression->setData( currentStoredExpression.id );
406
407 //update bookmark button
408 storeExpressionButtonInit();
409}
410
411void QgsFeatureFilterWidget::setFilterExpression( const QString &filterString, QgsAttributeForm::FilterType type, bool alwaysShowFilter )
412{
413 QString filter;
414 if ( !mFilterQuery->text().isEmpty() && !filterString.isEmpty() )
415 {
416 switch ( type )
417 {
419 filter = filterString;
420 break;
421
423 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterQuery->text(), filterString );
424 break;
425
427 filter = QStringLiteral( "(%1) OR (%2)" ).arg( mFilterQuery->text(), filterString );
428 break;
429 }
430 }
431 else if ( !filterString.isEmpty() )
432 {
433 filter = filterString;
434 }
435 else
436 {
437 filterShowAll();
438 return;
439 }
440
441 mFilterQuery->setText( filter );
442
443 if ( alwaysShowFilter || !mCurrentSearchWidgetWrapper || !mCurrentSearchWidgetWrapper->applyDirectly() )
444 {
445 mFilterButton->setDefaultAction( mActionAdvancedFilter );
446 mFilterButton->setPopupMode( QToolButton::MenuButtonPopup );
447 mFilterQuery->setVisible( true );
448 mApplyFilterButton->setVisible( true );
449 mStoreFilterExpressionButton->setVisible( true );
450 if ( mCurrentSearchWidgetWrapper )
451 {
452 // replace search widget widget with the normal filter query line edit
453 replaceSearchWidget( mCurrentSearchWidgetWrapper->widget(), mFilterQuery );
454 }
455 }
456
457 // parse search string and build parsed tree
458 QgsExpression filterExpression( filter );
459 if ( filterExpression.hasParserError() )
460 {
461 mMessageBar->pushMessage( tr( "Parsing error" ), filterExpression.parserErrorString(), Qgis::MessageLevel::Warning );
462 return;
463 }
464
466
467 if ( !filterExpression.prepare( &context ) )
468 {
469 mMessageBar->pushMessage( tr( "Evaluation error" ), filterExpression.evalErrorString(), Qgis::MessageLevel::Warning );
470 }
471
472 mMainView->filterFeatures( filterExpression, context );
473 mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowFilteredList );
474}
475
476void QgsFeatureFilterWidget::replaceSearchWidget( QWidget *oldw, QWidget *neww )
477{
478 mFilterLayout->removeWidget( oldw );
479 oldw->setVisible( false );
480 mFilterLayout->addWidget( neww, 0, 0 );
481 neww->setVisible( true );
482 neww->setFocus();
483}
484
485void QgsFeatureFilterWidget::onFilterQueryTextChanged( const QString &value )
486{
487 Q_UNUSED( value );
488 mFilterQueryTimer.start( 300 );
489}
490
@ Warning
Warning message.
Definition qgis.h:158
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:44
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:54
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:106
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.