33#include "moc_qgsidentifymenu.cpp"
39 , mDefaultActionName( tr(
"Identify" ) )
50 QList<QgsMapToolIdentify::IdentifyResult> results;
51 const QMap<QString, QString> derivedAttributes;
54 const double x = mapPoint.
x();
55 const double y = mapPoint.
y();
58 const QList<QgsMapLayer *> layers = canvas->
layers(
true );
63 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
65 bool typeIsSelectable =
false;
70 typeIsSelectable =
true;
74 if ( typeIsSelectable )
86 QgsDebugError( QStringLiteral(
"Could not transform geometry to layer CRS" ) );
90 .setFilterRect( rect )
108 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
118 QgsDebugError( QStringLiteral(
"invalid value for number of layers displayed." ) );
124QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
127 mLayerIdResults.clear();
129 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
131 if ( idResults.isEmpty() )
133 return returnResults;
135 if ( idResults.count() == 1 && !mExecWithSingleResult )
137 returnResults << idResults[0];
138 return returnResults;
142 const auto constIdResults = idResults;
146 if ( mLayerIdResults.contains( layer ) )
148 mLayerIdResults[layer].append( result );
152 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
157 const bool singleLayer = mLayerIdResults.count() == 1;
159 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
160 while ( it.hasNext() )
162 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
167 switch ( layer->
type() )
171 addRasterLayer( layer );
179 addVectorLayer( vl, it.value(), singleLayer );
198 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
201 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ),
this );
202 allAction->setData( QVariant::fromValue<ActionData>(
ActionData(
nullptr ) ) );
203 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
204 addAction( allAction );
208 QAction *selectedAction = QMenu::exec( pos );
210 returnResults = results( selectedAction, externalAction );
215 qDeleteAll( findChildren<QgsActionMenu *>() );
217 if ( externalAction && !mResultsIfExternalAction )
219 return QList<QgsMapToolIdentify::IdentifyResult>();
223 return returnResults;
230 QMenu::closeEvent( e );
233void QgsIdentifyMenu::addRasterLayer(
QgsMapLayer *layer )
235 QAction *layerAction =
nullptr;
236 QMenu *layerMenu =
nullptr;
238 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
241 const int nCustomActions = layerActions.count();
242 if ( nCustomActions )
244 separators.append( layerActions[0] );
246 if ( mShowFeatureActions )
249 if ( layerActions.count() > nCustomActions )
251 separators.append( layerActions[nCustomActions] );
256 if ( layerActions.isEmpty() )
258 layerAction =
new QAction( layer->
name(),
this );
262 layerMenu =
new QMenu( layer->
name(),
this );
263 layerAction = layerMenu->menuAction();
268 layerAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer ) ) );
269 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
270 addAction( layerAction );
277 QAction *identifyFeatureAction =
new QAction( mDefaultActionName, layerMenu );
278 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
279 identifyFeatureAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer ) ) );
280 layerMenu->addAction( identifyFeatureAction );
283 const auto constLayerActions = layerActions;
284 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
286 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
287 action->setData( QVariant::fromValue<ActionData>(
ActionData( layer,
true ) ) );
288 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
289 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
290 layerMenu->addAction( action );
291 if ( separators.contains( mapLayerAction ) )
293 layerMenu->insertSeparator( action );
298void QgsIdentifyMenu::addVectorLayer(
QgsVectorLayer *layer,
const QList<QgsMapToolIdentify::IdentifyResult> &results,
bool singleLayer )
300 QAction *layerAction =
nullptr;
301 QMenu *layerMenu =
nullptr;
307 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
308 QgsMapLayerActionContext actionContext;
309 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
310 const int nCustomActions = layerActions.count();
311 if ( nCustomActions )
313 separators << layerActions[0];
315 if ( mShowFeatureActions )
319 if ( layerActions.count() > nCustomActions )
321 separators << layerActions[nCustomActions];
330 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
337 if ( !createMenu && mShowFeatureActions )
339 QgsActionMenu *featureActionMenu =
new QgsActionMenu( layer, results[0].mFeature, QStringLiteral(
"Feature" ),
this );
343 createMenu = !featureActionMenu->actions().
isEmpty();
344 delete featureActionMenu;
350 exp.prepare( &context );
351 context.setFeature( results[0].mFeature );
356 QString featureTitle = exp.evaluate( &context ).toString();
357 if ( featureTitle.isEmpty() )
358 featureTitle = QString::number( results[0].mFeature.id() );
359 layerAction =
new QAction( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
371 if ( results.count() > 1 )
373 layerMenu =
new QMenu( layer->
name(),
this );
378 QString featureTitle = exp.evaluate( &context ).toString();
379 if ( featureTitle.isEmpty() )
380 featureTitle = QString::number( results[0].mFeature.id() );
381 layerMenu =
new QMenu( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
383 layerAction = layerMenu->menuAction();
393 layerAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer ) ) );
394 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
395 addAction( layerAction );
404 const auto constResults = results;
405 for (
const QgsMapToolIdentify::IdentifyResult &result : constResults )
407 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
411 QAction *featureAction =
nullptr;
412 QMenu *featureMenu =
nullptr;
413 QgsActionMenu *featureActionMenu =
nullptr;
416 if ( mShowFeatureActions )
418 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() );
433 if ( layerMenu ==
this )
434 featureTitle = QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle );
436 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().
isEmpty() ) )
438 featureAction =
new QAction( featureTitle, layerMenu );
440 featureAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer, result.mFeature.id() ) ) );
441 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
442 layerMenu->addAction( featureAction );
444 else if ( results.count() == 1 )
449 featureMenu = layerMenu;
453 featureMenu =
new QMenu( featureTitle, layerMenu );
456 featureAction = featureMenu->menuAction();
458 featureAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer, result.mFeature.id() ) ) );
459 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
460 layerMenu->addAction( featureAction );
468 QAction *identifyFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
469 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
470 identifyFeatureAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer, result.mFeature.id() ) ) );
471 featureMenu->addAction( identifyFeatureAction );
472 featureMenu->addSeparator();
475 const auto constCustomFeatureActions = customFeatureActions;
476 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
478 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
479 action->setData( QVariant::fromValue<ActionData>(
ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
480 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
481 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
482 featureMenu->addAction( action );
485 if ( featureActionMenu )
487 const auto constActions = featureActionMenu->actions();
488 for ( QAction *action : constActions )
490 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
491 featureMenu->addAction( action );
499 if ( mAllowMultipleReturn && results.count() > 1 )
501 layerMenu->addSeparator();
502 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
503 allAction->setData( QVariant::fromValue<ActionData>(
ActionData( layer ) ) );
504 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
505 layerMenu->addAction( allAction );
509 const auto constLayerActions = layerActions;
510 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
512 QString title = mapLayerAction->text();
514 title.append( QStringLiteral(
" (%1)" ).arg( results.count() ) );
515 QAction *action =
new QAction( mapLayerAction->icon(), title, layerMenu );
516 action->setData( QVariant::fromValue<ActionData>(
ActionData( layer, mapLayerAction ) ) );
517 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
518 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
519 layerMenu->addAction( action );
520 if ( separators.contains( mapLayerAction ) )
522 layerMenu->insertSeparator( action );
527void QgsIdentifyMenu::triggerMapLayerAction()
529 QAction *action = qobject_cast<QAction *>( sender() );
532 const QVariant varData = action->data();
533 if ( !varData.isValid() || !varData.canConvert<
ActionData>() )
538 if ( actData.mIsValid && actData.mMapLayerAction )
540 QgsMapLayerActionContext context;
546 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
548 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
554 QList<QgsFeature> featureList;
555 const auto results { mLayerIdResults[actData.mLayer] };
556 for (
const QgsMapToolIdentify::IdentifyResult &result : results )
558 featureList << result.mFeature;
561 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
563 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
569 const auto results { mLayerIdResults[actData.mLayer] };
570 for (
const QgsMapToolIdentify::IdentifyResult &result : results )
572 if ( result.mFeature.id() == actData.mFeatureId )
575 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
577 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
581 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
587QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action,
bool &externalAction )
589 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
591 externalAction =
false;
594 bool hasData =
false;
599 const QVariant varData = action->data();
600 if ( !varData.isValid() )
602 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (invalid data)" ) );
609 if ( actData.mIsValid )
611 externalAction = actData.mIsExternalAction;
616 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
618 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
621 externalAction =
true;
629 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (no data found)" ) );
634 if ( actData.mAllResults )
637 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
638 while ( it.hasNext() )
641 idResults << it.value();
646 if ( !mLayerIdResults.contains( actData.mLayer ) )
648 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (layer not found)" ) );
654 return mLayerIdResults[actData.mLayer];
659 const auto results { mLayerIdResults[actData.mLayer] };
660 for (
const QgsMapToolIdentify::IdentifyResult &res : results )
662 if ( res.mFeature.id() == actData.mFeatureId )
670 QgsDebugError( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
674void QgsIdentifyMenu::handleMenuHover()
681 QAction *senderAction = qobject_cast<QAction *>( sender() );
686 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
688 const auto constIdResults = idResults;
689 for (
const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
691 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
695 QgsHighlight *hl =
new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
697 mRubberBands.append( hl );
698 connect( vl, &QObject::destroyed,
this, &QgsIdentifyMenu::layerDestroyed );
707void QgsIdentifyMenu::deleteRubberBands()
709 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
710 for ( ; it != mRubberBands.constEnd(); ++it )
712 mRubberBands.clear();
715void QgsIdentifyMenu::layerDestroyed()
717 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
718 while ( it != mRubberBands.end() )
720 if ( ( *it )->layer() == sender() )
723 it = mRubberBands.erase( it );
734 mCustomActionRegistry.clear();
739 mExpressionContextScope = scope;
744 return mExpressionContextScope;
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.
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.
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.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QString displayExpression
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugError(str)