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;
128QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos ) {
…}
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;
Provides global constants and enumerations 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...
Handles 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.
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.
Highlights 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.
Base class for all map layer types.
A mouse event which is the result of a user interaction with 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 rectangle specified with double values.
Represents a vector layer which manages a vector based dataset.
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)