25 #include <QDomDocument>
26 #include <QDomElement>
27 #include <QFileDialog>
28 #include <QInputDialog>
30 #include <QMessageBox>
32 #include <QPushButton>
33 #include <QTextStream>
39 QWidget *parent, Qt::WindowFlags fl )
41 , mPreviousFieldRow( -1 )
46 connect( btnEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnEqual_clicked );
47 connect( btnLessThan, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLessThan_clicked );
48 connect( btnGreaterThan, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGreaterThan_clicked );
49 connect( btnPct, &QPushButton::clicked,
this, &QgsQueryBuilder::btnPct_clicked );
50 connect( btnIn, &QPushButton::clicked,
this, &QgsQueryBuilder::btnIn_clicked );
51 connect( btnNotIn, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNotIn_clicked );
52 connect( btnLike, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLike_clicked );
53 connect( btnILike, &QPushButton::clicked,
this, &QgsQueryBuilder::btnILike_clicked );
54 connect( lstFields, &QListView::clicked,
this, &QgsQueryBuilder::lstFields_clicked );
55 connect( lstFields, &QListView::doubleClicked,
this, &QgsQueryBuilder::lstFields_doubleClicked );
56 connect( lstValues, &QListView::doubleClicked,
this, &QgsQueryBuilder::lstValues_doubleClicked );
57 connect( btnLessEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLessEqual_clicked );
58 connect( btnGreaterEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGreaterEqual_clicked );
59 connect( btnNotEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNotEqual_clicked );
60 connect( btnAnd, &QPushButton::clicked,
this, &QgsQueryBuilder::btnAnd_clicked );
61 connect( btnNot, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNot_clicked );
62 connect( btnOr, &QPushButton::clicked,
this, &QgsQueryBuilder::btnOr_clicked );
63 connect( btnGetAllValues, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGetAllValues_clicked );
64 connect( btnSampleValues, &QPushButton::clicked,
this, &QgsQueryBuilder::btnSampleValues_clicked );
65 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsQueryBuilder::showHelp );
67 QPushButton *pbn =
new QPushButton( tr(
"&Test" ) );
68 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
71 pbn =
new QPushButton( tr(
"&Clear" ) );
72 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
75 pbn =
new QPushButton( tr(
"&Save…" ) );
76 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
77 pbn->setToolTip( tr(
"Save query to QQF file" ) );
80 pbn =
new QPushButton( tr(
"&Load…" ) );
81 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
82 pbn->setToolTip( tr(
"Load query from QQF file" ) );
89 layerSubsetStringChanged();
91 lblDataUri->setText( tr(
"Set provider filter on %1" ).arg( layer->
name() ) );
92 mTxtSql->setText( mOrigSubsetString );
94 mFilterLineEdit->setShowSearchIcon(
true );
95 mFilterLineEdit->setPlaceholderText( tr(
"Search…" ) );
96 connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged,
this, &QgsQueryBuilder::onTextChanged );
104 QDialog::showEvent( event );
107 void QgsQueryBuilder::populateFields()
110 mTxtSql->setFields( fields );
111 for (
int idx = 0; idx < fields.
count(); ++idx )
119 myItem->setData( idx );
120 myItem->setEditable(
false );
121 mModelFields->insertRow( mModelFields->rowCount(), myItem );
125 setupLstFieldsModel();
128 void QgsQueryBuilder::setupLstFieldsModel()
130 lstFields->setModel( mModelFields );
133 void QgsQueryBuilder::setupGuiViews()
136 mModelFields =
new QStandardItemModel();
137 mModelValues =
new QStandardItemModel();
138 mProxyValues =
new QSortFilterProxyModel();
139 mProxyValues->setSourceModel( mModelValues );
141 lstFields->setViewMode( QListView::ListMode );
142 lstValues->setViewMode( QListView::ListMode );
143 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
144 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
146 lstFields->setUniformItemSizes(
true );
147 lstValues->setUniformItemSizes(
true );
149 lstFields->setAlternatingRowColors(
true );
150 lstValues->setAlternatingRowColors(
true );
151 lstValues->setModel( mProxyValues );
154 void QgsQueryBuilder::fillValues(
int idx,
int limit )
157 mModelValues->clear();
160 QList<QVariant> values = qgis::setToList( mLayer->
uniqueValues( idx, limit ) );
161 std::sort( values.begin(), values.end() );
165 QgsDebugMsg( QStringLiteral(
"nullValue: %1" ).arg( nullValue ) );
167 const auto constValues = values;
168 for (
const QVariant &var : constValues )
173 else if ( var.type() == QVariant::Date && mLayer->
providerType() == QLatin1String(
"ogr" ) && mLayer->
storageType() == QLatin1String(
"ESRI Shapefile" ) )
174 value = var.toDate().toString( QStringLiteral(
"yyyy/MM/dd" ) );
175 else if ( var.type() == QVariant::List || var.type() == QVariant::StringList )
177 const QVariantList list = var.toList();
178 for (
const QVariant &val : list )
180 if ( !value.isEmpty() )
181 value.append(
", " );
182 value.append( val.isNull() ? nullValue : val.toString() );
186 value = var.toString();
188 QStandardItem *myItem =
new QStandardItem( value );
189 myItem->setEditable(
false );
190 myItem->setData( var, Qt::UserRole + 1 );
191 mModelValues->insertRow( mModelValues->rowCount(), myItem );
192 QgsDebugMsg( QStringLiteral(
"Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
196 void QgsQueryBuilder::btnSampleValues_clicked()
198 lstValues->setCursor( Qt::WaitCursor );
200 const QString prevSubsetString = mLayer->
subsetString();
201 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
203 mIgnoreLayerSubsetStringChangedSignal =
true;
208 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
213 mIgnoreLayerSubsetStringChangedSignal =
false;
216 lstValues->setCursor( Qt::ArrowCursor );
219 void QgsQueryBuilder::btnGetAllValues_clicked()
221 lstValues->setCursor( Qt::WaitCursor );
223 const QString prevSubsetString = mLayer->
subsetString();
224 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
226 mIgnoreLayerSubsetStringChangedSignal =
true;
231 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
236 mIgnoreLayerSubsetStringChangedSignal =
false;
239 lstValues->setCursor( Qt::ArrowCursor );
250 const long long featureCount { mLayer->
featureCount() };
252 if ( featureCount < 0 )
254 QMessageBox::warning(
this,
255 tr(
"Query Result" ),
256 tr(
"An error occurred when executing the query, please check the expression syntax." ) );
260 QMessageBox::information(
this,
261 tr(
"Query Result" ),
262 tr(
"The where clause returned %n row(s).",
"returned test rows", featureCount ) );
267 QMessageBox::warning(
this,
268 tr(
"Query Result" ),
269 tr(
"An error occurred when executing the query." )
270 + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->
dataProvider()->
errors().join( QLatin1Char(
'\n' ) ) ) );
275 QMessageBox::warning(
this,
276 tr(
"Query Result" ),
277 tr(
"An error occurred when executing the query." ) );
283 if ( mTxtSql->text() != mOrigSubsetString )
290 QMessageBox::warning(
this,
291 tr(
"Query Result" ),
292 tr(
"An error occurred when executing the query." )
293 + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->
dataProvider()->
errors().join( QLatin1Char(
'\n' ) ) ) );
298 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"Error in query. The subset string could not be set." ) );
316 void QgsQueryBuilder::btnEqual_clicked()
318 mTxtSql->insertText( QStringLiteral(
" = " ) );
322 void QgsQueryBuilder::btnLessThan_clicked()
324 mTxtSql->insertText( QStringLiteral(
" < " ) );
328 void QgsQueryBuilder::btnGreaterThan_clicked()
330 mTxtSql->insertText( QStringLiteral(
" > " ) );
334 void QgsQueryBuilder::btnPct_clicked()
336 mTxtSql->insertText( QStringLiteral(
"%" ) );
340 void QgsQueryBuilder::btnIn_clicked()
342 mTxtSql->insertText( QStringLiteral(
" IN " ) );
346 void QgsQueryBuilder::btnNotIn_clicked()
348 mTxtSql->insertText( QStringLiteral(
" NOT IN " ) );
352 void QgsQueryBuilder::btnLike_clicked()
354 mTxtSql->insertText( QStringLiteral(
" LIKE " ) );
360 return mTxtSql->text();
365 mTxtSql->setText( sqlStatement );
368 void QgsQueryBuilder::lstFields_clicked(
const QModelIndex &index )
370 if ( mPreviousFieldRow != index.row() )
372 mPreviousFieldRow = index.row();
374 btnSampleValues->setEnabled(
true );
375 btnGetAllValues->setEnabled(
true );
377 mModelValues->clear();
378 mFilterLineEdit->clear();
382 void QgsQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
384 mTxtSql->insertText(
'\"' + mLayer->
fields().
at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).
name() +
'\"' );
388 void QgsQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
390 const QVariant value = index.data( Qt::UserRole + 1 );
391 if ( value.isNull() )
392 mTxtSql->insertText( QStringLiteral(
"NULL" ) );
393 else if ( value.type() == QVariant::Date && mLayer->
providerType() == QLatin1String(
"ogr" ) && mLayer->
storageType() == QLatin1String(
"ESRI Shapefile" ) )
394 mTxtSql->insertText(
'\'' + value.toDate().toString( QStringLiteral(
"yyyy/MM/dd" ) ) +
'\'' );
395 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
396 mTxtSql->insertText( value.toString() );
398 mTxtSql->insertText(
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
403 void QgsQueryBuilder::btnLessEqual_clicked()
405 mTxtSql->insertText( QStringLiteral(
" <= " ) );
409 void QgsQueryBuilder::btnGreaterEqual_clicked()
411 mTxtSql->insertText( QStringLiteral(
" >= " ) );
415 void QgsQueryBuilder::btnNotEqual_clicked()
417 mTxtSql->insertText( QStringLiteral(
" != " ) );
421 void QgsQueryBuilder::btnAnd_clicked()
423 mTxtSql->insertText( QStringLiteral(
" AND " ) );
427 void QgsQueryBuilder::btnNot_clicked()
429 mTxtSql->insertText( QStringLiteral(
" NOT " ) );
433 void QgsQueryBuilder::btnOr_clicked()
435 mTxtSql->insertText( QStringLiteral(
" OR " ) );
439 void QgsQueryBuilder::onTextChanged(
const QString &text )
441 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
442 mProxyValues->setFilterWildcard( text );
451 void QgsQueryBuilder::btnILike_clicked()
453 mTxtSql->insertText( QStringLiteral(
" ILIKE " ) );
459 lblDataUri->setText( uri );
462 void QgsQueryBuilder::showHelp()
464 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#query-builder" ) );
476 const QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
478 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, tr(
"Query files (*.qqf *.QQF)" ) );
479 if ( saveFileName.isNull() )
484 if ( !saveFileName.endsWith( QLatin1String(
".qqf" ), Qt::CaseInsensitive ) )
486 saveFileName += QLatin1String(
".qqf" );
489 QFile saveFile( saveFileName );
490 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
492 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
497 QDomElement queryElem = xmlDoc.createElement( QStringLiteral(
"Query" ) );
498 const QDomText queryTextNode = xmlDoc.createTextNode( subset );
499 queryElem.appendChild( queryTextNode );
500 xmlDoc.appendChild( queryElem );
502 QTextStream fileStream( &saveFile );
503 xmlDoc.save( fileStream, 2 );
505 const QFileInfo fi( saveFile );
506 s.
setValue( QStringLiteral(
"/UI/lastQueryFileDir" ), fi.absolutePath() );
516 mTxtSql->insertText( subset );
523 const QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
525 const QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf);;" + tr(
"All files" ) +
" (*)" );
526 if ( queryFileName.isNull() )
531 QFile queryFile( queryFileName );
532 if ( !queryFile.open( QIODevice::ReadOnly ) )
534 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
537 QDomDocument queryDoc;
538 if ( !queryDoc.setContent( &queryFile ) )
540 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
544 const QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral(
"Query" ) );
545 if ( queryElem.isNull() )
547 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
551 subset = queryElem.text();
555 void QgsQueryBuilder::layerSubsetStringChanged()
557 if ( mIgnoreLayerSubsetStringChangedSignal )
559 mUseUnfilteredLayer->setDisabled( mLayer->
subsetString().isEmpty() );