Skip to content

Commit

Permalink
switch select by radius map tool to click-click
Browse files Browse the repository at this point in the history
fix #17868

this is seen as a bugfix and will be merged in 3.0 to have coherent map tools changes
also add a user input (also features keyboard modifiers)
  • Loading branch information
3nids committed Jan 18, 2018
1 parent 851cdb2 commit aca2078
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 62 deletions.
2 changes: 1 addition & 1 deletion src/app/qgsmaptoolselect.cpp
Expand Up @@ -50,6 +50,6 @@ void QgsMapToolSelect::canvasReleaseEvent( QgsMapMouseEvent *e )
QgsMapToolSelectUtils::expandSelectRectangle( selectRect, vlayer, e->pos() );
QgsMapToolSelectUtils::setRubberBand( mCanvas, selectRect, &rubberBand );
QgsGeometry selectGeom( rubberBand.asGeometry() );
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, selectGeom, e );
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, selectGeom, e->modifiers() );
rubberBand.reset( QgsWkbTypes::PolygonGeometry );
}
4 changes: 2 additions & 2 deletions src/app/qgsmaptoolselectfreehand.cpp
Expand Up @@ -86,9 +86,9 @@ void QgsMapToolSelectFreehand::canvasReleaseEvent( QgsMapMouseEvent *e )
{
QgsGeometry shapeGeom = mRubberBand->asGeometry();
if ( singleSelect )
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, shapeGeom, e );
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, shapeGeom, e->modifiers() );
else
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, shapeGeom, e );
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, shapeGeom, e->modifiers() );
}

mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsmaptoolselectpolygon.cpp
Expand Up @@ -54,7 +54,7 @@ void QgsMapToolSelectPolygon::canvasPressEvent( QgsMapMouseEvent *e )
if ( mRubberBand->numberOfVertices() > 2 )
{
QgsGeometry polygonGeom = mRubberBand->asGeometry();
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, polygonGeom, e );
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, polygonGeom, e->modifiers() );
}
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
delete mRubberBand;
Expand Down
212 changes: 176 additions & 36 deletions src/app/qgsmaptoolselectradius.cpp
Expand Up @@ -13,22 +13,91 @@ email : jpalmer at linz dot govt dot nz
* *
***************************************************************************/


#include <cmath>
#include <QMouseEvent>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>

#include "qgisapp.h"
#include "qgsmaptoolselectradius.h"
#include "qgsmaptoolselectutils.h"
#include "qgsgeometry.h"
#include "qgsrubberband.h"
#include "qgsmapcanvas.h"
#include "qgis.h"
#include "qgslogger.h"
#include "qgsdoublespinbox.h"


#include <cmath>
#include <QMouseEvent>

const int RADIUS_SEGMENTS = 40;

QgsDistanceWidget::QgsDistanceWidget( const QString &label, QWidget *parent )
: QWidget( parent )
{
mLayout = new QHBoxLayout( this );
mLayout->setContentsMargins( 0, 0, 0, 0 );
//mLayout->setAlignment( Qt::AlignLeft );
setLayout( mLayout );

if ( !label.isEmpty() )
{
QLabel *lbl = new QLabel( label, this );
lbl->setAlignment( Qt::AlignRight | Qt::AlignCenter );
mLayout->addWidget( lbl );
}

mDistanceSpinBox = new QgsDoubleSpinBox( this );
mDistanceSpinBox->setSingleStep( 1 );
mDistanceSpinBox->setValue( 0 );
mDistanceSpinBox->setMinimum( 0 );
mDistanceSpinBox->setMaximum( 1000000000 );
mDistanceSpinBox->setShowClearButton( false );
mDistanceSpinBox->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
mLayout->addWidget( mDistanceSpinBox );

// connect signals
mDistanceSpinBox->installEventFilter( this );
connect( mDistanceSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsDistanceWidget::distanceSpinBoxValueChanged );

// config focus
setFocusProxy( mDistanceSpinBox );
}

void QgsDistanceWidget::setDistance( double distance )
{
mDistanceSpinBox->setValue( distance );
}

double QgsDistanceWidget::distance()
{
return mDistanceSpinBox->value();
}

bool QgsDistanceWidget::eventFilter( QObject *obj, QEvent *ev )
{
if ( obj == mDistanceSpinBox && ev->type() == QEvent::KeyPress )
{
QKeyEvent *event = static_cast<QKeyEvent *>( ev );
if ( event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return )
{
emit distanceEditingFinished( distance(), event->modifiers() );
return true;
}
}

return false;
}

void QgsDistanceWidget::distanceSpinBoxValueChanged( double distance )
{
emit distanceChanged( distance );
}

QgsMapToolSelectRadius::QgsMapToolSelectRadius( QgsMapCanvas *canvas )
: QgsMapTool( canvas )
, mDragging( false )
{
mRubberBand = nullptr;
mCursor = Qt::ArrowCursor;
Expand All @@ -38,74 +107,145 @@ QgsMapToolSelectRadius::QgsMapToolSelectRadius( QgsMapCanvas *canvas )

QgsMapToolSelectRadius::~QgsMapToolSelectRadius()
{
delete mRubberBand;
}

void QgsMapToolSelectRadius::canvasPressEvent( QgsMapMouseEvent *e )
{
if ( e->button() != Qt::LeftButton )
return;

mRadiusCenter = toMapCoordinates( e->pos() );
deleteRotationWidget();
deleteRubberband();
}


void QgsMapToolSelectRadius::canvasMoveEvent( QgsMapMouseEvent *e )
{
if ( e->buttons() != Qt::LeftButton )
if ( !mActive )
return;

if ( !mDragging )
if ( !mRubberBand )
{
if ( !mRubberBand )
{
mRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
mRubberBand->setFillColor( mFillColor );
mRubberBand->setStrokeColor( mStrokeColor );
}
mDragging = true;
mRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
mRubberBand->setFillColor( mFillColor );
mRubberBand->setStrokeColor( mStrokeColor );
}

QgsPointXY radiusEdge = toMapCoordinates( e->pos() );
setRadiusRubberBand( radiusEdge );
updateRadiusFromEdge( radiusEdge );
}


void QgsMapToolSelectRadius::canvasReleaseEvent( QgsMapMouseEvent *e )
{
if ( e->button() == Qt::RightButton )
{
deleteRotationWidget();
deleteRubberband();
mActive = false;
return;
}

if ( e->button() != Qt::LeftButton )
return;

if ( !mDragging )
if ( !mActive )
{
mActive = true;
mRadiusCenter = toMapCoordinates( e->pos() );
createRotationWidget();
}
else
{
if ( !mRubberBand )
{
mRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
mRubberBand->setFillColor( mFillColor );
mRubberBand->setStrokeColor( mStrokeColor );
}
mRadiusCenter = toMapCoordinates( e->pos() );
QgsPointXY radiusEdge = toMapCoordinates( QPoint( e->pos().x() + 1, e->pos().y() + 1 ) );
setRadiusRubberBand( radiusEdge );
updateRadiusFromEdge( radiusEdge );
selectFromRubberband( e->modifiers() );
}
QgsGeometry radiusGeometry = mRubberBand->asGeometry();
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, radiusGeometry, e );
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
delete mRubberBand;
mRubberBand = nullptr;
mDragging = false;
}


void QgsMapToolSelectRadius::setRadiusRubberBand( QgsPointXY &radiusEdge )
void QgsMapToolSelectRadius::updateRadiusFromEdge( QgsPointXY &radiusEdge )
{
double radius = std::sqrt( mRadiusCenter.sqrDist( radiusEdge ) );
if ( mDistanceWidget )
{
mDistanceWidget->setDistance( radius );
mDistanceWidget->setFocus( Qt::TabFocusReason );
mDistanceWidget->editor()->selectAll();
}
else
{
updateRubberband( radius );
}
}

void QgsMapToolSelectRadius::deactivate()
{
deleteRotationWidget();
deleteRubberband();
mActive = false;
QgsMapTool::deactivate();
}

void QgsMapToolSelectRadius::updateRubberband( const double &radius )
{
double r = std::sqrt( mRadiusCenter.sqrDist( radiusEdge ) );
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
for ( int i = 0; i <= RADIUS_SEGMENTS; ++i )
{
double theta = i * ( 2.0 * M_PI / RADIUS_SEGMENTS );
QgsPointXY radiusPoint( mRadiusCenter.x() + r * std::cos( theta ),
mRadiusCenter.y() + r * std::sin( theta ) );
QgsPointXY radiusPoint( mRadiusCenter.x() + radius * std::cos( theta ),
mRadiusCenter.y() + radius * std::sin( theta ) );
mRubberBand->addPoint( radiusPoint, false );
}
mRubberBand->closePoints( true );
}

void QgsMapToolSelectRadius::selectFromRubberband( const Qt::KeyboardModifiers &modifiers )
{
QgsGeometry radiusGeometry = mRubberBand->asGeometry();
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, radiusGeometry, modifiers );
mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
deleteRotationWidget();
deleteRubberband();
mActive = false;
}

void QgsMapToolSelectRadius::radiusValueEntered( const double &radius, const Qt::KeyboardModifiers &modifiers )
{
updateRubberband( radius );
selectFromRubberband( modifiers );
}

void QgsMapToolSelectRadius::deleteRubberband()
{
delete mRubberBand;
mRubberBand = nullptr;
}


void QgsMapToolSelectRadius::createRotationWidget()
{
if ( !mCanvas )
{
return;
}

deleteRotationWidget();

mDistanceWidget = new QgsDistanceWidget( QStringLiteral( "Selection radius:" ) );
QgisApp::instance()->addUserInputWidget( mDistanceWidget );
mDistanceWidget->setFocus( Qt::TabFocusReason );

connect( mDistanceWidget, &QgsDistanceWidget::distanceChanged, this, &QgsMapToolSelectRadius::updateRubberband );
connect( mDistanceWidget, &QgsDistanceWidget::distanceEditingFinished, this, &QgsMapToolSelectRadius::radiusValueEntered );
}

void QgsMapToolSelectRadius::deleteRotationWidget()
{
if ( mDistanceWidget )
{
disconnect( mDistanceWidget, &QgsDistanceWidget::distanceChanged, this, &QgsMapToolSelectRadius::updateRubberband );
disconnect( mDistanceWidget, &QgsDistanceWidget::distanceEditingFinished, this, &QgsMapToolSelectRadius::radiusValueEntered );
mDistanceWidget->releaseKeyboard();
mDistanceWidget->deleteLater();
}
mDistanceWidget = nullptr;
}
65 changes: 57 additions & 8 deletions src/app/qgsmaptoolselectradius.h
Expand Up @@ -21,9 +21,41 @@ email : jpalmer at linz dot govt dot nz
#include "qgspointxy.h"
#include "qgis_app.h"

class QHBoxLayout;

class QgsDoubleSpinBox;
class QgsMapCanvas;
class QgsRubberBand;

class APP_EXPORT QgsDistanceWidget : public QWidget
{
Q_OBJECT

public:

explicit QgsDistanceWidget( const QString &label = QString(), QWidget *parent = nullptr );

void setDistance( double distance );

double distance();

QgsDoubleSpinBox *editor() {return mDistanceSpinBox;}

signals:
void distanceChanged( double distance );
void distanceEditingFinished( double distance, const Qt::KeyboardModifiers &modifiers );

protected:
bool eventFilter( QObject *obj, QEvent *ev ) override;

private slots:
void distanceSpinBoxValueChanged( double distance );

private:
QHBoxLayout *mLayout = nullptr;
QgsDoubleSpinBox *mDistanceSpinBox = nullptr;
};


class APP_EXPORT QgsMapToolSelectRadius : public QgsMapTool
{
Expand All @@ -36,29 +68,46 @@ class APP_EXPORT QgsMapToolSelectRadius : public QgsMapTool
//! Overridden mouse move event
void canvasMoveEvent( QgsMapMouseEvent *e ) override;

//! Overridden mouse press event
void canvasPressEvent( QgsMapMouseEvent *e ) override;

//! Overridden mouse release event
void canvasReleaseEvent( QgsMapMouseEvent *e ) override;

//! called when map tool is being deactivated
void deactivate() override;

private slots:
//! update the rubber band from the input widget
void updateRubberband( const double &radius );

/**
* triggered when the user input widget has a new value
* either programmatically (from the mouse event) or entered by the user
*/
void radiusValueEntered( const double &radius, const Qt::KeyboardModifiers &modifiers );

private:
//! perform selection using radius from rubberband
void selectFromRubberband( const Qt::KeyboardModifiers &modifiers );

//! on mouse events update the rubber band using the mouse position
void updateRadiusFromEdge( QgsPointXY &radiusEdge );

//! sets the rubber band to a circle approximated using 40 segments.
// The radius center point is defined in the canvasPressEvent event.
void setRadiusRubberBand( QgsPointXY &radiusEdge );
void deleteRubberband();
void deleteRotationWidget();
void createRotationWidget();

//! used for storing all of the maps point for the polygon
QgsRubberBand *mRubberBand = nullptr;

//! Center point for the radius
QgsPointXY mRadiusCenter;

bool mDragging;
bool mActive = false;

QColor mFillColor;

QColor mStrokeColor;

//! Shows current angle value and allows numerical editing
QgsDistanceWidget *mDistanceWidget = nullptr;
};

#endif

0 comments on commit aca2078

Please sign in to comment.