QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 <QMouseEvent>
17
18#include "qgsidentifymenu.h"
19#include "qgsapplication.h"
20#include "qgshighlight.h"
21#include "qgsmapcanvas.h"
22#include "qgsactionmenu.h"
23#include "qgsvectorlayer.h"
24#include "qgslogger.h"
25#include "qgsgui.h"
27#include "qgsiconutils.h"
28#include "qgsmapmouseevent.h"
29#include "qgsmaplayeraction.h"
31
32//TODO 4.0 add explicitly qobject parent to constructor
34 : QMenu( canvas )
35 , mCanvas( canvas )
36 , mAllowMultipleReturn( true )
37 , mExecWithSingleResult( false )
38 , mShowFeatureActions( false )
39 , mResultsIfExternalAction( false )
40 , mMaxLayerDisplay( 10 )
41 , mMaxFeatureDisplay( 10 )
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( QStringLiteral( "Could not transform geometry to layer CRS" ) );
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
107void QgsIdentifyMenu::setMaxLayerDisplay( int maxLayerDisplay )
108{
109 if ( maxLayerDisplay < 0 )
110 {
111 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
112 }
113 mMaxLayerDisplay = maxLayerDisplay;
114}
115
116
117void QgsIdentifyMenu::setMaxFeatureDisplay( int maxFeatureDisplay )
118{
119 if ( maxFeatureDisplay < 0 )
120 {
121 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
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( 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 );
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( QStringLiteral( "/mIconRasterLayer.svg" ) ) );
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
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, QStringLiteral( "Feature" ), this );
344 createMenu = !featureActionMenu->actions().isEmpty();
345 delete featureActionMenu;
346 }
347 }
348
350 QgsExpression exp( layer->displayExpression() );
351 exp.prepare( &context );
352 context.setFeature( results[0].mFeature );
353 // use a menu only if actions will be listed
354 if ( !createMenu )
355 {
356 // case 1
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 );
361 }
362 else
363 {
364 if ( singleLayer )
365 {
366 // case 3
367 layerMenu = this;
368 }
369 else
370 {
371 // case 2a
372 if ( results.count() > 1 )
373 {
374 layerMenu = new QMenu( layer->name(), this );
375 }
376 // case 2b
377 else
378 {
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 );
383 }
384 layerAction = layerMenu->menuAction();
385 }
386 }
387
388 // case 1 or 2
389 if ( layerAction )
390 {
391 layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
392
393 // add layer action to the top menu
394 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
395 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
396 addAction( layerAction );
397 }
398
399 // case 1. no need to go further
400 if ( !layerMenu )
401 return;
402
403 // add results to the menu
404 int count = 0;
405 const auto constResults = results;
406 for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
407 {
408 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
409 break;
410 ++count;
411
412 QAction *featureAction = nullptr;
413 QMenu *featureMenu = nullptr;
414 QgsActionMenu *featureActionMenu = nullptr;
415
416 const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext );
417 if ( mShowFeatureActions )
418 {
419 featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
421 featureActionMenu->setExpressionContextScope( mExpressionContextScope );
422 }
423
424 // feature title
425 context.setFeature( result.mFeature );
426 QString featureTitle = exp.evaluate( &context ).toString();
427 if ( featureTitle.isEmpty() )
428 featureTitle = QString::number( result.mFeature.id() );
429
430 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
431 {
432 featureAction = new QAction( featureTitle, layerMenu );
433 // add the feature action (or menu) to the layer menu
434 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
435 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
436 layerMenu->addAction( featureAction );
437 }
438 else if ( results.count() == 1 )
439 {
440 // if we are here with only one results, this means there is a sub-feature level (for actions)
441 // => skip the feature level since there would be only a single entry
442 // => give the layer menu as pointer instead of a new feature menu
443 featureMenu = layerMenu;
444 }
445 else
446 {
447 featureMenu = new QMenu( featureTitle, layerMenu );
448
449 // get the action from the menu
450 featureAction = featureMenu->menuAction();
451 // add the feature action (or menu) to the layer menu
452 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
453 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
454 layerMenu->addAction( featureAction );
455 }
456
457 // if no feature menu, no need to go further
458 if ( !featureMenu )
459 continue;
460
461 // add default identify action
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();
467
468 // custom action at feature level
469 const auto constCustomFeatureActions = customFeatureActions;
470 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
471 {
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 );
477 }
478 // use QgsActionMenu for feature actions
479 if ( featureActionMenu )
480 {
481 const auto constActions = featureActionMenu->actions();
482 for ( QAction *action : constActions )
483 {
484 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
485 featureMenu->addAction( action );
486 }
487 }
488 }
489
490 // back to layer level
491
492 // identify all action
493 if ( mAllowMultipleReturn && results.count() > 1 )
494 {
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 );
500 }
501
502 // add custom/layer actions
503 const auto constLayerActions = layerActions;
504 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
505 {
506 QString title = mapLayerAction->text();
507 if ( mapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
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 ) )
515 {
516 layerMenu->insertSeparator( action );
517 }
518 }
519}
520
521void QgsIdentifyMenu::triggerMapLayerAction()
522{
523 QAction *action = qobject_cast<QAction *>( sender() );
524 if ( !action )
525 return;
526 const QVariant varData = action->data();
527 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
528 return;
529
530 ActionData actData = action->data().value<ActionData>();
531
532 if ( actData.mIsValid && actData.mMapLayerAction )
533 {
535
536 // layer
537 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::Layer ) )
538 {
540 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
542 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
543 }
544
545 // multiples features
546 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
547 {
548 QList<QgsFeature> featureList;
549 const auto results { mLayerIdResults[actData.mLayer] };
550 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
551 {
552 featureList << result.mFeature;
553 }
555 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
557 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
558 }
559
560 // single feature
561 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::SingleFeature ) )
562 {
563 const auto results { mLayerIdResults[actData.mLayer] };
564 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
565 {
566 if ( result.mFeature.id() == actData.mFeatureId )
567 {
569 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
571 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
572 return;
573 }
574 }
575 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
576 }
577 }
578}
579
580
581QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
582{
583 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
584
585 externalAction = false;
586
587 ActionData actData;
588 bool hasData = false;
589
590 if ( !action )
591 return idResults;
592
593 const QVariant varData = action->data();
594 if ( !varData.isValid() )
595 {
596 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (invalid data)" ) );
597 return idResults;
598 }
599
600 if ( varData.canConvert<ActionData>() )
601 {
602 actData = action->data().value<ActionData>();
603 if ( actData.mIsValid )
604 {
605 externalAction = actData.mIsExternalAction;
606 hasData = true;
607 }
608 }
609
610 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
611 {
612 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
613 if ( dataSrc.actionType != Qgis::ActionType::Invalid )
614 {
615 externalAction = true;
616 actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
617 hasData = true;
618 }
619 }
620
621 if ( !hasData )
622 {
623 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (no data found)" ) );
624 return idResults;
625 }
626
627 // return all results
628 if ( actData.mAllResults )
629 {
630 // this means "All" action was triggered
631 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
632 while ( it.hasNext() )
633 {
634 it.next();
635 idResults << it.value();
636 }
637 return idResults;
638 }
639
640 if ( !mLayerIdResults.contains( actData.mLayer ) )
641 {
642 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (layer not found)" ) );
643 return idResults;
644 }
645
646 if ( actData.mLevel == LayerLevel )
647 {
648 return mLayerIdResults[actData.mLayer];
649 }
650
651 if ( actData.mLevel == FeatureLevel )
652 {
653 const auto results {mLayerIdResults[actData.mLayer]};
654 for ( const QgsMapToolIdentify::IdentifyResult &res : results )
655 {
656 if ( res.mFeature.id() == actData.mFeatureId )
657 {
658 idResults << res;
659 return idResults;
660 }
661 }
662 }
663
664 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
665 return idResults;
666}
667
668void QgsIdentifyMenu::handleMenuHover()
669{
670 if ( !mCanvas )
671 return;
672
673 deleteRubberBands();
674
675 QAction *senderAction = qobject_cast<QAction *>( sender() );
676 if ( !senderAction )
677 return;
678
679 bool externalAction;
680 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
681
682 const auto constIdResults = idResults;
683 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
684 {
685 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
686 if ( !vl )
687 continue;
688
689 QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
690 hl->applyDefaultStyle();
691 mRubberBands.append( hl );
692 connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
693 }
694}
695
697{
698 highlight->applyDefaultStyle();
699}
700
701void QgsIdentifyMenu::deleteRubberBands()
702{
703 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
704 for ( ; it != mRubberBands.constEnd(); ++it )
705 delete *it;
706 mRubberBands.clear();
707}
708
709void QgsIdentifyMenu::layerDestroyed()
710{
711 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
712 while ( it != mRubberBands.end() )
713 {
714 if ( ( *it )->layer() == sender() )
715 {
716 delete *it;
717 it = mRubberBands.erase( it );
718 }
719 else
720 {
721 ++it;
722 }
723 }
724}
725
727{
728 mCustomActionRegistry.clear();
729
730}
731
733{
734 mExpressionContextScope = scope;
735}
736
738{
739 return mExpressionContextScope;
740}
QFlags< MapLayerActionTarget > MapLayerActionTargets
Map layer action targets.
Definition: qgis.h:3842
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ Invalid
Invalid.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:255
@ 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.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ Reverse
Reverse/inverse transform (from destination to source)
This class is a menu that is populated automatically with the actions defined for a given layer.
Definition: qgsactionmenu.h:39
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
void setMode(QgsAttributeEditorContext::Mode mode)
Change the mode of the actions.
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.
Class for doing transforms between two map 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.
Definition: qgsexception.h:67
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...
Definition: qgsfeature.h:56
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition: qgsgui.cpp:129
A class for highlight features on the map.
Definition: qgshighlight.h:62
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 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 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.
Definition: qgsmapcanvas.h:93
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.
Definition: qgsmaplayer.h:75
QString name
Definition: qgsmaplayer.h:78
Qgis::LayerType type
Definition: qgsmaplayer.h:82
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.
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:238
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
A rectangle specified with double values.
Definition: qgsrectangle.h:42
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
Definition: qgis.h:5776
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5775
#define QgsDebugError(str)
Definition: qgslogger.h:38
Qgis::ActionType actionType
Definition: qgsactionmenu.h:54