QGIS API Documentation  3.6.0-Noosa (5873452)
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  Q_FOREACH ( const QVariant &var, values )
157  {
158  QString value;
159  if ( var.isNull() )
160  value = nullValue;
161  else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
162  value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
163  else
164  value = var.toString();
165 
166  QStandardItem *myItem = new QStandardItem( value );
167  myItem->setEditable( false );
168  myItem->setData( var, Qt::UserRole + 1 );
169  mModelValues->insertRow( mModelValues->rowCount(), myItem );
170  QgsDebugMsg( QStringLiteral( "Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
171  }
172 }
173 
174 void QgsQueryBuilder::btnSampleValues_clicked()
175 {
176  lstValues->setCursor( Qt::WaitCursor );
177 
178  QString prevSubsetString = mLayer->subsetString();
179  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
180  {
181  mLayer->setSubsetString( QString() );
182  }
183 
184  //Clear and fill the mModelValues
185  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
186 
187  if ( prevSubsetString != mLayer->subsetString() )
188  {
189  mLayer->setSubsetString( prevSubsetString );
190  }
191 
192  lstValues->setCursor( Qt::ArrowCursor );
193 }
194 
195 void QgsQueryBuilder::btnGetAllValues_clicked()
196 {
197  lstValues->setCursor( Qt::WaitCursor );
198 
199  QString prevSubsetString = mLayer->subsetString();
200  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
201  {
202  mLayer->setSubsetString( QString() );
203  }
204 
205  //Clear and fill the mModelValues
206  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
207 
208  if ( prevSubsetString != mLayer->subsetString() )
209  {
210  mLayer->setSubsetString( prevSubsetString );
211  }
212 
213  lstValues->setCursor( Qt::ArrowCursor );
214 }
215 
217 {
218  // test the sql statement to see if it works
219  // by counting the number of records that would be
220  // returned
221 
222  if ( mLayer->setSubsetString( txtSQL->text() ) )
223  {
224  mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
225 
226  QMessageBox::information( this,
227  tr( "Query Result" ),
228  tr( "The where clause returned %n row(s).", "returned test rows", mLayer->featureCount() ) );
229  }
230  else if ( mLayer->dataProvider()->hasErrors() )
231  {
232  QMessageBox::warning( this,
233  tr( "Query Result" ),
234  tr( "An error occurred when executing the query." )
235  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
236  mLayer->dataProvider()->clearErrors();
237  }
238  else
239  {
240  QMessageBox::warning( this,
241  tr( "Query Result" ),
242  tr( "An error occurred when executing the query." ) );
243  }
244 }
245 
247 {
248  if ( txtSQL->text() != mOrigSubsetString )
249  {
250  if ( !mLayer->setSubsetString( txtSQL->text() ) )
251  {
252  //error in query - show the problem
253  if ( mLayer->dataProvider()->hasErrors() )
254  {
255  QMessageBox::warning( this,
256  tr( "Query Result" ),
257  tr( "An error occurred when executing the query." )
258  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
259  mLayer->dataProvider()->clearErrors();
260  }
261  else
262  {
263  QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
264  }
265 
266  return;
267  }
268  }
269 
270  QDialog::accept();
271 }
272 
274 {
275  if ( mLayer->subsetString() != mOrigSubsetString )
276  mLayer->setSubsetString( mOrigSubsetString );
277 
278  QDialog::reject();
279 }
280 
281 void QgsQueryBuilder::btnEqual_clicked()
282 {
283  txtSQL->insertText( QStringLiteral( " = " ) );
284  txtSQL->setFocus();
285 }
286 
287 void QgsQueryBuilder::btnLessThan_clicked()
288 {
289  txtSQL->insertText( QStringLiteral( " < " ) );
290  txtSQL->setFocus();
291 }
292 
293 void QgsQueryBuilder::btnGreaterThan_clicked()
294 {
295  txtSQL->insertText( QStringLiteral( " > " ) );
296  txtSQL->setFocus();
297 }
298 
299 void QgsQueryBuilder::btnPct_clicked()
300 {
301  txtSQL->insertText( QStringLiteral( "%" ) );
302  txtSQL->setFocus();
303 }
304 
305 void QgsQueryBuilder::btnIn_clicked()
306 {
307  txtSQL->insertText( QStringLiteral( " IN " ) );
308  txtSQL->setFocus();
309 }
310 
311 void QgsQueryBuilder::btnNotIn_clicked()
312 {
313  txtSQL->insertText( QStringLiteral( " NOT IN " ) );
314  txtSQL->setFocus();
315 }
316 
317 void QgsQueryBuilder::btnLike_clicked()
318 {
319  txtSQL->insertText( QStringLiteral( " LIKE " ) );
320  txtSQL->setFocus();
321 }
322 
324 {
325  return txtSQL->text();
326 }
327 
328 void QgsQueryBuilder::setSql( const QString &sqlStatement )
329 {
330  txtSQL->setText( sqlStatement );
331 }
332 
333 void QgsQueryBuilder::lstFields_clicked( const QModelIndex &index )
334 {
335  if ( mPreviousFieldRow != index.row() )
336  {
337  mPreviousFieldRow = index.row();
338 
339  btnSampleValues->setEnabled( true );
340  btnGetAllValues->setEnabled( true );
341 
342  mModelValues->clear();
343  mFilterLineEdit->clear();
344  }
345 }
346 
347 void QgsQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
348 {
349  txtSQL->insertText( '\"' + mLayer->fields().at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).name() + '\"' );
350  txtSQL->setFocus();
351 }
352 
353 void QgsQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
354 {
355  QVariant value = index.data( Qt::DisplayRole );
356  if ( value.isNull() )
357  txtSQL->insertText( QStringLiteral( "NULL" ) );
358  else if ( value.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
359  txtSQL->insertText( '\'' + value.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) ) + '\'' );
360  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
361  txtSQL->insertText( value.toString() );
362  else
363  txtSQL->insertText( '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'' );
364 
365  txtSQL->setFocus();
366 }
367 
368 void QgsQueryBuilder::btnLessEqual_clicked()
369 {
370  txtSQL->insertText( QStringLiteral( " <= " ) );
371  txtSQL->setFocus();
372 }
373 
374 void QgsQueryBuilder::btnGreaterEqual_clicked()
375 {
376  txtSQL->insertText( QStringLiteral( " >= " ) );
377  txtSQL->setFocus();
378 }
379 
380 void QgsQueryBuilder::btnNotEqual_clicked()
381 {
382  txtSQL->insertText( QStringLiteral( " != " ) );
383  txtSQL->setFocus();
384 }
385 
386 void QgsQueryBuilder::btnAnd_clicked()
387 {
388  txtSQL->insertText( QStringLiteral( " AND " ) );
389  txtSQL->setFocus();
390 }
391 
392 void QgsQueryBuilder::btnNot_clicked()
393 {
394  txtSQL->insertText( QStringLiteral( " NOT " ) );
395  txtSQL->setFocus();
396 }
397 
398 void QgsQueryBuilder::btnOr_clicked()
399 {
400  txtSQL->insertText( QStringLiteral( " OR " ) );
401  txtSQL->setFocus();
402 }
403 
404 void QgsQueryBuilder::onTextChanged( const QString &text )
405 {
406  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
407  mProxyValues->setFilterWildcard( text );
408 }
409 
411 {
412  txtSQL->clear();
413  mLayer->setSubsetString( QString() );
414  mUseUnfilteredLayer->setDisabled( true );
415 }
416 
417 void QgsQueryBuilder::btnILike_clicked()
418 {
419  txtSQL->insertText( QStringLiteral( " ILIKE " ) );
420  txtSQL->setFocus();
421 }
422 
424 {
425  lblDataUri->setText( uri );
426 }
427 
428 void QgsQueryBuilder::showHelp()
429 {
430  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
431 }
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)
Set the string (typically sql) used to define a subset of the layer.
void reject() override
QString name
Definition: qgsmaplayer.h:68
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be null.
Represents a vector layer which manages a vector based data sets.
void showEvent(QShowEvent *event) override