16 #include <QDomDocument>
17 #include <QDomElement>
18 #include <QFileDialog>
20 #include <QInputDialog>
22 #include <QMessageBox>
23 #include <QStandardItem>
24 #include <QTextStream>
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( txtSQL->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 ( txtSQL->text().trimmed().length() > 0 )
269 long numRecs = countRecords( txtSQL->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 txtSQL->insertText( QStringLiteral(
" = " ) );
290 void QgsSearchQueryBuilder::btnLessThan_clicked()
292 txtSQL->insertText( QStringLiteral(
" < " ) );
295 void QgsSearchQueryBuilder::btnGreaterThan_clicked()
297 txtSQL->insertText( QStringLiteral(
" > " ) );
300 void QgsSearchQueryBuilder::btnPct_clicked()
302 txtSQL->insertText( QStringLiteral(
"%" ) );
305 void QgsSearchQueryBuilder::btnIn_clicked()
307 txtSQL->insertText( QStringLiteral(
" IN " ) );
310 void QgsSearchQueryBuilder::btnNotIn_clicked()
312 txtSQL->insertText( QStringLiteral(
" NOT IN " ) );
315 void QgsSearchQueryBuilder::btnLike_clicked()
317 txtSQL->insertText( QStringLiteral(
" LIKE " ) );
322 return txtSQL->text();
330 void QgsSearchQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
335 void QgsSearchQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
337 txtSQL->insertText( mModelValues->data( index ).toString() );
340 void QgsSearchQueryBuilder::btnLessEqual_clicked()
342 txtSQL->insertText( QStringLiteral(
" <= " ) );
345 void QgsSearchQueryBuilder::btnGreaterEqual_clicked()
347 txtSQL->insertText( QStringLiteral(
" >= " ) );
350 void QgsSearchQueryBuilder::btnNotEqual_clicked()
352 txtSQL->insertText( QStringLiteral(
" != " ) );
355 void QgsSearchQueryBuilder::btnAnd_clicked()
357 txtSQL->insertText( QStringLiteral(
" AND " ) );
360 void QgsSearchQueryBuilder::btnNot_clicked()
362 txtSQL->insertText( QStringLiteral(
" NOT " ) );
365 void QgsSearchQueryBuilder::btnOr_clicked()
367 txtSQL->insertText( QStringLiteral(
" OR " ) );
370 void QgsSearchQueryBuilder::btnClear_clicked()
375 void QgsSearchQueryBuilder::btnILike_clicked()
377 txtSQL->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( txtSQL->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 txtSQL->insertText( newQueryText );
513 void QgsSearchQueryBuilder::showHelp()
515 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#query-builder" ) );