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.