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