QGIS API Documentation 3.41.0-Master (cea29feecf2)
Loading...
Searching...
No Matches
qgssearchquerybuilder.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssearchquerybuilder.cpp - Query builder for search strings
3 ----------------------
4 begin : March 2006
5 copyright : (C) 2006 by Martin Dobias
6 email : wonder.sk at gmail dot 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
16#include <QDomDocument>
17#include <QDomElement>
18#include <QFileDialog>
19#include <QFileInfo>
20#include <QInputDialog>
21#include <QListView>
22#include <QMessageBox>
23#include <QStandardItem>
24#include <QTextStream>
25
26#include "qgsfeature.h"
27#include "qgsfeatureiterator.h"
28#include "qgsfields.h"
30#include "moc_qgssearchquerybuilder.cpp"
31#include "qgsexpression.h"
32#include "qgsvectorlayer.h"
33#include "qgshelp.h"
35#include "qgsquerybuilder.h"
36
37
38QgsSearchQueryBuilder::QgsSearchQueryBuilder( QgsVectorLayer *layer, QWidget *parent, Qt::WindowFlags fl )
39 : QDialog( parent, fl )
40 , mLayer( layer )
41{
42 setupUi( this );
43 connect( btnEqual, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnEqual_clicked );
44 connect( btnLessThan, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnLessThan_clicked );
45 connect( btnGreaterThan, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnGreaterThan_clicked );
46 connect( btnLike, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnLike_clicked );
47 connect( btnILike, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnILike_clicked );
48 connect( btnPct, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnPct_clicked );
49 connect( btnIn, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnIn_clicked );
50 connect( btnNotIn, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnNotIn_clicked );
51 connect( lstFields, &QListView::doubleClicked, this, &QgsSearchQueryBuilder::lstFields_doubleClicked );
52 connect( lstValues, &QListView::doubleClicked, this, &QgsSearchQueryBuilder::lstValues_doubleClicked );
53 connect( btnLessEqual, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnLessEqual_clicked );
54 connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnGreaterEqual_clicked );
55 connect( btnNotEqual, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnNotEqual_clicked );
56 connect( btnAnd, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnAnd_clicked );
57 connect( btnNot, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnNot_clicked );
58 connect( btnOr, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnOr_clicked );
59 connect( btnGetAllValues, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnGetAllValues_clicked );
60 connect( btnSampleValues, &QPushButton::clicked, this, &QgsSearchQueryBuilder::btnSampleValues_clicked );
61 setupListViews();
62 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsSearchQueryBuilder::showHelp );
63
64 setWindowTitle( tr( "Search Query Builder" ) );
65
66 QPushButton *pbn = new QPushButton( tr( "&Test" ) );
67 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
68 connect( pbn, &QAbstractButton::clicked, this, &QgsSearchQueryBuilder::btnTest_clicked );
69
70 pbn = new QPushButton( tr( "&Clear" ) );
71 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
72 connect( pbn, &QAbstractButton::clicked, this, &QgsSearchQueryBuilder::btnClear_clicked );
73
74 pbn = new QPushButton( tr( "&Save…" ) );
75 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
76 pbn->setToolTip( tr( "Save query to an xml file" ) );
77 connect( pbn, &QAbstractButton::clicked, this, &QgsSearchQueryBuilder::saveQuery );
78
79 pbn = new QPushButton( tr( "&Load…" ) );
80 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
81 pbn->setToolTip( tr( "Load query from xml file" ) );
82 connect( pbn, &QAbstractButton::clicked, this, &QgsSearchQueryBuilder::loadQuery );
83
84 if ( layer )
85 lblDataUri->setText( layer->name() );
86 populateFields();
87}
88
89void QgsSearchQueryBuilder::populateFields()
90{
91 if ( !mLayer )
92 return;
93
94 const QgsFields &fields = mLayer->fields();
95 for ( int idx = 0; idx < fields.count(); ++idx )
96 {
97 const QString fieldName = fields.at( idx ).name();
98 mFieldMap[fieldName] = idx;
99 QStandardItem *myItem = new QStandardItem( fieldName );
100 myItem->setEditable( false );
101 mModelFields->insertRow( mModelFields->rowCount(), myItem );
102 }
103}
104
105void QgsSearchQueryBuilder::setupListViews()
106{
107 //Models
108 mModelFields = new QStandardItemModel();
109 mModelValues = new QStandardItemModel();
110 lstFields->setModel( mModelFields );
111 lstValues->setModel( mModelValues );
112 // Modes
113 lstFields->setViewMode( QListView::ListMode );
114 lstValues->setViewMode( QListView::ListMode );
115 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
116 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
117 // Performance tip since Qt 4.1
118 lstFields->setUniformItemSizes( true );
119 lstValues->setUniformItemSizes( true );
120}
121
122void QgsSearchQueryBuilder::getFieldValues( int limit )
123{
124 if ( !mLayer )
125 {
126 return;
127 }
128 // clear the values list
129 mModelValues->clear();
130
131 // determine the field type
132 const QString fieldName = mModelFields->data( lstFields->currentIndex() ).toString();
133 const int fieldIndex = mFieldMap[fieldName];
134 const QgsField field = mLayer->fields().at( fieldIndex ); //provider->fields().at( fieldIndex );
135 const bool numeric = ( field.type() == QMetaType::Type::Int || field.type() == QMetaType::Type::Double );
136
137 QgsFeature feat;
138 QString value;
139
140 QgsAttributeList attrs;
141 attrs.append( fieldIndex );
142
144
145 lstValues->setCursor( Qt::WaitCursor );
146 // Block for better performance
147 mModelValues->blockSignals( true );
148 lstValues->setUpdatesEnabled( false );
149
150 // MH: keep already inserted values in a set. Querying is much faster compared to QStandardItemModel::findItems
151 QSet<QString> insertedValues;
152
153 while ( fit.nextFeature( feat ) && ( limit == 0 || mModelValues->rowCount() != limit ) )
154 {
155 value = feat.attribute( fieldIndex ).toString();
156
157 if ( !numeric )
158 {
159 // put string in single quotes and escape single quotes in the string
160 value = '\'' + value.replace( '\'', QLatin1String( "''" ) ) + '\'';
161 }
162
163 // add item only if it's not there already
164 if ( !insertedValues.contains( value ) )
165 {
166 QStandardItem *myItem = new QStandardItem( value );
167 myItem->setEditable( false );
168 mModelValues->insertRow( mModelValues->rowCount(), myItem );
169 insertedValues.insert( value );
170 }
171 }
172 // Unblock for normal use
173 mModelValues->blockSignals( false );
174 lstValues->setUpdatesEnabled( true );
175 // TODO: already sorted, signal emit to refresh model
176 mModelValues->sort( 0 );
177 lstValues->setCursor( Qt::ArrowCursor );
178}
179
180void QgsSearchQueryBuilder::btnSampleValues_clicked()
181{
182 getFieldValues( 25 );
183}
184
185void QgsSearchQueryBuilder::btnGetAllValues_clicked()
186{
187 getFieldValues( 0 );
188}
189
190void QgsSearchQueryBuilder::btnTest_clicked()
191{
192 const long count = countRecords( mTxtSql->text() );
193
194 // error?
195 if ( count == -1 )
196 return;
197
198 QMessageBox::information( this, tr( "Test Query" ), tr( "Found %n matching feature(s).", "test result", count ) );
199}
200
201// This method tests the number of records that would be returned
202long QgsSearchQueryBuilder::countRecords( const QString &searchString )
203{
204 QgsExpression search( searchString );
205 if ( search.hasParserError() )
206 {
207 QMessageBox::critical( this, tr( "Query Result" ), search.parserErrorString() );
208 return -1;
209 }
210
211 if ( !mLayer )
212 return -1;
213
214 const bool fetchGeom = search.needsGeometry();
215
216 int count = 0;
217 QgsFeature feat;
218
220
221 if ( !search.prepare( &context ) )
222 {
223 QMessageBox::critical( this, tr( "Query Result" ), search.evalErrorString() );
224 return -1;
225 }
226
227 QApplication::setOverrideCursor( Qt::WaitCursor );
228
229 QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFlags( fetchGeom ? Qgis::FeatureRequestFlag::NoFlags : Qgis::FeatureRequestFlag::NoGeometry ) );
230
231 while ( fit.nextFeature( feat ) )
232 {
233 context.setFeature( feat );
234 const QVariant value = search.evaluate( &context );
235 if ( value.toInt() != 0 )
236 {
237 count++;
238 }
239
240 // check if there were errors during evaluating
241 if ( search.hasEvalError() )
242 break;
243 }
244
245 QApplication::restoreOverrideCursor();
246
247 if ( search.hasEvalError() )
248 {
249 QMessageBox::critical( this, tr( "Query Result" ), search.evalErrorString() );
250 return -1;
251 }
252
253 return count;
254}
255
256
257void QgsSearchQueryBuilder::btnOk_clicked()
258{
259 // if user hits OK and there is no query, skip the validation
260 if ( mTxtSql->text().trimmed().length() > 0 )
261 {
262 accept();
263 return;
264 }
265
266 // test the query to see if it will result in a valid layer
267 const long numRecs = countRecords( mTxtSql->text() );
268 if ( numRecs == -1 )
269 {
270 // error shown in countRecords
271 }
272 else if ( numRecs == 0 )
273 {
274 QMessageBox::warning( this, tr( "Query Result" ), tr( "The query you specified results in zero records being returned." ) );
275 }
276 else
277 {
278 accept();
279 }
280}
281
282void QgsSearchQueryBuilder::btnEqual_clicked()
283{
284 mTxtSql->insertText( QStringLiteral( " = " ) );
285}
286
287void QgsSearchQueryBuilder::btnLessThan_clicked()
288{
289 mTxtSql->insertText( QStringLiteral( " < " ) );
290}
291
292void QgsSearchQueryBuilder::btnGreaterThan_clicked()
293{
294 mTxtSql->insertText( QStringLiteral( " > " ) );
295}
296
297void QgsSearchQueryBuilder::btnPct_clicked()
298{
299 mTxtSql->insertText( QStringLiteral( "%" ) );
300}
301
302void QgsSearchQueryBuilder::btnIn_clicked()
303{
304 mTxtSql->insertText( QStringLiteral( " IN " ) );
305}
306
307void QgsSearchQueryBuilder::btnNotIn_clicked()
308{
309 mTxtSql->insertText( QStringLiteral( " NOT IN " ) );
310}
311
312void QgsSearchQueryBuilder::btnLike_clicked()
313{
314 mTxtSql->insertText( QStringLiteral( " LIKE " ) );
315}
316
318{
319 return mTxtSql->text();
320}
321
322void QgsSearchQueryBuilder::setSearchString( const QString &searchString )
323{
324 mTxtSql->setText( searchString );
325}
326
327void QgsSearchQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
328{
329 mTxtSql->insertText( QgsExpression::quotedColumnRef( mModelFields->data( index ).toString() ) );
330}
331
332void QgsSearchQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
333{
334 mTxtSql->insertText( mModelValues->data( index ).toString() );
335}
336
337void QgsSearchQueryBuilder::btnLessEqual_clicked()
338{
339 mTxtSql->insertText( QStringLiteral( " <= " ) );
340}
341
342void QgsSearchQueryBuilder::btnGreaterEqual_clicked()
343{
344 mTxtSql->insertText( QStringLiteral( " >= " ) );
345}
346
347void QgsSearchQueryBuilder::btnNotEqual_clicked()
348{
349 mTxtSql->insertText( QStringLiteral( " != " ) );
350}
351
352void QgsSearchQueryBuilder::btnAnd_clicked()
353{
354 mTxtSql->insertText( QStringLiteral( " AND " ) );
355}
356
357void QgsSearchQueryBuilder::btnNot_clicked()
358{
359 mTxtSql->insertText( QStringLiteral( " NOT " ) );
360}
361
362void QgsSearchQueryBuilder::btnOr_clicked()
363{
364 mTxtSql->insertText( QStringLiteral( " OR " ) );
365}
366
367void QgsSearchQueryBuilder::btnClear_clicked()
368{
369 mTxtSql->clear();
370}
371
372void QgsSearchQueryBuilder::btnILike_clicked()
373{
374 mTxtSql->insertText( QStringLiteral( " ILIKE " ) );
375}
376
381
383{
384 QString query;
386 {
387 mTxtSql->clear();
388 mTxtSql->insertText( query );
389 }
390}
391
392void QgsSearchQueryBuilder::showHelp()
393{
394 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
395}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ NoFlags
No flags are set.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QString name
Definition qgsfield.h:62
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
QString name
Definition qgsmaplayer.h:80
static bool loadQueryFromFile(QString &subset)
Load query from the XML file.
static bool saveQueryToFile(const QString &subset)
Save query to the XML file.
QgsSearchQueryBuilder(QgsVectorLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor - takes pointer to vector layer as a parameter.
void setSearchString(const QString &searchString)
change search string shown in text field
QString searchString()
returns newly created search string
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QList< int > QgsAttributeList
Definition qgsfield.h:27