QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsquerybuilder.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsquerybuilder.cpp - Query Builder
3  --------------------------------------
4  Date : 2004-11-19
5  Copyright : (C) 2004 by Gary E.Sherman
6  Email : sherman at mrcc.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 #include "qgsquerybuilder.h"
16 #include "qgslogger.h"
17 #include "qgsproject.h"
18 #include "qgssettings.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsvectordataprovider.h"
21 #include "qgsapplication.h"
22 #include "qgshelp.h"
23 #include "qgsgui.h"
24 
25 #include <QDomDocument>
26 #include <QDomElement>
27 #include <QFileDialog>
28 #include <QInputDialog>
29 #include <QListView>
30 #include <QMessageBox>
31 #include <QRegExp>
32 #include <QPushButton>
33 #include <QTextStream>
34 
35 
36 // constructor used when the query builder must make its own
37 // connection to the database
39  QWidget *parent, Qt::WindowFlags fl )
40  : QgsSubsetStringEditorInterface( parent, fl )
41  , mPreviousFieldRow( -1 )
42  , mLayer( layer )
43 {
44  setupUi( this );
46  connect( btnEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnEqual_clicked );
47  connect( btnLessThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessThan_clicked );
48  connect( btnGreaterThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterThan_clicked );
49  connect( btnPct, &QPushButton::clicked, this, &QgsQueryBuilder::btnPct_clicked );
50  connect( btnIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnIn_clicked );
51  connect( btnNotIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotIn_clicked );
52  connect( btnLike, &QPushButton::clicked, this, &QgsQueryBuilder::btnLike_clicked );
53  connect( btnILike, &QPushButton::clicked, this, &QgsQueryBuilder::btnILike_clicked );
54  connect( lstFields, &QListView::clicked, this, &QgsQueryBuilder::lstFields_clicked );
55  connect( lstFields, &QListView::doubleClicked, this, &QgsQueryBuilder::lstFields_doubleClicked );
56  connect( lstValues, &QListView::doubleClicked, this, &QgsQueryBuilder::lstValues_doubleClicked );
57  connect( btnLessEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessEqual_clicked );
58  connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterEqual_clicked );
59  connect( btnNotEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotEqual_clicked );
60  connect( btnAnd, &QPushButton::clicked, this, &QgsQueryBuilder::btnAnd_clicked );
61  connect( btnNot, &QPushButton::clicked, this, &QgsQueryBuilder::btnNot_clicked );
62  connect( btnOr, &QPushButton::clicked, this, &QgsQueryBuilder::btnOr_clicked );
63  connect( btnGetAllValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnGetAllValues_clicked );
64  connect( btnSampleValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnSampleValues_clicked );
65  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsQueryBuilder::showHelp );
66 
67  QPushButton *pbn = new QPushButton( tr( "&Test" ) );
68  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
69  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::test );
70 
71  pbn = new QPushButton( tr( "&Clear" ) );
72  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
73  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::clear );
74 
75  pbn = new QPushButton( tr( "&Save…" ) );
76  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
77  pbn->setToolTip( tr( "Save query to QQF file" ) );
78  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::saveQuery );
79 
80  pbn = new QPushButton( tr( "&Load…" ) );
81  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
82  pbn->setToolTip( tr( "Load query from QQF file" ) );
83  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::loadQuery );
84 
85  setupGuiViews();
86 
87  mOrigSubsetString = layer->subsetString();
88  connect( layer, &QgsVectorLayer::subsetStringChanged, this, &QgsQueryBuilder::layerSubsetStringChanged );
89  layerSubsetStringChanged();
90 
91  lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
92  mTxtSql->setText( mOrigSubsetString );
93 
94  mFilterLineEdit->setShowSearchIcon( true );
95  mFilterLineEdit->setPlaceholderText( tr( "Search…" ) );
96  connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsQueryBuilder::onTextChanged );
97 
98  populateFields();
99 }
100 
101 void QgsQueryBuilder::showEvent( QShowEvent *event )
102 {
103  mTxtSql->setFocus();
104  QDialog::showEvent( event );
105 }
106 
107 void QgsQueryBuilder::populateFields()
108 {
109  const QgsFields &fields = mLayer->fields();
110  mTxtSql->setFields( fields );
111  for ( int idx = 0; idx < fields.count(); ++idx )
112  {
113  if ( fields.fieldOrigin( idx ) != QgsFields::OriginProvider )
114  {
115  // only consider native fields
116  continue;
117  }
118  QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
119  myItem->setData( idx );
120  myItem->setEditable( false );
121  mModelFields->insertRow( mModelFields->rowCount(), myItem );
122  }
123 
124  // All fields get ... setup
125  setupLstFieldsModel();
126 }
127 
128 void QgsQueryBuilder::setupLstFieldsModel()
129 {
130  lstFields->setModel( mModelFields );
131 }
132 
133 void QgsQueryBuilder::setupGuiViews()
134 {
135  //Initialize the models
136  mModelFields = new QStandardItemModel();
137  mModelValues = new QStandardItemModel();
138  mProxyValues = new QSortFilterProxyModel();
139  mProxyValues->setSourceModel( mModelValues );
140  // Modes
141  lstFields->setViewMode( QListView::ListMode );
142  lstValues->setViewMode( QListView::ListMode );
143  lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
144  lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
145  // Performance tip since Qt 4.1
146  lstFields->setUniformItemSizes( true );
147  lstValues->setUniformItemSizes( true );
148  // Colored rows
149  lstFields->setAlternatingRowColors( true );
150  lstValues->setAlternatingRowColors( true );
151  lstValues->setModel( mProxyValues );
152 }
153 
154 void QgsQueryBuilder::fillValues( int idx, int limit )
155 {
156  // clear the model
157  mModelValues->clear();
158 
159  // determine the field type
160  QList<QVariant> values = qgis::setToList( mLayer->uniqueValues( idx, limit ) );
161  std::sort( values.begin(), values.end() );
162 
163  QString nullValue = QgsApplication::nullRepresentation();
164 
165  QgsDebugMsg( QStringLiteral( "nullValue: %1" ).arg( nullValue ) );
166 
167  const auto constValues = values;
168  for ( const QVariant &var : constValues )
169  {
170  QString value;
171  if ( var.isNull() )
172  value = nullValue;
173  else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
174  value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
175  else
176  value = var.toString();
177 
178  QStandardItem *myItem = new QStandardItem( value );
179  myItem->setEditable( false );
180  myItem->setData( var, Qt::UserRole + 1 );
181  mModelValues->insertRow( mModelValues->rowCount(), myItem );
182  QgsDebugMsg( QStringLiteral( "Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
183  }
184 }
185 
186 void QgsQueryBuilder::btnSampleValues_clicked()
187 {
188  lstValues->setCursor( Qt::WaitCursor );
189 
190  QString prevSubsetString = mLayer->subsetString();
191  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
192  {
193  mIgnoreLayerSubsetStringChangedSignal = true;
194  mLayer->setSubsetString( QString() );
195  }
196 
197  //Clear and fill the mModelValues
198  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
199 
200  if ( prevSubsetString != mLayer->subsetString() )
201  {
202  mLayer->setSubsetString( prevSubsetString );
203  mIgnoreLayerSubsetStringChangedSignal = false;
204  }
205 
206  lstValues->setCursor( Qt::ArrowCursor );
207 }
208 
209 void QgsQueryBuilder::btnGetAllValues_clicked()
210 {
211  lstValues->setCursor( Qt::WaitCursor );
212 
213  QString prevSubsetString = mLayer->subsetString();
214  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
215  {
216  mIgnoreLayerSubsetStringChangedSignal = true;
217  mLayer->setSubsetString( QString() );
218  }
219 
220  //Clear and fill the mModelValues
221  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
222 
223  if ( prevSubsetString != mLayer->subsetString() )
224  {
225  mLayer->setSubsetString( prevSubsetString );
226  mIgnoreLayerSubsetStringChangedSignal = false;
227  }
228 
229  lstValues->setCursor( Qt::ArrowCursor );
230 }
231 
233 {
234  // test the sql statement to see if it works
235  // by counting the number of records that would be
236  // returned
237 
238  if ( mLayer->setSubsetString( mTxtSql->text() ) )
239  {
240  const long featureCount { mLayer->featureCount() };
241  // Check for errors
242  if ( featureCount < 0 )
243  {
244  QMessageBox::warning( this,
245  tr( "Query Result" ),
246  tr( "An error occurred when executing the query, please check the expression syntax." ) );
247  }
248  else
249  {
250  QMessageBox::information( this,
251  tr( "Query Result" ),
252  tr( "The where clause returned %n row(s).", "returned test rows", featureCount ) );
253  }
254  }
255  else if ( mLayer->dataProvider()->hasErrors() )
256  {
257  QMessageBox::warning( this,
258  tr( "Query Result" ),
259  tr( "An error occurred when executing the query." )
260  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char( '\n' ) ) ) );
261  mLayer->dataProvider()->clearErrors();
262  }
263  else
264  {
265  QMessageBox::warning( this,
266  tr( "Query Result" ),
267  tr( "An error occurred when executing the query." ) );
268  }
269 }
270 
272 {
273  if ( mTxtSql->text() != mOrigSubsetString )
274  {
275  if ( !mLayer->setSubsetString( mTxtSql->text() ) )
276  {
277  //error in query - show the problem
278  if ( mLayer->dataProvider()->hasErrors() )
279  {
280  QMessageBox::warning( this,
281  tr( "Query Result" ),
282  tr( "An error occurred when executing the query." )
283  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char( '\n' ) ) ) );
284  mLayer->dataProvider()->clearErrors();
285  }
286  else
287  {
288  QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
289  }
290 
291  return;
292  }
293  }
294 
295  QDialog::accept();
296 }
297 
299 {
300  if ( mLayer->subsetString() != mOrigSubsetString )
301  mLayer->setSubsetString( mOrigSubsetString );
302 
303  QDialog::reject();
304 }
305 
306 void QgsQueryBuilder::btnEqual_clicked()
307 {
308  mTxtSql->insertText( QStringLiteral( " = " ) );
309  mTxtSql->setFocus();
310 }
311 
312 void QgsQueryBuilder::btnLessThan_clicked()
313 {
314  mTxtSql->insertText( QStringLiteral( " < " ) );
315  mTxtSql->setFocus();
316 }
317 
318 void QgsQueryBuilder::btnGreaterThan_clicked()
319 {
320  mTxtSql->insertText( QStringLiteral( " > " ) );
321  mTxtSql->setFocus();
322 }
323 
324 void QgsQueryBuilder::btnPct_clicked()
325 {
326  mTxtSql->insertText( QStringLiteral( "%" ) );
327  mTxtSql->setFocus();
328 }
329 
330 void QgsQueryBuilder::btnIn_clicked()
331 {
332  mTxtSql->insertText( QStringLiteral( " IN " ) );
333  mTxtSql->setFocus();
334 }
335 
336 void QgsQueryBuilder::btnNotIn_clicked()
337 {
338  mTxtSql->insertText( QStringLiteral( " NOT IN " ) );
339  mTxtSql->setFocus();
340 }
341 
342 void QgsQueryBuilder::btnLike_clicked()
343 {
344  mTxtSql->insertText( QStringLiteral( " LIKE " ) );
345  mTxtSql->setFocus();
346 }
347 
348 QString QgsQueryBuilder::sql() const
349 {
350  return mTxtSql->text();
351 }
352 
353 void QgsQueryBuilder::setSql( const QString &sqlStatement )
354 {
355  mTxtSql->setText( sqlStatement );
356 }
357 
358 void QgsQueryBuilder::lstFields_clicked( const QModelIndex &index )
359 {
360  if ( mPreviousFieldRow != index.row() )
361  {
362  mPreviousFieldRow = index.row();
363 
364  btnSampleValues->setEnabled( true );
365  btnGetAllValues->setEnabled( true );
366 
367  mModelValues->clear();
368  mFilterLineEdit->clear();
369  }
370 }
371 
372 void QgsQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
373 {
374  mTxtSql->insertText( '\"' + mLayer->fields().at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).name() + '\"' );
375  mTxtSql->setFocus();
376 }
377 
378 void QgsQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
379 {
380  QVariant value = index.data( Qt::UserRole + 1 );
381  if ( value.isNull() )
382  mTxtSql->insertText( QStringLiteral( "NULL" ) );
383  else if ( value.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
384  mTxtSql->insertText( '\'' + value.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) ) + '\'' );
385  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
386  mTxtSql->insertText( value.toString() );
387  else
388  mTxtSql->insertText( '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'' );
389 
390  mTxtSql->setFocus();
391 }
392 
393 void QgsQueryBuilder::btnLessEqual_clicked()
394 {
395  mTxtSql->insertText( QStringLiteral( " <= " ) );
396  mTxtSql->setFocus();
397 }
398 
399 void QgsQueryBuilder::btnGreaterEqual_clicked()
400 {
401  mTxtSql->insertText( QStringLiteral( " >= " ) );
402  mTxtSql->setFocus();
403 }
404 
405 void QgsQueryBuilder::btnNotEqual_clicked()
406 {
407  mTxtSql->insertText( QStringLiteral( " != " ) );
408  mTxtSql->setFocus();
409 }
410 
411 void QgsQueryBuilder::btnAnd_clicked()
412 {
413  mTxtSql->insertText( QStringLiteral( " AND " ) );
414  mTxtSql->setFocus();
415 }
416 
417 void QgsQueryBuilder::btnNot_clicked()
418 {
419  mTxtSql->insertText( QStringLiteral( " NOT " ) );
420  mTxtSql->setFocus();
421 }
422 
423 void QgsQueryBuilder::btnOr_clicked()
424 {
425  mTxtSql->insertText( QStringLiteral( " OR " ) );
426  mTxtSql->setFocus();
427 }
428 
429 void QgsQueryBuilder::onTextChanged( const QString &text )
430 {
431  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
432  mProxyValues->setFilterWildcard( text );
433 }
434 
436 {
437  mTxtSql->clear();
438  mLayer->setSubsetString( QString() );
439 }
440 
441 void QgsQueryBuilder::btnILike_clicked()
442 {
443  mTxtSql->insertText( QStringLiteral( " ILIKE " ) );
444  mTxtSql->setFocus();
445 }
446 
448 {
449  lblDataUri->setText( uri );
450 }
451 
452 void QgsQueryBuilder::showHelp()
453 {
454  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
455 }
456 
458 {
459  QgsSettings s;
460  QString lastQueryFileDir = s.value( QStringLiteral( "/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
461  //save as qqf (QGIS query file)
462  QString saveFileName = QFileDialog::getSaveFileName( nullptr, tr( "Save Query to File" ), lastQueryFileDir, tr( "Query files (*.qqf *.QQF)" ) );
463  if ( saveFileName.isNull() )
464  {
465  return;
466  }
467 
468  if ( !saveFileName.endsWith( QLatin1String( ".qqf" ), Qt::CaseInsensitive ) )
469  {
470  saveFileName += QLatin1String( ".qqf" );
471  }
472 
473  QFile saveFile( saveFileName );
474  if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
475  {
476  QMessageBox::critical( nullptr, tr( "Save Query to File" ), tr( "Could not open file for writing." ) );
477  return;
478  }
479 
480  QDomDocument xmlDoc;
481  QDomElement queryElem = xmlDoc.createElement( QStringLiteral( "Query" ) );
482  QDomText queryTextNode = xmlDoc.createTextNode( mTxtSql->text() );
483  queryElem.appendChild( queryTextNode );
484  xmlDoc.appendChild( queryElem );
485 
486  QTextStream fileStream( &saveFile );
487  xmlDoc.save( fileStream, 2 );
488 
489  QFileInfo fi( saveFile );
490  s.setValue( QStringLiteral( "/UI/lastQueryFileDir" ), fi.absolutePath() );
491 }
492 
494 {
495  QgsSettings s;
496  QString lastQueryFileDir = s.value( QStringLiteral( "/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
497 
498  QString queryFileName = QFileDialog::getOpenFileName( nullptr, tr( "Load Query from File" ), lastQueryFileDir, tr( "Query files" ) + " (*.qqf);;" + tr( "All files" ) + " (*)" );
499  if ( queryFileName.isNull() )
500  {
501  return;
502  }
503 
504  QFile queryFile( queryFileName );
505  if ( !queryFile.open( QIODevice::ReadOnly ) )
506  {
507  QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "Could not open file for reading." ) );
508  return;
509  }
510  QDomDocument queryDoc;
511  if ( !queryDoc.setContent( &queryFile ) )
512  {
513  QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "File is not a valid xml document." ) );
514  return;
515  }
516 
517  QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral( "Query" ) );
518  if ( queryElem.isNull() )
519  {
520  QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "File is not a valid query document." ) );
521  return;
522  }
523 
524  QString query = queryElem.text();
525 
526  //TODO: test if all the attributes are valid
527  QgsExpression search( query );
528  if ( search.hasParserError() )
529  {
530  QMessageBox::critical( this, tr( "Query Result" ), search.parserErrorString() );
531  return;
532  }
533 
534  mTxtSql->clear();
535  mTxtSql->insertText( query );
536 }
537 
538 void QgsQueryBuilder::layerSubsetStringChanged()
539 {
540  if ( mIgnoreLayerSubsetStringChangedSignal )
541  return;
542  mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
543 }
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString parserErrorString() const
Returns parser error.
QString name
Definition: qgsfield.h:60
QString displayNameWithAlias() const
Returns the name to use when displaying this field and adds the alias in parenthesis if it is defined...
Definition: qgsfield.cpp:96
Container of fields for a vector layer.
Definition: qgsfields.h:45
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
Definition: qgsfields.cpp:189
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:65
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:156
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QString name
Definition: qgsmaplayer.h:88
QString providerType() const
Returns the provider type (provider key) for this layer.
void loadQuery()
Load query from the XML file.
void saveQuery()
Save query to the XML file.
void accept() override
void reject() override
void setDatasourceDescription(const QString &uri)
void setSql(const QString &sqlStatement)
Set the sql statement to display in the dialog.
virtual void test()
The default implementation tests that the constructed sql statement to see if the vector layer data p...
void showEvent(QShowEvent *event) override
QgsQueryBuilder(QgsVectorLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
This constructor is used when the query builder is called from the vector layer properties dialog.
QString sql() const
Returns the sql statement entered in the dialog.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Interface for a dialog that can edit subset strings.
void clearErrors()
Clear recorded errors.
QStringList errors() const
Gets recorded errors.
bool hasErrors() const
Provider has errors to report.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString subsetString
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38