QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  for ( int idx = 0; idx < fields.count(); ++idx )
95  {
96  if ( fields.fieldOrigin( idx ) != QgsFields::OriginProvider )
97  {
98  // only consider native fields
99  continue;
100  }
101  QStandardItem *myItem = new QStandardItem( fields.at( idx ).name() );
102  myItem->setData( idx );
103  myItem->setEditable( false );
104  mModelFields->insertRow( mModelFields->rowCount(), myItem );
105  }
106 
107  // All fields get ... setup
108  setupLstFieldsModel();
109 }
110 
111 void QgsQueryBuilder::setupLstFieldsModel()
112 {
113  lstFields->setModel( mModelFields );
114 }
115 
116 void QgsQueryBuilder::setupGuiViews()
117 {
118  //Initialize the models
119  mModelFields = new QStandardItemModel();
120  mModelValues = new QStandardItemModel();
121  mProxyValues = new QSortFilterProxyModel();
122  mProxyValues->setSourceModel( mModelValues );
123  // Modes
124  lstFields->setViewMode( QListView::ListMode );
125  lstValues->setViewMode( QListView::ListMode );
126  lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
127  lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
128  // Performance tip since Qt 4.1
129  lstFields->setUniformItemSizes( true );
130  lstValues->setUniformItemSizes( true );
131  // Colored rows
132  lstFields->setAlternatingRowColors( true );
133  lstValues->setAlternatingRowColors( true );
134  lstValues->setModel( mProxyValues );
135 }
136 
137 void QgsQueryBuilder::fillValues( int idx, int limit )
138 {
139  // clear the model
140  mModelValues->clear();
141 
142  // determine the field type
143  QList<QVariant> values = mLayer->uniqueValues( idx, limit ).toList();
144  std::sort( values.begin(), values.end() );
145 
146  QString nullValue = QgsApplication::nullRepresentation();
147 
148  QgsDebugMsg( QStringLiteral( "nullValue: %1" ).arg( nullValue ) );
149 
150  const auto constValues = values;
151  for ( const QVariant &var : constValues )
152  {
153  QString value;
154  if ( var.isNull() )
155  value = nullValue;
156  else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
157  value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
158  else
159  value = var.toString();
160 
161  QStandardItem *myItem = new QStandardItem( value );
162  myItem->setEditable( false );
163  myItem->setData( var, Qt::UserRole + 1 );
164  mModelValues->insertRow( mModelValues->rowCount(), myItem );
165  QgsDebugMsg( QStringLiteral( "Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
166  }
167 }
168 
169 void QgsQueryBuilder::btnSampleValues_clicked()
170 {
171  lstValues->setCursor( Qt::WaitCursor );
172 
173  QString prevSubsetString = mLayer->subsetString();
174  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
175  {
176  mLayer->setSubsetString( QString() );
177  }
178 
179  //Clear and fill the mModelValues
180  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
181 
182  if ( prevSubsetString != mLayer->subsetString() )
183  {
184  mLayer->setSubsetString( prevSubsetString );
185  }
186 
187  lstValues->setCursor( Qt::ArrowCursor );
188 }
189 
190 void QgsQueryBuilder::btnGetAllValues_clicked()
191 {
192  lstValues->setCursor( Qt::WaitCursor );
193 
194  QString prevSubsetString = mLayer->subsetString();
195  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
196  {
197  mLayer->setSubsetString( QString() );
198  }
199 
200  //Clear and fill the mModelValues
201  fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
202 
203  if ( prevSubsetString != mLayer->subsetString() )
204  {
205  mLayer->setSubsetString( prevSubsetString );
206  }
207 
208  lstValues->setCursor( Qt::ArrowCursor );
209 }
210 
212 {
213  // test the sql statement to see if it works
214  // by counting the number of records that would be
215  // returned
216 
217  if ( mLayer->setSubsetString( txtSQL->text() ) )
218  {
219  mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
220 
221  QMessageBox::information( this,
222  tr( "Query Result" ),
223  tr( "The where clause returned %n row(s).", "returned test rows", mLayer->featureCount() ) );
224  }
225  else if ( mLayer->dataProvider()->hasErrors() )
226  {
227  QMessageBox::warning( this,
228  tr( "Query Result" ),
229  tr( "An error occurred when executing the query." )
230  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
231  mLayer->dataProvider()->clearErrors();
232  }
233  else
234  {
235  QMessageBox::warning( this,
236  tr( "Query Result" ),
237  tr( "An error occurred when executing the query." ) );
238  }
239 }
240 
242 {
243  if ( txtSQL->text() != mOrigSubsetString )
244  {
245  if ( !mLayer->setSubsetString( txtSQL->text() ) )
246  {
247  //error in query - show the problem
248  if ( mLayer->dataProvider()->hasErrors() )
249  {
250  QMessageBox::warning( this,
251  tr( "Query Result" ),
252  tr( "An error occurred when executing the query." )
253  + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QStringLiteral( "\n" ) ) ) );
254  mLayer->dataProvider()->clearErrors();
255  }
256  else
257  {
258  QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
259  }
260 
261  return;
262  }
263  }
264 
265  QDialog::accept();
266 }
267 
269 {
270  if ( mLayer->subsetString() != mOrigSubsetString )
271  mLayer->setSubsetString( mOrigSubsetString );
272 
273  QDialog::reject();
274 }
275 
276 void QgsQueryBuilder::btnEqual_clicked()
277 {
278  txtSQL->insertText( QStringLiteral( " = " ) );
279  txtSQL->setFocus();
280 }
281 
282 void QgsQueryBuilder::btnLessThan_clicked()
283 {
284  txtSQL->insertText( QStringLiteral( " < " ) );
285  txtSQL->setFocus();
286 }
287 
288 void QgsQueryBuilder::btnGreaterThan_clicked()
289 {
290  txtSQL->insertText( QStringLiteral( " > " ) );
291  txtSQL->setFocus();
292 }
293 
294 void QgsQueryBuilder::btnPct_clicked()
295 {
296  txtSQL->insertText( QStringLiteral( "%" ) );
297  txtSQL->setFocus();
298 }
299 
300 void QgsQueryBuilder::btnIn_clicked()
301 {
302  txtSQL->insertText( QStringLiteral( " IN " ) );
303  txtSQL->setFocus();
304 }
305 
306 void QgsQueryBuilder::btnNotIn_clicked()
307 {
308  txtSQL->insertText( QStringLiteral( " NOT IN " ) );
309  txtSQL->setFocus();
310 }
311 
312 void QgsQueryBuilder::btnLike_clicked()
313 {
314  txtSQL->insertText( QStringLiteral( " LIKE " ) );
315  txtSQL->setFocus();
316 }
317 
319 {
320  return txtSQL->text();
321 }
322 
323 void QgsQueryBuilder::setSql( const QString &sqlStatement )
324 {
325  txtSQL->setText( sqlStatement );
326 }
327 
328 void QgsQueryBuilder::lstFields_clicked( const QModelIndex &index )
329 {
330  if ( mPreviousFieldRow != index.row() )
331  {
332  mPreviousFieldRow = index.row();
333 
334  btnSampleValues->setEnabled( true );
335  btnGetAllValues->setEnabled( true );
336 
337  mModelValues->clear();
338  mFilterLineEdit->clear();
339  }
340 }
341 
342 void QgsQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
343 {
344  txtSQL->insertText( '\"' + mLayer->fields().at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).name() + '\"' );
345  txtSQL->setFocus();
346 }
347 
348 void QgsQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
349 {
350  QVariant value = index.data( Qt::UserRole + 1 );
351  if ( value.isNull() )
352  txtSQL->insertText( QStringLiteral( "NULL" ) );
353  else if ( value.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
354  txtSQL->insertText( '\'' + value.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) ) + '\'' );
355  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
356  txtSQL->insertText( value.toString() );
357  else
358  txtSQL->insertText( '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'' );
359 
360  txtSQL->setFocus();
361 }
362 
363 void QgsQueryBuilder::btnLessEqual_clicked()
364 {
365  txtSQL->insertText( QStringLiteral( " <= " ) );
366  txtSQL->setFocus();
367 }
368 
369 void QgsQueryBuilder::btnGreaterEqual_clicked()
370 {
371  txtSQL->insertText( QStringLiteral( " >= " ) );
372  txtSQL->setFocus();
373 }
374 
375 void QgsQueryBuilder::btnNotEqual_clicked()
376 {
377  txtSQL->insertText( QStringLiteral( " != " ) );
378  txtSQL->setFocus();
379 }
380 
381 void QgsQueryBuilder::btnAnd_clicked()
382 {
383  txtSQL->insertText( QStringLiteral( " AND " ) );
384  txtSQL->setFocus();
385 }
386 
387 void QgsQueryBuilder::btnNot_clicked()
388 {
389  txtSQL->insertText( QStringLiteral( " NOT " ) );
390  txtSQL->setFocus();
391 }
392 
393 void QgsQueryBuilder::btnOr_clicked()
394 {
395  txtSQL->insertText( QStringLiteral( " OR " ) );
396  txtSQL->setFocus();
397 }
398 
399 void QgsQueryBuilder::onTextChanged( const QString &text )
400 {
401  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
402  mProxyValues->setFilterWildcard( text );
403 }
404 
406 {
407  txtSQL->clear();
408  mLayer->setSubsetString( QString() );
409  mUseUnfilteredLayer->setDisabled( true );
410 }
411 
412 void QgsQueryBuilder::btnILike_clicked()
413 {
414  txtSQL->insertText( QStringLiteral( " ILIKE " ) );
415  txtSQL->setFocus();
416 }
417 
419 {
420  lblDataUri->setText( uri );
421 }
422 
423 void QgsQueryBuilder::showHelp()
424 {
425  QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
426 }
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...
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#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
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:61
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
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 setDatasourceDescription(const QString &uri)
bool hasErrors() const
Provider has errors to report.
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:127
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:83
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