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.