16 #include <QMouseEvent>
35 , mAllowMultipleReturn( true )
36 , mExecWithSingleResult( false )
37 , mShowFeatureActions( false )
38 , mResultsIfExternalAction( false )
39 , mMaxLayerDisplay( 10 )
40 , mMaxFeatureDisplay( 10 )
41 , mDefaultActionName( tr(
"Identify" ) )
55 QgsDebugMsg( QStringLiteral(
"invalid value for number of layers displayed." ) );
65 QgsDebugMsg( QStringLiteral(
"invalid value for number of layers displayed." ) );
71 QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
74 mLayerIdResults.clear();
76 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
78 if ( idResults.isEmpty() )
82 if ( idResults.count() == 1 && !mExecWithSingleResult )
84 returnResults << idResults[0];
89 const auto constIdResults = idResults;
93 if ( mLayerIdResults.contains( layer ) )
95 mLayerIdResults[layer].append( result );
99 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
104 const bool singleLayer = mLayerIdResults.count() == 1;
106 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
107 while ( it.hasNext() )
109 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
114 switch ( layer->
type() )
118 addRasterLayer( layer );
126 addVectorLayer( vl, it.value(), singleLayer );
143 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
146 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ),
this );
147 allAction->setData( QVariant::fromValue<ActionData>(
ActionData(
nullptr ) ) );
148 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
149 addAction( allAction );
153 QAction *selectedAction = QMenu::exec( pos );
155 returnResults = results( selectedAction, externalAction );
160 qDeleteAll( findChildren<QgsActionMenu *>() );
162 if ( externalAction && !mResultsIfExternalAction )
164 return QList<QgsMapToolIdentify::IdentifyResult>();
168 return returnResults;
175 QMenu::closeEvent( e );
178 void QgsIdentifyMenu::addRasterLayer(
QgsMapLayer *layer )
180 QAction *layerAction =
nullptr;
181 QMenu *layerMenu =
nullptr;
183 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
185 const int nCustomActions = layerActions.count();
186 if ( nCustomActions )
188 separators.append( layerActions[0] );
190 if ( mShowFeatureActions )
193 if ( layerActions.count() > nCustomActions )
195 separators.append( layerActions[nCustomActions] );
200 if ( layerActions.isEmpty() )
202 layerAction =
new QAction( layer->
name(),
this );
206 layerMenu =
new QMenu( layer->
name(),
this );
207 layerAction = layerMenu->menuAction();
212 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
213 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
214 addAction( layerAction );
221 QAction *identifyFeatureAction =
new QAction( mDefaultActionName, layerMenu );
222 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
223 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
224 layerMenu->addAction( identifyFeatureAction );
227 const auto constLayerActions = layerActions;
230 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
231 action->setData( QVariant::fromValue<ActionData>( ActionData( layer,
true ) ) );
232 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
233 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
234 layerMenu->addAction( action );
235 if ( separators.contains( mapLayerAction ) )
237 layerMenu->insertSeparator( action );
242 void QgsIdentifyMenu::addVectorLayer(
QgsVectorLayer *layer,
const QList<QgsMapToolIdentify::IdentifyResult> &results,
bool singleLayer )
244 QAction *layerAction =
nullptr;
245 QMenu *layerMenu =
nullptr;
251 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
252 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets );
253 const int nCustomActions = layerActions.count();
254 if ( nCustomActions )
256 separators << layerActions[0];
258 if ( mShowFeatureActions )
262 if ( layerActions.count() > nCustomActions )
264 separators << layerActions[nCustomActions];
273 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
280 if ( !createMenu && mShowFeatureActions )
284 createMenu = !featureActionMenu->actions().isEmpty();
285 delete featureActionMenu;
291 exp.prepare( &context );
292 context.setFeature( results[0].mFeature );
297 QString featureTitle = exp.evaluate( &context ).toString();
298 if ( featureTitle.isEmpty() )
299 featureTitle = QString::number( results[0].mFeature.id() );
300 layerAction =
new QAction( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
312 if ( results.count() > 1 )
314 layerMenu =
new QMenu( layer->
name(),
this );
319 QString featureTitle = exp.evaluate( &context ).toString();
320 if ( featureTitle.isEmpty() )
321 featureTitle = QString::number( results[0].mFeature.id() );
322 layerMenu =
new QMenu( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
324 layerAction = layerMenu->menuAction();
334 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
335 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
336 addAction( layerAction );
345 const auto constResults = results;
348 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
352 QAction *featureAction =
nullptr;
353 QMenu *featureMenu =
nullptr;
357 if ( mShowFeatureActions )
359 featureActionMenu =
new QgsActionMenu( layer, result.mFeature, QStringLiteral(
"Feature" ), layerMenu );
365 context.setFeature( result.mFeature );
366 QString featureTitle = exp.evaluate( &context ).toString();
367 if ( featureTitle.isEmpty() )
368 featureTitle = QString::number( result.mFeature.id() );
370 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
372 featureAction =
new QAction( featureTitle, layerMenu );
374 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
375 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
376 layerMenu->addAction( featureAction );
378 else if ( results.count() == 1 )
383 featureMenu = layerMenu;
387 featureMenu =
new QMenu( featureTitle, layerMenu );
390 featureAction = featureMenu->menuAction();
392 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
393 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
394 layerMenu->addAction( featureAction );
402 QAction *identifyFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
403 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
404 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
405 featureMenu->addAction( identifyFeatureAction );
406 featureMenu->addSeparator();
409 const auto constCustomFeatureActions = customFeatureActions;
412 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
413 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
414 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
415 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
416 featureMenu->addAction( action );
419 if ( featureActionMenu )
421 const auto constActions = featureActionMenu->actions();
422 for ( QAction *action : constActions )
424 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
425 featureMenu->addAction( action );
433 if ( mAllowMultipleReturn && results.count() > 1 )
435 layerMenu->addSeparator();
436 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
437 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
438 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
439 layerMenu->addAction( allAction );
443 const auto constLayerActions = layerActions;
446 QString title = mapLayerAction->text();
448 title.append( QStringLiteral(
" (%1)" ).arg( results.count() ) );
449 QAction *action =
new QAction( mapLayerAction->icon(), title, layerMenu );
450 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
451 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
452 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
453 layerMenu->addAction( action );
454 if ( separators.contains( mapLayerAction ) )
456 layerMenu->insertSeparator( action );
461 void QgsIdentifyMenu::triggerMapLayerAction()
463 QAction *action = qobject_cast<QAction *>( sender() );
466 const QVariant varData = action->data();
467 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
470 ActionData actData = action->data().value<ActionData>();
472 if ( actData.mIsValid && actData.mMapLayerAction )
477 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
483 QList<QgsFeature> featureList;
484 const auto results { mLayerIdResults[actData.mLayer] };
487 featureList << result.mFeature;
489 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
495 const auto results { mLayerIdResults[actData.mLayer] };
498 if ( result.mFeature.id() == actData.mFeatureId )
500 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
504 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
510 QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action,
bool &externalAction )
512 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
514 externalAction =
false;
517 bool hasData =
false;
522 const QVariant varData = action->data();
523 if ( !varData.isValid() )
525 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (invalid data)" ) );
529 if ( varData.canConvert<ActionData>() )
531 actData = action->data().value<ActionData>();
532 if ( actData.mIsValid )
534 externalAction = actData.mIsExternalAction;
544 externalAction =
true;
552 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (no data found)" ) );
557 if ( actData.mAllResults )
560 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
561 while ( it.hasNext() )
564 idResults << it.value();
569 if ( !mLayerIdResults.contains( actData.mLayer ) )
571 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (layer not found)" ) );
577 return mLayerIdResults[actData.mLayer];
582 const auto results {mLayerIdResults[actData.mLayer]};
585 if ( res.mFeature.id() == actData.mFeatureId )
593 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
597 void QgsIdentifyMenu::handleMenuHover()
604 QAction *senderAction = qobject_cast<QAction *>( sender() );
609 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
611 const auto constIdResults = idResults;
614 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
620 mRubberBands.append( hl );
621 connect( vl, &QObject::destroyed,
this, &QgsIdentifyMenu::layerDestroyed );
634 color.setAlpha( alpha );
640 void QgsIdentifyMenu::deleteRubberBands()
642 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
643 for ( ; it != mRubberBands.constEnd(); ++it )
645 mRubberBands.clear();
648 void QgsIdentifyMenu::layerDestroyed()
650 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
651 while ( it != mRubberBands.end() )
653 if ( ( *it )->layer() == sender() )
656 it = mRubberBands.erase( it );
667 mCustomActionRegistry.clear();
673 mExpressionContextScope = scope;
678 return mExpressionContextScope;
static const double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/stroke minimum width in mm.
static const double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ IdentifyMode
Identify the feature.
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").
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 setBuffer(double buffer)
Set line / stroke buffer in millimeters.
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.
void setFillColor(const QColor &fillColor)
Fill color for the highlight.
void setColor(const QColor &color)
Set line/stroke to color, polygon fill to color with alpha = 63.
static QIcon iconForWkbType(QgsWkbTypes::Type 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< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, QgsMapLayerAction::Targets targets=QgsMapLayerAction::AllActions)
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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QString displayExpression
@ PointCloudLayer
Added in 3.18.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.