19#include "moc_qgsidentifymenu.cpp"
37 , mAllowMultipleReturn( true )
38 , mExecWithSingleResult( false )
39 , mShowFeatureActions( false )
40 , mResultsIfExternalAction( false )
41 , mMaxLayerDisplay( 10 )
42 , mMaxFeatureDisplay( 10 )
43 , mDefaultActionName( tr(
"Identify" ) )
54 QList<QgsMapToolIdentify::IdentifyResult> results;
55 const QMap<QString, QString> derivedAttributes;
58 const double x = mapPoint.
x();
59 const double y = mapPoint.
y();
62 const QList<QgsMapLayer *> layers = canvas->
layers(
true );
67 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
69 bool typeIsSelectable =
false;
74 typeIsSelectable =
true;
78 if ( typeIsSelectable )
90 QgsDebugError( QStringLiteral(
"Could not transform geometry to layer CRS" ) );
94 .setFilterRect( rect )
112 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
122 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
128QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
131 mLayerIdResults.clear();
133 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
135 if ( idResults.isEmpty() )
137 return returnResults;
139 if ( idResults.count() == 1 && !mExecWithSingleResult )
141 returnResults << idResults[0];
142 return returnResults;
146 const auto constIdResults = idResults;
150 if ( mLayerIdResults.contains( layer ) )
152 mLayerIdResults[layer].append( result );
156 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
161 const bool singleLayer = mLayerIdResults.count() == 1;
163 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
164 while ( it.hasNext() )
166 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
171 switch ( layer->
type() )
175 addRasterLayer( layer );
183 addVectorLayer( vl, it.value(), singleLayer );
202 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
205 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ),
this );
206 allAction->setData( QVariant::fromValue<ActionData>(
ActionData(
nullptr ) ) );
207 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
208 addAction( allAction );
212 QAction *selectedAction = QMenu::exec( pos );
214 returnResults = results( selectedAction, externalAction );
219 qDeleteAll( findChildren<QgsActionMenu *>() );
221 if ( externalAction && !mResultsIfExternalAction )
223 return QList<QgsMapToolIdentify::IdentifyResult>();
227 return returnResults;
234 QMenu::closeEvent( e );
237void QgsIdentifyMenu::addRasterLayer(
QgsMapLayer *layer )
239 QAction *layerAction =
nullptr;
240 QMenu *layerMenu =
nullptr;
242 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
245 const int nCustomActions = layerActions.count();
246 if ( nCustomActions )
248 separators.append( layerActions[0] );
250 if ( mShowFeatureActions )
253 if ( layerActions.count() > nCustomActions )
255 separators.append( layerActions[nCustomActions] );
260 if ( layerActions.isEmpty() )
262 layerAction =
new QAction( layer->
name(),
this );
266 layerMenu =
new QMenu( layer->
name(),
this );
267 layerAction = layerMenu->menuAction();
272 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
273 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
274 addAction( layerAction );
281 QAction *identifyFeatureAction =
new QAction( mDefaultActionName, layerMenu );
282 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
283 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
284 layerMenu->addAction( identifyFeatureAction );
287 const auto constLayerActions = layerActions;
290 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
291 action->setData( QVariant::fromValue<ActionData>( ActionData( layer,
true ) ) );
292 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
293 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
294 layerMenu->addAction( action );
295 if ( separators.contains( mapLayerAction ) )
297 layerMenu->insertSeparator( action );
302void QgsIdentifyMenu::addVectorLayer(
QgsVectorLayer *layer,
const QList<QgsMapToolIdentify::IdentifyResult> &results,
bool singleLayer )
304 QAction *layerAction =
nullptr;
305 QMenu *layerMenu =
nullptr;
311 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
313 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
314 const int nCustomActions = layerActions.count();
315 if ( nCustomActions )
317 separators << layerActions[0];
319 if ( mShowFeatureActions )
323 if ( layerActions.count() > nCustomActions )
325 separators << layerActions[nCustomActions];
334 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
341 if ( !createMenu && mShowFeatureActions )
345 createMenu = !featureActionMenu->actions().
isEmpty();
346 delete featureActionMenu;
352 exp.prepare( &context );
353 context.setFeature( results[0].mFeature );
358 QString featureTitle = exp.evaluate( &context ).toString();
359 if ( featureTitle.isEmpty() )
360 featureTitle = QString::number( results[0].mFeature.id() );
361 layerAction =
new QAction( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
373 if ( results.count() > 1 )
375 layerMenu =
new QMenu( layer->
name(),
this );
380 QString featureTitle = exp.evaluate( &context ).toString();
381 if ( featureTitle.isEmpty() )
382 featureTitle = QString::number( results[0].mFeature.id() );
383 layerMenu =
new QMenu( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
385 layerAction = layerMenu->menuAction();
395 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
396 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
397 addAction( layerAction );
406 const auto constResults = results;
409 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
413 QAction *featureAction =
nullptr;
414 QMenu *featureMenu =
nullptr;
418 if ( mShowFeatureActions )
420 featureActionMenu =
new QgsActionMenu( layer, result.mFeature, QStringLiteral(
"Feature" ), layerMenu );
426 context.setFeature( result.mFeature );
427 QString featureTitle = exp.evaluate( &context ).toString();
428 if ( featureTitle.isEmpty() )
429 featureTitle = QString::number( result.mFeature.id() );
431 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().
isEmpty() ) )
433 featureAction =
new QAction( featureTitle, layerMenu );
435 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
436 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
437 layerMenu->addAction( featureAction );
439 else if ( results.count() == 1 )
444 featureMenu = layerMenu;
448 featureMenu =
new QMenu( featureTitle, layerMenu );
451 featureAction = featureMenu->menuAction();
453 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
454 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
455 layerMenu->addAction( featureAction );
463 QAction *identifyFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
464 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
465 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
466 featureMenu->addAction( identifyFeatureAction );
467 featureMenu->addSeparator();
470 const auto constCustomFeatureActions = customFeatureActions;
473 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
474 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
475 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
476 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
477 featureMenu->addAction( action );
480 if ( featureActionMenu )
482 const auto constActions = featureActionMenu->actions();
483 for ( QAction *action : constActions )
485 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
486 featureMenu->addAction( action );
494 if ( mAllowMultipleReturn && results.count() > 1 )
496 layerMenu->addSeparator();
497 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
498 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
499 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
500 layerMenu->addAction( allAction );
504 const auto constLayerActions = layerActions;
507 QString title = mapLayerAction->text();
509 title.append( QStringLiteral(
" (%1)" ).arg( results.count() ) );
510 QAction *action =
new QAction( mapLayerAction->icon(), title, layerMenu );
511 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
512 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
513 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
514 layerMenu->addAction( action );
515 if ( separators.contains( mapLayerAction ) )
517 layerMenu->insertSeparator( action );
522void QgsIdentifyMenu::triggerMapLayerAction()
524 QAction *action = qobject_cast<QAction *>( sender() );
527 const QVariant varData = action->data();
528 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
531 ActionData actData = action->data().value<ActionData>();
533 if ( actData.mIsValid && actData.mMapLayerAction )
541 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
543 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
549 QList<QgsFeature> featureList;
550 const auto results { mLayerIdResults[actData.mLayer] };
553 featureList << result.mFeature;
556 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
558 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
564 const auto results { mLayerIdResults[actData.mLayer] };
567 if ( result.mFeature.id() == actData.mFeatureId )
570 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
572 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
576 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
582QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action,
bool &externalAction )
584 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
586 externalAction =
false;
589 bool hasData =
false;
594 const QVariant varData = action->data();
595 if ( !varData.isValid() )
597 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (invalid data)" ) );
601 if ( varData.canConvert<ActionData>() )
603 actData = action->data().value<ActionData>();
604 if ( actData.mIsValid )
606 externalAction = actData.mIsExternalAction;
616 externalAction =
true;
624 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (no data found)" ) );
629 if ( actData.mAllResults )
632 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
633 while ( it.hasNext() )
636 idResults << it.value();
641 if ( !mLayerIdResults.contains( actData.mLayer ) )
643 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (layer not found)" ) );
649 return mLayerIdResults[actData.mLayer];
654 const auto results { mLayerIdResults[actData.mLayer] };
657 if ( res.mFeature.id() == actData.mFeatureId )
665 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
669void QgsIdentifyMenu::handleMenuHover()
676 QAction *senderAction = qobject_cast<QAction *>( sender() );
681 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
683 const auto constIdResults = idResults;
686 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
692 mRubberBands.append( hl );
693 connect( vl, &QObject::destroyed,
this, &QgsIdentifyMenu::layerDestroyed );
702void QgsIdentifyMenu::deleteRubberBands()
704 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
705 for ( ; it != mRubberBands.constEnd(); ++it )
707 mRubberBands.clear();
710void QgsIdentifyMenu::layerDestroyed()
712 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
713 while ( it != mRubberBands.end() )
715 if ( ( *it )->layer() == sender() )
718 it = mRubberBands.erase( it );
729 mCustomActionRegistry.clear();
734 mExpressionContextScope = scope;
739 return mExpressionContextScope;
The Qgis class provides global constants for use throughout the application.
QFlags< MapLayerActionTarget > MapLayerActionTargets
Map layer action targets.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
@ MultipleFeatures
Action targets multiple features from a layer.
@ Layer
Action targets a complete layer.
@ SingleFeature
Action targets a single feature from a layer.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ Reverse
Reverse/inverse transform (from destination to source)
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ IdentifyMode
Identify the feature.
Custom exception class for Coordinate Reference System related exceptions.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
A class for highlight features on the map.
void applyDefaultStyle()
Applies the default style from the user settings to the highlight.
static QIcon iconForWkbType(Qgis::WkbType type)
Returns the icon for a vector layer whose geometry type is provided.
Map canvas is a class for displaying all GIS data types on a canvas.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers shown within the map canvas.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Encapsulates the context in which a QgsMapLayerAction action is executed.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, Qgis::MapLayerActionTargets targets=Qgis::MapLayerActionTarget::AllActions, const QgsMapLayerActionContext &context=QgsMapLayerActionContext())
Returns the map layer actions which can run on the specified layer.
An action which can run on map layers The class can be used in two manners:
Base class for all map layer types.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
A class to represent a 2D point.
A rectangle specified with double values.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QString displayExpression
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugError(str)