Skip to content

Commit 10b34b9

Browse files
authoredNov 22, 2017
Merge pull request #5660 from pblottiere/bugfix-clickxy-218
[backport][bugfix] Fixes #16852 by adding click_x and click_y variables to resolve actions
2 parents dbd4f9e + 4fe0013 commit 10b34b9

16 files changed

+208
-12
lines changed
 

‎python/core/qgsactionmanager.sip

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,18 @@ class QgsActionManager
6262
//! Remove an action at given index
6363
void removeAction( int index );
6464

65-
/** Does the given values. defaultValueIndex is the index of the
66-
* field to be used if the action has a $currfield placeholder.
67-
* @note available in python bindings as doActionFeature
65+
/**
66+
* Does the given action.
67+
*
68+
* @param index Index of the action
69+
* @param feat Feature to run action for
70+
* @param defaultValueIndex Index of the field to be used if the action has a $currfield placeholder.
71+
* @param scope Expression context scope to add during expression evaluation
6872
*/
6973
void doAction( int index,
7074
const QgsFeature &feat,
71-
int defaultValueIndex = 0 ) /PyName=doActionFeature/;
75+
int defaultValueIndex = 0,
76+
const QgsExpressionContextScope &scope = QgsExpressionContextScope() ) /PyName=doActionFeature/;
7277

7378
/** Does the action using the expression engine to replace any embedded expressions
7479
* in the action definition.

‎python/gui/qgsactionmenu.sip

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ class QgsActionMenu : QMenu
6262
*/
6363
void setFeature( QgsFeature* feature );
6464

65+
/**
66+
* Sets an expression context scope used to resolve underlying actions.
67+
*
68+
* @note Added in QGIS 2.18
69+
*/
70+
void setExpressionContextScope( const QgsExpressionContextScope &scope );
71+
72+
/**
73+
* Returns an expression context scope used to resolve underlying actions.
74+
*
75+
* @note Added in QGIS 2.18
76+
*/
77+
QgsExpressionContextScope expressionContextScope() const;
78+
6579
signals:
6680
void reinit();
6781

‎python/gui/qgsidentifymenu.sip

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ class QgsIdentifyMenu : QMenu
8383
*/
8484
QList<QgsMapToolIdentify::IdentifyResult> exec( const QList<QgsMapToolIdentify::IdentifyResult>& idResults, QPoint pos );
8585

86+
/**
87+
* Sets an expression context scope used to resolve underlying actions.
88+
*
89+
* @note Added in QGIS 2.18
90+
*/
91+
void setExpressionContextScope( const QgsExpressionContextScope &scope );
92+
93+
/**
94+
* Returns an expression context scope used to resolve underlying actions.
95+
*
96+
* @note Added in QGIS 2.18
97+
*/
98+
QgsExpressionContextScope expressionContextScope() const;
99+
86100
protected:
87101
virtual void closeEvent( QCloseEvent *e );
88102
};

‎src/app/qgisapp.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,8 @@ QgisApp::QgisApp()
11901190
mLayerTreeView = new QgsLayerTreeView( this );
11911191
mUndoWidget = new QgsUndoWidget( nullptr, mMapCanvas );
11921192
mInfoBar = new QgsMessageBar( centralWidget() );
1193+
mPanelMenu = new QMenu( this );
1194+
mProgressBar = new QProgressBar( this );
11931195
// More tests may need more members to be initialized
11941196
}
11951197

‎src/app/qgsidentifyresultsdialog.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ void QgsIdentifyResultsDialog::doAction( QTreeWidgetItem *item, int action )
12371237
}
12381238

12391239
int featIdx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
1240-
layer->actions()->doAction( action, mFeatures[ featIdx ], idx );
1240+
layer->actions()->doAction( action, mFeatures[ featIdx ], idx, mExpressionContextScope );
12411241
}
12421242

12431243
void QgsIdentifyResultsDialog::doMapLayerAction( QTreeWidgetItem *item, QgsMapLayerAction* action )
@@ -1951,6 +1951,16 @@ void QgsIdentifyResultsDialog::formatChanged( int index )
19511951
}
19521952
}
19531953

1954+
void QgsIdentifyResultsDialog::setExpressionContextScope( const QgsExpressionContextScope &scope )
1955+
{
1956+
mExpressionContextScope = scope;
1957+
}
1958+
1959+
QgsExpressionContextScope QgsIdentifyResultsDialog::expressionContextScope() const
1960+
{
1961+
return mExpressionContextScope;
1962+
}
1963+
19541964
/*
19551965
* QgsIdentifyResultsDialogMapLayerAction
19561966
*/

‎src/app/qgsidentifyresultsdialog.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
152152
/** Map tool was activated */
153153
void activate();
154154

155+
/**
156+
* Sets an expression context scope to consider for resolving underlying
157+
* actions.
158+
*
159+
* @note Added in QGIS 2.18
160+
*/
161+
void setExpressionContextScope( const QgsExpressionContextScope &scope );
162+
163+
/**
164+
* Returns an expression context scope used for resolving underlying
165+
* actions.
166+
*
167+
* @note Added in QGIS 2.18
168+
*/
169+
QgsExpressionContextScope expressionContextScope() const;
170+
155171
signals:
156172
void selectedFeatureChanged( QgsVectorLayer *, QgsFeatureId featureId );
157173

@@ -263,6 +279,8 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
263279
QgsDockWidget *mDock;
264280

265281
QVector<QgsIdentifyPlotCurve *> mPlotCurves;
282+
283+
QgsExpressionContextScope mExpressionContextScope;
266284
};
267285

268286
class QgsIdentifyResultsDialogMapLayerAction : public QAction

‎src/app/qgsmaptoolidentifyaction.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent* e )
118118
connect( this, SIGNAL( identifyProgress( int, int ) ), QgisApp::instance(), SLOT( showProgress( int, int ) ) );
119119
connect( this, SIGNAL( identifyMessage( QString ) ), QgisApp::instance(), SLOT( showStatusMessage( QString ) ) );
120120

121+
setClickContextScope( toMapCoordinates( e->pos() ) );
122+
121123
identifyMenu()->setResultsIfExternalAction( false );
122124

123125
// enable the right click for extended menu so it behaves as a contextual menu
@@ -200,4 +202,16 @@ void QgsMapToolIdentifyAction::handleCopyToClipboard( QgsFeatureStore & featureS
200202
emit copyToClipboard( featureStore );
201203
}
202204

205+
void QgsMapToolIdentifyAction::setClickContextScope( const QgsPoint &point )
206+
{
207+
QgsExpressionContextScope clickScope;
208+
clickScope.addVariable( QgsExpressionContextScope::StaticVariable( QString( "click_x" ), point.x(), true ) );
209+
clickScope.addVariable( QgsExpressionContextScope::StaticVariable( QString( "click_y" ), point.y(), true ) );
210+
211+
resultsDialog()->setExpressionContextScope( clickScope );
203212

213+
if ( mIdentifyMenu )
214+
{
215+
mIdentifyMenu->setExpressionContextScope( clickScope );
216+
}
217+
}

‎src/app/qgsmaptoolidentifyaction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ class APP_EXPORT QgsMapToolIdentifyAction : public QgsMapToolIdentify
8282

8383
virtual QGis::UnitType displayDistanceUnits() const override;
8484
virtual QgsUnitTypes::AreaUnit displayAreaUnits() const override;
85+
void setClickContextScope( const QgsPoint &point );
8586

87+
friend class TestQgsMapToolIdentifyAction;
8688
};
8789

8890
#endif

‎src/core/qgsactionmanager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ void QgsActionManager::removeAction( int index )
6363
}
6464
}
6565

66-
void QgsActionManager::doAction( int index, const QgsFeature& feat, int defaultValueIndex )
66+
void QgsActionManager::doAction( int index, const QgsFeature& feat, int defaultValueIndex, const QgsExpressionContextScope &scope )
6767
{
6868
QgsExpressionContext context = createExpressionContext();
69-
QgsExpressionContextScope* actionScope = new QgsExpressionContextScope();
69+
QgsExpressionContextScope* actionScope = new QgsExpressionContextScope( scope );
7070
actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QString( "current_field" ), feat.attribute( defaultValueIndex ), true ) );
7171
context << actionScope;
7272
doAction( index, feat, context );

‎src/core/qgsactionmanager.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,20 @@ class CORE_EXPORT QgsActionManager
7878
//! Remove an action at given index
7979
void removeAction( int index );
8080

81-
/** Does the given values. defaultValueIndex is the index of the
82-
* field to be used if the action has a $currfield placeholder.
83-
* @note available in python bindings as doActionFeature
81+
/**
82+
* Does the given action.
83+
*
84+
* @param index Index of the action
85+
* @param feat Feature to run action for
86+
* @param defaultValueIndex Index of the field to be used if the action has a $currfield placeholder.
87+
* @param scope Expression context scope to add during expression evaluation
88+
*
89+
* @note available in python bindings as doActionFeature
8490
*/
8591
void doAction( int index,
8692
const QgsFeature &feat,
87-
int defaultValueIndex = 0 );
93+
int defaultValueIndex = 0,
94+
const QgsExpressionContextScope &scope = QgsExpressionContextScope() );
8895

8996
/** Does the action using the expression engine to replace any embedded expressions
9097
* in the action definition.

‎src/gui/qgsactionmenu.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void QgsActionMenu::triggerAction()
106106
}
107107
else if ( data.actionType == AttributeAction )
108108
{
109-
mActions->doAction( data.actionId.id, *feature() );
109+
mActions->doAction( data.actionId.id, *feature(), 0, mExpressionContextScope );
110110
}
111111
}
112112

@@ -159,3 +159,12 @@ void QgsActionMenu::reloadActions()
159159
emit reinit();
160160
}
161161

162+
void QgsActionMenu::setExpressionContextScope( const QgsExpressionContextScope &scope )
163+
{
164+
mExpressionContextScope = scope;
165+
}
166+
167+
QgsExpressionContextScope QgsActionMenu::expressionContextScope() const
168+
{
169+
return mExpressionContextScope;
170+
}

‎src/gui/qgsactionmenu.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ class GUI_EXPORT QgsActionMenu : public QMenu
108108
*/
109109
void setFeature( QgsFeature* feature );
110110

111+
/**
112+
* Sets an expression context scope used to resolve underlying actions.
113+
*
114+
* @note Added in QGIS 2.18
115+
*/
116+
void setExpressionContextScope( const QgsExpressionContextScope &scope );
117+
118+
/**
119+
* Returns an expression context scope used to resolve underlying actions.
120+
*
121+
* @note Added in QGIS 2.18
122+
*/
123+
QgsExpressionContextScope expressionContextScope() const;
124+
111125
private slots:
112126
void triggerAction();
113127
void reloadActions();
@@ -124,6 +138,7 @@ class GUI_EXPORT QgsActionMenu : public QMenu
124138
const QgsFeature* mFeature;
125139
QgsFeatureId mFeatureId;
126140
bool mOwnsFeature;
141+
QgsExpressionContextScope mExpressionContextScope;
127142
};
128143

129144
Q_DECLARE_METATYPE( QgsActionMenu::ActionData )

‎src/gui/qgsidentifymenu.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer* layer, const QList<QgsMapT
349349
if ( mShowFeatureActions )
350350
{
351351
featureActionMenu = new QgsActionMenu( layer, result.mFeature.id(), layerMenu );
352+
featureActionMenu->setExpressionContextScope( mExpressionContextScope );
352353
}
353354

354355
// feature title
@@ -643,3 +644,13 @@ void QgsIdentifyMenu::removeCustomActions()
643644
mCustomActionRegistry.clear();
644645

645646
}
647+
648+
void QgsIdentifyMenu::setExpressionContextScope( const QgsExpressionContextScope &scope )
649+
{
650+
mExpressionContextScope = scope;
651+
}
652+
653+
QgsExpressionContextScope QgsIdentifyMenu::expressionContextScope() const
654+
{
655+
return mExpressionContextScope;
656+
}

‎src/gui/qgsidentifymenu.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,20 @@ class GUI_EXPORT QgsIdentifyMenu : public QMenu
148148
*/
149149
QList<QgsMapToolIdentify::IdentifyResult> exec( const QList<QgsMapToolIdentify::IdentifyResult>& idResults, QPoint pos );
150150

151+
/**
152+
* Sets an expression context scope used to resolve underlying actions.
153+
*
154+
* @note Added in QGIS 2.18
155+
*/
156+
void setExpressionContextScope( const QgsExpressionContextScope &scope );
157+
158+
/**
159+
* Returns an expression context scope used to resolve underlying actions.
160+
*
161+
* @note Added in QGIS 2.18
162+
*/
163+
QgsExpressionContextScope expressionContextScope() const;
164+
151165
protected:
152166
virtual void closeEvent( QCloseEvent *e ) override;
153167

@@ -178,6 +192,8 @@ class GUI_EXPORT QgsIdentifyMenu : public QMenu
178192
int mMaxLayerDisplay;
179193
int mMaxFeatureDisplay;
180194

195+
QgsExpressionContextScope mExpressionContextScope;
196+
181197
// name of the action to be displayed for feature default action, if other actions are shown
182198
QString mDefaultActionName;
183199

‎tests/src/app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
2323
)
2424
INCLUDE_DIRECTORIES(SYSTEM
2525
${QT_INCLUDE_DIR}
26+
${QWT_INCLUDE_DIR}
2627
${GDAL_INCLUDE_DIR}
2728
${PROJ_INCLUDE_DIR}
2829
${GEOS_INCLUDE_DIR}

‎tests/src/app/testqgsmaptoolidentifyaction.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#include "qgsmapcanvas.h"
2525
#include "qgsunittypes.h"
2626
#include "qgsmaptoolidentifyaction.h"
27+
#include "qgisapp.h"
28+
#include "qgsidentifymenu.h"
29+
#include "qgsidentifyresultsdialog.h"
2730

2831
#include "cpl_conv.h"
2932

@@ -46,9 +49,11 @@ class TestQgsMapToolIdentifyAction : public QObject
4649
void identifyRasterFloat32(); // test pixel identification and decimal precision
4750
void identifyRasterFloat64(); // test pixel identification and decimal precision
4851
void identifyInvalidPolygons(); // test selecting invalid polygons
52+
void clickxy(); // test if click_x and click_y variables are propagated
4953

5054
private:
5155
QgsMapCanvas* canvas;
56+
QgisApp *mQgisApp;
5257

5358
QString testIdentifyRaster( QgsRasterLayer* layer, double xGeoref, double yGeoref );
5459
QList<QgsMapToolIdentify::IdentifyResult> testIdentifyVector( QgsVectorLayer* layer, double xGeoref, double yGeoref );
@@ -91,6 +96,8 @@ void TestQgsMapToolIdentifyAction::initTestCase()
9196
// enforce C locale because the tests expect it
9297
// (decimal separators / thousand separators)
9398
QLocale::setDefault( QLocale::c() );
99+
100+
mQgisApp = new QgisApp();
94101
}
95102

96103
void TestQgsMapToolIdentifyAction::cleanupTestCase()
@@ -108,6 +115,57 @@ void TestQgsMapToolIdentifyAction::cleanup()
108115
delete canvas;
109116
}
110117

118+
void TestQgsMapToolIdentifyAction::clickxy()
119+
{
120+
int clickxOk = 2484588;
121+
int clickyOk = 2425722;
122+
123+
// create temp layer
124+
QScopedPointer<QgsVectorLayer> tempLayer( new QgsVectorLayer( QString( "Point?crs=epsg:3111" ), QString( "vl" ), QString( "memory" ) ) );
125+
QVERIFY( tempLayer->isValid() );
126+
127+
// add feature
128+
QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
129+
QgsPoint wordPoint( clickxOk, clickyOk );
130+
QgsGeometry *geom = QgsGeometry::fromPoint( wordPoint ) ;
131+
f1.setGeometry( geom );
132+
tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
133+
134+
// prepare canvas
135+
QgsCoordinateReferenceSystem srs( 3111, QgsCoordinateReferenceSystem::EpsgCrsId );
136+
canvas->setDestinationCrs( srs );
137+
canvas->setCurrentLayer( tempLayer.data() );
138+
139+
// init map tool identify action
140+
QScopedPointer<QgsMapToolIdentifyAction> identifyAction( new QgsMapToolIdentifyAction( canvas ) );
141+
142+
// simulate a click on the canvas
143+
QgsPoint mapPoint = canvas->getCoordinateTransform()->transform( 2484588, 2425722 );
144+
QPoint point = QPoint( mapPoint.x(), mapPoint.y() );
145+
QMouseEvent releases( QEvent::MouseButtonRelease, point,
146+
Qt::RightButton, Qt::LeftButton, Qt::NoModifier );
147+
QgsMapMouseEvent mapReleases( 0, &releases );
148+
149+
identifyAction->canvasReleaseEvent( &mapReleases );
150+
151+
// test QgsIdentifyMenu expression context scope
152+
bool ok;
153+
QgsIdentifyMenu *menu = identifyAction->identifyMenu();
154+
int clickx = menu->expressionContextScope().variable( "click_x" ).toString().toInt( &ok, 10 );
155+
QCOMPARE( clickx, clickxOk );
156+
157+
int clicky = menu->expressionContextScope().variable( "click_y" ).toString().toInt( &ok, 10 );
158+
QCOMPARE( clicky, clickyOk );
159+
160+
// test QgsIdentifyResultsDialog expression context scope
161+
QgsIdentifyResultsDialog *dlg = identifyAction->resultsDialog();
162+
clickx = dlg->expressionContextScope().variable( "click_x" ).toString().toInt( &ok, 10 );
163+
QCOMPARE( clickx, clickxOk );
164+
165+
clicky = dlg->expressionContextScope().variable( "click_y" ).toString().toInt( &ok, 10 );
166+
QCOMPARE( clicky, clickyOk );
167+
}
168+
111169
void TestQgsMapToolIdentifyAction::lengthCalculation()
112170
{
113171
QSettings s;

0 commit comments

Comments
 (0)
Please sign in to comment.