Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move 'coordinate capture' functionality to map canvas
Instead of requiring users to enable the core c++ plugin 'Coordinate Capture'
in order to copy coordinates for a map point, instead add this functionality
to a map canvas right click context menu.

Now, when compatible map tools are activated (pan, zoom, select by rect), right clicking
on the canvas shows a context menu with a "Copy Coordinate" submenu. The
submenu shows options for copying the coordinate in the map CRS, WGS84 or
a custom preset CRS.

Also adds API to allow 3rd party QgsMapTool subclasses to implement their own
context menus which include this Copy Coordinate action.
  • Loading branch information
nyalldawson committed May 26, 2020
1 parent c03ac22 commit ea32682
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 7 deletions.
1 change: 1 addition & 0 deletions python/gui/auto_generated/qgsmapcanvas.sip.in
Expand Up @@ -20,6 +20,7 @@







class QgsMapCanvas : QGraphicsView class QgsMapCanvas : QGraphicsView
{ {
%Docstring %Docstring
Expand Down
19 changes: 19 additions & 0 deletions python/gui/auto_generated/qgsmaptool.sip.in
Expand Up @@ -52,6 +52,7 @@ implemented as map tools.
Transient, Transient,
EditTool, EditTool,
AllowZoomRect, AllowZoomRect,
ShowContextMenu,
}; };
typedef QFlags<QgsMapTool::Flag> Flags; typedef QFlags<QgsMapTool::Flag> Flags;


Expand Down Expand Up @@ -191,6 +192,24 @@ Gets search radius in map units for given canvas. Used by identify, tip etc.
The values is calculated from searchRadiusMM(). The values is calculated from searchRadiusMM().


.. versionadded:: 2.3 .. versionadded:: 2.3
%End

virtual void populateContextMenu( QMenu *menu );
%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``.

The default implementation does nothing.

.. note::

The context menu is only shown when the ShowContextMenu flag
is present in flags().

.. versionadded:: 3.14
%End %End


signals: signals:
Expand Down
1 change: 1 addition & 0 deletions python/gui/auto_generated/qgsmaptoolpan.sip.in
Expand Up @@ -34,6 +34,7 @@ constructor




virtual Flags flags() const; virtual Flags flags() const;

virtual void canvasPressEvent( QgsMapMouseEvent *e ); virtual void canvasPressEvent( QgsMapMouseEvent *e );


virtual void canvasMoveEvent( QgsMapMouseEvent *e ); virtual void canvasMoveEvent( QgsMapMouseEvent *e );
Expand Down
1 change: 1 addition & 0 deletions python/gui/auto_generated/qgsmaptoolzoom.sip.in
Expand Up @@ -28,6 +28,7 @@ constructor
~QgsMapToolZoom(); ~QgsMapToolZoom();


virtual Flags flags() const; virtual Flags flags() const;

virtual void canvasMoveEvent( QgsMapMouseEvent *e ); virtual void canvasMoveEvent( QgsMapMouseEvent *e );


virtual void canvasPressEvent( QgsMapMouseEvent *e ); virtual void canvasPressEvent( QgsMapMouseEvent *e );
Expand Down
16 changes: 16 additions & 0 deletions src/app/qgsmaptoolselect.cpp
Expand Up @@ -122,6 +122,22 @@ void QgsMapToolSelect::deactivate()
QgsMapTool::deactivate(); QgsMapTool::deactivate();
} }


QgsMapTool::Flags QgsMapToolSelect::flags() const
{
switch ( mSelectionHandler->selectionMode() )
{
case QgsMapToolSelectionHandler::SelectPolygon:
break;

case QgsMapToolSelectionHandler::SelectSimple:
case QgsMapToolSelectionHandler::SelectFreehand:
case QgsMapToolSelectionHandler::SelectRadius:
return QgsMapTool::flags() | QgsMapTool::ShowContextMenu;
}

return QgsMapTool::flags();
}

void QgsMapToolSelect::selectFeatures( Qt::KeyboardModifiers modifiers ) void QgsMapToolSelect::selectFeatures( Qt::KeyboardModifiers modifiers )
{ {
if ( mSelectionHandler->selectionMode() == QgsMapToolSelectionHandler::SelectSimple && if ( mSelectionHandler->selectionMode() == QgsMapToolSelectionHandler::SelectSimple &&
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsmaptoolselect.h
Expand Up @@ -50,6 +50,7 @@ class APP_EXPORT QgsMapToolSelect : public QgsMapTool
void keyPressEvent( QKeyEvent *e ) override; void keyPressEvent( QKeyEvent *e ) override;
void keyReleaseEvent( QKeyEvent *e ) override; void keyReleaseEvent( QKeyEvent *e ) override;
void deactivate() override; void deactivate() override;
Flags flags() const override;


signals: signals:


Expand Down
95 changes: 91 additions & 4 deletions src/gui/qgsmapcanvas.cpp
Expand Up @@ -37,6 +37,8 @@ email : sherman at mrcc.com
#include <QStringList> #include <QStringList>
#include <QWheelEvent> #include <QWheelEvent>
#include <QWindow> #include <QWindow>
#include <QMenu>
#include <QClipboard>


#include "qgis.h" #include "qgis.h"
#include "qgssettings.h" #include "qgssettings.h"
Expand Down Expand Up @@ -80,6 +82,7 @@ email : sherman at mrcc.com
#include "qgsvectorlayertemporalproperties.h" #include "qgsvectorlayertemporalproperties.h"
#include "qgstemporalcontroller.h" #include "qgstemporalcontroller.h"
#include "qgsruntimeprofiler.h" #include "qgsruntimeprofiler.h"
#include "qgsprojectionselectiondialog.h"


/** /**
* \ingroup gui * \ingroup gui
Expand Down Expand Up @@ -114,6 +117,7 @@ class QgsMapCanvas::CanvasProperties
QgsMapCanvas::QgsMapCanvas( QWidget *parent ) QgsMapCanvas::QgsMapCanvas( QWidget *parent )
: QGraphicsView( parent ) : QGraphicsView( parent )
, mCanvasProperties( new CanvasProperties ) , mCanvasProperties( new CanvasProperties )
, mMenu( new QMenu( this ) )
, mExpressionContextScope( tr( "Map Canvas" ) ) , mExpressionContextScope( tr( "Map Canvas" ) )
{ {
mScene = new QGraphicsScene(); mScene = new QGraphicsScene();
Expand Down Expand Up @@ -795,6 +799,85 @@ void QgsMapCanvas::clearTemporalCache()
} }
} }


void QgsMapCanvas::showContextMenu( QgsMapMouseEvent *event )
{
const QgsPointXY mapPoint = event->originalMapPoint();

mMenu->clear();

QMenu *copyCoordinateMenu = new QMenu( tr( "Copy Coordinate" ), mMenu );
copyCoordinateMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );

auto addCoordinateFormat = [ = ]( const QString identifier, const QgsCoordinateReferenceSystem & crs )
{
QgsCoordinateTransform ct( mSettings.destinationCrs(), crs, mSettings.transformContext() );
try
{
const QgsPointXY transformedPoint = ct.transform( mapPoint );

const int displayPrecision = crs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 5 : 3;
QAction *copyCoordinateAction = new QAction( QStringLiteral( "%1, %2 (%3)" ).arg(
QString::number( transformedPoint.x(), 'f', displayPrecision ),
QString::number( transformedPoint.y(), 'f', displayPrecision ),
identifier ), mMenu );

connect( copyCoordinateAction, &QAction::triggered, this, [displayPrecision, transformedPoint]
{
QClipboard *clipboard = QApplication::clipboard();

const QString coordinates = QString::number( transformedPoint.x(), 'f', displayPrecision ) + ',' + QString::number( transformedPoint.y(), 'f', displayPrecision );

//if we are on x11 system put text into selection ready for middle button pasting
if ( clipboard->supportsSelection() )
{
clipboard->setText( coordinates, QClipboard::Selection );
}
clipboard->setText( coordinates, QClipboard::Clipboard );

} );
copyCoordinateMenu->addAction( copyCoordinateAction );
}
catch ( QgsCsException & )
{

}
};

addCoordinateFormat( tr( "%1 - Map CRS" ).arg( mSettings.destinationCrs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ), mSettings.destinationCrs() );
if ( mSettings.destinationCrs() != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
addCoordinateFormat( tr( "WGS84" ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );

QgsSettings settings;
const QString customCrsString = settings.value( QStringLiteral( "qgis/custom_coordinate_crs" ) ).toString();
if ( !customCrsString.isEmpty() )
{
QgsCoordinateReferenceSystem customCrs( customCrsString );
if ( customCrs != mSettings.destinationCrs() && customCrs != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
{
addCoordinateFormat( tr( "%1 - Custom CRS" ).arg( customCrs.userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ), customCrs );
}
}
copyCoordinateMenu->addSeparator();
QAction *setCustomCrsAction = new QAction( QStringLiteral( "Set Custom CRS…" ), mMenu );
connect( setCustomCrsAction, &QAction::triggered, this, [ = ]
{
QgsProjectionSelectionDialog selector( this );
selector.setCrs( QgsCoordinateReferenceSystem( customCrsString ) );
if ( selector.exec() )
{
QgsSettings().setValue( QStringLiteral( "qgis/custom_coordinate_crs" ), selector.crs().authid().isEmpty() ? selector.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : selector.crs().authid() );
}
} );
copyCoordinateMenu->addAction( setCustomCrsAction );

mMenu->addMenu( copyCoordinateMenu );

if ( mMapTool )
mMapTool->populateContextMenu( mMenu );

mMenu->exec( event->globalPos() );
}

void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange ) void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
{ {
if ( temporalRange() == dateTimeRange ) if ( temporalRange() == dateTimeRange )
Expand Down Expand Up @@ -1601,6 +1684,12 @@ void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
beginZoomRect( e->pos() ); beginZoomRect( e->pos() );
return; return;
} }
else if ( mMapTool->flags() & QgsMapTool::ShowContextMenu && e->button() == Qt::RightButton )
{
std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
showContextMenu( me.get() );
return;
}
else else
{ {
std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) ); std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
Expand All @@ -1616,9 +1705,7 @@ void QgsMapCanvas::mousePressEvent( QMouseEvent *e )


mCanvasProperties->mouseButtonDown = true; mCanvasProperties->mouseButtonDown = true;
mCanvasProperties->rubberStartPoint = e->pos(); mCanvasProperties->rubberStartPoint = e->pos();

}
} // mousePressEvent



void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e ) void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
{ {
Expand Down Expand Up @@ -1679,7 +1766,7 @@ void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
if ( mCanvasProperties->panSelectorDown ) if ( mCanvasProperties->panSelectorDown )
return; return;


} // mouseReleaseEvent }


void QgsMapCanvas::resizeEvent( QResizeEvent *e ) void QgsMapCanvas::resizeEvent( QResizeEvent *e )
{ {
Expand Down
8 changes: 8 additions & 0 deletions src/gui/qgsmapcanvas.h
Expand Up @@ -71,6 +71,9 @@ class QgsReferencedRectangle;


class QgsTemporalController; class QgsTemporalController;


class QMenu;
class QgsMapMouseEvent;



/** /**
* \ingroup gui * \ingroup gui
Expand Down Expand Up @@ -1106,6 +1109,9 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! previous tool if current is for zooming/panning //! previous tool if current is for zooming/panning
QgsMapTool *mLastNonZoomMapTool = nullptr; QgsMapTool *mLastNonZoomMapTool = nullptr;


//! Context menu
QMenu *mMenu = nullptr;

//! recently used extent //! recently used extent
QList <QgsRectangle> mLastExtent; QList <QgsRectangle> mLastExtent;
int mLastExtentIndex = -1; int mLastExtentIndex = -1;
Expand Down Expand Up @@ -1239,6 +1245,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
*/ */
void clearTemporalCache(); void clearTemporalCache();


void showContextMenu( QgsMapMouseEvent *event );

friend class TestQgsMapCanvas; friend class TestQgsMapCanvas;


}; // class QgsMapCanvas }; // class QgsMapCanvas
Expand Down
5 changes: 5 additions & 0 deletions src/gui/qgsmaptool.cpp
Expand Up @@ -227,3 +227,8 @@ double QgsMapTool::searchRadiusMU( QgsMapCanvas *canvas )
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings ); QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
return searchRadiusMU( context ); return searchRadiusMU( context );
} }

void QgsMapTool::populateContextMenu( QMenu * )
{

}
18 changes: 18 additions & 0 deletions src/gui/qgsmaptool.h
Expand Up @@ -39,6 +39,7 @@ class QPoint;
class QAction; class QAction;
class QAbstractButton; class QAbstractButton;
class QgsMapMouseEvent; class QgsMapMouseEvent;
class QMenu;


#ifdef SIP_RUN #ifdef SIP_RUN
% ModuleHeaderCode % ModuleHeaderCode
Expand Down Expand Up @@ -92,6 +93,7 @@ class GUI_EXPORT QgsMapTool : public QObject
tool automatically restored. */ tool automatically restored. */
EditTool = 1 << 2, //!< Map tool is an edit tool, which can only be used when layer is editable EditTool = 1 << 2, //!< Map tool is an edit tool, which can only be used when layer is editable
AllowZoomRect = 1 << 3, //!< Allow zooming by rectangle (by holding shift and dragging) while the tool is active AllowZoomRect = 1 << 3, //!< Allow zooming by rectangle (by holding shift and dragging) while the tool is active
ShowContextMenu = 1 << 4, //!< Show a context menu when right-clicking with the tool (since QGIS 3.14). See populateContextMenu().
}; };
Q_DECLARE_FLAGS( Flags, Flag ) Q_DECLARE_FLAGS( Flags, Flag )


Expand Down Expand Up @@ -191,6 +193,22 @@ class GUI_EXPORT QgsMapTool : public QObject
* \since QGIS 2.3 */ * \since QGIS 2.3 */
static double searchRadiusMU( QgsMapCanvas *canvas ); static double searchRadiusMU( QgsMapCanvas *canvas );


/**
* Allows the tool to populate and customize the given \a menu,
* prior to showing it in response to a right-mouse button click.
*
* \a menu will be initially populated with a set of default, generic actions.
* Any new actions added to the menu should be correctly parented to \a menu.
*
* The default implementation does nothing.
*
* \note The context menu is only shown when the ShowContextMenu flag
* is present in flags().
*
* \since QGIS 3.14
*/
virtual void populateContextMenu( QMenu *menu );

signals: signals:
//! emit a message //! emit a message
void messageEmitted( const QString &message, Qgis::MessageLevel = Qgis::Info ); void messageEmitted( const QString &message, Qgis::MessageLevel = Qgis::Info );
Expand Down
6 changes: 5 additions & 1 deletion src/gui/qgsmaptoolpan.cpp
Expand Up @@ -15,7 +15,6 @@


#include <QBitmap> #include <QBitmap>
#include <QCursor> #include <QCursor>

#include "qgsmaptoolpan.h" #include "qgsmaptoolpan.h"
#include "qgsmapcanvas.h" #include "qgsmapcanvas.h"
#include "qgsmaptopixel.h" #include "qgsmaptopixel.h"
Expand Down Expand Up @@ -50,6 +49,11 @@ void QgsMapToolPan::deactivate()
QgsMapTool::deactivate(); QgsMapTool::deactivate();
} }


QgsMapTool::Flags QgsMapToolPan::flags() const
{
return QgsMapTool::Transient | QgsMapTool::AllowZoomRect | QgsMapTool::ShowContextMenu;
}

void QgsMapToolPan::canvasPressEvent( QgsMapMouseEvent *e ) void QgsMapToolPan::canvasPressEvent( QgsMapMouseEvent *e )
{ {
if ( e->button() == Qt::LeftButton ) if ( e->button() == Qt::LeftButton )
Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgsmaptoolpan.h
Expand Up @@ -41,7 +41,7 @@ class GUI_EXPORT QgsMapToolPan : public QgsMapTool
void activate() override; void activate() override;
void deactivate() override; void deactivate() override;


Flags flags() const override { return QgsMapTool::Transient | QgsMapTool::AllowZoomRect; } Flags flags() const override;
void canvasPressEvent( QgsMapMouseEvent *e ) override; void canvasPressEvent( QgsMapMouseEvent *e ) override;
void canvasMoveEvent( QgsMapMouseEvent *e ) override; void canvasMoveEvent( QgsMapMouseEvent *e ) override;
void canvasReleaseEvent( QgsMapMouseEvent *e ) override; void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
Expand Down
4 changes: 4 additions & 0 deletions src/gui/qgsmaptoolzoom.cpp
Expand Up @@ -47,6 +47,10 @@ QgsMapToolZoom::~QgsMapToolZoom()
delete mRubberBand; delete mRubberBand;
} }


QgsMapTool::Flags QgsMapToolZoom::flags() const
{
return QgsMapTool::Transient | QgsMapTool::ShowContextMenu;
}


void QgsMapToolZoom::canvasMoveEvent( QgsMapMouseEvent *e ) void QgsMapToolZoom::canvasMoveEvent( QgsMapMouseEvent *e )
{ {
Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgsmaptoolzoom.h
Expand Up @@ -36,7 +36,7 @@ class GUI_EXPORT QgsMapToolZoom : public QgsMapTool
QgsMapToolZoom( QgsMapCanvas *canvas, bool zoomOut ); QgsMapToolZoom( QgsMapCanvas *canvas, bool zoomOut );
~QgsMapToolZoom() override; ~QgsMapToolZoom() override;


Flags flags() const override { return QgsMapTool::Transient; } Flags flags() const override;
void canvasMoveEvent( QgsMapMouseEvent *e ) override; void canvasMoveEvent( QgsMapMouseEvent *e ) override;
void canvasPressEvent( QgsMapMouseEvent *e ) override; void canvasPressEvent( QgsMapMouseEvent *e ) override;
void canvasReleaseEvent( QgsMapMouseEvent *e ) override; void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
Expand Down

0 comments on commit ea32682

Please sign in to comment.