QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 <QListView>
26 #include <QMessageBox>
27 #include <QRegExp>
28 #include <QPushButton>
29 
30 // constructor used when the query builder must make its own
31 // connection to the database
33  QWidget *parent, Qt::WindowFlags fl )
34  : QDialog( parent, fl )
35  , mPreviousFieldRow( -1 )
36  , mLayer( layer )
37 {
38  setupUi( this );
40  connect( btnEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnEqual_clicked );
41  connect( btnLessThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessThan_clicked );
42  connect( btnGreaterThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterThan_clicked );
43  connect( btnPct, &QPushButton::clicked, this, &QgsQueryBuilder::btnPct_clicked );
44  connect( btnIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnIn_clicked );
45  connect( btnNotIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotIn_clicked );
46  connect( btnLike, &QPushButton::clicked, this, &QgsQueryBuilder::btnLike_clicked );
47  connect( btnILike, &QPushButton::clicked, this, &QgsQueryBuilder::btnILike_clicked );
48  connect( lstFields, &QListView::clicked, this, &QgsQueryBuilder::lstFields_clicked );
49  connect( lstFields, &QListView::doubleClicked, this, &QgsQueryBuilder::lstFields_doubleClicked );
50  connect( lstValues, &QListView::doubleClicked, this, &QgsQueryBuilder::lstValues_doubleClicked );
51  connect( btnLessEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessEqual_clicked );
52  connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterEqual_clicked );
53  connect( btnNotEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotEqual_clicked );
54  connect( btnAnd, &QPushButton::clicked, this, &QgsQueryBuilder::btnAnd_clicked );
55  connect( btnNot, &QPushButton::clicked, this, &QgsQueryBuilder::btnNot_clicked );
56  connect( btnOr, &QPushButton::clicked, this, &QgsQueryBuilder::btnOr_clicked );
57  connect( btnGetAllValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnGetAllValues_clicked );
58  connect( btnSampleValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnSampleValues_clicked );
59  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsQueryBuilder::showHelp );
60 
61  QPushButton *pbn = new QPushButton( tr( "&Test" ) );
62  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
63  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::test );
64 
65  pbn = new QPushButton( tr( "&Clear" ) );
66  buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
67  connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::clear );
68 
69  setupGuiViews();
70 
71  mOrigSubsetString = layer->subsetString();
72 
73  mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
74 
75  lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
76  txtSQL->setText( mOrigSubsetString );
77 
78  mFilterLineEdit->setShowSearchIcon( true );
79  mFilterLineEdit->setPlaceholderText( tr( "Search…" ) );
80  connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsQueryBuilder::onTextChanged );
81 
82  populateFields();
83 }
84 
85 void QgsQueryBuilder::showEvent( QShowEvent *event )
86 {
87  txtSQL->setFocus();
88  QDialog::showEvent( event );
89 }
90 
91 void QgsQueryBuilder::populateFields()
92 {
93  const QgsFields &fields = mLayer->fields();
94  txtSQL->setFields( fields );
95  for ( int idx = 0; idx < fields.count(); ++idx )
96  {
97  if ( fields.fieldOrigin( idx ) != QgsFields::OriginProvider )
98  {
99  // only consider native fields
100  continue;
101  }
102  QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
103  myItem->setData( idx );
104  myItem->setEditable( false );
105  mModelFields->insertRow( mModelFields->rowCount(), myItem );
106  }
107 
108  // All fields get ... setup
109  setupLstFieldsModel();
110 }
111 
112 void QgsQueryBuilder::setupLstFieldsModel()
113 {
114  lstFields->setModel( mModelFields );
115 }
116 
117 void QgsQueryBuilder::setupGuiViews()
118 {
119  //Initialize the models
120  mModelFields = new QStandardItemModel();
121  mModelValues = new QStandardItemModel();
122  mProxyValues = new QSortFilterProxyModel();
123  mProxyValues->setSourceModel( mModelValues );
124  // Modes
125  lstFields->setViewMode( QListView::ListMode );
126  lstValues->setViewMode( QListView::ListMode );
127  lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
128  lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
129  // Performance tip since Qt 4.1
130  lstFields->setUniformItemSizes( true );
131  lstValues->setUniformItemSizes( true );
132  // Colored rows
133  lstFields->setAlternatingRowColors( true );
134  lstValues->setAlternatingRowColors( true );
135  lstValues->setModel( mProxyValues );
136 }
137 
138 void QgsQueryBuilder::fillValues( int idx, int limit )
139 {
140  // clear the model
141  mModelValues->clear();
142 
143  // determine the field type
144  QList<QVariant> values = qgis::setToList( mLayer->uniqueValues( idx, limit ) );
145  std::sort( values.begin(), values.end() );
146 
147  QString nullValue = QgsApplication::nullRepresentation();
148 
149  QgsDebugMsg( QStringLiteral( "nullValue: %1" ).arg( nullValue ) );
150 
151  const auto constValues = values;
152  for ( const QVariant &var : constValues )
153  {
154  QString value;
155  if ( var.isNull() )
156  value = nullValue;
157  else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
158  value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
159  else
160  value = var.toString();
161 
162  QStandardItem *myItem = new QStandardItem( value );
163  myItem->setEditable( false );
164  myItem->setData( var, Qt::UserRole + 1 );
165  mModelValues->insertRow( mModelValues->rowCount(), myItem );
166  QgsDebugMsg( QStringLiteral( "Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
167  }
168 }
169 
170 void QgsQueryBuilder::btnSampleValues_clicked()
171 {
172  lstValues->setCursor( Qt::WaitCursor );
173 
174  QString prevSubsetString = mLayer->subsetString();
175  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
176  {
177  mLayer->setSubsetString( QString() );
178  }
179 
180  //Clear and fill the mModelValues
181  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
182 
183  if ( prevSubsetString != mLayer->subsetString() )
184  {
185  mLayer->setSubsetString( prevSubsetString );
186  }
187 
188  lstValues->setCursor( Qt::ArrowCursor );
189 }
190 
191 void QgsQueryBuilder::btnGetAllValues_clicked()
192 {
193  lstValues->setCursor( Qt::WaitCursor );
194 
195  QString prevSubsetString = mLayer->subsetString();
196  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
197  {
198  mLayer->setSubsetString( QString() );
199  }
200 
201  //Clear and fill the mModelValues
202  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
203 
204  if ( prevSubsetString != mLayer->subsetString() )
205  {
206  mLayer->setSubsetString( prevSubsetString );
207  }
208 
209  lstValues->setCursor( Qt::ArrowCursor );
210 }
211 
213 {
214  // test the sql statement to see if it works
215  // by counting the number of records that would be
216  // returned
217 
218  if ( mLayer->setSubsetString( txtSQL->text() ) )
219  {
220  mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
221 
222  const long featureCount { mLayer->featureCount() };
223  // Check for errors
224  if ( featureCount < 0 )
225  {
226  QMessageBox::warning( this,
227  tr( "Query Result" ),
228  tr( "An error occurred when executing the query, please check the expression syntax." ) );
229  }
230  else
231  {
232  QMessageBox::information( this,
233  tr( "Query Result" ),
234  tr( "The where clause returned %n row(s).", "returned test rows", featureCount ) );
235  }
236  }
237  else if ( mLayer->dataProvider()->hasErrors() )
238  {
239  QMessageBox::warning( this,
240  tr( "Query Result" ),
241  tr( "An error occurred when executing the query." )
242  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
243  mLayer->dataProvider()->clearErrors();
244  }
245  else
246  {
247  QMessageBox::warning( this,
248  tr( "Query Result" ),
249  tr( "An error occurred when executing the query." ) );
250  }
251 }
252 
254 {
255  if ( txtSQL->text() != mOrigSubsetString )
256  {
257  if ( !mLayer->setSubsetString( txtSQL->text() ) )
258  {
259  //error in query - show the problem
260  if ( mLayer->dataProvider()->hasErrors() )
261  {
262  QMessageBox::warning( this,
263  tr( "Query Result" ),
264  tr( "An error occurred when executing the query." )
265  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
266  mLayer->dataProvider()->clearErrors();
267  }
268  else
269  {
270  QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
271  }
272 
273  return;
274  }
275  }
276 
277  QDialog::accept();
278 }
279 
281 {
282  if ( mLayer->subsetString() != mOrigSubsetString )
283  mLayer->setSubsetString( mOrigSubsetString );
284 
285  QDialog::reject();
286 }
287 
288 void QgsQueryBuilder::btnEqual_clicked()
289 {
290  txtSQL->insertText( QStringLiteral( " = " ) );
291  txtSQL->setFocus();
292 }
293 
294 void QgsQueryBuilder::btnLessThan_clicked()
295 {
296  txtSQL->insertText( QStringLiteral( " < " ) );
297  txtSQL->setFocus();
298 }
299 
300 void QgsQueryBuilder::btnGreaterThan_clicked()
301 {
302  txtSQL->insertText( QStringLiteral( " > " ) );
303  txtSQL->setFocus();
304 }
305 
306 void QgsQueryBuilder::btnPct_clicked()
307 {
308  txtSQL->insertText( QStringLiteral( "%" ) );
309  txtSQL->setFocus();
310 }
311 
312 void QgsQueryBuilder::btnIn_clicked()
313 {
314  txtSQL->insertText( QStringLiteral( " IN " ) );
315  txtSQL->setFocus();
316 }
317 
318 void QgsQueryBuilder::btnNotIn_clicked()
319 {
320  txtSQL->insertText( QStringLiteral( " NOT IN " ) );
321  txtSQL->setFocus();
322 }
323 
324 void QgsQueryBuilder::btnLike_clicked()
325 {
326  txtSQL->insertText( QStringLiteral( " LIKE " ) );
327  txtSQL->setFocus();
328 }
329 
331 {
332  return txtSQL->text();
333 }
334 
335 void QgsQueryBuilder::setSql( const QString &sqlStatement )
336 {
337  txtSQL->setText( sqlStatement );
338 }
339 
340 void QgsQueryBuilder::lstFields_clicked( const QModelIndex &index )
341 {
342  if ( mPreviousFieldRow != index.row() )
343  {
344  mPreviousFieldRow = index.row();
345 
346  btnSampleValues->setEnabled( true );
347  btnGetAllValues->setEnabled( true );
348 
349  mModelValues->clear();
350  mFilterLineEdit->clear();
351  }
352 }
353 
354 void QgsQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
355 {
356  txtSQL->insertText( '\"' + mLayer->fields().at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).name() + '\"' );
357  txtSQL->setFocus();
358 }
359 
360 void QgsQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
361 {
362  QVariant value = index.data( Qt::UserRole + 1 );
363  if ( value.isNull() )
364  txtSQL->insertText( QStringLiteral( "NULL" ) );
365  else if ( value.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
366  txtSQL->insertText( '\'' + value.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) ) + '\'' );
367  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
368  txtSQL->insertText( value.toString() );
369  else
370  txtSQL->insertText( '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'' );
371 
372  txtSQL->setFocus();
373 }
374 
375 void QgsQueryBuilder::btnLessEqual_clicked()
376 {
377  txtSQL->insertText( QStringLiteral( " <= " ) );
378  txtSQL->setFocus();
379 }
380 
381 void QgsQueryBuilder::btnGreaterEqual_clicked()
382 {
383  txtSQL->insertText( QStringLiteral( " >= " ) );
384  txtSQL->setFocus();
385 }
386 
387 void QgsQueryBuilder::btnNotEqual_clicked()
388 {
389  txtSQL->insertText( QStringLiteral( " != " ) );
390  txtSQL->setFocus();
391 }
392 
393 void QgsQueryBuilder::btnAnd_clicked()
394 {
395  txtSQL->insertText( QStringLiteral( " AND " ) );
396  txtSQL->setFocus();
397 }
398 
399 void QgsQueryBuilder::btnNot_clicked()
400 {
401  txtSQL->insertText( QStringLiteral( " NOT " ) );
402  txtSQL->setFocus();
403 }
404 
405 void QgsQueryBuilder::btnOr_clicked()
406 {
407  txtSQL->insertText( QStringLiteral( " OR " ) );
408  txtSQL->setFocus();
409 }
410 
411 void QgsQueryBuilder::onTextChanged( const QString &text )
412 {
413  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
414  mProxyValues->setFilterWildcard( text );
415 }
416 
418 {
419  txtSQL->clear();
420  mLayer->setSubsetString( QString() );
421  mUseUnfilteredLayer->setDisabled( true );
422 }
423 
424 void QgsQueryBuilder::btnILike_clicked()
425 {
426  txtSQL->insertText( QStringLiteral( " ILIKE " ) );
427  txtSQL->setFocus();
428 }
429 
431 {
432  lblDataUri->setText( uri );
433 }
434 
435 void QgsQueryBuilder::showHelp()
436 {
437  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
438 }
QgsFields::OriginProvider
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
QgsVectorDataProvider::hasErrors
bool hasErrors() const
Provider has errors to report.
Definition: qgsvectordataprovider.cpp:702
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:627
qgsgui.h
QgsField::displayNameWithAlias
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:97
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Definition: qgsfields.h:44
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsVectorLayer::featureCount
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Definition: qgsvectorlayer.cpp:751
QgsQueryBuilder::accept
void accept() override
Definition: qgsquerybuilder.cpp:253
QgsField::name
QString name
Definition: qgsfield.h:59
QgsQueryBuilder::showEvent
void showEvent(QShowEvent *event) override
Definition: qgsquerybuilder.cpp:85
qgsquerybuilder.h
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1614
qgsapplication.h
QgsGui::enableAutoGeometryRestore
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:133
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3280
QgsVectorDataProvider::errors
QStringList errors() const
Gets recorded errors.
Definition: qgsvectordataprovider.cpp:707
QgsApplication::nullRepresentation
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
Definition: qgsapplication.cpp:1802
QgsVectorLayer::uniqueValues
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
Definition: qgsvectorlayer.cpp:3949
QgsQueryBuilder::reject
void reject() override
Definition: qgsquerybuilder.cpp:280
QgsFields::fieldOrigin
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
Definition: qgsfields.cpp:189
qgsvectordataprovider.h
QgsQueryBuilder::QgsQueryBuilder
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.
Definition: qgsquerybuilder.cpp:32
QgsVectorDataProvider::clearErrors
void clearErrors()
Clear recorded errors.
Definition: qgsvectordataprovider.cpp:697
qgsvectorlayer.h
QgsQueryBuilder::test
void test()
Test the constructed sql statement to see if the vector layer data provider likes it.
Definition: qgsquerybuilder.cpp:212
QgsGui::instance
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:62
QgsHelp::openHelp
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QgsVectorLayer
Definition: qgsvectorlayer.h:385
qgssettings.h
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:85
QgsQueryBuilder::sql
QString sql()
Definition: qgsquerybuilder.cpp:330
QgsVectorLayer::storageType
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Definition: qgsvectorlayer.cpp:340
QgsVectorLayer::subsetString
QString subsetString
Definition: qgsvectorlayer.h:389
QgsQueryBuilder::setDatasourceDescription
void setDatasourceDescription(const QString &uri)
Definition: qgsquerybuilder.cpp:430
qgslogger.h
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsQueryBuilder::clear
void clear()
Definition: qgsquerybuilder.cpp:417
QgsQueryBuilder::setSql
void setSql(const QString &sqlStatement)
Definition: qgsquerybuilder.cpp:335
qgshelp.h
qgsproject.h
QgsVectorLayer::setSubsetString
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
Definition: qgsvectorlayer.cpp:943