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