16#include "moc_qgsquerybuilder.cpp" 
   27#include <QDomDocument> 
   30#include <QInputDialog> 
   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" ) );
 
   90  lstFields->setModel( mModelFields );
 
   94  layerSubsetStringChanged();
 
   96  QString subsetStringDialect;
 
   97  QString subsetStringHelpUrl;
 
  101    lblDataUri->setText( tr( 
"Set provider filter on %1 (provider: %2)" ).arg( layer->
name(), provider->name() ) );
 
  102    subsetStringDialect = provider->subsetStringDialect();
 
  103    subsetStringHelpUrl = provider->subsetStringHelpUrl();
 
  107    lblDataUri->setText( tr( 
"Set provider filter on %1 (provider: %2)" ).arg( layer->
name(), layer->
providerType() ) );
 
  110  if ( !subsetStringDialect.isEmpty() && !subsetStringHelpUrl.isEmpty() )
 
  112    lblProviderFilterInfo->setOpenExternalLinks( 
true );
 
  113    lblProviderFilterInfo->setText( tr( 
"Enter a <a href=\"%1\">%2</a> to filter the layer" ).arg( subsetStringHelpUrl ).arg( subsetStringDialect ) );
 
  115  else if ( !subsetStringDialect.isEmpty() )
 
  117    lblProviderFilterInfo->setText( tr( 
"Enter a %1 to filter the layer" ).arg( subsetStringDialect ) );
 
  121    lblProviderFilterInfo->hide();
 
  124  mTxtSql->setText( mOrigSubsetString );
 
  126  mFilterLineEdit->setShowSearchIcon( 
true );
 
  127  mFilterLineEdit->setPlaceholderText( tr( 
"Search…" ) );
 
  128  connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, 
this, &QgsQueryBuilder::onTextChanged );
 
 
  134  QDialog::showEvent( event );
 
 
  137void QgsQueryBuilder::setupGuiViews()
 
  140  mModelValues = 
new QStandardItemModel();
 
  141  mProxyValues = 
new QSortFilterProxyModel();
 
  142  mProxyValues->setSourceModel( mModelValues );
 
  144  lstFields->setViewMode( QListView::ListMode );
 
  145  lstValues->setViewMode( QListView::ListMode );
 
  146  lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
 
  147  lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
 
  149  lstFields->setUniformItemSizes( 
true );
 
  150  lstValues->setUniformItemSizes( 
true );
 
  152  lstFields->setAlternatingRowColors( 
true );
 
  153  lstValues->setAlternatingRowColors( 
true );
 
  154  lstValues->setModel( mProxyValues );
 
  157void QgsQueryBuilder::fillValues( 
const QString &field, 
int limit )
 
  160  mModelValues->clear();
 
  165  QList<QVariant> values = qgis::setToList( mLayer->
uniqueValues( fieldIndex, limit ) );
 
  166  std::sort( values.begin(), values.end() );
 
  172  const auto constValues = values;
 
  173  for ( 
const QVariant &var : constValues )
 
  178    else if ( var.userType() == QMetaType::Type::QDate && mLayer->
providerType() == QLatin1String( 
"ogr" ) && mLayer->
storageType() == QLatin1String( 
"ESRI Shapefile" ) )
 
  179      value = var.toDate().toString( QStringLiteral( 
"yyyy/MM/dd" ) );
 
  180    else if ( var.userType() == QMetaType::Type::QVariantList || var.userType() == QMetaType::Type::QStringList )
 
  182      const QVariantList list = var.toList();
 
  183      for ( 
const QVariant &val : list )
 
  185        if ( !value.isEmpty() )
 
  186          value.append( 
", " );
 
  191      value = var.toString();
 
  193    QStandardItem *myItem = 
new QStandardItem( value );
 
  194    myItem->setEditable( 
false );
 
  195    myItem->setData( var, Qt::UserRole + 1 );
 
  196    mModelValues->insertRow( mModelValues->rowCount(), myItem );
 
  201void QgsQueryBuilder::btnSampleValues_clicked()
 
  203  lstValues->setCursor( Qt::WaitCursor );
 
  205  const QString prevSubsetString = mLayer->
subsetString();
 
  206  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
 
  208    mIgnoreLayerSubsetStringChangedSignal = 
true;
 
  218    mIgnoreLayerSubsetStringChangedSignal = 
false;
 
  221  lstValues->setCursor( Qt::ArrowCursor );
 
  224void QgsQueryBuilder::btnGetAllValues_clicked()
 
  226  lstValues->setCursor( Qt::WaitCursor );
 
  228  const QString prevSubsetString = mLayer->
subsetString();
 
  229  if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
 
  231    mIgnoreLayerSubsetStringChangedSignal = 
true;
 
  241    mIgnoreLayerSubsetStringChangedSignal = 
false;
 
  244  lstValues->setCursor( Qt::ArrowCursor );
 
  255    const long long featureCount { mLayer->
featureCount() };
 
  257    if ( featureCount < 0 )
 
  259      QMessageBox::warning( 
this, tr( 
"Query Result" ), tr( 
"An error occurred when executing the query, please check the expression syntax." ) );
 
  263      QMessageBox::information( 
this, tr( 
"Query Result" ), tr( 
"The where clause returned %n row(s).", 
"returned test rows", featureCount ) );
 
  268    QMessageBox::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    QMessageBox::warning( 
this, tr( 
"Query Result" ), tr( 
"An error occurred when executing the query." ) );
 
 
  279  if ( mTxtSql->text() != mOrigSubsetString )
 
  286        QMessageBox::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' ) ) ) );
 
  291        QMessageBox::warning( 
this, tr( 
"Query Result" ), tr( 
"Error in query. The subset string could not be set." ) );
 
 
  309void QgsQueryBuilder::btnEqual_clicked()
 
  311  mTxtSql->insertText( QStringLiteral( 
" = " ) );
 
  315void QgsQueryBuilder::btnLessThan_clicked()
 
  317  mTxtSql->insertText( QStringLiteral( 
" < " ) );
 
  321void QgsQueryBuilder::btnGreaterThan_clicked()
 
  323  mTxtSql->insertText( QStringLiteral( 
" > " ) );
 
  327void QgsQueryBuilder::btnPct_clicked()
 
  329  mTxtSql->insertText( QStringLiteral( 
"%" ) );
 
  333void QgsQueryBuilder::btnIn_clicked()
 
  335  mTxtSql->insertText( QStringLiteral( 
" IN " ) );
 
  339void QgsQueryBuilder::btnNotIn_clicked()
 
  341  mTxtSql->insertText( QStringLiteral( 
" NOT IN " ) );
 
  345void QgsQueryBuilder::btnLike_clicked()
 
  347  mTxtSql->insertText( QStringLiteral( 
" LIKE " ) );
 
  353  return mTxtSql->text();
 
 
  358  mTxtSql->setText( sqlStatement );
 
 
  361void QgsQueryBuilder::lstFields_clicked( 
const QModelIndex &index )
 
  363  if ( mPreviousFieldRow != index.row() )
 
  365    mPreviousFieldRow = index.row();
 
  367    btnSampleValues->setEnabled( 
true );
 
  368    btnGetAllValues->setEnabled( 
true );
 
  370    mModelValues->clear();
 
  371    mFilterLineEdit->clear();
 
  375void QgsQueryBuilder::lstFields_doubleClicked( 
const QModelIndex &index )
 
  381void QgsQueryBuilder::lstValues_doubleClicked( 
const QModelIndex &index )
 
  383  const QVariant value = index.data( Qt::UserRole + 1 );
 
  385    mTxtSql->insertText( QStringLiteral( 
"NULL" ) );
 
  386  else if ( value.userType() == QMetaType::Type::QDate && mLayer->
providerType() == QLatin1String( 
"ogr" ) && mLayer->
storageType() == QLatin1String( 
"ESRI Shapefile" ) )
 
  387    mTxtSql->insertText( 
'\'' + value.toDate().toString( QStringLiteral( 
"yyyy/MM/dd" ) ) + 
'\'' );
 
  388  else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
 
  389    mTxtSql->insertText( value.toString() );
 
  391    mTxtSql->insertText( 
'\'' + value.toString().replace( 
'\'', QLatin1String( 
"''" ) ) + 
'\'' );
 
  396void QgsQueryBuilder::btnLessEqual_clicked()
 
  398  mTxtSql->insertText( QStringLiteral( 
" <= " ) );
 
  402void QgsQueryBuilder::btnGreaterEqual_clicked()
 
  404  mTxtSql->insertText( QStringLiteral( 
" >= " ) );
 
  408void QgsQueryBuilder::btnNotEqual_clicked()
 
  410  mTxtSql->insertText( QStringLiteral( 
" != " ) );
 
  414void QgsQueryBuilder::btnAnd_clicked()
 
  416  mTxtSql->insertText( QStringLiteral( 
" AND " ) );
 
  420void QgsQueryBuilder::btnNot_clicked()
 
  422  mTxtSql->insertText( QStringLiteral( 
" NOT " ) );
 
  426void QgsQueryBuilder::btnOr_clicked()
 
  428  mTxtSql->insertText( QStringLiteral( 
" OR " ) );
 
  432void QgsQueryBuilder::onTextChanged( 
const QString &text )
 
  434  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
 
  435  mProxyValues->setFilterWildcard( text );
 
  444void QgsQueryBuilder::btnILike_clicked()
 
  446  mTxtSql->insertText( QStringLiteral( 
" ILIKE " ) );
 
  452  lblDataUri->setText( uri );
 
 
  455void QgsQueryBuilder::showHelp()
 
  457  QgsHelp::openHelp( QStringLiteral( 
"working_with_vector/vector_properties.html#query-builder" ) );
 
  469  const QString lastQueryFileDir = s.
value( QStringLiteral( 
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
 
  471  QString saveFileName = QFileDialog::getSaveFileName( 
nullptr, tr( 
"Save Query to File" ), lastQueryFileDir, tr( 
"Query files (*.qqf *.QQF)" ) );
 
  472  if ( saveFileName.isNull() )
 
  477  if ( !saveFileName.endsWith( QLatin1String( 
".qqf" ), Qt::CaseInsensitive ) )
 
  479    saveFileName += QLatin1String( 
".qqf" );
 
  482  QFile saveFile( saveFileName );
 
  483  if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
 
  485    QMessageBox::critical( 
nullptr, tr( 
"Save Query to File" ), tr( 
"Could not open file for writing." ) );
 
  490  QDomElement queryElem = xmlDoc.createElement( QStringLiteral( 
"Query" ) );
 
  491  const QDomText queryTextNode = xmlDoc.createTextNode( subset );
 
  492  queryElem.appendChild( queryTextNode );
 
  493  xmlDoc.appendChild( queryElem );
 
  495  QTextStream fileStream( &saveFile );
 
  496  xmlDoc.save( fileStream, 2 );
 
  498  const QFileInfo fi( saveFile );
 
  499  s.
setValue( QStringLiteral( 
"/UI/lastQueryFileDir" ), fi.absolutePath() );
 
 
  509    mTxtSql->insertText( subset );
 
 
  516  const QString lastQueryFileDir = s.
value( QStringLiteral( 
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
 
  518  const QString queryFileName = QFileDialog::getOpenFileName( 
nullptr, tr( 
"Load Query from File" ), lastQueryFileDir, tr( 
"Query files" ) + 
" (*.qqf);;" + tr( 
"All files" ) + 
" (*)" );
 
  519  if ( queryFileName.isNull() )
 
  524  QFile queryFile( queryFileName );
 
  525  if ( !queryFile.open( QIODevice::ReadOnly ) )
 
  527    QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"Could not open file for reading." ) );
 
  530  QDomDocument queryDoc;
 
  531  if ( !queryDoc.setContent( &queryFile ) )
 
  533    QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"File is not a valid xml document." ) );
 
  537  const QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral( 
"Query" ) );
 
  538  if ( queryElem.isNull() )
 
  540    QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"File is not a valid query document." ) );
 
  544  subset = queryElem.text();
 
 
  548void QgsQueryBuilder::layerSubsetStringChanged()
 
  550  if ( mIgnoreLayerSubsetStringChangedSignal )
 
  552  mUseUnfilteredLayer->setDisabled( mLayer->
subsetString().isEmpty() );
 
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.
 
void setLayer(QgsVectorLayer *layer)
Set the layer from which fields are displayed.
 
A proxy model to filter the list of fields of a layer.
 
QgsFieldModel * sourceFieldModel()
Returns the QgsFieldModel used in this QSortFilterProxyModel.
 
@ AllTypes
All field types.
 
@ OriginProvider
Fields with a provider origin, since QGIS 3.38.
 
QgsFieldProxyModel * setFilters(QgsFieldProxyModel::Filters filters)
Set flags that affect how fields are filtered in the model.
 
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
 
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.
 
Interface for a dialog that can edit subset strings.
 
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
 
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 dataset.
 
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
 
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.
 
#define QgsDebugMsgLevel(str, level)