diff --git a/python/gui/qgsexpressionselectiondialog.sip b/python/gui/qgsexpressionselectiondialog.sip index 77e24028091..4d0e2ce96ab 100644 --- a/python/gui/qgsexpressionselectiondialog.sip +++ b/python/gui/qgsexpressionselectiondialog.sip @@ -35,12 +35,18 @@ class QgsExpressionSelectionDialog : QDialog */ void setGeomCalculator( const QgsDistanceArea & da ); - public slots: - void on_mActionSelect_triggered(); - void on_mActionAddToSelection_triggered(); - void on_mActionRemoveFromSelection_triggered(); - void on_mActionSelectIntersect_triggered(); - void on_mPbnClose_clicked(); + /** Sets the message bar to display feedback from the dialog. This is used when zooming to + * features to display the count of selected features. + * @param messageBar target message bar + * @note added in QGIS 3.0 + */ + void setMessageBar( QgsMessageBar* messageBar ); + + /** + * Sets a map canvas associated with the dialog. + * @note added in QGIS 3.0 + */ + void setMapCanvas( QgsMapCanvas* canvas ); protected: /** diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 128158490ef..179259a08ad 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -7496,6 +7496,8 @@ void QgisApp::selectByExpression() } QgsExpressionSelectionDialog* dlg = new QgsExpressionSelectionDialog( vlayer, QString(), this ); + dlg->setMessageBar( messageBar() ); + dlg->setMapCanvas( mapCanvas() ); dlg->setAttribute( Qt::WA_DeleteOnClose ); dlg->show(); } diff --git a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp index 7e1163eb9b8..5b272c5ef96 100644 --- a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp +++ b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp @@ -50,7 +50,7 @@ QString QgsSearchWidgetWrapper::toString( QgsSearchWidgetWrapper::FilterFlag fla case EqualTo: return QObject::tr( "Equal to (=)" ); case NotEqualTo: - return QObject::tr( "Not equal to" ); + return QObject::tr( "Not equal to (!=)" ); case GreaterThan: return QObject::tr( "Greater than (>)" ); case LessThan: diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index e684eddecd2..f52ca04d1a1 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -1328,21 +1328,27 @@ void QgsAttributeForm::init() QToolButton* selectButton = new QToolButton(); selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); selectButton->setText( tr( "&Select features" ) ); + selectButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) ); selectButton->setPopupMode( QToolButton::MenuButtonPopup ); + selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); connect( selectButton, &QToolButton::clicked, this, &QgsAttributeForm::searchSetSelection ); QMenu* selectMenu = new QMenu( selectButton ); QAction* selectAction = new QAction( tr( "Select features" ), selectMenu ); + selectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) ); connect( selectAction, &QAction::triggered, this, &QgsAttributeForm::searchSetSelection ); selectMenu->addAction( selectAction ); QAction* addSelectAction = new QAction( tr( "Add to current selection" ), selectMenu ); + addSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectAdd.svg" ) ) ); connect( addSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchAddToSelection ); selectMenu->addAction( addSelectAction ); - QAction* filterSelectAction = new QAction( tr( "Filter current selection" ), selectMenu ); - connect( filterSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchIntersectSelection ); - selectMenu->addAction( filterSelectAction ); QAction* deselectAction = new QAction( tr( "Remove from current selection" ), selectMenu ); + deselectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectRemove.svg" ) ) ); connect( deselectAction, &QAction::triggered, this, &QgsAttributeForm::searchRemoveFromSelection ); selectMenu->addAction( deselectAction ); + QAction* filterSelectAction = new QAction( tr( "Filter current selection" ), selectMenu ); + filterSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectIntersect.svg" ) ) ); + connect( filterSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchIntersectSelection ); + selectMenu->addAction( filterSelectAction ); selectButton->setMenu( selectMenu ); boxLayout->addWidget( selectButton ); diff --git a/src/gui/qgsexpressionselectiondialog.cpp b/src/gui/qgsexpressionselectiondialog.cpp index 501f6fd3776..4c6c1a455b0 100644 --- a/src/gui/qgsexpressionselectiondialog.cpp +++ b/src/gui/qgsexpressionselectiondialog.cpp @@ -14,8 +14,11 @@ ***************************************************************************/ #include "qgsexpressionselectiondialog.h" + #include "qgsapplication.h" #include "qgsexpression.h" +#include "qgsgeometry.h" +#include "qgsmessagebar.h" #include "qgsvectorlayer.h" #include @@ -23,6 +26,8 @@ QgsExpressionSelectionDialog::QgsExpressionSelectionDialog( QgsVectorLayer* layer, const QString& startText, QWidget* parent ) : QDialog( parent ) , mLayer( layer ) + , mMessageBar( nullptr ) + , mMapCanvas( nullptr ) { setupUi( this ); @@ -50,6 +55,9 @@ QgsExpressionSelectionDialog::QgsExpressionSelectionDialog( QgsVectorLayer* laye << QgsExpressionContextUtils::layerScope( mLayer ); mExpressionBuilder->setExpressionContext( context ); + // by default, zoom to features is hidden, shown only if canvas is set + mButtonZoomToFeatures->setVisible( false ); + QSettings settings; restoreGeometry( settings.value( QStringLiteral( "/Windows/ExpressionSelectionDialog/geometry" ) ).toByteArray() ); } @@ -75,6 +83,17 @@ void QgsExpressionSelectionDialog::setGeomCalculator( const QgsDistanceArea & da mExpressionBuilder->setGeomCalculator( da ); } +void QgsExpressionSelectionDialog::setMessageBar( QgsMessageBar* messageBar ) +{ + mMessageBar = messageBar; +} + +void QgsExpressionSelectionDialog::setMapCanvas( QgsMapCanvas* canvas ) +{ + mMapCanvas = canvas; + mButtonZoomToFeatures->setVisible( true ); +} + void QgsExpressionSelectionDialog::on_mActionSelect_triggered() { mLayer->selectByExpression( mExpressionBuilder->expressionText(), @@ -103,6 +122,63 @@ void QgsExpressionSelectionDialog::on_mActionRemoveFromSelection_triggered() saveRecent(); } +void QgsExpressionSelectionDialog::on_mButtonZoomToFeatures_clicked() +{ + if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas ) + return; + + QgsFeatureIds ids; + + QgsExpressionContext context; + context << QgsExpressionContextUtils::globalScope() + << QgsExpressionContextUtils::projectScope() + << QgsExpressionContextUtils::layerScope( mLayer ); + + QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() ) + .setExpressionContext( context ) + .setSubsetOfAttributes( QgsAttributeList() ); + + QgsFeatureIterator features = mLayer->getFeatures( request ); + + QgsRectangle bbox; + bbox.setMinimal(); + QgsFeature feat; + int featureCount = 0; + while ( features.nextFeature( feat ) ) + { + QgsGeometry geom = feat.geometry(); + if ( geom.isEmpty() || geom.geometry()->isEmpty() ) + continue; + + QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() ); + bbox.combineExtentWith( r ); + featureCount++; + } + features.close(); + + QSettings settings; + int timeout = settings.value( QStringLiteral( "/qgis/messageTimeout" ), 5 ).toInt(); + if ( featureCount > 0 ) + { + mMapCanvas->zoomToFeatureExtent( bbox ); + if ( mMessageBar ) + { + mMessageBar->pushMessage( QString(), + tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ), + QgsMessageBar::INFO, + timeout ); + } + } + else if ( mMessageBar ) + { + mMessageBar->pushMessage( QString(), + tr( "No matching features found" ), + QgsMessageBar::INFO, + timeout ); + } + saveRecent(); +} + void QgsExpressionSelectionDialog::closeEvent( QCloseEvent *closeEvent ) { QDialog::closeEvent( closeEvent ); diff --git a/src/gui/qgsexpressionselectiondialog.h b/src/gui/qgsexpressionselectiondialog.h index e4eb59d3d18..efe509df4b5 100644 --- a/src/gui/qgsexpressionselectiondialog.h +++ b/src/gui/qgsexpressionselectiondialog.h @@ -16,9 +16,13 @@ #ifndef QGSEXPRESSIONSELECTIONDIALOG_H #define QGSEXPRESSIONSELECTIONDIALOG_H -#include #include "ui_qgsexpressionselectiondialogbase.h" +#include "qgsmapcanvas.h" +#include "qgsmessagebar.h" + +#include + /** \ingroup gui * This class offers a dialog to change feature selections. * To do so, a QgsExpressionBuilderWidget is shown in a dialog. @@ -62,11 +66,25 @@ class GUI_EXPORT QgsExpressionSelectionDialog : public QDialog, private Ui::QgsE */ void setGeomCalculator( const QgsDistanceArea & da ); - public slots: + /** Sets the message bar to display feedback from the dialog. This is used when zooming to + * features to display the count of selected features. + * @param messageBar target message bar + * @note added in QGIS 3.0 + */ + void setMessageBar( QgsMessageBar* messageBar ); + + /** + * Sets a map canvas associated with the dialog. + * @note added in QGIS 3.0 + */ + void setMapCanvas( QgsMapCanvas* canvas ); + + private slots: void on_mActionSelect_triggered(); void on_mActionAddToSelection_triggered(); void on_mActionRemoveFromSelection_triggered(); void on_mActionSelectIntersect_triggered(); + void on_mButtonZoomToFeatures_clicked(); void on_mPbnClose_clicked(); protected: @@ -88,6 +106,8 @@ class GUI_EXPORT QgsExpressionSelectionDialog : public QDialog, private Ui::QgsE private: void saveRecent(); QgsVectorLayer* mLayer; + QgsMessageBar* mMessageBar; + QgsMapCanvas* mMapCanvas; }; #endif diff --git a/src/ui/qgsexpressionselectiondialogbase.ui b/src/ui/qgsexpressionselectiondialogbase.ui index e1e4eafeae1..361739221f5 100644 --- a/src/ui/qgsexpressionselectiondialogbase.ui +++ b/src/ui/qgsexpressionselectiondialogbase.ui @@ -30,14 +30,14 @@ - + Close - + @@ -56,28 +56,35 @@ + + + + Zoom to features + + + - Select + Select features - Add to selection + Add to current selection - Remove from selection + Remove from current selection - Select within selection + Filter current selection