Skip to content

Commit efaf5ea

Browse files
authored
[ui] select by form/expression harmonization and feature parity (#3912)
1 parent 95ff9a1 commit efaf5ea

7 files changed

Lines changed: 135 additions & 18 deletions

‎python/gui/qgsexpressionselectiondialog.sip‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,18 @@ class QgsExpressionSelectionDialog : QDialog
3535
*/
3636
void setGeomCalculator( const QgsDistanceArea & da );
3737

38-
public slots:
39-
void on_mActionSelect_triggered();
40-
void on_mActionAddToSelection_triggered();
41-
void on_mActionRemoveFromSelection_triggered();
42-
void on_mActionSelectIntersect_triggered();
43-
void on_mPbnClose_clicked();
38+
/** Sets the message bar to display feedback from the dialog. This is used when zooming to
39+
* features to display the count of selected features.
40+
* @param messageBar target message bar
41+
* @note added in QGIS 3.0
42+
*/
43+
void setMessageBar( QgsMessageBar* messageBar );
44+
45+
/**
46+
* Sets a map canvas associated with the dialog.
47+
* @note added in QGIS 3.0
48+
*/
49+
void setMapCanvas( QgsMapCanvas* canvas );
4450

4551
protected:
4652
/**

‎src/app/qgisapp.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7496,6 +7496,8 @@ void QgisApp::selectByExpression()
74967496
}
74977497

74987498
QgsExpressionSelectionDialog* dlg = new QgsExpressionSelectionDialog( vlayer, QString(), this );
7499+
dlg->setMessageBar( messageBar() );
7500+
dlg->setMapCanvas( mapCanvas() );
74997501
dlg->setAttribute( Qt::WA_DeleteOnClose );
75007502
dlg->show();
75017503
}

‎src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ QString QgsSearchWidgetWrapper::toString( QgsSearchWidgetWrapper::FilterFlag fla
5050
case EqualTo:
5151
return QObject::tr( "Equal to (=)" );
5252
case NotEqualTo:
53-
return QObject::tr( "Not equal to" );
53+
return QObject::tr( "Not equal to (!=)" );
5454
case GreaterThan:
5555
return QObject::tr( "Greater than (>)" );
5656
case LessThan:

‎src/gui/qgsattributeform.cpp‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,21 +1328,27 @@ void QgsAttributeForm::init()
13281328
QToolButton* selectButton = new QToolButton();
13291329
selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
13301330
selectButton->setText( tr( "&Select features" ) );
1331+
selectButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) );
13311332
selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1333+
selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
13321334
connect( selectButton, &QToolButton::clicked, this, &QgsAttributeForm::searchSetSelection );
13331335
QMenu* selectMenu = new QMenu( selectButton );
13341336
QAction* selectAction = new QAction( tr( "Select features" ), selectMenu );
1337+
selectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) );
13351338
connect( selectAction, &QAction::triggered, this, &QgsAttributeForm::searchSetSelection );
13361339
selectMenu->addAction( selectAction );
13371340
QAction* addSelectAction = new QAction( tr( "Add to current selection" ), selectMenu );
1341+
addSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectAdd.svg" ) ) );
13381342
connect( addSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchAddToSelection );
13391343
selectMenu->addAction( addSelectAction );
1340-
QAction* filterSelectAction = new QAction( tr( "Filter current selection" ), selectMenu );
1341-
connect( filterSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchIntersectSelection );
1342-
selectMenu->addAction( filterSelectAction );
13431344
QAction* deselectAction = new QAction( tr( "Remove from current selection" ), selectMenu );
1345+
deselectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectRemove.svg" ) ) );
13441346
connect( deselectAction, &QAction::triggered, this, &QgsAttributeForm::searchRemoveFromSelection );
13451347
selectMenu->addAction( deselectAction );
1348+
QAction* filterSelectAction = new QAction( tr( "Filter current selection" ), selectMenu );
1349+
filterSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectIntersect.svg" ) ) );
1350+
connect( filterSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchIntersectSelection );
1351+
selectMenu->addAction( filterSelectAction );
13461352
selectButton->setMenu( selectMenu );
13471353
boxLayout->addWidget( selectButton );
13481354

‎src/gui/qgsexpressionselectiondialog.cpp‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,20 @@
1414
***************************************************************************/
1515

1616
#include "qgsexpressionselectiondialog.h"
17+
1718
#include "qgsapplication.h"
1819
#include "qgsexpression.h"
20+
#include "qgsgeometry.h"
21+
#include "qgsmessagebar.h"
1922
#include "qgsvectorlayer.h"
2023

2124
#include <QSettings>
2225

2326
QgsExpressionSelectionDialog::QgsExpressionSelectionDialog( QgsVectorLayer* layer, const QString& startText, QWidget* parent )
2427
: QDialog( parent )
2528
, mLayer( layer )
29+
, mMessageBar( nullptr )
30+
, mMapCanvas( nullptr )
2631
{
2732
setupUi( this );
2833

@@ -50,6 +55,9 @@ QgsExpressionSelectionDialog::QgsExpressionSelectionDialog( QgsVectorLayer* laye
5055
<< QgsExpressionContextUtils::layerScope( mLayer );
5156
mExpressionBuilder->setExpressionContext( context );
5257

58+
// by default, zoom to features is hidden, shown only if canvas is set
59+
mButtonZoomToFeatures->setVisible( false );
60+
5361
QSettings settings;
5462
restoreGeometry( settings.value( QStringLiteral( "/Windows/ExpressionSelectionDialog/geometry" ) ).toByteArray() );
5563
}
@@ -75,6 +83,17 @@ void QgsExpressionSelectionDialog::setGeomCalculator( const QgsDistanceArea & da
7583
mExpressionBuilder->setGeomCalculator( da );
7684
}
7785

86+
void QgsExpressionSelectionDialog::setMessageBar( QgsMessageBar* messageBar )
87+
{
88+
mMessageBar = messageBar;
89+
}
90+
91+
void QgsExpressionSelectionDialog::setMapCanvas( QgsMapCanvas* canvas )
92+
{
93+
mMapCanvas = canvas;
94+
mButtonZoomToFeatures->setVisible( true );
95+
}
96+
7897
void QgsExpressionSelectionDialog::on_mActionSelect_triggered()
7998
{
8099
mLayer->selectByExpression( mExpressionBuilder->expressionText(),
@@ -103,6 +122,63 @@ void QgsExpressionSelectionDialog::on_mActionRemoveFromSelection_triggered()
103122
saveRecent();
104123
}
105124

125+
void QgsExpressionSelectionDialog::on_mButtonZoomToFeatures_clicked()
126+
{
127+
if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas )
128+
return;
129+
130+
QgsFeatureIds ids;
131+
132+
QgsExpressionContext context;
133+
context << QgsExpressionContextUtils::globalScope()
134+
<< QgsExpressionContextUtils::projectScope()
135+
<< QgsExpressionContextUtils::layerScope( mLayer );
136+
137+
QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() )
138+
.setExpressionContext( context )
139+
.setSubsetOfAttributes( QgsAttributeList() );
140+
141+
QgsFeatureIterator features = mLayer->getFeatures( request );
142+
143+
QgsRectangle bbox;
144+
bbox.setMinimal();
145+
QgsFeature feat;
146+
int featureCount = 0;
147+
while ( features.nextFeature( feat ) )
148+
{
149+
QgsGeometry geom = feat.geometry();
150+
if ( geom.isEmpty() || geom.geometry()->isEmpty() )
151+
continue;
152+
153+
QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() );
154+
bbox.combineExtentWith( r );
155+
featureCount++;
156+
}
157+
features.close();
158+
159+
QSettings settings;
160+
int timeout = settings.value( QStringLiteral( "/qgis/messageTimeout" ), 5 ).toInt();
161+
if ( featureCount > 0 )
162+
{
163+
mMapCanvas->zoomToFeatureExtent( bbox );
164+
if ( mMessageBar )
165+
{
166+
mMessageBar->pushMessage( QString(),
167+
tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ),
168+
QgsMessageBar::INFO,
169+
timeout );
170+
}
171+
}
172+
else if ( mMessageBar )
173+
{
174+
mMessageBar->pushMessage( QString(),
175+
tr( "No matching features found" ),
176+
QgsMessageBar::INFO,
177+
timeout );
178+
}
179+
saveRecent();
180+
}
181+
106182
void QgsExpressionSelectionDialog::closeEvent( QCloseEvent *closeEvent )
107183
{
108184
QDialog::closeEvent( closeEvent );

‎src/gui/qgsexpressionselectiondialog.h‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
#ifndef QGSEXPRESSIONSELECTIONDIALOG_H
1717
#define QGSEXPRESSIONSELECTIONDIALOG_H
1818

19-
#include <QDialog>
2019
#include "ui_qgsexpressionselectiondialogbase.h"
2120

21+
#include "qgsmapcanvas.h"
22+
#include "qgsmessagebar.h"
23+
24+
#include <QDialog>
25+
2226
/** \ingroup gui
2327
* This class offers a dialog to change feature selections.
2428
* To do so, a QgsExpressionBuilderWidget is shown in a dialog.
@@ -62,11 +66,25 @@ class GUI_EXPORT QgsExpressionSelectionDialog : public QDialog, private Ui::QgsE
6266
*/
6367
void setGeomCalculator( const QgsDistanceArea & da );
6468

65-
public slots:
69+
/** Sets the message bar to display feedback from the dialog. This is used when zooming to
70+
* features to display the count of selected features.
71+
* @param messageBar target message bar
72+
* @note added in QGIS 3.0
73+
*/
74+
void setMessageBar( QgsMessageBar* messageBar );
75+
76+
/**
77+
* Sets a map canvas associated with the dialog.
78+
* @note added in QGIS 3.0
79+
*/
80+
void setMapCanvas( QgsMapCanvas* canvas );
81+
82+
private slots:
6683
void on_mActionSelect_triggered();
6784
void on_mActionAddToSelection_triggered();
6885
void on_mActionRemoveFromSelection_triggered();
6986
void on_mActionSelectIntersect_triggered();
87+
void on_mButtonZoomToFeatures_clicked();
7088
void on_mPbnClose_clicked();
7189

7290
protected:
@@ -88,6 +106,8 @@ class GUI_EXPORT QgsExpressionSelectionDialog : public QDialog, private Ui::QgsE
88106
private:
89107
void saveRecent();
90108
QgsVectorLayer* mLayer;
109+
QgsMessageBar* mMessageBar;
110+
QgsMapCanvas* mMapCanvas;
91111
};
92112

93113
#endif

‎src/ui/qgsexpressionselectiondialogbase.ui‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@
3030
</property>
3131
</spacer>
3232
</item>
33-
<item row="1" column="2">
33+
<item row="1" column="3">
3434
<widget class="QPushButton" name="mPbnClose">
3535
<property name="text">
3636
<string>Close</string>
3737
</property>
3838
</widget>
3939
</item>
40-
<item row="1" column="1">
40+
<item row="1" column="2">
4141
<widget class="QToolButton" name="mButtonSelect">
4242
<property name="minimumSize">
4343
<size>
@@ -56,28 +56,35 @@
5656
</property>
5757
</widget>
5858
</item>
59+
<item row="1" column="1">
60+
<widget class="QToolButton" name="mButtonZoomToFeatures">
61+
<property name="text">
62+
<string>Zoom to features</string>
63+
</property>
64+
</widget>
65+
</item>
5966
<item row="0" column="0" colspan="4">
6067
<widget class="QgsExpressionBuilderWidget" name="mExpressionBuilder" native="true"/>
6168
</item>
6269
</layout>
6370
<action name="mActionSelect">
6471
<property name="text">
65-
<string>Select</string>
72+
<string>Select features</string>
6673
</property>
6774
</action>
6875
<action name="mActionAddToSelection">
6976
<property name="text">
70-
<string>Add to selection</string>
77+
<string>Add to current selection</string>
7178
</property>
7279
</action>
7380
<action name="mActionRemoveFromSelection">
7481
<property name="text">
75-
<string>Remove from selection</string>
82+
<string>Remove from current selection</string>
7683
</property>
7784
</action>
7885
<action name="mActionSelectIntersect">
7986
<property name="text">
80-
<string>Select within selection</string>
87+
<string>Filter current selection</string>
8188
</property>
8289
</action>
8390
</widget>

0 commit comments

Comments
 (0)