Skip to content

Commit

Permalink
select feature context menu
Browse files Browse the repository at this point in the history
  • Loading branch information
vcloarec committed Nov 24, 2020
1 parent 99fa35c commit 39b1116
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 16 deletions.
5 changes: 4 additions & 1 deletion python/gui/auto_generated/qgsmaptool.sip.in
Expand Up @@ -11,6 +11,7 @@




%ModuleHeaderCode
// fix to allow compilation with sip 4.7 that for some reason
// doesn't add these includes to the file where the code from
Expand Down Expand Up @@ -194,14 +195,16 @@ The values is calculated from :py:func:`~QgsMapTool.searchRadiusMM`.
.. versionadded:: 2.3
%End

virtual void populateContextMenu( QMenu *menu );
virtual void populateContextMenu( QMenu *menu, QgsMapMouseEvent *event = 0 );
%Docstring
Allows the tool to populate and customize the given ``menu``,
prior to showing it in response to a right-mouse button click.

``menu`` will be initially populated with a set of default, generic actions.
Any new actions added to the menu should be correctly parented to ``menu``.

A pointer to the map mouse ``event`` can be provided to allow particular behavior depending on the map tool.

The default implementation does nothing.

.. note::
Expand Down
52 changes: 52 additions & 0 deletions src/app/qgsmaptoolselect.cpp
Expand Up @@ -18,14 +18,17 @@
#include "qgsmaptoolselectutils.h"
#include "qgsrubberband.h"
#include "qgsmapcanvas.h"
#include "qgsmapmouseevent.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h"
#include "qgspointxy.h"
#include "qgis.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgshighlight.h"

#include <QMouseEvent>
#include <QMenu>
#include <QRect>
#include <QColor>

Expand Down Expand Up @@ -138,6 +141,37 @@ QgsMapTool::Flags QgsMapToolSelect::flags() const
return QgsMapTool::flags();
}



void QgsMapToolSelect::populateContextMenu( QMenu *menu, QgsMapMouseEvent *event )
{
menu->addSeparator();
QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( mCanvas );

Qt::KeyboardModifiers modifiers = Qt::NoModifier;
QgsPointXY mapPoint;
if ( event )
{
modifiers = event->modifiers();
mapPoint = event->mapPoint();
}
QgsVectorLayer::SelectBehavior behavior = QgsVectorLayer::SetSelection;
if ( modifiers & Qt::ShiftModifier && modifiers & Qt::ControlModifier )
behavior = QgsVectorLayer::IntersectSelection;
else if ( modifiers & Qt::ShiftModifier )
behavior = QgsVectorLayer::AddToSelection;
else if ( modifiers & Qt::ControlModifier )
behavior = QgsVectorLayer::RemoveFromSelection;

QgsMapToolSelectUtils::QgsMapToolSelectMenuActions *menuActions
= new QgsMapToolSelectUtils::QgsMapToolSelectMenuActions( mCanvas, vlayer, behavior, menu );

QgsRectangle r = QgsMapToolSelectUtils::expandSelectRectangle( mapPoint, mCanvas, vlayer );
QgsFeatureIds selectedFeatures = QgsMapToolSelectUtils::getMatchingFeatures( mCanvas, QgsGeometry::fromRect( r ), false, false );

menu->addActions( menuActions->actions( selectedFeatures ) );
}

void QgsMapToolSelect::selectFeatures( Qt::KeyboardModifiers modifiers )
{
if ( mSelectionHandler->selectionMode() == QgsMapToolSelectionHandler::SelectSimple &&
Expand Down Expand Up @@ -170,3 +204,21 @@ void QgsMapToolSelect::modifiersChanged( bool ctrlModifier, bool shiftModifier,
else if ( ctrlModifier && shiftModifier && altModifier )
emit modeChanged( GeometryWithinIntersectWithSelection );
}

void QgsMapToolSelect::hightLightFeature( const QgsFeatureId &id, QgsVectorLayer *layer )
{
for ( QgsHighlight *hl : mHightlights )
delete hl;

mHightlights.clear();

QgsFeature feat = layer->getFeature( id );
mHightlights.append( new QgsHighlight( mCanvas, feat.geometry(), layer ) );

}






10 changes: 10 additions & 0 deletions src/app/qgsmaptoolselect.h
Expand Up @@ -21,6 +21,8 @@
#include "qgsmaptoolselectionhandler.h"

class QgsMapCanvas;
class QgsHighlight;

class QMouseEvent;

class APP_EXPORT QgsMapToolSelect : public QgsMapTool
Expand Down Expand Up @@ -52,6 +54,9 @@ class APP_EXPORT QgsMapToolSelect : public QgsMapTool
void deactivate() override;
Flags flags() const override;


void populateContextMenu( QMenu *menu, QgsMapMouseEvent *event = nullptr ) override;

signals:

void modeChanged( Mode mode );
Expand All @@ -63,6 +68,11 @@ class APP_EXPORT QgsMapToolSelect : public QgsMapTool
std::unique_ptr<QgsMapToolSelectionHandler> mSelectionHandler;

void modifiersChanged( bool ctrlModifier, bool shiftModifier, bool altModifier );

void hightLightFeature( const QgsFeatureId &id, QgsVectorLayer *layer );

QList<QgsHighlight *> mHightlights;

};

#endif
192 changes: 192 additions & 0 deletions src/app/qgsmaptoolselectutils.cpp
Expand Up @@ -23,6 +23,7 @@ email : jpalmer at linz dot govt dot nz
#include "qgsvectorlayer.h"
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include "qgshighlight.h"
#include "qgsrenderer.h"
#include "qgsrubberband.h"
#include "qgsexception.h"
Expand All @@ -34,6 +35,7 @@ email : jpalmer at linz dot govt dot nz

#include <QMouseEvent>
#include <QApplication>
#include <QAction>

QgsVectorLayer *QgsMapToolSelectUtils::getCurrentVectorLayer( QgsMapCanvas *canvas )
{
Expand Down Expand Up @@ -335,3 +337,193 @@ QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas,
return newSelectedFeatures;
}


QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::QgsMapToolSelectMenuActions(
QgsMapCanvas *canvas,
QgsVectorLayer *vectorLayer,
QgsVectorLayer::SelectBehavior behavior,
QObject *parent ):
QObject( parent ),
mCanvas( canvas ),
mVectorLayer( vectorLayer ),
mBehavior( behavior )
{
connect( mVectorLayer, &QgsMapLayer::destroyed, this, &QgsMapToolSelectMenuActions::onLayerDestroyed );
}

QList<QAction *> QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::actions( const QgsFeatureIds &featureCanditateIds )
{
QList<QAction *> actionsList;

QString beginningOfText;

QgsFeatureIds effectiveFeatureIds = featureCanditateIds;

switch ( mBehavior )
{
case QgsVectorLayer::SetSelection:
beginningOfText = tr( "Select " );
break;
case QgsVectorLayer::AddToSelection:
{
beginningOfText = tr( "Add " );
QgsFeatureIds existingSelection = mVectorLayer->selectedFeatureIds();
for ( const QgsFeatureId &selected : featureCanditateIds )
{
if ( existingSelection.contains( selected ) )
effectiveFeatureIds.remove( selected );
}
}
break;

case QgsVectorLayer::IntersectSelection:
{
beginningOfText = tr( "Intersect " );
QgsFeatureIds existingSelection = mVectorLayer->selectedFeatureIds();
for ( const QgsFeatureId &selected : featureCanditateIds )
{
if ( !existingSelection.contains( selected ) )
effectiveFeatureIds.remove( selected );
}
}
break;
case QgsVectorLayer::RemoveFromSelection:
{
beginningOfText = tr( "Remove " );
QgsFeatureIds existingSelection = mVectorLayer->selectedFeatureIds();
for ( const QgsFeatureId &selected : featureCanditateIds )
{
if ( !existingSelection.contains( selected ) )
effectiveFeatureIds.remove( selected );
}
}
break;
}

if ( !effectiveFeatureIds.isEmpty() )
{
if ( effectiveFeatureIds.size() > 1 )
{
QAction *actionAll = new QAction( beginningOfText + tr( "All Features (%1)" ).arg( effectiveFeatureIds.count() ), this );
connect( actionAll, &QAction::triggered, this, &QgsMapToolSelectMenuActions::selectFeature );
connect( actionAll, &QAction::hovered, this, &QgsMapToolSelectMenuActions::highLightFeatures );
QVariantList list;
for ( const QgsFeatureId &id : effectiveFeatureIds )
list.append( id );

actionAll->setData( list );
actionsList.append( actionAll );
}
for ( const QgsFeatureId &id : effectiveFeatureIds )
{
QAction *featureAction = new QAction( beginningOfText + tr( "Feature %1" ).arg( id ), this ) ;
featureAction->setData( id );
connect( featureAction, &QAction::triggered, this, &QgsMapToolSelectMenuActions::selectFeature );
connect( featureAction, &QAction::hovered, this, &QgsMapToolSelectMenuActions::highLightFeatures );
actionsList.append( featureAction );
}
}
return actionsList;
}

QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::~QgsMapToolSelectMenuActions()
{
removeHighLight();
}

void QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::selectFeature()
{
if ( !mVectorLayer )
return;
QAction *senderAction = qobject_cast<QAction *>( sender() );

if ( !senderAction )
return;

QVariant featureVariant = senderAction->data();

QgsFeatureIds ids;
if ( featureVariant.type() == QVariant::List )
{
QVariantList list = featureVariant.toList();
for ( const QVariant &var : list )
ids.insert( var.toLongLong() );
}

if ( featureVariant.type() == QVariant::LongLong )
ids.insert( featureVariant.toLongLong() );

if ( !ids.empty() )
mVectorLayer->selectByIds( ids, mBehavior );

}

void QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::highLightFeatures()
{
removeHighLight();

if ( !mVectorLayer )
return;

QAction *senderAction = qobject_cast<QAction *>( sender() );
if ( !senderAction )
return;

QVariant featureVariant = senderAction->data();

QgsFeatureIds ids;
if ( featureVariant.type() == QVariant::List )
{
QVariantList list = featureVariant.toList();
for ( const QVariant &var : list )
ids.insert( var.toLongLong() );
}

if ( featureVariant.type() == QVariant::LongLong )
ids.insert( featureVariant.toLongLong() );

if ( !ids.empty() )
{
for ( const QgsFeatureId &id : ids )
{
QgsFeature feat = mVectorLayer->getFeature( id );
QgsGeometry geom = feat.geometry();
if ( !geom.isEmpty() )
{
QgsHighlight *hl = new QgsHighlight( mCanvas, geom, mVectorLayer );
styleHighlight( hl );
mHighLight.append( hl );
}
}
}

}

void QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::onLayerDestroyed()
{
mVectorLayer = nullptr;
removeHighLight();
}

void QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::removeHighLight()
{
for ( QgsHighlight *hl : mHighLight )
delete hl;

mHighLight.clear();
}

void QgsMapToolSelectUtils::QgsMapToolSelectMenuActions::styleHighlight( QgsHighlight *highlight )
{
QgsSettings settings;
QColor color = QColor( settings.value( QStringLiteral( "Map/highlight/color" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
int alpha = settings.value( QStringLiteral( "Map/highlight/colorAlpha" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
double buffer = settings.value( QStringLiteral( "Map/highlight/buffer" ), Qgis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
double minWidth = settings.value( QStringLiteral( "Map/highlight/minWidth" ), Qgis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();

highlight->setColor( color ); // sets also fill with default alpha
color.setAlpha( alpha );
highlight->setFillColor( color ); // sets fill with alpha
highlight->setBuffer( buffer );
highlight->setMinWidth( minWidth );
}

0 comments on commit 39b1116

Please sign in to comment.