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" ) );
176 value = var.toString();
178 QStandardItem *myItem =
new QStandardItem( value );
179 myItem->setEditable(
false );
180 myItem->setData( var, Qt::UserRole + 1 );
181 mModelValues->insertRow( mModelValues->rowCount(), myItem );
182 QgsDebugMsg( QStringLiteral(
"Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
186 void QgsQueryBuilder::btnSampleValues_clicked()
188 lstValues->setCursor( Qt::WaitCursor );
191 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
193 mIgnoreLayerSubsetStringChangedSignal =
true;
198 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
203 mIgnoreLayerSubsetStringChangedSignal =
false;
206 lstValues->setCursor( Qt::ArrowCursor );
209 void QgsQueryBuilder::btnGetAllValues_clicked()
211 lstValues->setCursor( Qt::WaitCursor );
214 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
216 mIgnoreLayerSubsetStringChangedSignal =
true;
221 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
226 mIgnoreLayerSubsetStringChangedSignal =
false;
229 lstValues->setCursor( Qt::ArrowCursor );
242 if ( featureCount < 0 )
244 QMessageBox::warning(
this,
245 tr(
"Query Result" ),
246 tr(
"An error occurred when executing the query, please check the expression syntax." ) );
250 QMessageBox::information(
this,
251 tr(
"Query Result" ),
252 tr(
"The where clause returned %n row(s).",
"returned test rows", featureCount ) );
257 QMessageBox::warning(
this,
258 tr(
"Query Result" ),
259 tr(
"An error occurred when executing the query." )
260 + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->
dataProvider()->
errors().join( QLatin1Char(
'\n' ) ) ) );
265 QMessageBox::warning(
this,
266 tr(
"Query Result" ),
267 tr(
"An error occurred when executing the query." ) );
273 if ( mTxtSql->text() != mOrigSubsetString )
280 QMessageBox::warning(
this,
281 tr(
"Query Result" ),
282 tr(
"An error occurred when executing the query." )
283 + tr(
"\nThe data provider said:\n%1" ).arg( mLayer->
dataProvider()->
errors().join( QLatin1Char(
'\n' ) ) ) );
288 QMessageBox::warning(
this, tr(
"Query Result" ), tr(
"Error in query. The subset string could not be set." ) );
306 void QgsQueryBuilder::btnEqual_clicked()
308 mTxtSql->insertText( QStringLiteral(
" = " ) );
312 void QgsQueryBuilder::btnLessThan_clicked()
314 mTxtSql->insertText( QStringLiteral(
" < " ) );
318 void QgsQueryBuilder::btnGreaterThan_clicked()
320 mTxtSql->insertText( QStringLiteral(
" > " ) );
324 void QgsQueryBuilder::btnPct_clicked()
326 mTxtSql->insertText( QStringLiteral(
"%" ) );
330 void QgsQueryBuilder::btnIn_clicked()
332 mTxtSql->insertText( QStringLiteral(
" IN " ) );
336 void QgsQueryBuilder::btnNotIn_clicked()
338 mTxtSql->insertText( QStringLiteral(
" NOT IN " ) );
342 void QgsQueryBuilder::btnLike_clicked()
344 mTxtSql->insertText( QStringLiteral(
" LIKE " ) );
350 return mTxtSql->text();
355 mTxtSql->setText( sqlStatement );
358 void QgsQueryBuilder::lstFields_clicked(
const QModelIndex &index )
360 if ( mPreviousFieldRow != index.row() )
362 mPreviousFieldRow = index.row();
364 btnSampleValues->setEnabled(
true );
365 btnGetAllValues->setEnabled(
true );
367 mModelValues->clear();
368 mFilterLineEdit->clear();
372 void QgsQueryBuilder::lstFields_doubleClicked(
const QModelIndex &index )
374 mTxtSql->insertText(
'\"' + mLayer->
fields().
at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).
name() +
'\"' );
378 void QgsQueryBuilder::lstValues_doubleClicked(
const QModelIndex &index )
380 QVariant value = index.data( Qt::UserRole + 1 );
381 if ( value.isNull() )
382 mTxtSql->insertText( QStringLiteral(
"NULL" ) );
383 else if ( value.type() == QVariant::Date && mLayer->
providerType() == QLatin1String(
"ogr" ) && mLayer->
storageType() == QLatin1String(
"ESRI Shapefile" ) )
384 mTxtSql->insertText(
'\'' + value.toDate().toString( QStringLiteral(
"yyyy/MM/dd" ) ) +
'\'' );
385 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
386 mTxtSql->insertText( value.toString() );
388 mTxtSql->insertText(
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
393 void QgsQueryBuilder::btnLessEqual_clicked()
395 mTxtSql->insertText( QStringLiteral(
" <= " ) );
399 void QgsQueryBuilder::btnGreaterEqual_clicked()
401 mTxtSql->insertText( QStringLiteral(
" >= " ) );
405 void QgsQueryBuilder::btnNotEqual_clicked()
407 mTxtSql->insertText( QStringLiteral(
" != " ) );
411 void QgsQueryBuilder::btnAnd_clicked()
413 mTxtSql->insertText( QStringLiteral(
" AND " ) );
417 void QgsQueryBuilder::btnNot_clicked()
419 mTxtSql->insertText( QStringLiteral(
" NOT " ) );
423 void QgsQueryBuilder::btnOr_clicked()
425 mTxtSql->insertText( QStringLiteral(
" OR " ) );
429 void QgsQueryBuilder::onTextChanged(
const QString &text )
431 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
432 mProxyValues->setFilterWildcard( text );
441 void QgsQueryBuilder::btnILike_clicked()
443 mTxtSql->insertText( QStringLiteral(
" ILIKE " ) );
449 lblDataUri->setText( uri );
452 void QgsQueryBuilder::showHelp()
454 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#query-builder" ) );
460 QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
462 QString saveFileName = QFileDialog::getSaveFileName(
nullptr, tr(
"Save Query to File" ), lastQueryFileDir, tr(
"Query files (*.qqf *.QQF)" ) );
463 if ( saveFileName.isNull() )
468 if ( !saveFileName.endsWith( QLatin1String(
".qqf" ), Qt::CaseInsensitive ) )
470 saveFileName += QLatin1String(
".qqf" );
473 QFile saveFile( saveFileName );
474 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
476 QMessageBox::critical(
nullptr, tr(
"Save Query to File" ), tr(
"Could not open file for writing." ) );
481 QDomElement queryElem = xmlDoc.createElement( QStringLiteral(
"Query" ) );
482 QDomText queryTextNode = xmlDoc.createTextNode( mTxtSql->text() );
483 queryElem.appendChild( queryTextNode );
484 xmlDoc.appendChild( queryElem );
486 QTextStream fileStream( &saveFile );
487 xmlDoc.save( fileStream, 2 );
489 QFileInfo fi( saveFile );
490 s.
setValue( QStringLiteral(
"/UI/lastQueryFileDir" ), fi.absolutePath() );
496 QString lastQueryFileDir = s.
value( QStringLiteral(
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
498 QString queryFileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Query from File" ), lastQueryFileDir, tr(
"Query files" ) +
" (*.qqf);;" + tr(
"All files" ) +
" (*)" );
499 if ( queryFileName.isNull() )
504 QFile queryFile( queryFileName );
505 if ( !queryFile.open( QIODevice::ReadOnly ) )
507 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"Could not open file for reading." ) );
510 QDomDocument queryDoc;
511 if ( !queryDoc.setContent( &queryFile ) )
513 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid xml document." ) );
517 QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral(
"Query" ) );
518 if ( queryElem.isNull() )
520 QMessageBox::critical(
nullptr, tr(
"Load Query from File" ), tr(
"File is not a valid query document." ) );
524 QString query = queryElem.text();
535 mTxtSql->insertText( query );
538 void QgsQueryBuilder::layerSubsetStringChanged()
540 if ( mIgnoreLayerSubsetStringChangedSignal )
542 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
Gets field's origin (value from an enumeration)
QgsField at(int i) const
Gets 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.
This class is a composition of two QSettings instances:
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.
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.
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.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.