27#include <QDomDocument>
30#include <QInputDialog>
37#include "moc_qgsquerybuilder.cpp"
39using namespace Qt::StringLiterals;
49 connect( btnEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnEqual_clicked );
50 connect( btnLessThan, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLessThan_clicked );
51 connect( btnGreaterThan, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGreaterThan_clicked );
52 connect( btnPct, &QPushButton::clicked,
this, &QgsQueryBuilder::btnPct_clicked );
53 connect( btnIn, &QPushButton::clicked,
this, &QgsQueryBuilder::btnIn_clicked );
54 connect( btnNotIn, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNotIn_clicked );
55 connect( btnLike, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLike_clicked );
56 connect( btnILike, &QPushButton::clicked,
this, &QgsQueryBuilder::btnILike_clicked );
57 connect( lstFields, &QListView::clicked,
this, &QgsQueryBuilder::lstFields_clicked );
58 connect( lstFields, &QListView::doubleClicked,
this, &QgsQueryBuilder::lstFields_doubleClicked );
59 connect( lstValues, &QListView::doubleClicked,
this, &QgsQueryBuilder::lstValues_doubleClicked );
60 connect( btnLessEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnLessEqual_clicked );
61 connect( btnGreaterEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGreaterEqual_clicked );
62 connect( btnNotEqual, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNotEqual_clicked );
63 connect( btnAnd, &QPushButton::clicked,
this, &QgsQueryBuilder::btnAnd_clicked );
64 connect( btnNot, &QPushButton::clicked,
this, &QgsQueryBuilder::btnNot_clicked );
65 connect( btnOr, &QPushButton::clicked,
this, &QgsQueryBuilder::btnOr_clicked );
66 connect( btnGetAllValues, &QPushButton::clicked,
this, &QgsQueryBuilder::btnGetAllValues_clicked );
67 connect( btnSampleValues, &QPushButton::clicked,
this, &QgsQueryBuilder::btnSampleValues_clicked );
68 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsQueryBuilder::showHelp );
70 QPushButton *pbn =
new QPushButton( tr(
"&Test" ) );
71 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
74 pbn =
new QPushButton( tr(
"&Clear" ) );
75 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
78 pbn =
new QPushButton( tr(
"&Save…" ) );
79 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
80 pbn->setToolTip( tr(
"Save query to QQF file" ) );
83 pbn =
new QPushButton( tr(
"&Load…" ) );
84 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
85 pbn->setToolTip( tr(
"Load query from QQF file" ) );
92 mModelFields->sourceFieldModel()->setLayer( layer );
93 lstFields->setModel( mModelFields );
97 layerSubsetStringChanged();
99 QString subsetStringDialect;
100 QString subsetStringHelpUrl;
104 lblDataUri->setText( tr(
"Set provider filter on %1 (provider: %2)" ).arg( layer->
name(), provider->name() ) );
105 subsetStringDialect = provider->subsetStringDialect();
106 subsetStringHelpUrl = provider->subsetStringHelpUrl();
110 lblDataUri->setText( tr(
"Set provider filter on %1 (provider: %2)" ).arg( layer->
name(), layer->
providerType() ) );
113 if ( !subsetStringDialect.isEmpty() && !subsetStringHelpUrl.isEmpty() )
115 lblProviderFilterInfo->setOpenExternalLinks(
true );
116 lblProviderFilterInfo->setText( tr(
"Enter a <a href=\"%1\">%2</a> to filter the layer" ).arg( subsetStringHelpUrl ).arg( subsetStringDialect ) );
118 else if ( !subsetStringDialect.isEmpty() )
120 lblProviderFilterInfo->setText( tr(
"Enter a %1 to filter the layer" ).arg( subsetStringDialect ) );
124 lblProviderFilterInfo->hide();
127 mTxtSql->setText( mOrigSubsetString );
129 mFilterLineEdit->setShowSearchIcon(
true );
130 mFilterLineEdit->setPlaceholderText( tr(
"Search…" ) );
131 connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged,
this, &QgsQueryBuilder::onTextChanged );
137 QDialog::showEvent( event );
140void QgsQueryBuilder::setupGuiViews()
143 mModelValues =
new QStandardItemModel();
144 mProxyValues =
new QSortFilterProxyModel();
145 mProxyValues->setSourceModel( mModelValues );
147 lstFields->setViewMode( QListView::ListMode );
148 lstValues->setViewMode( QListView::ListMode );
149 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
150 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
152 lstFields->setUniformItemSizes(
true );
153 lstValues->setUniformItemSizes(
true );
155 lstFields->setAlternatingRowColors(
true );
156 lstValues->setAlternatingRowColors(
true );
157 lstValues->setModel( mProxyValues );
160void QgsQueryBuilder::fillValues(
const QString &field,
int limit )
163 mModelValues->clear();
165 const int fieldIndex = mLayer->fields().lookupField( field );
168 QList<QVariant> values = qgis::setToList( mLayer->uniqueValues( fieldIndex, limit ) );
169 std::sort( values.begin(), values.end() );
175 const auto constValues = values;
176 for (
const QVariant &var : constValues )
181 else if ( var.userType() == QMetaType::Type::QDate && mLayer->providerType() ==
"ogr"_L1 && mLayer->storageType() ==
"ESRI Shapefile"_L1 )
182 value = var.toDate().toString( u
"yyyy/MM/dd"_s );
183 else if ( var.userType() == QMetaType::Type::QVariantList || var.userType() == QMetaType::Type::QStringList )
185 const QVariantList list = var.toList();
186 for (
const QVariant &val : list )
188 if ( !value.isEmpty() )
189 value.append(
", " );
194 value = var.toString();
196 QStandardItem *myItem =
new QStandardItem( value );
197 myItem->setEditable(
false );
198 myItem->setData( var, Qt::UserRole + 1 );
199 mModelValues->insertRow( mModelValues->rowCount(), myItem );
204void QgsQueryBuilder::btnSampleValues_clicked()
206 lstValues->setCursor( Qt::WaitCursor );
208 const QString prevSubsetString = mLayer->subsetString();
209 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
211 mIgnoreLayerSubsetStringChangedSignal =
true;
212 mLayer->setSubsetString( QString() );
218 if ( prevSubsetString != mLayer->subsetString() )
220 mLayer->setSubsetString( prevSubsetString );
221 mIgnoreLayerSubsetStringChangedSignal =
false;
224 lstValues->setCursor( Qt::ArrowCursor );
227void QgsQueryBuilder::btnGetAllValues_clicked()
229 lstValues->setCursor( Qt::WaitCursor );
231 const QString prevSubsetString = mLayer->subsetString();
232 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
234 mIgnoreLayerSubsetStringChangedSignal =
true;
235 mLayer->setSubsetString( QString() );
241 if ( prevSubsetString != mLayer->subsetString() )
243 mLayer->setSubsetString( prevSubsetString );
244 mIgnoreLayerSubsetStringChangedSignal =
false;
247 lstValues->setCursor( Qt::ArrowCursor );
256 if ( mLayer->setSubsetString( mTxtSql->text() ) )
258 const long long featureCount { mLayer->featureCount() };
260 if ( featureCount < 0 )
262 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"An error occurred when executing the query, please check the expression syntax." ) );
266 QMessageBox::information(
this, tr(
"Query Result" ), tr(
"The where clause returned %n row(s).",
"returned test rows", featureCount ) );
269 else if ( mLayer->dataProvider()->hasErrors() )
272 warning(
this, tr(
"Query Result" ), tr(
"An error occurred when executing the query." ) + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char(
'\n' ) ) ) );
273 mLayer->dataProvider()->clearErrors();
277 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"An error occurred when executing the query." ) );
283 if ( mTxtSql->text() != mOrigSubsetString )
285 if ( !mLayer->setSubsetString( mTxtSql->text() ) )
288 if ( mLayer->dataProvider()->hasErrors() )
291 warning(
this, tr(
"Query Result" ), tr(
"An error occurred when executing the query." ) + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char(
'\n' ) ) ) );
292 mLayer->dataProvider()->clearErrors();
296 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"Error in query. The subset string could not be set." ) );
308 if ( mLayer->subsetString() != mOrigSubsetString )
309 mLayer->setSubsetString( mOrigSubsetString );
314void QgsQueryBuilder::btnEqual_clicked()
316 mTxtSql->insertText( u
" = "_s );
320void QgsQueryBuilder::btnLessThan_clicked()
322 mTxtSql->insertText( u
" < "_s );
326void QgsQueryBuilder::btnGreaterThan_clicked()
328 mTxtSql->insertText( u
" > "_s );
332void QgsQueryBuilder::btnPct_clicked()
334 mTxtSql->insertText( u
"%"_s );
338void QgsQueryBuilder::btnIn_clicked()
340 mTxtSql->insertText( u
" IN "_s );
344void QgsQueryBuilder::btnNotIn_clicked()
346 mTxtSql->insertText( u
" NOT IN "_s );
350void QgsQueryBuilder::btnLike_clicked()
352 mTxtSql->insertText( u
" LIKE "_s );
358 return mTxtSql->text();
363 mTxtSql->setText( sqlStatement );
366void QgsQueryBuilder::lstFields_clicked(
const QModelIndex &index )
368 if ( mPreviousFieldRow != index.row() )
370 mPreviousFieldRow = index.row();
372 btnSampleValues->setEnabled(
true );
373 btnGetAllValues->setEnabled(
true );
375 mModelValues->clear();
376 mFilterLineEdit->clear();
380void QgsQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
386void QgsQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
388 const QVariant value = index.data( Qt::UserRole + 1 );
390 mTxtSql->insertText( u
"NULL"_s );
391 else if ( value.userType() == QMetaType::Type::QDate && mLayer->providerType() ==
"ogr"_L1 && mLayer->storageType() ==
"ESRI Shapefile"_L1 )
392 mTxtSql->insertText(
'\'' + value.toDate().toString( u
"yyyy/MM/dd"_s ) +
'\'' );
393 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
394 mTxtSql->insertText( value.toString() );
396 mTxtSql->insertText(
'\'' + value.toString().replace(
'\'',
"''"_L1 ) +
'\'' );
401void QgsQueryBuilder::btnLessEqual_clicked()
403 mTxtSql->insertText( u
" <= "_s );
407void QgsQueryBuilder::btnGreaterEqual_clicked()
409 mTxtSql->insertText( u
" >= "_s );
413void QgsQueryBuilder::btnNotEqual_clicked()
415 mTxtSql->insertText( u
" != "_s );
419void QgsQueryBuilder::btnAnd_clicked()
421 mTxtSql->insertText( u
" AND "_s );
425void QgsQueryBuilder::btnNot_clicked()
427 mTxtSql->insertText( u
" NOT "_s );
431void QgsQueryBuilder::btnOr_clicked()
433 mTxtSql->insertText( u
" OR "_s );
437void QgsQueryBuilder::onTextChanged(
const QString &text )
439 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
440 mProxyValues->setFilterWildcard( text );
446 mLayer->setSubsetString( QString() );
449void QgsQueryBuilder::btnILike_clicked()
451 mTxtSql->insertText( u
" ILIKE "_s );
457 lblDataUri->setText( uri );
460void QgsQueryBuilder::showHelp()
462 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#query-builder"_s );
474 const QString lastQueryFileDir = s.
value( u
"/UI/lastQueryFileDir"_s, QDir::homePath() ).toString();
476 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, tr(
"Query files (*.qqf *.QQF)" ) );
477 if ( saveFileName.isNull() )
482 if ( !saveFileName.endsWith(
".qqf"_L1, Qt::CaseInsensitive ) )
484 saveFileName +=
".qqf"_L1;
487 QFile saveFile( saveFileName );
488 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
490 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
495 QDomElement queryElem = xmlDoc.createElement( u
"Query"_s );
496 const QDomText queryTextNode = xmlDoc.createTextNode( subset );
497 queryElem.appendChild( queryTextNode );
498 xmlDoc.appendChild( queryElem );
500 QTextStream fileStream( &saveFile );
501 xmlDoc.save( fileStream, 2 );
503 const QFileInfo fi( saveFile );
504 s.
setValue( u
"/UI/lastQueryFileDir"_s, fi.absolutePath() );
514 mTxtSql->insertText( subset );
521 const QString lastQueryFileDir = s.
value( u
"/UI/lastQueryFileDir"_s, QDir::homePath() ).toString();
523 const QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf);;" + tr(
"All files" ) +
" (*)" );
524 if ( queryFileName.isNull() )
529 QFile queryFile( queryFileName );
530 if ( !queryFile.open( QIODevice::ReadOnly ) )
532 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
535 QDomDocument queryDoc;
536 if ( !queryDoc.setContent( &queryFile ) )
538 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
542 const QDomElement queryElem = queryDoc.firstChildElement( u
"Query"_s );
543 if ( queryElem.isNull() )
545 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
549 subset = queryElem.text();
553void QgsQueryBuilder::layerSubsetStringChanged()
555 if ( mIgnoreLayerSubsetStringChangedSignal )
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
Abstract base class for spatial data provider implementations.
@ FieldName
Return field name if index corresponds to a field.
A proxy model to filter the list of fields of a layer.
@ AllTypes
All field types.
@ OriginProvider
Fields with a provider origin, since QGIS 3.38.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
QString providerType() const
Returns the provider type (provider key) for this layer.
static bool loadQueryFromFile(QString &subset)
Load query from the XML file.
void loadQuery()
Load query from the XML file.
void saveQuery()
Save query to the XML file.
void setDatasourceDescription(const QString &uri)
void setSql(const QString &sqlStatement)
Set the sql statement to display in the dialog.
virtual void test()
The default implementation tests that the constructed sql statement to see if the vector layer data p...
static bool saveQueryToFile(const QString &subset)
Save query to the XML file.
void showEvent(QShowEvent *event) override
QgsQueryBuilder(QgsVectorLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
This constructor is used when the query builder is called from the vector layer properties dialog.
QString sql() const
Returns the sql statement entered in the dialog.
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsSubsetStringEditorInterface(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
bool isSqlQuery() const
Returns true if the layer is a query (SQL) layer.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
#define QgsDebugMsgLevel(str, level)