16 #include <QDomDocument>
17 #include <QDomElement>
18 #include <QFileDialog>
20 #include <QInputDialog>
22 #include <QMessageBox>
23 #include <QStandardItem>
24 #include <QTextStream>
26 #include "qgssettings.h"
39 QWidget *parent, Qt::WindowFlags fl )
40 : QDialog( parent, fl )
44 connect( btnEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnEqual_clicked );
45 connect( btnLessThan, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLessThan_clicked );
46 connect( btnGreaterThan, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGreaterThan_clicked );
47 connect( btnLike, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLike_clicked );
48 connect( btnILike, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnILike_clicked );
49 connect( btnPct, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnPct_clicked );
50 connect( btnIn, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnIn_clicked );
51 connect( btnNotIn, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNotIn_clicked );
52 connect( lstFields, &QListView::doubleClicked,
this, &QgsSearchQueryBuilder::lstFields_doubleClicked );
53 connect( lstValues, &QListView::doubleClicked,
this, &QgsSearchQueryBuilder::lstValues_doubleClicked );
54 connect( btnLessEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLessEqual_clicked );
55 connect( btnGreaterEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGreaterEqual_clicked );
56 connect( btnNotEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNotEqual_clicked );
57 connect( btnAnd, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnAnd_clicked );
58 connect( btnNot, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNot_clicked );
59 connect( btnOr, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnOr_clicked );
60 connect( btnGetAllValues, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGetAllValues_clicked );
61 connect( btnSampleValues, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnSampleValues_clicked );
63 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsSearchQueryBuilder::showHelp );
65 setWindowTitle( tr(
"Search Query Builder" ) );
67 QPushButton *pbn =
new QPushButton( tr(
"&Test" ) );
68 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
69 connect( pbn, &QAbstractButton::clicked,
this, &QgsSearchQueryBuilder::btnTest_clicked );
71 pbn =
new QPushButton( tr(
"&Clear" ) );
72 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
73 connect( pbn, &QAbstractButton::clicked,
this, &QgsSearchQueryBuilder::btnClear_clicked );
75 pbn =
new QPushButton( tr(
"&Save…" ) );
76 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
77 pbn->setToolTip( tr(
"Save query to an xml file" ) );
80 pbn =
new QPushButton( tr(
"&Load…" ) );
81 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
82 pbn->setToolTip( tr(
"Load query from xml file" ) );
86 lblDataUri->setText( layer->
name() );
90 void QgsSearchQueryBuilder::populateFields()
96 for (
int idx = 0; idx < fields.
count(); ++idx )
98 QString fieldName = fields.
at( idx ).
name();
99 mFieldMap[fieldName] = idx;
100 QStandardItem *myItem =
new QStandardItem( fieldName );
101 myItem->setEditable(
false );
102 mModelFields->insertRow( mModelFields->rowCount(), myItem );
106 void QgsSearchQueryBuilder::setupListViews()
109 mModelFields =
new QStandardItemModel();
110 mModelValues =
new QStandardItemModel();
111 lstFields->setModel( mModelFields );
112 lstValues->setModel( mModelValues );
114 lstFields->setViewMode( QListView::ListMode );
115 lstValues->setViewMode( QListView::ListMode );
116 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
117 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
119 lstFields->setUniformItemSizes(
true );
120 lstValues->setUniformItemSizes(
true );
123 void QgsSearchQueryBuilder::getFieldValues(
int limit )
130 mModelValues->clear();
133 QString fieldName = mModelFields->data( lstFields->currentIndex() ).toString();
134 int fieldIndex = mFieldMap[fieldName];
142 attrs.append( fieldIndex );
146 lstValues->setCursor( Qt::WaitCursor );
148 mModelValues->blockSignals(
true );
149 lstValues->setUpdatesEnabled(
false );
152 QSet<QString> insertedValues;
155 ( limit == 0 || mModelValues->rowCount() != limit ) )
157 value = feat.
attribute( fieldIndex ).toString();
162 value =
'\'' + value.replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
166 if ( !insertedValues.contains( value ) )
168 QStandardItem *myItem =
new QStandardItem( value );
169 myItem->setEditable(
false );
170 mModelValues->insertRow( mModelValues->rowCount(), myItem );
171 insertedValues.insert( value );
175 mModelValues->blockSignals(
false );
176 lstValues->setUpdatesEnabled(
true );
178 mModelValues->sort( 0 );
179 lstValues->setCursor( Qt::ArrowCursor );
182 void QgsSearchQueryBuilder::btnSampleValues_clicked()
184 getFieldValues( 25 );
187 void QgsSearchQueryBuilder::btnGetAllValues_clicked()
192 void QgsSearchQueryBuilder::btnTest_clicked()
194 long count = countRecords( mTxtSql->text() );
200 QMessageBox::information(
this, tr(
"Test Query" ), tr(
"Found %n matching feature(s).",
"test result", count ) );
204 long QgsSearchQueryBuilder::countRecords(
const QString &searchString )
207 if ( search.hasParserError() )
209 QMessageBox::critical(
this, tr(
"Query Result" ), search.parserErrorString() );
216 bool fetchGeom = search.needsGeometry();
223 if ( !search.prepare( &context ) )
225 QMessageBox::critical(
this, tr(
"Query Result" ), search.evalErrorString() );
229 QApplication::setOverrideCursor( Qt::WaitCursor );
235 context.setFeature( feat );
236 QVariant value = search.evaluate( &context );
237 if ( value.toInt() != 0 )
243 if ( search.hasEvalError() )
247 QApplication::restoreOverrideCursor();
249 if ( search.hasEvalError() )
251 QMessageBox::critical(
this, tr(
"Query Result" ), search.evalErrorString() );
259 void QgsSearchQueryBuilder::btnOk_clicked()
262 if ( mTxtSql->text().trimmed().length() > 0 )
269 long numRecs = countRecords( mTxtSql->text() );
274 else if ( numRecs == 0 )
276 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"The query you specified results in zero records being returned." ) );
285 void QgsSearchQueryBuilder::btnEqual_clicked()
287 mTxtSql->insertText( QStringLiteral(
" = " ) );
290 void QgsSearchQueryBuilder::btnLessThan_clicked()
292 mTxtSql->insertText( QStringLiteral(
" < " ) );
295 void QgsSearchQueryBuilder::btnGreaterThan_clicked()
297 mTxtSql->insertText( QStringLiteral(
" > " ) );
300 void QgsSearchQueryBuilder::btnPct_clicked()
302 mTxtSql->insertText( QStringLiteral(
"%" ) );
305 void QgsSearchQueryBuilder::btnIn_clicked()
307 mTxtSql->insertText( QStringLiteral(
" IN " ) );
310 void QgsSearchQueryBuilder::btnNotIn_clicked()
312 mTxtSql->insertText( QStringLiteral(
" NOT IN " ) );
315 void QgsSearchQueryBuilder::btnLike_clicked()
317 mTxtSql->insertText( QStringLiteral(
" LIKE " ) );
322 return mTxtSql->text();
330 void QgsSearchQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
335 void QgsSearchQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
337 mTxtSql->insertText( mModelValues->data( index ).toString() );
340 void QgsSearchQueryBuilder::btnLessEqual_clicked()
342 mTxtSql->insertText( QStringLiteral(
" <= " ) );
345 void QgsSearchQueryBuilder::btnGreaterEqual_clicked()
347 mTxtSql->insertText( QStringLiteral(
" >= " ) );
350 void QgsSearchQueryBuilder::btnNotEqual_clicked()
352 mTxtSql->insertText( QStringLiteral(
" != " ) );
355 void QgsSearchQueryBuilder::btnAnd_clicked()
357 mTxtSql->insertText( QStringLiteral(
" AND " ) );
360 void QgsSearchQueryBuilder::btnNot_clicked()
362 mTxtSql->insertText( QStringLiteral(
" NOT " ) );
365 void QgsSearchQueryBuilder::btnOr_clicked()
367 mTxtSql->insertText( QStringLiteral(
" OR " ) );
370 void QgsSearchQueryBuilder::btnClear_clicked()
375 void QgsSearchQueryBuilder::btnILike_clicked()
377 mTxtSql->insertText( QStringLiteral(
" ILIKE " ) );
383 QString lastQueryFileDir = s.value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
385 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, tr(
"Query files (*.qqf *.QQF)" ) );
386 if ( saveFileName.isNull() )
391 if ( !saveFileName.endsWith( QLatin1String(
".qqf" ), Qt::CaseInsensitive ) )
393 saveFileName += QLatin1String(
".qqf" );
396 QFile saveFile( saveFileName );
397 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
399 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
404 QDomElement queryElem = xmlDoc.createElement( QStringLiteral(
"Query" ) );
405 QDomText queryTextNode = xmlDoc.createTextNode( mTxtSql->text() );
406 queryElem.appendChild( queryTextNode );
407 xmlDoc.appendChild( queryElem );
409 QTextStream fileStream( &saveFile );
410 xmlDoc.save( fileStream, 2 );
412 QFileInfo fi( saveFile );
413 s.setValue( QStringLiteral(
"/UI/lastQueryFileDir" ), fi.absolutePath() );
419 QString lastQueryFileDir = s.value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
421 QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf *.QQF);;" + tr(
"All files" ) +
" (*)" );
422 if ( queryFileName.isNull() )
427 QFile queryFile( queryFileName );
428 if ( !queryFile.open( QIODevice::ReadOnly ) )
430 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
433 QDomDocument queryDoc;
434 if ( !queryDoc.setContent( &queryFile ) )
436 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
440 QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral(
"Query" ) );
441 if ( queryElem.isNull() )
443 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
447 QString query = queryElem.text();
457 QString newQueryText = query;
462 QStringList attributes = searchTree->referencedColumns();
463 QMap< QString, QString> attributesToReplace;
464 QStringList existingAttributes;
467 QMap<QString, int>::const_iterator fieldIt = mFieldMap.constBegin();
468 for ( ; fieldIt != mFieldMap.constEnd(); ++fieldIt )
470 existingAttributes.push_back( fieldIt.key() );
474 QStringList::const_iterator attIt = attributes.constBegin();
475 for ( ; attIt != attributes.constEnd(); ++attIt )
478 if ( !mFieldMap.contains( attIt ) )
481 QString replaceAttribute = QInputDialog::getItem( 0, tr(
"Select Attribute" ), tr(
"There is no attribute '%1' in the current vector layer. Please select an existing attribute." ).arg( *attIt ),
482 existingAttributes, 0,
false, &ok );
483 if ( !ok || replaceAttribute.isEmpty() )
487 attributesToReplace.insert( *attIt, replaceAttribute );
492 QList<QgsSearchTreeNode *> columnRefList = searchTree->columnRefNodes();
493 QList<QgsSearchTreeNode *>::iterator columnIt = columnRefList.begin();
494 for ( ; columnIt != columnRefList.end(); ++columnIt )
496 QMap< QString, QString>::const_iterator replaceIt = attributesToReplace.find( ( *columnIt )->columnRef() );
497 if ( replaceIt != attributesToReplace.constEnd() )
499 ( *columnIt )->setColumnRef( replaceIt.value() );
503 if ( attributesToReplace.size() > 0 )
505 newQueryText = query;
510 mTxtSql->insertText( newQueryText );
513 void QgsSearchQueryBuilder::showHelp()
515 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#query-builder" ) );
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").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString parserErrorString() const
Returns parser error.
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
int count() const
Returns number of items.
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.
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QList< int > QgsAttributeList