QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsidentifymenu.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsidentifymenu.cpp - menu to be used in identify map tool
3 ---------------------
4 begin : August 2014
5 copyright : (C) 2014 by Denis Rouzaud
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsidentifymenu.h"
17
18#include "qgsactionmenu.h"
19#include "qgsapplication.h"
21#include "qgsgui.h"
22#include "qgshighlight.h"
23#include "qgsiconutils.h"
24#include "qgslogger.h"
25#include "qgsmapcanvas.h"
26#include "qgsmaplayeraction.h"
28#include "qgsmapmouseevent.h"
29#include "qgsvectorlayer.h"
30
31#include <QMouseEvent>
32#include <QString>
33
34#include "moc_qgsidentifymenu.cpp"
35
36using namespace Qt::StringLiterals;
37
38//TODO 4.0 add explicitly qobject parent to constructor
40 : QMenu( canvas )
41 , mCanvas( canvas )
42 , mDefaultActionName( tr( "Identify" ) )
43{
44}
45
47{
48 deleteRubberBands();
49}
50
51QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::findFeaturesOnCanvas( QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList<Qgis::GeometryType> &geometryTypes )
52{
53 QList<QgsMapToolIdentify::IdentifyResult> results;
54 const QMap<QString, QString> derivedAttributes;
55
56 const QgsPointXY mapPoint = canvas->getCoordinateTransform()->toMapCoordinates( event->pos() );
57 const double x = mapPoint.x();
58 const double y = mapPoint.y();
59 const double sr = QgsMapTool::searchRadiusMU( canvas );
60
61 const QList<QgsMapLayer *> layers = canvas->layers( true );
62 for ( QgsMapLayer *layer : layers )
63 {
64 if ( layer->type() == Qgis::LayerType::Vector )
65 {
66 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
67
68 bool typeIsSelectable = false;
69 for ( Qgis::GeometryType type : geometryTypes )
70 {
71 if ( vectorLayer->geometryType() == type )
72 {
73 typeIsSelectable = true;
74 break;
75 }
76 }
77 if ( typeIsSelectable )
78 {
79 QgsRectangle rect( x - sr, y - sr, x + sr, y + sr );
80 QgsCoordinateTransform transform = canvas->mapSettings().layerTransform( vectorLayer );
82
83 try
84 {
86 }
87 catch ( QgsCsException & )
88 {
89 QgsDebugError( u"Could not transform geometry to layer CRS"_s );
90 }
91
93 .setFilterRect( rect )
95 QgsFeature f;
96 while ( fit.nextFeature( f ) )
97 {
98 results << QgsMapToolIdentify::IdentifyResult( vectorLayer, f, derivedAttributes );
99 }
100 }
101 }
102 }
103
104 return results;
105}
106
108{
109 if ( maxLayerDisplay < 0 )
110 {
111 QgsDebugError( u"invalid value for number of layers displayed."_s );
112 }
113 mMaxLayerDisplay = maxLayerDisplay;
114}
115
116
118{
119 if ( maxFeatureDisplay < 0 )
120 {
121 QgsDebugError( u"invalid value for number of layers displayed."_s );
122 }
123 mMaxFeatureDisplay = maxFeatureDisplay;
124}
125
126
127QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::exec( const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
128{
129 clear();
130 mLayerIdResults.clear();
131
132 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
133
134 if ( idResults.isEmpty() )
135 {
136 return returnResults;
137 }
138 if ( idResults.count() == 1 && !mExecWithSingleResult )
139 {
140 returnResults << idResults[0];
141 return returnResults;
142 }
143
144 // sort results by layer
145 const auto constIdResults = idResults;
146 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
147 {
148 QgsMapLayer *layer = result.mLayer;
149 if ( mLayerIdResults.contains( layer ) )
150 {
151 mLayerIdResults[layer].append( result );
152 }
153 else
154 {
155 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
156 }
157 }
158
159 // add results to the menu
160 const bool singleLayer = mLayerIdResults.count() == 1;
161 int count = 0;
162 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
163 while ( it.hasNext() )
164 {
165 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
166 break;
167 ++count;
168 it.next();
169 QgsMapLayer *layer = it.key();
170 switch ( layer->type() )
171 {
173 {
174 addRasterLayer( layer );
175 break;
176 }
178 {
179 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
180 if ( !vl )
181 continue;
182 addVectorLayer( vl, it.value(), singleLayer );
183 break;
184 }
185
187 // TODO: add support
188 break;
189
196 break;
197 }
198 }
199
200 // add an "identify all" action on the top level
201 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
202 {
203 addSeparator();
204 QAction *allAction = new QAction( QgsApplication::getThemeIcon( u"/mActionIdentify.svg"_s ), 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 );
208 }
209
210 // exec
211 QAction *selectedAction = QMenu::exec( pos );
212 bool externalAction;
213 returnResults = results( selectedAction, externalAction );
214
215 // delete actions
216 clear();
217 // also remove the QgsActionMenu
218 qDeleteAll( findChildren<QgsActionMenu *>() );
219
220 if ( externalAction && !mResultsIfExternalAction )
221 {
222 return QList<QgsMapToolIdentify::IdentifyResult>();
223 }
224 else
225 {
226 return returnResults;
227 }
228}
229
230void QgsIdentifyMenu::closeEvent( QCloseEvent *e )
231{
232 deleteRubberBands();
233 QMenu::closeEvent( e );
234}
235
236void QgsIdentifyMenu::addRasterLayer( QgsMapLayer *layer )
237{
238 QAction *layerAction = nullptr;
239 QMenu *layerMenu = nullptr;
240
241 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
243 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context );
244 const int nCustomActions = layerActions.count();
245 if ( nCustomActions )
246 {
247 separators.append( layerActions[0] );
248 }
249 if ( mShowFeatureActions )
250 {
251 layerActions.append( QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context ) );
252 if ( layerActions.count() > nCustomActions )
253 {
254 separators.append( layerActions[nCustomActions] );
255 }
256 }
257
258 // use a menu only if actions will be listed
259 if ( layerActions.isEmpty() )
260 {
261 layerAction = new QAction( layer->name(), this );
262 }
263 else
264 {
265 layerMenu = new QMenu( layer->name(), this );
266 layerAction = layerMenu->menuAction();
267 }
268
269 // add layer action to the top menu
270 layerAction->setIcon( QgsApplication::getThemeIcon( u"/mIconRasterLayer.svg"_s ) );
271 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
272 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
273 addAction( layerAction );
274
275 // no need to go further if there is no menu
276 if ( !layerMenu )
277 return;
278
279 // add default identify action
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 );
284
285 // add custom/layer actions
286 const auto constLayerActions = layerActions;
287 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
288 {
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 ) )
295 {
296 layerMenu->insertSeparator( action );
297 }
298 }
299}
300
301void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapToolIdentify::IdentifyResult> &results, bool singleLayer )
302{
303 QAction *layerAction = nullptr;
304 QMenu *layerMenu = nullptr;
305
306 // do not add actions with MultipleFeatures as target if only 1 feature is found for this layer
307 // targets defines which actions will be shown
308 const Qgis::MapLayerActionTargets targets = results.count() > 1 ? ( Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::MultipleFeatures ) : Qgis::MapLayerActionTarget::Layer;
309
310 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
311 QgsMapLayerActionContext actionContext;
312 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
313 const int nCustomActions = layerActions.count();
314 if ( nCustomActions )
315 {
316 separators << layerActions[0];
317 }
318 if ( mShowFeatureActions )
319 {
320 layerActions << QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, targets, actionContext );
321
322 if ( layerActions.count() > nCustomActions )
323 {
324 separators << layerActions[nCustomActions];
325 }
326 }
327
328 // determines if a menu should be created or not. Following cases:
329 // 1. only one result and no feature action to be shown => just create an action
330 // 2. several features (2a) or display feature actions (2b) => create a menu
331 // 3. case 2 but only one layer (singeLayer) => do not create a menu, but give the top menu instead
332
333 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
334
335 // case 2b: still create a menu for layer, if there is a sub-level for features
336 // i.e custom actions or map layer actions at feature level
337 if ( !createMenu )
338 {
339 createMenu = !mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext ).isEmpty();
340 if ( !createMenu && mShowFeatureActions )
341 {
342 QgsActionMenu *featureActionMenu = new QgsActionMenu( layer, results[0].mFeature, u"Feature"_s, this );
343 connect( featureActionMenu, &QgsActionMenu::messageEmitted, this, &QgsIdentifyMenu::messageEmitted );
344 connect( featureActionMenu, &QgsActionMenu::messageDiscarded, this, &QgsIdentifyMenu::messageDiscarded );
346 createMenu = !featureActionMenu->actions().isEmpty();
347 delete featureActionMenu;
348 }
349 }
350
351 QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
352 QgsExpression exp( layer->displayExpression() );
353 exp.prepare( &context );
354 context.setFeature( results[0].mFeature );
355 // use a menu only if actions will be listed
356 if ( !createMenu )
357 {
358 // case 1
359 QString featureTitle = exp.evaluate( &context ).toString();
360 if ( featureTitle.isEmpty() )
361 featureTitle = QString::number( results[0].mFeature.id() );
362 layerAction = new QAction( u"%1 (%2)"_s.arg( layer->name(), featureTitle ), this );
363 }
364 else
365 {
366 if ( singleLayer )
367 {
368 // case 3
369 layerMenu = this;
370 }
371 else
372 {
373 // case 2a
374 if ( results.count() > 1 )
375 {
376 layerMenu = new QMenu( layer->name(), this );
377 }
378 // case 2b
379 else
380 {
381 QString featureTitle = exp.evaluate( &context ).toString();
382 if ( featureTitle.isEmpty() )
383 featureTitle = QString::number( results[0].mFeature.id() );
384 layerMenu = new QMenu( u"%1 (%2)"_s.arg( layer->name(), featureTitle ), this );
385 }
386 layerAction = layerMenu->menuAction();
387 }
388 }
389
390 // case 1 or 2
391 if ( layerAction )
392 {
393 layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
394
395 // add layer action to the top menu
396 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
397 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
398 addAction( layerAction );
399 }
400
401 // case 1. no need to go further
402 if ( !layerMenu )
403 return;
404
405 // add results to the menu
406 int count = 0;
407 const auto constResults = results;
408 for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
409 {
410 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
411 break;
412 ++count;
413
414 QAction *featureAction = nullptr;
415 QMenu *featureMenu = nullptr;
416 QgsActionMenu *featureActionMenu = nullptr;
417
418 const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext );
419 if ( mShowFeatureActions )
420 {
421 featureActionMenu = new QgsActionMenu( layer, result.mFeature, u"Feature"_s, layerMenu );
422 connect( featureActionMenu, &QgsActionMenu::messageEmitted, this, &QgsIdentifyMenu::messageEmitted );
423 connect( featureActionMenu, &QgsActionMenu::messageDiscarded, this, &QgsIdentifyMenu::messageDiscarded );
425 featureActionMenu->setExpressionContextScope( mExpressionContextScope );
426 }
427
428 // feature title
429 context.setFeature( result.mFeature );
430 QString featureTitle = exp.evaluate( &context ).toString();
431 if ( featureTitle.isEmpty() )
432 featureTitle = QString::number( result.mFeature.id() );
433
434 // if results are from the same layer we still add layer name to the feature
435 // title to be consistent with other cases, see https://github.com/qgis/QGIS/issues/50049
436 if ( layerMenu == this )
437 featureTitle = u"%1 (%2)"_s.arg( layer->name(), featureTitle );
438
439 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
440 {
441 featureAction = new QAction( featureTitle, layerMenu );
442 // add the feature action (or menu) to the layer menu
443 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
444 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
445 layerMenu->addAction( featureAction );
446 }
447 else if ( results.count() == 1 )
448 {
449 // if we are here with only one results, this means there is a sub-feature level (for actions)
450 // => skip the feature level since there would be only a single entry
451 // => give the layer menu as pointer instead of a new feature menu
452 featureMenu = layerMenu;
453 }
454 else
455 {
456 featureMenu = new QMenu( featureTitle, layerMenu );
457
458 // get the action from the menu
459 featureAction = featureMenu->menuAction();
460 // add the feature action (or menu) to the layer menu
461 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
462 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
463 layerMenu->addAction( featureAction );
464 }
465
466 // if no feature menu, no need to go further
467 if ( !featureMenu )
468 continue;
469
470 // add default identify action
471 QAction *identifyFeatureAction = new QAction( QgsApplication::getThemeIcon( u"/mActionIdentify.svg"_s ), mDefaultActionName, featureMenu );
472 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
473 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
474 featureMenu->addAction( identifyFeatureAction );
475 featureMenu->addSeparator();
476
477 // custom action at feature level
478 const auto constCustomFeatureActions = customFeatureActions;
479 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
480 {
481 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
482 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
483 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
484 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
485 featureMenu->addAction( action );
486 }
487 // use QgsActionMenu for feature actions
488 if ( featureActionMenu )
489 {
490 const auto constActions = featureActionMenu->actions();
491 for ( QAction *action : constActions )
492 {
493 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
494 featureMenu->addAction( action );
495 }
496 }
497 }
498
499 // back to layer level
500
501 // identify all action
502 if ( mAllowMultipleReturn && results.count() > 1 )
503 {
504 layerMenu->addSeparator();
505 QAction *allAction = new QAction( QgsApplication::getThemeIcon( u"/mActionIdentify.svg"_s ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
506 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
507 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
508 layerMenu->addAction( allAction );
509 }
510
511 // add custom/layer actions
512 const auto constLayerActions = layerActions;
513 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
514 {
515 QString title = mapLayerAction->text();
516 if ( mapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
517 title.append( u" (%1)"_s.arg( results.count() ) );
518 QAction *action = new QAction( mapLayerAction->icon(), title, layerMenu );
519 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
520 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
521 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
522 layerMenu->addAction( action );
523 if ( separators.contains( mapLayerAction ) )
524 {
525 layerMenu->insertSeparator( action );
526 }
527 }
528}
529
530void QgsIdentifyMenu::triggerMapLayerAction()
531{
532 QAction *action = qobject_cast<QAction *>( sender() );
533 if ( !action )
534 return;
535 const QVariant varData = action->data();
536 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
537 return;
538
539 ActionData actData = action->data().value<ActionData>();
540
541 if ( actData.mIsValid && actData.mMapLayerAction )
542 {
543 QgsMapLayerActionContext context;
544
545 // layer
546 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::Layer ) )
547 {
549 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
551 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
552 }
553
554 // multiples features
555 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
556 {
557 QList<QgsFeature> featureList;
558 const auto results { mLayerIdResults[actData.mLayer] };
559 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
560 {
561 featureList << result.mFeature;
562 }
564 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
566 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
567 }
568
569 // single feature
570 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::SingleFeature ) )
571 {
572 const auto results { mLayerIdResults[actData.mLayer] };
573 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
574 {
575 if ( result.mFeature.id() == actData.mFeatureId )
576 {
578 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
580 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
581 return;
582 }
583 }
584 QgsDebugError( u"Identify menu: could not retrieve feature for action %1"_s.arg( action->text() ) );
585 }
586 }
587}
588
589
590QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
591{
592 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
593
594 externalAction = false;
595
596 ActionData actData;
597 bool hasData = false;
598
599 if ( !action )
600 return idResults;
601
602 const QVariant varData = action->data();
603 if ( !varData.isValid() )
604 {
605 QgsDebugError( u"Identify menu: could not retrieve results from menu entry (invalid data)"_s );
606 return idResults;
607 }
608
609 if ( varData.canConvert<ActionData>() )
610 {
611 actData = action->data().value<ActionData>();
612 if ( actData.mIsValid )
613 {
614 externalAction = actData.mIsExternalAction;
615 hasData = true;
616 }
617 }
618
619 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
620 {
621 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
622 if ( dataSrc.actionType != Qgis::ActionType::Invalid )
623 {
624 externalAction = true;
625 actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
626 hasData = true;
627 }
628 }
629
630 if ( !hasData )
631 {
632 QgsDebugError( u"Identify menu: could not retrieve results from menu entry (no data found)"_s );
633 return idResults;
634 }
635
636 // return all results
637 if ( actData.mAllResults )
638 {
639 // this means "All" action was triggered
640 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
641 while ( it.hasNext() )
642 {
643 it.next();
644 idResults << it.value();
645 }
646 return idResults;
647 }
648
649 if ( !mLayerIdResults.contains( actData.mLayer ) )
650 {
651 QgsDebugError( u"Identify menu: could not retrieve results from menu entry (layer not found)"_s );
652 return idResults;
653 }
654
655 if ( actData.mLevel == LayerLevel )
656 {
657 return mLayerIdResults[actData.mLayer];
658 }
659
660 if ( actData.mLevel == FeatureLevel )
661 {
662 const auto results { mLayerIdResults[actData.mLayer] };
663 for ( const QgsMapToolIdentify::IdentifyResult &res : results )
664 {
665 if ( res.mFeature.id() == actData.mFeatureId )
666 {
667 idResults << res;
668 return idResults;
669 }
670 }
671 }
672
673 QgsDebugError( u"Identify menu: could not retrieve results from menu entry (don't know what happened')"_s );
674 return idResults;
675}
676
677void QgsIdentifyMenu::handleMenuHover()
678{
679 if ( !mCanvas )
680 return;
681
682 deleteRubberBands();
683
684 QAction *senderAction = qobject_cast<QAction *>( sender() );
685 if ( !senderAction )
686 return;
687
688 bool externalAction;
689 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
690
691 const auto constIdResults = idResults;
692 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
693 {
694 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
695 if ( !vl )
696 continue;
697
698 QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
699 hl->applyDefaultStyle();
700 mRubberBands.append( hl );
701 connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
702 }
703}
704
706{
707 highlight->applyDefaultStyle();
708}
709
710void QgsIdentifyMenu::deleteRubberBands()
711{
712 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
713 for ( ; it != mRubberBands.constEnd(); ++it )
714 delete *it;
715 mRubberBands.clear();
716}
717
718void QgsIdentifyMenu::layerDestroyed()
719{
720 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
721 while ( it != mRubberBands.end() )
722 {
723 if ( ( *it )->layer() == sender() )
724 {
725 delete *it;
726 it = mRubberBands.erase( it );
727 }
728 else
729 {
730 ++it;
731 }
732 }
733}
734
736{
737 mCustomActionRegistry.clear();
738}
739
741{
742 mExpressionContextScope = scope;
743}
744
746{
747 return mExpressionContextScope;
748}
QFlags< MapLayerActionTarget > MapLayerActionTargets
Map layer action targets.
Definition qgis.h:4712
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2256
@ Invalid
Invalid.
Definition qgis.h:4683
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:365
@ MultipleFeatures
Action targets multiple features from a layer.
Definition qgis.h:4700
@ Layer
Action targets a complete layer.
Definition qgis.h:4698
@ SingleFeature
Action targets a single feature from a layer.
Definition qgis.h:4699
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
@ Plugin
Plugin based layer.
Definition qgis.h:196
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Emitted when a message should be shown to the user in the application message bar.
void setMode(QgsAttributeEditorContext::Mode mode)
Change the mode of the actions.
void messageDiscarded()
Emitted when the previous message from the tool should be cleared from the application message bar.
bool isEmpty() const
Returns true if the menu has no valid actions.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ IdentifyMode
Identify the feature.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
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...
Definition qgsfeature.h:60
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:149
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.
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
void setMaxLayerDisplay(int maxLayerDisplay)
Defines the maximum number of layers displayed in the menu (default is 10).
~QgsIdentifyMenu() override
QgsExpressionContextScope expressionContextScope() const
Returns an expression context scope used to resolve underlying actions.
void setMaxFeatureDisplay(int maxFeatureDisplay)
Defines the maximum number of features displayed in the menu for vector layers (default is 10).
QgsIdentifyMenu(QgsMapCanvas *canvas)
QgsIdentifyMenu is a menu to be used to choose within a list of QgsMapTool::IdentifyReults.
void messageDiscarded()
Emitted when the previous message from the tool should be cleared from the application message bar.
void closeEvent(QCloseEvent *e) override
static QList< QgsMapToolIdentify::IdentifyResult > findFeaturesOnCanvas(QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList< Qgis::GeometryType > &geometryTypes)
Searches for features on the map canvas, which are located at the specified event point.
static Q_DECL_DEPRECATED void styleHighlight(QgsHighlight *highlight)
Applies style from the settings to the highlight.
void removeCustomActions()
remove all custom actions from the menu to be built
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Emitted when a message should be shown to the user in the application message bar.
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
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.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
Qgis::LayerType type
Definition qgsmaplayer.h:93
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.
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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
Definition qgis.h:7451
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
#define QgsDebugError(str)
Definition qgslogger.h:59
Qgis::ActionType actionType