18 #include "qgssettings.h"
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 );
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 );
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 %1 row(s).",
"returned test rows" ). arg( 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 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" ) );
470 QString lastQueryFileDir = s.value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
472 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, tr(
"Query files (*.qqf *.QQF)" ) );
473 if ( saveFileName.isNull() )
478 if ( !saveFileName.endsWith( QLatin1String(
".qqf" ), Qt::CaseInsensitive ) )
480 saveFileName += QLatin1String(
".qqf" );
483 QFile saveFile( saveFileName );
484 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
486 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
491 QDomElement queryElem = xmlDoc.createElement( QStringLiteral(
"Query" ) );
492 QDomText queryTextNode = xmlDoc.createTextNode( mTxtSql->text() );
493 queryElem.appendChild( queryTextNode );
494 xmlDoc.appendChild( queryElem );
496 QTextStream fileStream( &saveFile );
497 xmlDoc.save( fileStream, 2 );
499 QFileInfo fi( saveFile );
500 s.setValue( QStringLiteral(
"/UI/lastQueryFileDir" ), fi.absolutePath() );
506 QString lastQueryFileDir = s.value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
508 QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf);;" + tr(
"All files" ) +
" (*)" );
509 if ( queryFileName.isNull() )
514 QFile queryFile( queryFileName );
515 if ( !queryFile.open( QIODevice::ReadOnly ) )
517 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
520 QDomDocument queryDoc;
521 if ( !queryDoc.setContent( &queryFile ) )
523 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
527 QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral(
"Query" ) );
528 if ( queryElem.isNull() )
530 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
534 QString query = queryElem.text();
545 mTxtSql->insertText( query );
548 void QgsQueryBuilder::layerSubsetStringChanged()
550 if ( mIgnoreLayerSubsetStringChangedSignal )
552 mUseUnfilteredLayer->setDisabled( mLayer->
subsetString().isEmpty() );
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
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.
QString displayNameWithAlias() const
Returns the name to use when displaying this field and adds the alias in parenthesis if it is defined...
Container of fields for a vector layer.
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
static QgsGui * instance()
Returns a pointer to the singleton instance.
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.
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...
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.
Interface for a dialog that can edit subset strings.
void clearErrors()
Clear recorded errors.
QStringList errors() const
Gets recorded errors.
bool hasErrors() const
Provider has errors to report.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.