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