QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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 <QMouseEvent>
17
18#include "qgsidentifymenu.h"
19#include "moc_qgsidentifymenu.cpp"
20#include "qgsapplication.h"
21#include "qgshighlight.h"
22#include "qgsmapcanvas.h"
23#include "qgsactionmenu.h"
24#include "qgsvectorlayer.h"
25#include "qgslogger.h"
26#include "qgsgui.h"
28#include "qgsiconutils.h"
29#include "qgsmapmouseevent.h"
30#include "qgsmaplayeraction.h"
32
33//TODO 4.0 add explicitly qobject parent to constructor
35 : QMenu( canvas )
36 , mCanvas( canvas )
37 , mAllowMultipleReturn( true )
38 , mExecWithSingleResult( false )
39 , mShowFeatureActions( false )
40 , mResultsIfExternalAction( false )
41 , mMaxLayerDisplay( 10 )
42 , mMaxFeatureDisplay( 10 )
43 , mDefaultActionName( tr( "Identify" ) )
44{
45}
46
48{
49 deleteRubberBands();
50}
51
52QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::findFeaturesOnCanvas( QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList<Qgis::GeometryType> &geometryTypes )
53{
54 QList<QgsMapToolIdentify::IdentifyResult> results;
55 const QMap< QString, QString > derivedAttributes;
56
57 const QgsPointXY mapPoint = canvas->getCoordinateTransform()->toMapCoordinates( event->pos() ) ;
58 const double x = mapPoint.x();
59 const double y = mapPoint.y();
60 const double sr = QgsMapTool::searchRadiusMU( canvas );
61
62 const QList<QgsMapLayer *> layers = canvas->layers( true );
63 for ( QgsMapLayer *layer : layers )
64 {
65 if ( layer->type() == Qgis::LayerType::Vector )
66 {
67 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
68
69 bool typeIsSelectable = false;
70 for ( Qgis::GeometryType type : geometryTypes )
71 {
72 if ( vectorLayer->geometryType() == type )
73 {
74 typeIsSelectable = true;
75 break;
76 }
77 }
78 if ( typeIsSelectable )
79 {
80 QgsRectangle rect( x - sr, y - sr, x + sr, y + sr );
81 QgsCoordinateTransform transform = canvas->mapSettings().layerTransform( vectorLayer );
83
84 try
85 {
87 }
88 catch ( QgsCsException & )
89 {
90 QgsDebugError( QStringLiteral( "Could not transform geometry to layer CRS" ) );
91 }
92
94 .setFilterRect( rect )
96 QgsFeature f;
97 while ( fit.nextFeature( f ) )
98 {
99 results << QgsMapToolIdentify::IdentifyResult( vectorLayer, f, derivedAttributes );
100 }
101 }
102 }
103 }
104
105 return results;
106}
107
108void QgsIdentifyMenu::setMaxLayerDisplay( int maxLayerDisplay )
109{
110 if ( maxLayerDisplay < 0 )
111 {
112 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
113 }
114 mMaxLayerDisplay = maxLayerDisplay;
115}
116
117
118void QgsIdentifyMenu::setMaxFeatureDisplay( int maxFeatureDisplay )
119{
120 if ( maxFeatureDisplay < 0 )
121 {
122 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
123 }
124 mMaxFeatureDisplay = maxFeatureDisplay;
125}
126
127
128QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::exec( const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
129{
130 clear();
131 mLayerIdResults.clear();
132
133 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
134
135 if ( idResults.isEmpty() )
136 {
137 return returnResults;
138 }
139 if ( idResults.count() == 1 && !mExecWithSingleResult )
140 {
141 returnResults << idResults[0];
142 return returnResults;
143 }
144
145 // sort results by layer
146 const auto constIdResults = idResults;
147 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
148 {
149 QgsMapLayer *layer = result.mLayer;
150 if ( mLayerIdResults.contains( layer ) )
151 {
152 mLayerIdResults[layer].append( result );
153 }
154 else
155 {
156 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
157 }
158 }
159
160 // add results to the menu
161 const bool singleLayer = mLayerIdResults.count() == 1;
162 int count = 0;
163 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
164 while ( it.hasNext() )
165 {
166 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
167 break;
168 ++count;
169 it.next();
170 QgsMapLayer *layer = it.key();
171 switch ( layer->type() )
172 {
174 {
175 addRasterLayer( layer );
176 break;
177 }
179 {
180 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
181 if ( !vl )
182 continue;
183 addVectorLayer( vl, it.value(), singleLayer );
184 break;
185 }
186
188 // TODO: add support
189 break;
190
197 break;
198 }
199 }
200
201 // add an "identify all" action on the top level
202 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
203 {
204 addSeparator();
205 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ), this );
206 allAction->setData( QVariant::fromValue<ActionData>( ActionData( nullptr ) ) );
207 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
208 addAction( allAction );
209 }
210
211 // exec
212 QAction *selectedAction = QMenu::exec( pos );
213 bool externalAction;
214 returnResults = results( selectedAction, externalAction );
215
216 // delete actions
217 clear();
218 // also remove the QgsActionMenu
219 qDeleteAll( findChildren<QgsActionMenu *>() );
220
221 if ( externalAction && !mResultsIfExternalAction )
222 {
223 return QList<QgsMapToolIdentify::IdentifyResult>();
224 }
225 else
226 {
227 return returnResults;
228 }
229}
230
231void QgsIdentifyMenu::closeEvent( QCloseEvent *e )
232{
233 deleteRubberBands();
234 QMenu::closeEvent( e );
235}
236
237void QgsIdentifyMenu::addRasterLayer( QgsMapLayer *layer )
238{
239 QAction *layerAction = nullptr;
240 QMenu *layerMenu = nullptr;
241
242 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
244 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context );
245 const int nCustomActions = layerActions.count();
246 if ( nCustomActions )
247 {
248 separators.append( layerActions[0] );
249 }
250 if ( mShowFeatureActions )
251 {
252 layerActions.append( QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context ) );
253 if ( layerActions.count() > nCustomActions )
254 {
255 separators.append( layerActions[nCustomActions] );
256 }
257 }
258
259 // use a menu only if actions will be listed
260 if ( layerActions.isEmpty() )
261 {
262 layerAction = new QAction( layer->name(), this );
263 }
264 else
265 {
266 layerMenu = new QMenu( layer->name(), this );
267 layerAction = layerMenu->menuAction();
268 }
269
270 // add layer action to the top menu
271 layerAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconRasterLayer.svg" ) ) );
272 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
273 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
274 addAction( layerAction );
275
276 // no need to go further if there is no menu
277 if ( !layerMenu )
278 return;
279
280 // add default identify action
281 QAction *identifyFeatureAction = new QAction( mDefaultActionName, layerMenu );
282 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
283 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
284 layerMenu->addAction( identifyFeatureAction );
285
286 // add custom/layer actions
287 const auto constLayerActions = layerActions;
288 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
289 {
290 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
291 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, true ) ) );
292 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
293 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
294 layerMenu->addAction( action );
295 if ( separators.contains( mapLayerAction ) )
296 {
297 layerMenu->insertSeparator( action );
298 }
299 }
300}
301
302void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapToolIdentify::IdentifyResult> &results, bool singleLayer )
303{
304 QAction *layerAction = nullptr;
305 QMenu *layerMenu = nullptr;
306
307 // do not add actions with MultipleFeatures as target if only 1 feature is found for this layer
308 // targets defines which actions will be shown
309 const Qgis::MapLayerActionTargets targets = results.count() > 1 ? ( Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::MultipleFeatures ) : Qgis::MapLayerActionTarget::Layer;
310
311 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
312 QgsMapLayerActionContext actionContext;
313 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
314 const int nCustomActions = layerActions.count();
315 if ( nCustomActions )
316 {
317 separators << layerActions[0];
318 }
319 if ( mShowFeatureActions )
320 {
321 layerActions << QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, targets, actionContext );
322
323 if ( layerActions.count() > nCustomActions )
324 {
325 separators << layerActions[nCustomActions];
326 }
327 }
328
329 // determines if a menu should be created or not. Following cases:
330 // 1. only one result and no feature action to be shown => just create an action
331 // 2. several features (2a) or display feature actions (2b) => create a menu
332 // 3. case 2 but only one layer (singeLayer) => do not create a menu, but give the top menu instead
333
334 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
335
336 // case 2b: still create a menu for layer, if there is a sub-level for features
337 // i.e custom actions or map layer actions at feature level
338 if ( !createMenu )
339 {
340 createMenu = !mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext ).isEmpty();
341 if ( !createMenu && mShowFeatureActions )
342 {
343 QgsActionMenu *featureActionMenu = new QgsActionMenu( layer, results[0].mFeature, QStringLiteral( "Feature" ), this );
345 createMenu = !featureActionMenu->actions().isEmpty();
346 delete featureActionMenu;
347 }
348 }
349
351 QgsExpression exp( layer->displayExpression() );
352 exp.prepare( &context );
353 context.setFeature( results[0].mFeature );
354 // use a menu only if actions will be listed
355 if ( !createMenu )
356 {
357 // case 1
358 QString featureTitle = exp.evaluate( &context ).toString();
359 if ( featureTitle.isEmpty() )
360 featureTitle = QString::number( results[0].mFeature.id() );
361 layerAction = new QAction( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
362 }
363 else
364 {
365 if ( singleLayer )
366 {
367 // case 3
368 layerMenu = this;
369 }
370 else
371 {
372 // case 2a
373 if ( results.count() > 1 )
374 {
375 layerMenu = new QMenu( layer->name(), this );
376 }
377 // case 2b
378 else
379 {
380 QString featureTitle = exp.evaluate( &context ).toString();
381 if ( featureTitle.isEmpty() )
382 featureTitle = QString::number( results[0].mFeature.id() );
383 layerMenu = new QMenu( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
384 }
385 layerAction = layerMenu->menuAction();
386 }
387 }
388
389 // case 1 or 2
390 if ( layerAction )
391 {
392 layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
393
394 // add layer action to the top menu
395 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
396 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
397 addAction( layerAction );
398 }
399
400 // case 1. no need to go further
401 if ( !layerMenu )
402 return;
403
404 // add results to the menu
405 int count = 0;
406 const auto constResults = results;
407 for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
408 {
409 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
410 break;
411 ++count;
412
413 QAction *featureAction = nullptr;
414 QMenu *featureMenu = nullptr;
415 QgsActionMenu *featureActionMenu = nullptr;
416
417 const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext );
418 if ( mShowFeatureActions )
419 {
420 featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
422 featureActionMenu->setExpressionContextScope( mExpressionContextScope );
423 }
424
425 // feature title
426 context.setFeature( result.mFeature );
427 QString featureTitle = exp.evaluate( &context ).toString();
428 if ( featureTitle.isEmpty() )
429 featureTitle = QString::number( result.mFeature.id() );
430
431 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
432 {
433 featureAction = new QAction( featureTitle, layerMenu );
434 // add the feature action (or menu) to the layer menu
435 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
436 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
437 layerMenu->addAction( featureAction );
438 }
439 else if ( results.count() == 1 )
440 {
441 // if we are here with only one results, this means there is a sub-feature level (for actions)
442 // => skip the feature level since there would be only a single entry
443 // => give the layer menu as pointer instead of a new feature menu
444 featureMenu = layerMenu;
445 }
446 else
447 {
448 featureMenu = new QMenu( featureTitle, layerMenu );
449
450 // get the action from the menu
451 featureAction = featureMenu->menuAction();
452 // add the feature action (or menu) to the layer menu
453 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
454 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
455 layerMenu->addAction( featureAction );
456 }
457
458 // if no feature menu, no need to go further
459 if ( !featureMenu )
460 continue;
461
462 // add default identify action
463 QAction *identifyFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
464 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
465 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
466 featureMenu->addAction( identifyFeatureAction );
467 featureMenu->addSeparator();
468
469 // custom action at feature level
470 const auto constCustomFeatureActions = customFeatureActions;
471 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
472 {
473 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
474 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
475 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
476 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
477 featureMenu->addAction( action );
478 }
479 // use QgsActionMenu for feature actions
480 if ( featureActionMenu )
481 {
482 const auto constActions = featureActionMenu->actions();
483 for ( QAction *action : constActions )
484 {
485 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
486 featureMenu->addAction( action );
487 }
488 }
489 }
490
491 // back to layer level
492
493 // identify all action
494 if ( mAllowMultipleReturn && results.count() > 1 )
495 {
496 layerMenu->addSeparator();
497 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
498 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
499 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
500 layerMenu->addAction( allAction );
501 }
502
503 // add custom/layer actions
504 const auto constLayerActions = layerActions;
505 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
506 {
507 QString title = mapLayerAction->text();
508 if ( mapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
509 title.append( QStringLiteral( " (%1)" ).arg( results.count() ) );
510 QAction *action = new QAction( mapLayerAction->icon(), title, layerMenu );
511 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
512 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
513 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
514 layerMenu->addAction( action );
515 if ( separators.contains( mapLayerAction ) )
516 {
517 layerMenu->insertSeparator( action );
518 }
519 }
520}
521
522void QgsIdentifyMenu::triggerMapLayerAction()
523{
524 QAction *action = qobject_cast<QAction *>( sender() );
525 if ( !action )
526 return;
527 const QVariant varData = action->data();
528 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
529 return;
530
531 ActionData actData = action->data().value<ActionData>();
532
533 if ( actData.mIsValid && actData.mMapLayerAction )
534 {
536
537 // layer
538 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::Layer ) )
539 {
541 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
543 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
544 }
545
546 // multiples features
547 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
548 {
549 QList<QgsFeature> featureList;
550 const auto results { mLayerIdResults[actData.mLayer] };
551 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
552 {
553 featureList << result.mFeature;
554 }
556 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
558 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
559 }
560
561 // single feature
562 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::SingleFeature ) )
563 {
564 const auto results { mLayerIdResults[actData.mLayer] };
565 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
566 {
567 if ( result.mFeature.id() == actData.mFeatureId )
568 {
570 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
572 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
573 return;
574 }
575 }
576 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
577 }
578 }
579}
580
581
582QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
583{
584 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
585
586 externalAction = false;
587
588 ActionData actData;
589 bool hasData = false;
590
591 if ( !action )
592 return idResults;
593
594 const QVariant varData = action->data();
595 if ( !varData.isValid() )
596 {
597 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (invalid data)" ) );
598 return idResults;
599 }
600
601 if ( varData.canConvert<ActionData>() )
602 {
603 actData = action->data().value<ActionData>();
604 if ( actData.mIsValid )
605 {
606 externalAction = actData.mIsExternalAction;
607 hasData = true;
608 }
609 }
610
611 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
612 {
613 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
614 if ( dataSrc.actionType != Qgis::ActionType::Invalid )
615 {
616 externalAction = true;
617 actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
618 hasData = true;
619 }
620 }
621
622 if ( !hasData )
623 {
624 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (no data found)" ) );
625 return idResults;
626 }
627
628 // return all results
629 if ( actData.mAllResults )
630 {
631 // this means "All" action was triggered
632 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
633 while ( it.hasNext() )
634 {
635 it.next();
636 idResults << it.value();
637 }
638 return idResults;
639 }
640
641 if ( !mLayerIdResults.contains( actData.mLayer ) )
642 {
643 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (layer not found)" ) );
644 return idResults;
645 }
646
647 if ( actData.mLevel == LayerLevel )
648 {
649 return mLayerIdResults[actData.mLayer];
650 }
651
652 if ( actData.mLevel == FeatureLevel )
653 {
654 const auto results {mLayerIdResults[actData.mLayer]};
655 for ( const QgsMapToolIdentify::IdentifyResult &res : results )
656 {
657 if ( res.mFeature.id() == actData.mFeatureId )
658 {
659 idResults << res;
660 return idResults;
661 }
662 }
663 }
664
665 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
666 return idResults;
667}
668
669void QgsIdentifyMenu::handleMenuHover()
670{
671 if ( !mCanvas )
672 return;
673
674 deleteRubberBands();
675
676 QAction *senderAction = qobject_cast<QAction *>( sender() );
677 if ( !senderAction )
678 return;
679
680 bool externalAction;
681 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
682
683 const auto constIdResults = idResults;
684 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
685 {
686 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
687 if ( !vl )
688 continue;
689
690 QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
691 hl->applyDefaultStyle();
692 mRubberBands.append( hl );
693 connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
694 }
695}
696
698{
699 highlight->applyDefaultStyle();
700}
701
702void QgsIdentifyMenu::deleteRubberBands()
703{
704 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
705 for ( ; it != mRubberBands.constEnd(); ++it )
706 delete *it;
707 mRubberBands.clear();
708}
709
710void QgsIdentifyMenu::layerDestroyed()
711{
712 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
713 while ( it != mRubberBands.end() )
714 {
715 if ( ( *it )->layer() == sender() )
716 {
717 delete *it;
718 it = mRubberBands.erase( it );
719 }
720 else
721 {
722 ++it;
723 }
724 }
725}
726
728{
729 mCustomActionRegistry.clear();
730
731}
732
734{
735 mExpressionContextScope = scope;
736}
737
739{
740 return mExpressionContextScope;
741}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
QFlags< MapLayerActionTarget > MapLayerActionTargets
Map layer action targets.
Definition qgis.h:4297
@ 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:337
@ 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.
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.
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:58
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:134
A class for highlight 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 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.
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:76
QString name
Definition qgsmaplayer.h:80
Qgis::LayerType type
Definition qgsmaplayer.h:86
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.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
A rectangle specified with double values.
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:6494
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6493
#define QgsDebugError(str)
Definition qgslogger.h:38
Qgis::ActionType actionType