36 , mAllowMultipleReturn( true )
37 , mExecWithSingleResult( false )
38 , mShowFeatureActions( false )
39 , mResultsIfExternalAction( false )
40 , mMaxLayerDisplay( 10 )
41 , mMaxFeatureDisplay( 10 )
42 , mDefaultActionName( tr(
"Identify" ) )
53 QList<QgsMapToolIdentify::IdentifyResult> results;
54 const QMap< QString, QString > derivedAttributes;
57 const double x = mapPoint.
x();
58 const double y = mapPoint.
y();
61 const QList<QgsMapLayer *> layers = canvas->
layers(
true );
66 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
68 bool typeIsSelectable =
false;
73 typeIsSelectable =
true;
77 if ( typeIsSelectable )
89 QgsDebugError( QStringLiteral(
"Could not transform geometry to layer CRS" ) );
93 .setFilterRect( rect )
111 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
121 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
127QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
130 mLayerIdResults.clear();
132 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
134 if ( idResults.isEmpty() )
136 return returnResults;
138 if ( idResults.count() == 1 && !mExecWithSingleResult )
140 returnResults << idResults[0];
141 return returnResults;
145 const auto constIdResults = idResults;
149 if ( mLayerIdResults.contains( layer ) )
151 mLayerIdResults[layer].append( result );
155 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
160 const bool singleLayer = mLayerIdResults.count() == 1;
162 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
163 while ( it.hasNext() )
165 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
170 switch ( layer->
type() )
174 addRasterLayer( layer );
182 addVectorLayer( vl, it.value(), singleLayer );
201 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
204 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ),
this );
205 allAction->setData( QVariant::fromValue<ActionData>(
ActionData(
nullptr ) ) );
206 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
207 addAction( allAction );
211 QAction *selectedAction = QMenu::exec( pos );
213 returnResults = results( selectedAction, externalAction );
218 qDeleteAll( findChildren<QgsActionMenu *>() );
220 if ( externalAction && !mResultsIfExternalAction )
222 return QList<QgsMapToolIdentify::IdentifyResult>();
226 return returnResults;
233 QMenu::closeEvent( e );
236void QgsIdentifyMenu::addRasterLayer(
QgsMapLayer *layer )
238 QAction *layerAction =
nullptr;
239 QMenu *layerMenu =
nullptr;
241 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
244 const int nCustomActions = layerActions.count();
245 if ( nCustomActions )
247 separators.append( layerActions[0] );
249 if ( mShowFeatureActions )
252 if ( layerActions.count() > nCustomActions )
254 separators.append( layerActions[nCustomActions] );
259 if ( layerActions.isEmpty() )
261 layerAction =
new QAction( layer->
name(),
this );
265 layerMenu =
new QMenu( layer->
name(),
this );
266 layerAction = layerMenu->menuAction();
271 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
272 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
273 addAction( layerAction );
280 QAction *identifyFeatureAction =
new QAction( mDefaultActionName, layerMenu );
281 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
282 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
283 layerMenu->addAction( identifyFeatureAction );
286 const auto constLayerActions = layerActions;
289 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
290 action->setData( QVariant::fromValue<ActionData>( ActionData( layer,
true ) ) );
291 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
292 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
293 layerMenu->addAction( action );
294 if ( separators.contains( mapLayerAction ) )
296 layerMenu->insertSeparator( action );
301void QgsIdentifyMenu::addVectorLayer(
QgsVectorLayer *layer,
const QList<QgsMapToolIdentify::IdentifyResult> &results,
bool singleLayer )
303 QAction *layerAction =
nullptr;
304 QMenu *layerMenu =
nullptr;
310 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
312 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
313 const int nCustomActions = layerActions.count();
314 if ( nCustomActions )
316 separators << layerActions[0];
318 if ( mShowFeatureActions )
322 if ( layerActions.count() > nCustomActions )
324 separators << layerActions[nCustomActions];
333 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
340 if ( !createMenu && mShowFeatureActions )
344 createMenu = !featureActionMenu->actions().
isEmpty();
345 delete featureActionMenu;
351 exp.prepare( &context );
352 context.setFeature( results[0].mFeature );
357 QString featureTitle = exp.evaluate( &context ).toString();
358 if ( featureTitle.isEmpty() )
359 featureTitle = QString::number( results[0].mFeature.id() );
360 layerAction =
new QAction( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
372 if ( results.count() > 1 )
374 layerMenu =
new QMenu( layer->
name(),
this );
379 QString featureTitle = exp.evaluate( &context ).toString();
380 if ( featureTitle.isEmpty() )
381 featureTitle = QString::number( results[0].mFeature.id() );
382 layerMenu =
new QMenu( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
384 layerAction = layerMenu->menuAction();
394 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
395 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
396 addAction( layerAction );
405 const auto constResults = results;
408 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
412 QAction *featureAction =
nullptr;
413 QMenu *featureMenu =
nullptr;
417 if ( mShowFeatureActions )
419 featureActionMenu =
new QgsActionMenu( layer, result.mFeature, QStringLiteral(
"Feature" ), layerMenu );
425 context.setFeature( result.mFeature );
426 QString featureTitle = exp.evaluate( &context ).toString();
427 if ( featureTitle.isEmpty() )
428 featureTitle = QString::number( result.mFeature.id() );
430 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().
isEmpty() ) )
432 featureAction =
new QAction( featureTitle, layerMenu );
434 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
435 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
436 layerMenu->addAction( featureAction );
438 else if ( results.count() == 1 )
443 featureMenu = layerMenu;
447 featureMenu =
new QMenu( featureTitle, layerMenu );
450 featureAction = featureMenu->menuAction();
452 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
453 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
454 layerMenu->addAction( featureAction );
462 QAction *identifyFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
463 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
464 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
465 featureMenu->addAction( identifyFeatureAction );
466 featureMenu->addSeparator();
469 const auto constCustomFeatureActions = customFeatureActions;
472 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
473 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
474 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
475 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
476 featureMenu->addAction( action );
479 if ( featureActionMenu )
481 const auto constActions = featureActionMenu->actions();
482 for ( QAction *action : constActions )
484 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
485 featureMenu->addAction( action );
493 if ( mAllowMultipleReturn && results.count() > 1 )
495 layerMenu->addSeparator();
496 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
497 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
498 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
499 layerMenu->addAction( allAction );
503 const auto constLayerActions = layerActions;
506 QString title = mapLayerAction->text();
508 title.append( QStringLiteral(
" (%1)" ).arg( results.count() ) );
509 QAction *action =
new QAction( mapLayerAction->icon(), title, layerMenu );
510 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
511 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
512 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
513 layerMenu->addAction( action );
514 if ( separators.contains( mapLayerAction ) )
516 layerMenu->insertSeparator( action );
521void QgsIdentifyMenu::triggerMapLayerAction()
523 QAction *action = qobject_cast<QAction *>( sender() );
526 const QVariant varData = action->data();
527 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
530 ActionData actData = action->data().value<ActionData>();
532 if ( actData.mIsValid && actData.mMapLayerAction )
540 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
542 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
548 QList<QgsFeature> featureList;
549 const auto results { mLayerIdResults[actData.mLayer] };
552 featureList << result.mFeature;
555 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
557 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
563 const auto results { mLayerIdResults[actData.mLayer] };
566 if ( result.mFeature.id() == actData.mFeatureId )
569 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
571 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
575 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
581QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action,
bool &externalAction )
583 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
585 externalAction =
false;
588 bool hasData =
false;
593 const QVariant varData = action->data();
594 if ( !varData.isValid() )
596 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (invalid data)" ) );
600 if ( varData.canConvert<ActionData>() )
602 actData = action->data().value<ActionData>();
603 if ( actData.mIsValid )
605 externalAction = actData.mIsExternalAction;
615 externalAction =
true;
623 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (no data found)" ) );
628 if ( actData.mAllResults )
631 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
632 while ( it.hasNext() )
635 idResults << it.value();
640 if ( !mLayerIdResults.contains( actData.mLayer ) )
642 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (layer not found)" ) );
648 return mLayerIdResults[actData.mLayer];
653 const auto results {mLayerIdResults[actData.mLayer]};
656 if ( res.mFeature.id() == actData.mFeatureId )
664 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
668void QgsIdentifyMenu::handleMenuHover()
675 QAction *senderAction = qobject_cast<QAction *>( sender() );
680 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
682 const auto constIdResults = idResults;
685 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
691 mRubberBands.append( hl );
692 connect( vl, &QObject::destroyed,
this, &QgsIdentifyMenu::layerDestroyed );
701void QgsIdentifyMenu::deleteRubberBands()
703 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
704 for ( ; it != mRubberBands.constEnd(); ++it )
706 mRubberBands.clear();
709void QgsIdentifyMenu::layerDestroyed()
711 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
712 while ( it != mRubberBands.end() )
714 if ( ( *it )->layer() == sender() )
717 it = mRubberBands.erase( it );
728 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)