16 #include <QDomDocument> 17 #include <QDomElement> 18 #include <QFileDialog> 20 #include <QInputDialog> 22 #include <QMessageBox> 23 #include <QStandardItem> 24 #include <QTextStream> 31 #include "qgsexpression.h" 37 QWidget *parent, Qt::WindowFlags fl )
38 : QDialog( parent, fl )
42 connect( btnEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnEqual_clicked );
43 connect( btnLessThan, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLessThan_clicked );
44 connect( btnGreaterThan, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGreaterThan_clicked );
45 connect( btnLike, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLike_clicked );
46 connect( btnILike, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnILike_clicked );
47 connect( btnPct, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnPct_clicked );
48 connect( btnIn, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnIn_clicked );
49 connect( btnNotIn, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNotIn_clicked );
50 connect( lstFields, &QListView::doubleClicked,
this, &QgsSearchQueryBuilder::lstFields_doubleClicked );
51 connect( lstValues, &QListView::doubleClicked,
this, &QgsSearchQueryBuilder::lstValues_doubleClicked );
52 connect( btnLessEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnLessEqual_clicked );
53 connect( btnGreaterEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGreaterEqual_clicked );
54 connect( btnNotEqual, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNotEqual_clicked );
55 connect( btnAnd, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnAnd_clicked );
56 connect( btnNot, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnNot_clicked );
57 connect( btnOr, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnOr_clicked );
58 connect( btnGetAllValues, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnGetAllValues_clicked );
59 connect( btnSampleValues, &QPushButton::clicked,
this, &QgsSearchQueryBuilder::btnSampleValues_clicked );
61 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsSearchQueryBuilder::showHelp );
63 setWindowTitle( tr(
"Search Query Builder" ) );
65 QPushButton *pbn =
new QPushButton( tr(
"&Test" ) );
66 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
67 connect( pbn, &QAbstractButton::clicked,
this, &QgsSearchQueryBuilder::btnTest_clicked );
69 pbn =
new QPushButton( tr(
"&Clear" ) );
70 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
71 connect( pbn, &QAbstractButton::clicked,
this, &QgsSearchQueryBuilder::btnClear_clicked );
73 pbn =
new QPushButton( tr(
"&Save…" ) );
74 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
75 pbn->setToolTip( tr(
"Save query to an xml file" ) );
78 pbn =
new QPushButton( tr(
"&Load…" ) );
79 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
80 pbn->setToolTip( tr(
"Load query from xml file" ) );
84 lblDataUri->setText( layer->
name() );
88 void QgsSearchQueryBuilder::populateFields()
94 for (
int idx = 0; idx < fields.
count(); ++idx )
96 QString fieldName = fields.
at( idx ).
name();
97 mFieldMap[fieldName] = idx;
98 QStandardItem *myItem =
new QStandardItem( fieldName );
99 myItem->setEditable(
false );
100 mModelFields->insertRow( mModelFields->rowCount(), myItem );
104 void QgsSearchQueryBuilder::setupListViews()
107 mModelFields =
new QStandardItemModel();
108 mModelValues =
new QStandardItemModel();
109 lstFields->setModel( mModelFields );
110 lstValues->setModel( mModelValues );
112 lstFields->setViewMode( QListView::ListMode );
113 lstValues->setViewMode( QListView::ListMode );
114 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
115 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
117 lstFields->setUniformItemSizes(
true );
118 lstValues->setUniformItemSizes(
true );
121 void QgsSearchQueryBuilder::getFieldValues(
int limit )
128 mModelValues->clear();
131 QString fieldName = mModelFields->data( lstFields->currentIndex() ).toString();
132 int fieldIndex = mFieldMap[fieldName];
134 bool numeric = ( field.
type() == QVariant::Int || field.
type() == QVariant::Double );
140 attrs.append( fieldIndex );
144 lstValues->setCursor( Qt::WaitCursor );
146 mModelValues->blockSignals(
true );
147 lstValues->setUpdatesEnabled(
false );
150 QSet<QString> insertedValues;
153 ( limit == 0 || mModelValues->rowCount() != limit ) )
155 value = feat.
attribute( fieldIndex ).toString();
160 value =
'\'' + value.replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
164 if ( !insertedValues.contains( value ) )
166 QStandardItem *myItem =
new QStandardItem( value );
167 myItem->setEditable(
false );
168 mModelValues->insertRow( mModelValues->rowCount(), myItem );
169 insertedValues.insert( value );
173 mModelValues->blockSignals(
false );
174 lstValues->setUpdatesEnabled(
true );
176 mModelValues->sort( 0 );
177 lstValues->setCursor( Qt::ArrowCursor );
180 void QgsSearchQueryBuilder::btnSampleValues_clicked()
182 getFieldValues( 25 );
185 void QgsSearchQueryBuilder::btnGetAllValues_clicked()
190 void QgsSearchQueryBuilder::btnTest_clicked()
192 long count = countRecords( txtSQL->text() );
198 QMessageBox::information(
this, tr(
"Test Query" ), tr(
"Found %n matching feature(s).",
"test result", count ) );
202 long QgsSearchQueryBuilder::countRecords(
const QString &
searchString )
204 QgsExpression search( searchString );
205 if ( search.hasParserError() )
207 QMessageBox::critical(
this, tr(
"Query Result" ), search.parserErrorString() );
214 bool fetchGeom = search.needsGeometry();
221 if ( !search.prepare( &context ) )
223 QMessageBox::critical(
this, tr(
"Query Result" ), search.evalErrorString() );
227 QApplication::setOverrideCursor( Qt::WaitCursor );
234 QVariant value = search.evaluate( &context );
235 if ( value.toInt() != 0 )
241 if ( search.hasEvalError() )
245 QApplication::restoreOverrideCursor();
247 if ( search.hasEvalError() )
249 QMessageBox::critical(
this, tr(
"Query Result" ), search.evalErrorString() );
257 void QgsSearchQueryBuilder::btnOk_clicked()
260 if ( txtSQL->text().trimmed().length() > 0 )
267 long numRecs = countRecords( txtSQL->text() );
272 else if ( numRecs == 0 )
274 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"The query you specified results in zero records being returned." ) );
283 void QgsSearchQueryBuilder::btnEqual_clicked()
285 txtSQL->insertText( QStringLiteral(
" = " ) );
288 void QgsSearchQueryBuilder::btnLessThan_clicked()
290 txtSQL->insertText( QStringLiteral(
" < " ) );
293 void QgsSearchQueryBuilder::btnGreaterThan_clicked()
295 txtSQL->insertText( QStringLiteral(
" > " ) );
298 void QgsSearchQueryBuilder::btnPct_clicked()
300 txtSQL->insertText( QStringLiteral(
"%" ) );
303 void QgsSearchQueryBuilder::btnIn_clicked()
305 txtSQL->insertText( QStringLiteral(
" IN " ) );
308 void QgsSearchQueryBuilder::btnNotIn_clicked()
310 txtSQL->insertText( QStringLiteral(
" NOT IN " ) );
313 void QgsSearchQueryBuilder::btnLike_clicked()
315 txtSQL->insertText( QStringLiteral(
" LIKE " ) );
320 return txtSQL->text();
325 txtSQL->setText( searchString );
328 void QgsSearchQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
330 txtSQL->insertText( QgsExpression::quotedColumnRef( mModelFields->data( index ).toString() ) );
333 void QgsSearchQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
335 txtSQL->insertText( mModelValues->data( index ).toString() );
338 void QgsSearchQueryBuilder::btnLessEqual_clicked()
340 txtSQL->insertText( QStringLiteral(
" <= " ) );
343 void QgsSearchQueryBuilder::btnGreaterEqual_clicked()
345 txtSQL->insertText( QStringLiteral(
" >= " ) );
348 void QgsSearchQueryBuilder::btnNotEqual_clicked()
350 txtSQL->insertText( QStringLiteral(
" != " ) );
353 void QgsSearchQueryBuilder::btnAnd_clicked()
355 txtSQL->insertText( QStringLiteral(
" AND " ) );
358 void QgsSearchQueryBuilder::btnNot_clicked()
360 txtSQL->insertText( QStringLiteral(
" NOT " ) );
363 void QgsSearchQueryBuilder::btnOr_clicked()
365 txtSQL->insertText( QStringLiteral(
" OR " ) );
368 void QgsSearchQueryBuilder::btnClear_clicked()
373 void QgsSearchQueryBuilder::btnILike_clicked()
375 txtSQL->insertText( QStringLiteral(
" ILIKE " ) );
381 QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
383 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, QStringLiteral(
"*.qqf" ) );
384 if ( saveFileName.isNull() )
389 if ( !saveFileName.endsWith( QLatin1String(
".qqf" ), Qt::CaseInsensitive ) )
391 saveFileName += QLatin1String(
".qqf" );
394 QFile saveFile( saveFileName );
395 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
397 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
402 QDomElement queryElem = xmlDoc.createElement( QStringLiteral(
"Query" ) );
403 QDomText queryTextNode = xmlDoc.createTextNode( txtSQL->text() );
404 queryElem.appendChild( queryTextNode );
405 xmlDoc.appendChild( queryElem );
407 QTextStream fileStream( &saveFile );
408 xmlDoc.save( fileStream, 2 );
410 QFileInfo fi( saveFile );
411 s.
setValue( QStringLiteral(
"/UI/lastQueryFileDir" ), fi.absolutePath() );
417 QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
419 QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf);;" + tr(
"All files" ) +
" (*)" );
420 if ( queryFileName.isNull() )
425 QFile queryFile( queryFileName );
426 if ( !queryFile.open( QIODevice::ReadOnly ) )
428 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
431 QDomDocument queryDoc;
432 if ( !queryDoc.setContent( &queryFile ) )
434 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
438 QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral(
"Query" ) );
439 if ( queryElem.isNull() )
441 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
445 QString query = queryElem.text();
448 QgsExpression search( query );
449 if ( search.hasParserError() )
451 QMessageBox::critical(
this, tr(
"Query Result" ), search.parserErrorString() );
455 QString newQueryText = query;
460 QStringList attributes = searchTree->referencedColumns();
461 QMap< QString, QString> attributesToReplace;
462 QStringList existingAttributes;
465 QMap<QString, int>::const_iterator fieldIt = mFieldMap.constBegin();
466 for ( ; fieldIt != mFieldMap.constEnd(); ++fieldIt )
468 existingAttributes.push_back( fieldIt.key() );
472 QStringList::const_iterator attIt = attributes.constBegin();
473 for ( ; attIt != attributes.constEnd(); ++attIt )
476 if ( !mFieldMap.contains( attIt ) )
479 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 ),
480 existingAttributes, 0,
false, &ok );
481 if ( !ok || replaceAttribute.isEmpty() )
485 attributesToReplace.insert( *attIt, replaceAttribute );
490 QList<QgsSearchTreeNode *> columnRefList = searchTree->columnRefNodes();
491 QList<QgsSearchTreeNode *>::iterator columnIt = columnRefList.begin();
492 for ( ; columnIt != columnRefList.end(); ++columnIt )
494 QMap< QString, QString>::const_iterator replaceIt = attributesToReplace.find( ( *columnIt )->columnRef() );
495 if ( replaceIt != attributesToReplace.constEnd() )
497 ( *columnIt )->setColumnRef( replaceIt.value() );
501 if ( attributesToReplace.size() > 0 )
503 newQueryText = query;
508 txtSQL->insertText( newQueryText );
511 void QgsSearchQueryBuilder::showHelp()
513 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#query-builder" ) );
Wrapper for iterator of features from vector data provider or vector layer.
This class is a composition of two QSettings instances:
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setSearchString(const QString &searchString)
change search string shown in text field
Container of fields for a vector layer.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
int count() const
Return number of items.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsFields fields() const override
Returns the list of fields of this layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Encapsulate a field in an attribute table or data source.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
QgsSearchQueryBuilder(QgsVectorLayer *layer, QWidget *parent SIP_TRANSFERTHIS=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor - takes pointer to vector layer as a parameter.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
QList< int > QgsAttributeList
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QString searchString()
returns newly created search string