QGIS API Documentation 3.27.0-Master (95e00c50d2)
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
6 email : [email protected]il.com
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 "qgsactionmanager.h"
21#include "qgshighlight.h"
22#include "qgsmapcanvas.h"
23#include "qgsactionmenu.h"
24#include "qgsvectorlayer.h"
25#include "qgslogger.h"
26#include "qgssettings.h"
27#include "qgsgui.h"
29#include "qgsiconutils.h"
30#include "qgsmapmouseevent.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<QgsWkbTypes::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() == QgsMapLayerType::VectorLayer )
65 {
66 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
67
68 bool typeIsSelectable = false;
69 for ( QgsWkbTypes::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 {
85 rect = transform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
86 }
87 catch ( QgsCsException & )
88 {
89 QgsDebugMsg( 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 QgsDebugMsg( 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 QgsDebugMsg( 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
195 break;
196 }
197 }
198
199 // add an "identify all" action on the top level
200 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
201 {
202 addSeparator();
203 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ), this );
204 allAction->setData( QVariant::fromValue<ActionData>( ActionData( nullptr ) ) );
205 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
206 addAction( allAction );
207 }
208
209 // exec
210 QAction *selectedAction = QMenu::exec( pos );
211 bool externalAction;
212 returnResults = results( selectedAction, externalAction );
213
214 // delete actions
215 clear();
216 // also remove the QgsActionMenu
217 qDeleteAll( findChildren<QgsActionMenu *>() );
218
219 if ( externalAction && !mResultsIfExternalAction )
220 {
221 return QList<QgsMapToolIdentify::IdentifyResult>();
222 }
223 else
224 {
225 return returnResults;
226 }
227}
228
229void QgsIdentifyMenu::closeEvent( QCloseEvent *e )
230{
231 deleteRubberBands();
232 QMenu::closeEvent( e );
233}
234
235void QgsIdentifyMenu::addRasterLayer( QgsMapLayer *layer )
236{
237 QAction *layerAction = nullptr;
238 QMenu *layerMenu = nullptr;
239
240 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
241 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::Layer );
242 const int nCustomActions = layerActions.count();
243 if ( nCustomActions )
244 {
245 separators.append( layerActions[0] );
246 }
247 if ( mShowFeatureActions )
248 {
249 layerActions.append( QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, QgsMapLayerAction::Layer ) );
250 if ( layerActions.count() > nCustomActions )
251 {
252 separators.append( layerActions[nCustomActions] );
253 }
254 }
255
256 // use a menu only if actions will be listed
257 if ( layerActions.isEmpty() )
258 {
259 layerAction = new QAction( layer->name(), this );
260 }
261 else
262 {
263 layerMenu = new QMenu( layer->name(), this );
264 layerAction = layerMenu->menuAction();
265 }
266
267 // add layer action to the top menu
268 layerAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconRasterLayer.svg" ) ) );
269 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
270 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
271 addAction( layerAction );
272
273 // no need to go further if there is no menu
274 if ( !layerMenu )
275 return;
276
277 // add default identify action
278 QAction *identifyFeatureAction = new QAction( mDefaultActionName, layerMenu );
279 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
280 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
281 layerMenu->addAction( identifyFeatureAction );
282
283 // add custom/layer actions
284 const auto constLayerActions = layerActions;
285 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
286 {
287 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
288 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, true ) ) );
289 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
290 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
291 layerMenu->addAction( action );
292 if ( separators.contains( mapLayerAction ) )
293 {
294 layerMenu->insertSeparator( action );
295 }
296 }
297}
298
299void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapToolIdentify::IdentifyResult> &results, bool singleLayer )
300{
301 QAction *layerAction = nullptr;
302 QMenu *layerMenu = nullptr;
303
304 // do not add actions with MultipleFeatures as target if only 1 feature is found for this layer
305 // targets defines which actions will be shown
306 const QgsMapLayerAction::Targets targets = results.count() > 1 ? QgsMapLayerAction::Layer | QgsMapLayerAction::MultipleFeatures : QgsMapLayerAction::Layer;
307
308 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
309 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets );
310 const int nCustomActions = layerActions.count();
311 if ( nCustomActions )
312 {
313 separators << layerActions[0];
314 }
315 if ( mShowFeatureActions )
316 {
317 layerActions << QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, targets );
318
319 if ( layerActions.count() > nCustomActions )
320 {
321 separators << layerActions[nCustomActions];
322 }
323 }
324
325 // determines if a menu should be created or not. Following cases:
326 // 1. only one result and no feature action to be shown => just create an action
327 // 2. several features (2a) or display feature actions (2b) => create a menu
328 // 3. case 2 but only one layer (singeLayer) => do not create a menu, but give the top menu instead
329
330 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
331
332 // case 2b: still create a menu for layer, if there is a sub-level for features
333 // i.e custom actions or map layer actions at feature level
334 if ( !createMenu )
335 {
336 createMenu = !mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::SingleFeature ).isEmpty();
337 if ( !createMenu && mShowFeatureActions )
338 {
339 QgsActionMenu *featureActionMenu = new QgsActionMenu( layer, results[0].mFeature, QStringLiteral( "Feature" ), this );
341 createMenu = !featureActionMenu->actions().isEmpty();
342 delete featureActionMenu;
343 }
344 }
345
347 QgsExpression exp( layer->displayExpression() );
348 exp.prepare( &context );
349 context.setFeature( results[0].mFeature );
350 // use a menu only if actions will be listed
351 if ( !createMenu )
352 {
353 // case 1
354 QString featureTitle = exp.evaluate( &context ).toString();
355 if ( featureTitle.isEmpty() )
356 featureTitle = QString::number( results[0].mFeature.id() );
357 layerAction = new QAction( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
358 }
359 else
360 {
361 if ( singleLayer )
362 {
363 // case 3
364 layerMenu = this;
365 }
366 else
367 {
368 // case 2a
369 if ( results.count() > 1 )
370 {
371 layerMenu = new QMenu( layer->name(), this );
372 }
373 // case 2b
374 else
375 {
376 QString featureTitle = exp.evaluate( &context ).toString();
377 if ( featureTitle.isEmpty() )
378 featureTitle = QString::number( results[0].mFeature.id() );
379 layerMenu = new QMenu( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
380 }
381 layerAction = layerMenu->menuAction();
382 }
383 }
384
385 // case 1 or 2
386 if ( layerAction )
387 {
388 layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
389
390 // add layer action to the top menu
391 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
392 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
393 addAction( layerAction );
394 }
395
396 // case 1. no need to go further
397 if ( !layerMenu )
398 return;
399
400 // add results to the menu
401 int count = 0;
402 const auto constResults = results;
403 for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
404 {
405 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
406 break;
407 ++count;
408
409 QAction *featureAction = nullptr;
410 QMenu *featureMenu = nullptr;
411 QgsActionMenu *featureActionMenu = nullptr;
412
413 const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::SingleFeature );
414 if ( mShowFeatureActions )
415 {
416 featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
418 featureActionMenu->setExpressionContextScope( mExpressionContextScope );
419 }
420
421 // feature title
422 context.setFeature( result.mFeature );
423 QString featureTitle = exp.evaluate( &context ).toString();
424 if ( featureTitle.isEmpty() )
425 featureTitle = QString::number( result.mFeature.id() );
426
427 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
428 {
429 featureAction = new QAction( featureTitle, layerMenu );
430 // add the feature action (or menu) to the layer menu
431 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
432 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
433 layerMenu->addAction( featureAction );
434 }
435 else if ( results.count() == 1 )
436 {
437 // if we are here with only one results, this means there is a sub-feature level (for actions)
438 // => skip the feature level since there would be only a single entry
439 // => give the layer menu as pointer instead of a new feature menu
440 featureMenu = layerMenu;
441 }
442 else
443 {
444 featureMenu = new QMenu( featureTitle, layerMenu );
445
446 // get the action from the menu
447 featureAction = featureMenu->menuAction();
448 // add the feature action (or menu) to the layer menu
449 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
450 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
451 layerMenu->addAction( featureAction );
452 }
453
454 // if no feature menu, no need to go further
455 if ( !featureMenu )
456 continue;
457
458 // add default identify action
459 QAction *identifyFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
460 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
461 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
462 featureMenu->addAction( identifyFeatureAction );
463 featureMenu->addSeparator();
464
465 // custom action at feature level
466 const auto constCustomFeatureActions = customFeatureActions;
467 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
468 {
469 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
470 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
471 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
472 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
473 featureMenu->addAction( action );
474 }
475 // use QgsActionMenu for feature actions
476 if ( featureActionMenu )
477 {
478 const auto constActions = featureActionMenu->actions();
479 for ( QAction *action : constActions )
480 {
481 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
482 featureMenu->addAction( action );
483 }
484 }
485 }
486
487 // back to layer level
488
489 // identify all action
490 if ( mAllowMultipleReturn && results.count() > 1 )
491 {
492 layerMenu->addSeparator();
493 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
494 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
495 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
496 layerMenu->addAction( allAction );
497 }
498
499 // add custom/layer actions
500 const auto constLayerActions = layerActions;
501 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
502 {
503 QString title = mapLayerAction->text();
504 if ( mapLayerAction->targets().testFlag( QgsMapLayerAction::MultipleFeatures ) )
505 title.append( QStringLiteral( " (%1)" ).arg( results.count() ) );
506 QAction *action = new QAction( mapLayerAction->icon(), title, layerMenu );
507 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
508 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
509 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
510 layerMenu->addAction( action );
511 if ( separators.contains( mapLayerAction ) )
512 {
513 layerMenu->insertSeparator( action );
514 }
515 }
516}
517
518void QgsIdentifyMenu::triggerMapLayerAction()
519{
520 QAction *action = qobject_cast<QAction *>( sender() );
521 if ( !action )
522 return;
523 const QVariant varData = action->data();
524 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
525 return;
526
527 ActionData actData = action->data().value<ActionData>();
528
529 if ( actData.mIsValid && actData.mMapLayerAction )
530 {
531 // layer
532 if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::Layer ) )
533 {
534 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
535 }
536
537 // multiples features
538 if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::MultipleFeatures ) )
539 {
540 QList<QgsFeature> featureList;
541 const auto results { mLayerIdResults[actData.mLayer] };
542 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
543 {
544 featureList << result.mFeature;
545 }
546 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
547 }
548
549 // single feature
550 if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::SingleFeature ) )
551 {
552 const auto results { mLayerIdResults[actData.mLayer] };
553 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
554 {
555 if ( result.mFeature.id() == actData.mFeatureId )
556 {
557 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
558 return;
559 }
560 }
561 QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
562 }
563 }
564}
565
566
567QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
568{
569 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
570
571 externalAction = false;
572
573 ActionData actData;
574 bool hasData = false;
575
576 if ( !action )
577 return idResults;
578
579 const QVariant varData = action->data();
580 if ( !varData.isValid() )
581 {
582 QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (invalid data)" ) );
583 return idResults;
584 }
585
586 if ( varData.canConvert<ActionData>() )
587 {
588 actData = action->data().value<ActionData>();
589 if ( actData.mIsValid )
590 {
591 externalAction = actData.mIsExternalAction;
592 hasData = true;
593 }
594 }
595
596 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
597 {
598 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
599 if ( dataSrc.actionType != QgsActionMenu::Invalid )
600 {
601 externalAction = true;
602 actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
603 hasData = true;
604 }
605 }
606
607 if ( !hasData )
608 {
609 QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (no data found)" ) );
610 return idResults;
611 }
612
613 // return all results
614 if ( actData.mAllResults )
615 {
616 // this means "All" action was triggered
617 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
618 while ( it.hasNext() )
619 {
620 it.next();
621 idResults << it.value();
622 }
623 return idResults;
624 }
625
626 if ( !mLayerIdResults.contains( actData.mLayer ) )
627 {
628 QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (layer not found)" ) );
629 return idResults;
630 }
631
632 if ( actData.mLevel == LayerLevel )
633 {
634 return mLayerIdResults[actData.mLayer];
635 }
636
637 if ( actData.mLevel == FeatureLevel )
638 {
639 const auto results {mLayerIdResults[actData.mLayer]};
640 for ( const QgsMapToolIdentify::IdentifyResult &res : results )
641 {
642 if ( res.mFeature.id() == actData.mFeatureId )
643 {
644 idResults << res;
645 return idResults;
646 }
647 }
648 }
649
650 QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
651 return idResults;
652}
653
654void QgsIdentifyMenu::handleMenuHover()
655{
656 if ( !mCanvas )
657 return;
658
659 deleteRubberBands();
660
661 QAction *senderAction = qobject_cast<QAction *>( sender() );
662 if ( !senderAction )
663 return;
664
665 bool externalAction;
666 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
667
668 const auto constIdResults = idResults;
669 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
670 {
671 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
672 if ( !vl )
673 continue;
674
675 QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
676 styleHighlight( hl );
677 mRubberBands.append( hl );
678 connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
679 }
680}
681
683{
684 const QgsSettings settings;
685 QColor color = QColor( settings.value( QStringLiteral( "Map/highlight/color" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
686 const int alpha = settings.value( QStringLiteral( "Map/highlight/colorAlpha" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
687 const double buffer = settings.value( QStringLiteral( "Map/highlight/buffer" ), Qgis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
688 const double minWidth = settings.value( QStringLiteral( "Map/highlight/minWidth" ), Qgis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
689
690 highlight->setColor( color ); // sets also fill with default alpha
691 color.setAlpha( alpha );
692 highlight->setFillColor( color ); // sets fill with alpha
693 highlight->setBuffer( buffer );
694 highlight->setMinWidth( minWidth );
695}
696
697void QgsIdentifyMenu::deleteRubberBands()
698{
699 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
700 for ( ; it != mRubberBands.constEnd(); ++it )
701 delete *it;
702 mRubberBands.clear();
703}
704
705void QgsIdentifyMenu::layerDestroyed()
706{
707 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
708 while ( it != mRubberBands.end() )
709 {
710 if ( ( *it )->layer() == sender() )
711 {
712 delete *it;
713 it = mRubberBands.erase( it );
714 }
715 else
716 {
717 ++it;
718 }
719 }
720}
721
723{
724 mCustomActionRegistry.clear();
725
726}
727
729{
730 mExpressionContextScope = scope;
731}
732
734{
735 return mExpressionContextScope;
736}
static const double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/stroke minimum width in mm.
Definition: qgis.h:2275
static const double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
Definition: qgis.h:2269
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition: qgis.h:2263
This class is a menu that is populated automatically with the actions defined for a given layer.
Definition: qgsactionmenu.h:38
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.
@ Invalid
Invalid.
Definition: qgsactionmenu.h:44
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 SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
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:123
A class for highlight features on the map.
Definition: qgshighlight.h:62
void setBuffer(double buffer)
Set line / stroke buffer in millimeters.
Definition: qgshighlight.h:159
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.
Definition: qgshighlight.h:166
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.
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 void styleHighlight(QgsHighlight *highlight)
Applies style from the settings to the highlight.
static QList< QgsMapToolIdentify::IdentifyResult > findFeaturesOnCanvas(QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList< QgsWkbTypes::GeometryType > &geometryTypes)
Searches for features on the map canvas, which are located at the specified event point.
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:90
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.
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.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
QgsMapLayerType type
Definition: qgsmaplayer.h:80
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:232
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
A rectangle specified with double values.
Definition: qgsrectangle.h:42
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString displayExpression
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsActionMenu::ActionType actionType
Definition: qgsactionmenu.h:59