From 15f3a78a92541ad03dcc6efaffed8502f38a346e Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 26 May 2024 14:46:34 +0700 Subject: [PATCH 1/7] [advanced digitizing] Add circles intersection digitizing tool --- images/images.qrc | 1 + .../default/cadtools/circlesintersection.svg | 19 ++ .../qgsadvanceddigitizingdockwidget.sip.in | 22 +- .../qgsadvanceddigitizingtools.sip.in | 84 +++++++ python/PyQt6/gui/gui_auto.sip | 1 + .../qgsadvanceddigitizingdockwidget.sip.in | 22 +- .../qgsadvanceddigitizingtools.sip.in | 84 +++++++ python/gui/gui_auto.sip | 1 + src/app/qgisapp.cpp | 2 +- src/gui/CMakeLists.txt | 2 + src/gui/qgsadvanceddigitizingcanvasitem.cpp | 7 + src/gui/qgsadvanceddigitizingdockwidget.cpp | 122 +++++++++- src/gui/qgsadvanceddigitizingdockwidget.h | 31 ++- src/gui/qgsadvanceddigitizingtools.cpp | 224 ++++++++++++++++++ src/gui/qgsadvanceddigitizingtools.h | 147 ++++++++++++ src/gui/qgsmaptooladvanceddigitizing.cpp | 35 +-- src/ui/qgsadvanceddigitizingdockwidgetbase.ui | 14 +- 17 files changed, 787 insertions(+), 31 deletions(-) create mode 100644 images/themes/default/cadtools/circlesintersection.svg create mode 100644 python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in create mode 100644 python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in create mode 100644 src/gui/qgsadvanceddigitizingtools.cpp create mode 100644 src/gui/qgsadvanceddigitizingtools.h diff --git a/images/images.qrc b/images/images.qrc index 6dc35903e4f9..d348bc6e9668 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -693,6 +693,7 @@ themes/default/mActionPanHighlightFeature.svg themes/default/mActionPrevious.svg themes/default/mActionPlay.svg + themes/default/cadtools/circlesintersection.svg themes/default/cadtools/construction.svg themes/default/cadtools/delta.svg themes/default/cadtools/floater.svg diff --git a/images/themes/default/cadtools/circlesintersection.svg b/images/themes/default/cadtools/circlesintersection.svg new file mode 100644 index 000000000000..3682daa54ca3 --- /dev/null +++ b/images/themes/default/cadtools/circlesintersection.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index 874873f94a2c..e9c720fc8e4b 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -204,13 +204,15 @@ Removes unit suffix from the constraint text. }; - explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = 0 ); + explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = 0, QgsUserInputWidget *userInputWidget = 0 ); %Docstring Create an advanced digitizing dock widget :param canvas: The map canvas on which the widget operates :param parent: The parent +:param userInputWidget: The user input widget on which tools can add widget overlays on top of the map canvas (since QGIS 3.40) %End + ~QgsAdvancedDigitizingDockWidget(); bool canvasKeyPressEventFilter( QKeyEvent *e ); %Docstring @@ -232,6 +234,24 @@ apply the CAD constraints. The will modify the position of the map event in map %Docstring align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. +%End + + bool processCanvasPressEvent( QgsMapMouseEvent *e ); + bool processCanvasMoveEvent( QgsMapMouseEvent *e ); + bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + + void setTool( QgsAdvancedDigitizingTool *tool ); +%Docstring +Sets an advanced digitizing tool which will take over digitizing until the tool is close. + +.. versionadded:: 3.40 +%End + + QgsAdvancedDigitizingTool *tool() const; +%Docstring +Returns the current advanced digitizing tool. Returns ``None`` if not set. + +.. versionadded:: 3.40 %End void releaseLocks( bool releaseRepeatingLocks = true ); diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in new file mode 100644 index 000000000000..c3511108cb71 --- /dev/null +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -0,0 +1,84 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtools.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsAdvancedDigitizingTool : QWidget +{ +%Docstring(signature="appended") +An abstract class for advanced digitizing tools. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtools.h" +%End + public: + + explicit QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); +%Docstring +The advanced digitizing tool constructor. + +:param canvas: The map canvas on which the widget operates +:param cadDockWidget: The cadDockWidget to which the floater belongs +%End + + QgsMapCanvas *mapCanvas() const; +%Docstring +Returns the map canvas associated with the tool. +%End + + QgsAdvancedDigitizingDockWidget *cadDockWidget() const; +%Docstring +Returns the advanced digitizing widget associated with the tool. +%End + + virtual void paint( QPainter *painter ); +%Docstring +Paints tool content onto the advanced digitizing canvas item. +%End + + virtual bool canvasPressEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas press event. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + virtual bool canvasMoveEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas press move. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas release event. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + signals: + + void paintRequested(); +%Docstring +Requests a new painting event to the advanced digitizing canvas item. +%End + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtools.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/PyQt6/gui/gui_auto.sip b/python/PyQt6/gui/gui_auto.sip index 915f89163b66..913c0e8b2786 100644 --- a/python/PyQt6/gui/gui_auto.sip +++ b/python/PyQt6/gui/gui_auto.sip @@ -6,6 +6,7 @@ %Include auto_generated/qgsadvanceddigitizingcanvasitem.sip %Include auto_generated/qgsadvanceddigitizingdockwidget.sip %Include auto_generated/qgsadvanceddigitizingfloater.sip +%Include auto_generated/qgsadvanceddigitizingtools.sip %Include auto_generated/qgsaggregatetoolbutton.sip %Include auto_generated/qgsalignmentcombobox.sip %Include auto_generated/qgsapplicationexitblockerinterface.sip diff --git a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index bb6476e19a17..40635d06a6b2 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -204,13 +204,15 @@ Removes unit suffix from the constraint text. }; - explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = 0 ); + explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = 0, QgsUserInputWidget *userInputWidget = 0 ); %Docstring Create an advanced digitizing dock widget :param canvas: The map canvas on which the widget operates :param parent: The parent +:param userInputWidget: The user input widget on which tools can add widget overlays on top of the map canvas (since QGIS 3.40) %End + ~QgsAdvancedDigitizingDockWidget(); bool canvasKeyPressEventFilter( QKeyEvent *e ); %Docstring @@ -232,6 +234,24 @@ apply the CAD constraints. The will modify the position of the map event in map %Docstring align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. +%End + + bool processCanvasPressEvent( QgsMapMouseEvent *e ); + bool processCanvasMoveEvent( QgsMapMouseEvent *e ); + bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + + void setTool( QgsAdvancedDigitizingTool *tool ); +%Docstring +Sets an advanced digitizing tool which will take over digitizing until the tool is close. + +.. versionadded:: 3.40 +%End + + QgsAdvancedDigitizingTool *tool() const; +%Docstring +Returns the current advanced digitizing tool. Returns ``None`` if not set. + +.. versionadded:: 3.40 %End void releaseLocks( bool releaseRepeatingLocks = true ); diff --git a/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in new file mode 100644 index 000000000000..c3511108cb71 --- /dev/null +++ b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -0,0 +1,84 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtools.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsAdvancedDigitizingTool : QWidget +{ +%Docstring(signature="appended") +An abstract class for advanced digitizing tools. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtools.h" +%End + public: + + explicit QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); +%Docstring +The advanced digitizing tool constructor. + +:param canvas: The map canvas on which the widget operates +:param cadDockWidget: The cadDockWidget to which the floater belongs +%End + + QgsMapCanvas *mapCanvas() const; +%Docstring +Returns the map canvas associated with the tool. +%End + + QgsAdvancedDigitizingDockWidget *cadDockWidget() const; +%Docstring +Returns the advanced digitizing widget associated with the tool. +%End + + virtual void paint( QPainter *painter ); +%Docstring +Paints tool content onto the advanced digitizing canvas item. +%End + + virtual bool canvasPressEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas press event. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + virtual bool canvasMoveEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas press move. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ); +%Docstring +Handles canvas release event. If ``True`` is returned, the tool will have +blocked the event for propagating. +%End + + signals: + + void paintRequested(); +%Docstring +Requests a new painting event to the advanced digitizing canvas item. +%End + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtools.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 915f89163b66..913c0e8b2786 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -6,6 +6,7 @@ %Include auto_generated/qgsadvanceddigitizingcanvasitem.sip %Include auto_generated/qgsadvanceddigitizingdockwidget.sip %Include auto_generated/qgsadvanceddigitizingfloater.sip +%Include auto_generated/qgsadvanceddigitizingtools.sip %Include auto_generated/qgsaggregatetoolbutton.sip %Include auto_generated/qgsalignmentcombobox.sip %Include auto_generated/qgsapplicationexitblockerinterface.sip diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 8043e196a281..54345a47ac8c 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1186,7 +1186,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers // Advanced Digitizing dock startProfile( tr( "Advanced digitize panel" ) ); - mAdvancedDigitizingDockWidget = new QgsAdvancedDigitizingDockWidget( mMapCanvas, this ); + mAdvancedDigitizingDockWidget = new QgsAdvancedDigitizingDockWidget( mMapCanvas, this, mUserInputDockWidget ); mAdvancedDigitizingDockWidget->setWindowTitle( tr( "Advanced Digitizing" ) ); mAdvancedDigitizingDockWidget->setObjectName( QStringLiteral( "AdvancedDigitizingTools" ) ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 1481da0395f6..f346e387b0b0 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -518,6 +518,7 @@ set(QGIS_GUI_SRCS qgsadvanceddigitizingcanvasitem.cpp qgsadvanceddigitizingdockwidget.cpp qgsadvanceddigitizingfloater.cpp + qgsadvanceddigitizingtools.cpp qgsaggregatetoolbutton.cpp qgsalignmentcombobox.cpp qgsapplicationexitblockerinterface.cpp @@ -790,6 +791,7 @@ set(QGIS_GUI_HDRS qgsadvanceddigitizingcanvasitem.h qgsadvanceddigitizingdockwidget.h qgsadvanceddigitizingfloater.h + qgsadvanceddigitizingtools.h qgsaggregatetoolbutton.h qgsalignmentcombobox.h qgsapplicationexitblockerinterface.h diff --git a/src/gui/qgsadvanceddigitizingcanvasitem.cpp b/src/gui/qgsadvanceddigitizingcanvasitem.cpp index fcc9e0dc5c85..571601be1613 100644 --- a/src/gui/qgsadvanceddigitizingcanvasitem.cpp +++ b/src/gui/qgsadvanceddigitizingcanvasitem.cpp @@ -59,6 +59,13 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) } } + // Draw current intersection tool + if ( mAdvancedDigitizingDockWidget->tool() ) + { + mAdvancedDigitizingDockWidget->tool()->paint( painter ); + return; + } + // Use visible polygon rather than extent to properly handle rotated maps QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon(); const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length(); diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 550ee670218b..e93fb8e11618 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -38,6 +38,7 @@ #include "qgsunittypes.h" #include "qgssettingsentryimpl.h" #include "qgssettingstree.h" +#include "qgsuserinputwidget.h" #include @@ -48,9 +49,10 @@ const QgsSettingsEntryBool *QgsAdvancedDigitizingDockWidget::settingsCadShowCons const QgsSettingsEntryBool *QgsAdvancedDigitizingDockWidget::settingsCadSnapToConstructionGuides = new QgsSettingsEntryBool( QStringLiteral( "cad-snap-to-construction-guides" ), QgsSettingsTree::sTreeDigitizing, false, tr( "Determines if points will snap to construction guides." ) ) ; -QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent ) +QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent, QgsUserInputWidget *userInputWidget ) : QgsDockWidget( parent ) , mMapCanvas( canvas ) + , mUserInputWidget( userInputWidget ) , mSnapIndicator( std::make_unique< QgsSnapIndicator>( canvas ) ) , mCommonAngleConstraint( QgsSettings().value( QStringLiteral( "/Cad/CommonAngle" ), 0.0 ).toDouble() ) { @@ -96,6 +98,7 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * connect( mConstructionModeAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::setConstructionMode ); connect( mParallelAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked ); connect( mPerpendicularAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked ); + connect( mCirclesIntersectionAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::circlesIntersectionClicked ); connect( mLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); connect( mLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); connect( mLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); @@ -183,7 +186,6 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * mCommonAngleActionsMenu->addMenu( snappingPriorityMenu ); } - for ( QList< QPair >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it ) { QAction *action = new QAction( it->second, mCommonAngleActionsMenu ); @@ -420,6 +422,14 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * disable(); } +QgsAdvancedDigitizingDockWidget::~QgsAdvancedDigitizingDockWidget() +{ + if ( mCurrentTool ) + { + mCurrentTool->deleteLater(); + } +} + QString QgsAdvancedDigitizingDockWidget::formatCommonAngleSnapping( double angle ) { if ( angle == 0 ) @@ -536,6 +546,7 @@ void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled ) mInputWidgets->setEnabled( enabled ); mFloaterAction->setEnabled( enabled ); mConstructionAction->setEnabled( enabled ); + mCirclesIntersectionAction->setEnabled( enabled ); if ( !enabled ) { @@ -545,6 +556,10 @@ void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled ) // will be reactivated in updateCapacities mParallelAction->setEnabled( false ); mPerpendicularAction->setEnabled( false ); + if ( mCurrentTool ) + { + mCurrentTool->deleteLater(); + } } @@ -644,6 +659,44 @@ void QgsAdvancedDigitizingDockWidget::activateCad( bool enabled ) setCadEnabled( enabled ); } +void QgsAdvancedDigitizingDockWidget::setTool( QgsAdvancedDigitizingTool *tool ) +{ + if ( mCurrentTool ) + { + mCurrentTool->deleteLater(); + mCurrentTool = nullptr; + } + + mCurrentTool = tool; + + if ( mCurrentTool ) + { + mUserInputWidget->addUserInputWidget( mCurrentTool.data() ); + connect( mCurrentTool.data(), &QgsAdvancedDigitizingTool::paintRequested, this, &QgsAdvancedDigitizingDockWidget::updateCadPaintItem ); + } +} + +QgsAdvancedDigitizingTool *QgsAdvancedDigitizingDockWidget::tool() const +{ + return mCurrentTool.data(); +} + +void QgsAdvancedDigitizingDockWidget::circlesIntersectionClicked() +{ + if ( mCurrentTool && dynamic_cast( mCurrentTool.data() ) ) + { + setTool( nullptr ); + mCirclesIntersectionAction->setChecked( false ); + } + else + { + QgsAdvancedDigitizingCirclesIntersectionTool *circlesIntersectionTool = new QgsAdvancedDigitizingCirclesIntersectionTool( mMapCanvas, this ); + connect( circlesIntersectionTool, &QObject::destroyed, this, [ = ] { mCirclesIntersectionAction->setChecked( false ); } ); + setTool( circlesIntersectionTool ); + mCirclesIntersectionAction->setChecked( true ); + } +} + void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked( bool activated ) { if ( !activated ) @@ -1492,6 +1545,70 @@ QList QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const return segment; } +bool QgsAdvancedDigitizingDockWidget::processCanvasPressEvent( QgsMapMouseEvent *e ) +{ + if ( mCurrentTool ) + { + mCurrentTool->canvasPressEvent( e ); + } + + return constructionMode(); +} + +bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent *e ) +{ + // perpendicular/parallel constraint + // do a soft lock when snapping to a segment + alignToSegment( e, QgsAdvancedDigitizingDockWidget::CadConstraint::SoftLock ); + + if ( mCurrentTool ) + { + mCurrentTool->canvasMoveEvent( e ); + } + + updateCadPaintItem(); + + return false; +} + +bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEvent *e ) +{ + if ( alignToSegment( e ) ) + { + return true; + } + + if ( mCurrentTool ) + { + if ( mCurrentTool->canvasReleaseEvent( e ) ) + { + return true; + } + else + { + // update the point list + QgsPoint point( e->mapPoint() ); + point.setZ( QgsMapToolEdit::defaultZValue() ); + point.setM( QgsMapToolEdit::defaultMValue() ); + + if ( mLockZButton->isChecked() ) + { + point.setZ( QLocale().toDouble( mZLineEdit->text() ) ); + } + if ( mLockMButton->isChecked() ) + { + point.setM( QLocale().toDouble( mMLineEdit->text() ) ); + } + updateCurrentPoint( point ); + } + } + + addPoint( e->mapPoint() ); + releaseLocks( false ); + + return constructionMode(); +} + bool QgsAdvancedDigitizingDockWidget::alignToSegment( QgsMapMouseEvent *e, CadConstraint::LockMode lockMode ) { if ( mBetweenLineConstraint == Qgis::BetweenLineConstraint::NoConstraint ) @@ -2001,7 +2118,6 @@ void QgsAdvancedDigitizingDockWidget::clearLockedSnapVertices( bool force ) mLockedSnapVertices.clear(); } - void QgsAdvancedDigitizingDockWidget::addPoint( const QgsPointXY &point ) { QgsPoint pt = pointXYToPoint( point ); diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index 13d3d7ecc3ed..805cd428f9f0 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -24,6 +24,7 @@ #include "ui_qgsadvanceddigitizingdockwidgetbase.h" #include "qgis_gui.h" #include "qgis_sip.h" +#include "qgsadvanceddigitizingtools.h" #include "qgsdockwidget.h" #include "qgsmessagebaritem.h" #include "qgspointxy.h" @@ -33,10 +34,12 @@ class QgsAdvancedDigitizingCanvasItem; class QgsAdvancedDigitizingFloater; +class QgsAdvancedDigitizingTool; class QgsMapCanvas; class QgsMapTool; class QgsMapToolAdvancedDigitizing; class QgsMapMouseEvent; +class QgsUserInputWidget; /** * \ingroup gui @@ -248,8 +251,10 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private * Create an advanced digitizing dock widget * \param canvas The map canvas on which the widget operates * \param parent The parent + * \param userInputWidget The user input widget on which tools can add widget overlays on top of the map canvas (since QGIS 3.40) */ - explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = nullptr ); + explicit QgsAdvancedDigitizingDockWidget( QgsMapCanvas *canvas, QWidget *parent = nullptr, QgsUserInputWidget *userInputWidget = nullptr ); + ~QgsAdvancedDigitizingDockWidget(); /** * Filter key events to e.g. toggle construction mode or adapt constraints @@ -271,6 +276,22 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private */ bool alignToSegment( QgsMapMouseEvent *e, QgsAdvancedDigitizingDockWidget::CadConstraint::LockMode lockMode = QgsAdvancedDigitizingDockWidget::CadConstraint::HardLock ); + bool processCanvasPressEvent( QgsMapMouseEvent *e ); + bool processCanvasMoveEvent( QgsMapMouseEvent *e ); + bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + + /** + * Sets an advanced digitizing tool which will take over digitizing until the tool is close. + * \since QGIS 3.40 + */ + void setTool( QgsAdvancedDigitizingTool *tool ); + + /** + * Returns the current advanced digitizing tool. Returns NULLPTR if not set. + * \since QGIS 3.40 + */ + QgsAdvancedDigitizingTool *tool() const; + /** * unlock all constraints * \param releaseRepeatingLocks set to FALSE to preserve the lock for any constraints set to repeating lock mode @@ -946,6 +967,9 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private //! Sets the between line constraint by clicking on the perpendicular/parallel buttons void betweenLineConstraintClicked( bool activated ); + //! Activate the circles intersection tool + void circlesIntersectionClicked(); + //! lock/unlock a constraint and set its value void lockConstraint( bool activate = true ); @@ -1059,6 +1083,8 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private void updateConstructionGuidesCrs(); QgsMapCanvas *mMapCanvas = nullptr; + QgsUserInputWidget *mUserInputWidget = nullptr; + QgsAdvancedDigitizingCanvasItem *mCadPaintItem = nullptr; //! Snapping indicator std::unique_ptr mSnapIndicator; @@ -1124,6 +1150,9 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private double mSoftLockY; QQueue< QgsPointLocator::Match > mLockedSnapVertices; + // Advanced digitizing tool + QPointer mCurrentTool; + #ifdef SIP_RUN //! event filter for line edits in the dock UI (angle/distance/x/y line edits) bool eventFilter( QObject *obj, QEvent *event ); diff --git a/src/gui/qgsadvanceddigitizingtools.cpp b/src/gui/qgsadvanceddigitizingtools.cpp new file mode 100644 index 000000000000..23abb12ab125 --- /dev/null +++ b/src/gui/qgsadvanceddigitizingtools.cpp @@ -0,0 +1,224 @@ +/*************************************************************************** + qgsadvanceddigitizingintersectiontools.cpp + ---------------------- + begin : May 2024 + copyright : (C) Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "qgsadvanceddigitizingtools.h" +#include "qgsapplication.h" +#include "qgsdoublespinbox.h" +#include "qgsmapcanvas.h" + +QgsAdvancedDigitizingTool::QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) + : QWidget( canvas->viewport() ) + , mMapCanvas( canvas ) + , mCadDockWidget( cadDockWidget ) +{ +} + +QgsAdvancedDigitizingCirclesIntersectionTool::QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) + : QgsAdvancedDigitizingTool( canvas, cadDockWidget ) +{ + QGridLayout *layout = new QGridLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + setLayout( layout ); + + QLabel *label = new QLabel( QStringLiteral( "Circle #1" ), this ); + layout->addWidget( label, 0, 0, 1, 3 ); + + mCircle1Digitize = new QToolButton( this ); + mCircle1Digitize->setCheckable( true ); + mCircle1Digitize->setChecked( false ); + mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); + layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 ); + + label = new QLabel( QStringLiteral( "X" ), this ); + layout->addWidget( label, 1, 0 ); + + mCircle1X = new QgsDoubleSpinBox( this ); + mCircle1X->setMinimum( std::numeric_limits::min() ); + mCircle1X->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle1X, 1, 1 ); + + label = new QLabel( QStringLiteral( "Y" ), this ); + layout->addWidget( label, 2, 0 ); + + mCircle1Y = new QgsDoubleSpinBox( this ); + mCircle1Y->setMinimum( std::numeric_limits::min() ); + mCircle1Y->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle1Y, 2, 1 ); + + label = new QLabel( QStringLiteral( "Distance" ), this ); + layout->addWidget( label, 3, 0 ); + + mCircle1Distance = new QgsDoubleSpinBox( this ); + mCircle1Distance->setMinimum( 0 ); + mCircle1Distance->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle1Distance, 3, 1 ); + + label = new QLabel( QStringLiteral( "Circle #2" ), this ); + layout->addWidget( label, 4, 0, 1, 3 ); + + mCircle2Digitize = new QToolButton( this ); + mCircle2Digitize->setCheckable( true ); + mCircle2Digitize->setChecked( false ); + mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); + layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 ); + + label = new QLabel( QStringLiteral( "X" ), this ); + layout->addWidget( label, 5, 0 ); + + mCircle2X = new QgsDoubleSpinBox( this ); + mCircle2X->setMinimum( std::numeric_limits::min() ); + mCircle2X->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle2X, 5, 1 ); + + label = new QLabel( QStringLiteral( "Y" ), this ); + layout->addWidget( label, 6, 0 ); + + mCircle2Y = new QgsDoubleSpinBox( this ); + mCircle2Y->setMinimum( std::numeric_limits::min() ); + mCircle2Y->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle2Y, 6, 1 ); + + label = new QLabel( QStringLiteral( "Distance" ), this ); + layout->addWidget( label, 7, 0 ); + + mCircle2Distance = new QgsDoubleSpinBox( this ); + mCircle2Distance->setMinimum( 0 ); + mCircle2Distance->setMaximum( std::numeric_limits::max() ); + layout->addWidget( mCircle2Distance, 7, 1 ); + + connect( mCircle1X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + connect( mCircle1Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + connect( mCircle1Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + connect( mCircle2X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); +} + +void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters() +{ + mP1 = QgsPointXY(); + mP2 = QgsPointXY(); + QgsGeometryUtils::circleCircleIntersections( QgsPointXY( mCircle1X->value(), mCircle1Y->value() ), mCircle1Distance->value(), + QgsPointXY( mCircle2X->value(), mCircle2Y->value() ), mCircle2Distance->value(), + mP1, mP2 ); + emit paintRequested(); +} + +bool QgsAdvancedDigitizingCirclesIntersectionTool::canvasMoveEvent( QgsMapMouseEvent *event ) +{ + if ( mCircle1Digitize->isChecked() ) + { + mCircle1X->setValue( event->mapPoint().x() ); + mCircle1Y->setValue( event->mapPoint().y() ); + } + else if ( mCircle2Digitize->isChecked() ) + { + mCircle2X->setValue( event->mapPoint().x() ); + mCircle2Y->setValue( event->mapPoint().y() ); + } + + if ( !mP1.isEmpty() ) + { + mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) ); + } + + return true; +} + +bool QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMouseEvent *event ) +{ + if ( mCircle1Digitize->isChecked() ) + { + mCircle1X->setValue( event->mapPoint().x() ); + mCircle1Y->setValue( event->mapPoint().y() ); + mCircle1Digitize->setChecked( false ); + return true; + } + else if ( mCircle2Digitize->isChecked() ) + { + mCircle2X->setValue( event->mapPoint().x() ); + mCircle2Y->setValue( event->mapPoint().y() ); + mCircle2Digitize->setChecked( false ); + return true; + } + + if ( !mP1.isEmpty() ) + { + mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) ); + event->setMapPoint( mP1Closest ? mP1 : mP2 ); + deleteLater(); + return false; + } + + return true; +} + +void QgsAdvancedDigitizingCirclesIntersectionTool::drawCircle( QPainter *painter, double x, double y, double distance ) +{ + painter->setPen( QPen( QColor( 0, 127, 0, 200 ), 2 ) ); + painter->setBrush( Qt::NoBrush ); + + mapCanvas()->getCoordinateTransform()->transformInPlace( x, y ); + painter->drawLine( QLineF( x - 3, y - 3, x + 3, y + 3 ) ); + painter->drawLine( QLineF( x - 3, y + 3, x + 3, y - 3 ) ); + + painter->setPen( QPen( QColor( 0, 127, 0, 150 ), 1, Qt::DashLine ) ); + distance = distance / mapCanvas()->mapSettings().mapUnitsPerPixel(); + painter->drawEllipse( QRectF( x - distance, y - distance, distance * 2, distance * 2 ) ); +} + +void QgsAdvancedDigitizingCirclesIntersectionTool::drawCandidate( QPainter *painter, double x, double y, bool closest ) +{ + if ( closest ) + { + painter->setPen( QPen( QColor( 127, 0, 0, 222 ), 4 ) ); + } + else + { + painter->setPen( QPen( QColor( 0, 127, 0, 222 ), 2 ) ); + } + + mapCanvas()->getCoordinateTransform()->transformInPlace( x, y ); + double size = closest ? 5 : 3; + painter->drawRect( QRectF( x - size, y - size, size * 2, size * 2 ) ); +} + +void QgsAdvancedDigitizingCirclesIntersectionTool::paint( QPainter *painter ) +{ + painter->save(); + + drawCircle( painter, mCircle1X->value(), mCircle1Y->value(), mCircle1Distance->value() ); + drawCircle( painter, mCircle2X->value(), mCircle2Y->value(), mCircle2Distance->value() ); + + if ( !mP1.isEmpty() ) + { + if ( mP1Closest ) + { + drawCandidate( painter, mP2.x(), mP2.y(), false ); + drawCandidate( painter, mP1.x(), mP1.y(), true ); + } + else + { + drawCandidate( painter, mP1.x(), mP1.y(), false ); + drawCandidate( painter, mP2.x(), mP2.y(), true ); + } + } + + painter->restore(); +} diff --git a/src/gui/qgsadvanceddigitizingtools.h b/src/gui/qgsadvanceddigitizingtools.h new file mode 100644 index 000000000000..d535a4b6016c --- /dev/null +++ b/src/gui/qgsadvanceddigitizingtools.h @@ -0,0 +1,147 @@ +/*************************************************************************** + qgsadvanceddigitizingtools.h + ---------------------- + begin : May 2024 + copyright : (C) Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSADVANCEDDIGITIZINGTOOLS +#define QGSADVANCEDDIGITIZINGTOOLS + +#include +#include + +#include "qgsadvanceddigitizingdockwidget.h" +#include "qgis_gui.h" +#include "qgis_sip.h" +#include "qgsmapmouseevent.h" + +#include + +class QgsAdvancedDigitizingDockWidget; +class QgsDoubleSpinBox; +class QgsMapCanvas; + +/** + * \ingroup gui + * \brief An abstract class for advanced digitizing tools. + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsAdvancedDigitizingTool : public QWidget +{ + Q_OBJECT + + public: + + /** + * The advanced digitizing tool constructor. + * \param canvas The map canvas on which the widget operates + * \param cadDockWidget The cadDockWidget to which the floater belongs + */ + explicit QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); + + /** + * Returns the map canvas associated with the tool. + */ + QgsMapCanvas *mapCanvas() const { return mMapCanvas; } + + /** + * Returns the advanced digitizing widget associated with the tool. + */ + QgsAdvancedDigitizingDockWidget *cadDockWidget() const { return mCadDockWidget.data(); } + + /** + * Paints tool content onto the advanced digitizing canvas item. + */ + virtual void paint( QPainter *painter ) { Q_UNUSED( painter ) } + + /** + * Handles canvas press event. If TRUE is returned, the tool will have + * blocked the event for propagating. + */ + virtual bool canvasPressEvent( QgsMapMouseEvent *event ) + { + Q_UNUSED( event ) + return true; + } + + /** + * Handles canvas press move. If TRUE is returned, the tool will have + * blocked the event for propagating. + */ + virtual bool canvasMoveEvent( QgsMapMouseEvent *event ) + { + Q_UNUSED( event ) + return true; + } + + /** + * Handles canvas release event. If TRUE is returned, the tool will have + * blocked the event for propagating. + */ + virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ) + { + Q_UNUSED( event ) + return true; + } + + signals: + + /** + * Requests a new painting event to the advanced digitizing canvas item. + */ + void paintRequested(); + + private: + + QgsMapCanvas *mMapCanvas = nullptr; + QPointer< QgsAdvancedDigitizingDockWidget > mCadDockWidget; +}; + +#ifndef SIP_RUN + +class GUI_EXPORT QgsAdvancedDigitizingCirclesIntersectionTool : public QgsAdvancedDigitizingTool +{ + Q_OBJECT + + public: + + explicit QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); + + void paint( QPainter *painter ) override; + + bool canvasMoveEvent( QgsMapMouseEvent *event ) override; + bool canvasReleaseEvent( QgsMapMouseEvent *event ) override; + + private: + void processParameters(); + + void drawCircle( QPainter *painter, double x, double y, double distance ); + void drawCandidate( QPainter *painter, double x, double y, bool closest ); + + QToolButton *mCircle1Digitize = nullptr; + QgsDoubleSpinBox *mCircle1X = nullptr; + QgsDoubleSpinBox *mCircle1Y = nullptr; + QgsDoubleSpinBox *mCircle1Distance = nullptr; + + QToolButton *mCircle2Digitize = nullptr; + QgsDoubleSpinBox *mCircle2X = nullptr; + QgsDoubleSpinBox *mCircle2Y = nullptr; + QgsDoubleSpinBox *mCircle2Distance = nullptr; + + QgsPointXY mP1; + QgsPointXY mP2; + bool mP1Closest = false; +}; + +#endif + +#endif // QGSADVANCEDDIGITIZINGTOOLS diff --git a/src/gui/qgsmaptooladvanceddigitizing.cpp b/src/gui/qgsmaptooladvanceddigitizing.cpp index ae2f19dce5e2..30e08a0c9d57 100644 --- a/src/gui/qgsmaptooladvanceddigitizing.cpp +++ b/src/gui/qgsmaptooladvanceddigitizing.cpp @@ -35,10 +35,11 @@ void QgsMapToolAdvancedDigitizing::canvasPressEvent( QgsMapMouseEvent *e ) { if ( isAdvancedDigitizingAllowed() && mCadDockWidget->cadEnabled() ) { - mCadDockWidget->applyConstraints( e ); // updates event's map point - - if ( mCadDockWidget->constructionMode() ) - return; // decided to eat the event and not pass it to the map tool (construction mode) + mCadDockWidget->applyConstraints( e ); // updates event's map point + if ( mCadDockWidget->processCanvasPressEvent( e ) ) + { + return; // The dock widget has eaten the event + } } else if ( isAutoSnapEnabled() ) { @@ -64,20 +65,11 @@ void QgsMapToolAdvancedDigitizing::canvasReleaseEvent( QgsMapMouseEvent *e ) } else { - mCadDockWidget->applyConstraints( e ); // updates event's map point - - if ( mCadDockWidget->alignToSegment( e ) ) + mCadDockWidget->applyConstraints( e ); // updates event's map point + if ( mCadDockWidget->processCanvasReleaseEvent( e ) ) { - // Parallel or perpendicular mode and snapped to segment: do not pass the event to map tool - return; + return; // The dock widget has eaten the event } - - mCadDockWidget->addPoint( e->mapPoint() ); - - mCadDockWidget->releaseLocks( false ); - - if ( mCadDockWidget->constructionMode() ) - return; // decided to eat the event and not pass it to the map tool (construction mode) } } else if ( isAutoSnapEnabled() ) @@ -98,12 +90,11 @@ void QgsMapToolAdvancedDigitizing::canvasMoveEvent( QgsMapMouseEvent *e ) { if ( isAdvancedDigitizingAllowed() && mCadDockWidget->cadEnabled() ) { - mCadDockWidget->applyConstraints( e ); // updates event's map point - - // perpendicular/parallel constraint - // do a soft lock when snapping to a segment - mCadDockWidget->alignToSegment( e, QgsAdvancedDigitizingDockWidget::CadConstraint::SoftLock ); - mCadDockWidget->updateCadPaintItem(); + mCadDockWidget->applyConstraints( e ); // updates event's map point + if ( mCadDockWidget->processCanvasMoveEvent( e ) ) + { + return; // The dock widget has eaten the event + } } else if ( isAutoSnapEnabled() ) { diff --git a/src/ui/qgsadvanceddigitizingdockwidgetbase.ui b/src/ui/qgsadvanceddigitizingdockwidgetbase.ui index ac8e5ab24976..d97bc8ed333d 100644 --- a/src/ui/qgsadvanceddigitizingdockwidgetbase.ui +++ b/src/ui/qgsadvanceddigitizingdockwidgetbase.ui @@ -46,12 +46,13 @@ + + + - - @@ -605,6 +606,15 @@ + + + true + + + + :/images/themes/default/cadtools/circlesintersection.svg:/images/themes/default/cadtools/circlesintersection.svg + + From 244d784bcc6ed568c8e7d72bef8c6a057e9a04bd Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Tue, 4 Jun 2024 11:33:00 +0700 Subject: [PATCH 2/7] Add missing documentation --- .../qgsadvanceddigitizingdockwidget.sip.in | 23 ++++++++++++++++--- .../qgsadvanceddigitizingdockwidget.sip.in | 23 ++++++++++++++++--- src/gui/qgsadvanceddigitizingdockwidget.cpp | 20 ++++++++-------- src/gui/qgsadvanceddigitizingdockwidget.h | 20 +++++++++++++--- src/gui/qgsadvanceddigitizingtools.h | 11 +++++++++ 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index e9c720fc8e4b..d465295e75f9 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -236,9 +236,26 @@ align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. %End - bool processCanvasPressEvent( QgsMapMouseEvent *e ); - bool processCanvasMoveEvent( QgsMapMouseEvent *e ); - bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + bool processCanvasPressEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas press ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End + + bool processCanvasMoveEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas move ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End + + bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas release ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End void setTool( QgsAdvancedDigitizingTool *tool ); %Docstring diff --git a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index 40635d06a6b2..fc9a4d70c705 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -236,9 +236,26 @@ align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. %End - bool processCanvasPressEvent( QgsMapMouseEvent *e ); - bool processCanvasMoveEvent( QgsMapMouseEvent *e ); - bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + bool processCanvasPressEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas press ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End + + bool processCanvasMoveEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas move ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End + + bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); +%Docstring +Processes the canvas release ``event``. + +:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. +%End void setTool( QgsAdvancedDigitizingTool *tool ); %Docstring diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index e93fb8e11618..2b3cc95005fd 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -1545,25 +1545,25 @@ QList QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const return segment; } -bool QgsAdvancedDigitizingDockWidget::processCanvasPressEvent( QgsMapMouseEvent *e ) +bool QgsAdvancedDigitizingDockWidget::processCanvasPressEvent( QgsMapMouseEvent *event ) { if ( mCurrentTool ) { - mCurrentTool->canvasPressEvent( e ); + mCurrentTool->canvasPressEvent( event ); } return constructionMode(); } -bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent *e ) +bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent *event ) { // perpendicular/parallel constraint // do a soft lock when snapping to a segment - alignToSegment( e, QgsAdvancedDigitizingDockWidget::CadConstraint::SoftLock ); + alignToSegment( event, QgsAdvancedDigitizingDockWidget::CadConstraint::SoftLock ); if ( mCurrentTool ) { - mCurrentTool->canvasMoveEvent( e ); + mCurrentTool->canvasMoveEvent( event ); } updateCadPaintItem(); @@ -1571,23 +1571,23 @@ bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent * return false; } -bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEvent *e ) +bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEvent *event ) { - if ( alignToSegment( e ) ) + if ( alignToSegment( event ) ) { return true; } if ( mCurrentTool ) { - if ( mCurrentTool->canvasReleaseEvent( e ) ) + if ( mCurrentTool->canvasReleaseEvent( event ) ) { return true; } else { // update the point list - QgsPoint point( e->mapPoint() ); + QgsPoint point( event->mapPoint() ); point.setZ( QgsMapToolEdit::defaultZValue() ); point.setM( QgsMapToolEdit::defaultMValue() ); @@ -1603,7 +1603,7 @@ bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEven } } - addPoint( e->mapPoint() ); + addPoint( event->mapPoint() ); releaseLocks( false ); return constructionMode(); diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index 805cd428f9f0..acc11fc9145b 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -276,9 +276,23 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private */ bool alignToSegment( QgsMapMouseEvent *e, QgsAdvancedDigitizingDockWidget::CadConstraint::LockMode lockMode = QgsAdvancedDigitizingDockWidget::CadConstraint::HardLock ); - bool processCanvasPressEvent( QgsMapMouseEvent *e ); - bool processCanvasMoveEvent( QgsMapMouseEvent *e ); - bool processCanvasReleaseEvent( QgsMapMouseEvent *e ); + /** + * Processes the canvas press \a event. + * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. + */ + bool processCanvasPressEvent( QgsMapMouseEvent *event ); + + /** + * Processes the canvas move \a event. + * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. + */ + bool processCanvasMoveEvent( QgsMapMouseEvent *event ); + + /** + * Processes the canvas release \a event. + * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. + */ + bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); /** * Sets an advanced digitizing tool which will take over digitizing until the tool is close. diff --git a/src/gui/qgsadvanceddigitizingtools.h b/src/gui/qgsadvanceddigitizingtools.h index d535a4b6016c..dd4275a91941 100644 --- a/src/gui/qgsadvanceddigitizingtools.h +++ b/src/gui/qgsadvanceddigitizingtools.h @@ -108,12 +108,23 @@ class GUI_EXPORT QgsAdvancedDigitizingTool : public QWidget #ifndef SIP_RUN +/** + * \ingroup gui + * \brief A advanced digitizing tools to handle the selection of a point at the intersection + * of two circles. + * \since QGIS 3.40 + */ class GUI_EXPORT QgsAdvancedDigitizingCirclesIntersectionTool : public QgsAdvancedDigitizingTool { Q_OBJECT public: + /** + * The advanced digitizing's circles intersection tool constructor. + * \param canvas The map canvas on which the widget operates + * \param cadDockWidget The cadDockWidget to which the floater belongs + */ explicit QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); void paint( QPainter *painter ) override; From 36ad5f9dfa55b788b0ad1448546cd23b31f46775 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 28 Jul 2024 17:26:09 +0700 Subject: [PATCH 3/7] Add advanced digitizing tools registry --- .../qgsadvanceddigitizingtoolsregistry.sip.in | 113 +++++++++++++ python/PyQt6/gui/auto_generated/qgsgui.sip.in | 7 + python/PyQt6/gui/gui_auto.sip | 1 + .../qgsadvanceddigitizingtoolsregistry.sip.in | 113 +++++++++++++ python/gui/auto_generated/qgsgui.sip.in | 7 + python/gui/gui_auto.sip | 1 + src/gui/CMakeLists.txt | 2 + src/gui/qgsadvanceddigitizingdockwidget.cpp | 38 ++--- src/gui/qgsadvanceddigitizingdockwidget.h | 3 - .../qgsadvanceddigitizingtoolsregistry.cpp | 76 +++++++++ src/gui/qgsadvanceddigitizingtoolsregistry.h | 153 ++++++++++++++++++ src/gui/qgsgui.cpp | 10 ++ src/gui/qgsgui.h | 9 ++ src/ui/qgsadvanceddigitizingdockwidgetbase.ui | 4 +- 14 files changed, 514 insertions(+), 23 deletions(-) create mode 100644 python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in create mode 100644 python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in create mode 100644 src/gui/qgsadvanceddigitizingtoolsregistry.cpp create mode 100644 src/gui/qgsadvanceddigitizingtoolsregistry.h diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in new file mode 100644 index 000000000000..fb48ea403aaa --- /dev/null +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in @@ -0,0 +1,113 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtoolsregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsAdvancedDigitizingToolAbstractMetadata +{ +%Docstring(signature="appended") +Stores metadata about one advanced digitizing tool class. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtoolsregistry.h" +%End + public: + + QgsAdvancedDigitizingToolAbstractMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon() ); +%Docstring +Constructor for QgsAdvancedDigitizingToolAbstractMetadata with the specified tool ``name``. + +``visibleName`` should be set to a translated, user visible name identifying the corresponding annotation item. + +An optional ``icon`` can be set, which will be used by the advanced digitizing dock widget. +%End + + virtual ~QgsAdvancedDigitizingToolAbstractMetadata(); + + QString name() const; +%Docstring +Returns the tool's unique name +%End + + QString visibleName() const; +%Docstring +Returns the tool's translatable user-friendly name +%End + + QIcon icon() const; +%Docstring +Returns the tool's icon +%End + + virtual QgsAdvancedDigitizingTool *createTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) /Factory/; +%Docstring +Returns new tool of this type. Return ``None`` on error +%End + + protected: +}; + + +class QgsAdvancedDigitizingToolsRegistry +{ +%Docstring(signature="appended") +Registry of available advanced digitizing tools. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtoolsregistry.h" +%End + public: + + QgsAdvancedDigitizingToolsRegistry(); + ~QgsAdvancedDigitizingToolsRegistry(); + + + void addDefaultTools(); +%Docstring +Adds the default tools shipped in QGIS +%End + + bool addTool( QgsAdvancedDigitizingToolAbstractMetadata *toolMetaData /Transfer/ ); +%Docstring +Adds an advanced digitizing tool (take ownership) and return ``True`` on success +%End + + bool removeTool( const QString &name ); +%Docstring +Removes the advanced digitizing tool matching the provided ``name`` and return ``True`` on success +%End + + QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata( const QString &name ); +%Docstring +Returns the advanced digitizing tool matching the provided ``name`` or ``None`` when no match available +%End + + const QStringList toolMetadataNames() const; +%Docstring +Returns the list of registered tool names +%End + + private: + QgsAdvancedDigitizingToolsRegistry( const QgsAdvancedDigitizingToolsRegistry &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtoolsregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/PyQt6/gui/auto_generated/qgsgui.sip.in b/python/PyQt6/gui/auto_generated/qgsgui.sip.in index 8f3a4bf91991..cec1874c1a49 100644 --- a/python/PyQt6/gui/auto_generated/qgsgui.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsgui.sip.in @@ -77,6 +77,13 @@ Returns the global layout item GUI registry, used for registering the GUI behavi Returns the global annotation item GUI registry, used for registering the GUI behavior of annotation items. .. versionadded:: 3.22 +%End + + static QgsAdvancedDigitizingToolsRegistry *advancedDigitizingToolsRegistry() /KeepReference/; +%Docstring +Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools. + +.. versionadded:: 3.40 %End static QgsProcessingGuiRegistry *processingGuiRegistry() /KeepReference/; diff --git a/python/PyQt6/gui/gui_auto.sip b/python/PyQt6/gui/gui_auto.sip index 913c0e8b2786..a69c84652cb2 100644 --- a/python/PyQt6/gui/gui_auto.sip +++ b/python/PyQt6/gui/gui_auto.sip @@ -7,6 +7,7 @@ %Include auto_generated/qgsadvanceddigitizingdockwidget.sip %Include auto_generated/qgsadvanceddigitizingfloater.sip %Include auto_generated/qgsadvanceddigitizingtools.sip +%Include auto_generated/qgsadvanceddigitizingtoolsregistry.sip %Include auto_generated/qgsaggregatetoolbutton.sip %Include auto_generated/qgsalignmentcombobox.sip %Include auto_generated/qgsapplicationexitblockerinterface.sip diff --git a/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in new file mode 100644 index 000000000000..fb48ea403aaa --- /dev/null +++ b/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in @@ -0,0 +1,113 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtoolsregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsAdvancedDigitizingToolAbstractMetadata +{ +%Docstring(signature="appended") +Stores metadata about one advanced digitizing tool class. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtoolsregistry.h" +%End + public: + + QgsAdvancedDigitizingToolAbstractMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon() ); +%Docstring +Constructor for QgsAdvancedDigitizingToolAbstractMetadata with the specified tool ``name``. + +``visibleName`` should be set to a translated, user visible name identifying the corresponding annotation item. + +An optional ``icon`` can be set, which will be used by the advanced digitizing dock widget. +%End + + virtual ~QgsAdvancedDigitizingToolAbstractMetadata(); + + QString name() const; +%Docstring +Returns the tool's unique name +%End + + QString visibleName() const; +%Docstring +Returns the tool's translatable user-friendly name +%End + + QIcon icon() const; +%Docstring +Returns the tool's icon +%End + + virtual QgsAdvancedDigitizingTool *createTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) /Factory/; +%Docstring +Returns new tool of this type. Return ``None`` on error +%End + + protected: +}; + + +class QgsAdvancedDigitizingToolsRegistry +{ +%Docstring(signature="appended") +Registry of available advanced digitizing tools. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgsadvanceddigitizingtoolsregistry.h" +%End + public: + + QgsAdvancedDigitizingToolsRegistry(); + ~QgsAdvancedDigitizingToolsRegistry(); + + + void addDefaultTools(); +%Docstring +Adds the default tools shipped in QGIS +%End + + bool addTool( QgsAdvancedDigitizingToolAbstractMetadata *toolMetaData /Transfer/ ); +%Docstring +Adds an advanced digitizing tool (take ownership) and return ``True`` on success +%End + + bool removeTool( const QString &name ); +%Docstring +Removes the advanced digitizing tool matching the provided ``name`` and return ``True`` on success +%End + + QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata( const QString &name ); +%Docstring +Returns the advanced digitizing tool matching the provided ``name`` or ``None`` when no match available +%End + + const QStringList toolMetadataNames() const; +%Docstring +Returns the list of registered tool names +%End + + private: + QgsAdvancedDigitizingToolsRegistry( const QgsAdvancedDigitizingToolsRegistry &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsadvanceddigitizingtoolsregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsgui.sip.in b/python/gui/auto_generated/qgsgui.sip.in index 3645bd698614..b911c74bc038 100644 --- a/python/gui/auto_generated/qgsgui.sip.in +++ b/python/gui/auto_generated/qgsgui.sip.in @@ -77,6 +77,13 @@ Returns the global layout item GUI registry, used for registering the GUI behavi Returns the global annotation item GUI registry, used for registering the GUI behavior of annotation items. .. versionadded:: 3.22 +%End + + static QgsAdvancedDigitizingToolsRegistry *advancedDigitizingToolsRegistry() /KeepReference/; +%Docstring +Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools. + +.. versionadded:: 3.40 %End static QgsProcessingGuiRegistry *processingGuiRegistry() /KeepReference/; diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 913c0e8b2786..a69c84652cb2 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -7,6 +7,7 @@ %Include auto_generated/qgsadvanceddigitizingdockwidget.sip %Include auto_generated/qgsadvanceddigitizingfloater.sip %Include auto_generated/qgsadvanceddigitizingtools.sip +%Include auto_generated/qgsadvanceddigitizingtoolsregistry.sip %Include auto_generated/qgsaggregatetoolbutton.sip %Include auto_generated/qgsalignmentcombobox.sip %Include auto_generated/qgsapplicationexitblockerinterface.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index f346e387b0b0..021fdddbeded 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -519,6 +519,7 @@ set(QGIS_GUI_SRCS qgsadvanceddigitizingdockwidget.cpp qgsadvanceddigitizingfloater.cpp qgsadvanceddigitizingtools.cpp + qgsadvanceddigitizingtoolsregistry.cpp qgsaggregatetoolbutton.cpp qgsalignmentcombobox.cpp qgsapplicationexitblockerinterface.cpp @@ -792,6 +793,7 @@ set(QGIS_GUI_HDRS qgsadvanceddigitizingdockwidget.h qgsadvanceddigitizingfloater.h qgsadvanceddigitizingtools.h + qgsadvanceddigitizingtoolsregistry.h qgsaggregatetoolbutton.h qgsalignmentcombobox.h qgsapplicationexitblockerinterface.h diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 2b3cc95005fd..52cebd0450e1 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -22,9 +22,11 @@ #include "qgsadvanceddigitizingdockwidget.h" #include "qgsadvanceddigitizingfloater.h" #include "qgsadvanceddigitizingcanvasitem.h" +#include "qgsadvanceddigitizingtoolsregistry.h" #include "qgsbearingnumericformat.h" #include "qgscadutils.h" #include "qgsexpression.h" +#include "qgsgui.h" #include "qgsmapcanvas.h" #include "qgsmaptooledit.h" #include "qgsmaptooladvanceddigitizing.h" @@ -98,7 +100,6 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * connect( mConstructionModeAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::setConstructionMode ); connect( mParallelAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked ); connect( mPerpendicularAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked ); - connect( mCirclesIntersectionAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::circlesIntersectionClicked ); connect( mLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); connect( mLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); connect( mLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint ); @@ -239,6 +240,22 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * constructionModeToolButton->setMenu( constructionSettingsMenu ); constructionModeToolButton->setObjectName( QStringLiteral( "ConstructionModeButton" ) ); + // Tools + QMenu *toolsMenu = new QMenu( this ); + const QStringList toolMetadataNames = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadataNames(); + for ( const QString &name : toolMetadataNames ) + { + QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadata( name ); + QAction *toolAction = new QAction( toolMetadata->icon(), toolMetadata->visibleName(), this ); + connect( toolAction, &QAction::triggered, this, [ = ]() + { + setTool( toolMetadata->createTool( mMapCanvas, this ) ); + } ); + toolsMenu->addAction( toolAction ); + } + qobject_cast< QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup ); + mToolsAction->setMenu( toolsMenu ); + qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup ); mSettingsAction->setMenu( mCommonAngleActionsMenu ); mSettingsAction->setCheckable( true ); @@ -546,7 +563,7 @@ void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled ) mInputWidgets->setEnabled( enabled ); mFloaterAction->setEnabled( enabled ); mConstructionAction->setEnabled( enabled ); - mCirclesIntersectionAction->setEnabled( enabled ); + mToolsAction->setEnabled( enabled ); if ( !enabled ) { @@ -667,6 +684,7 @@ void QgsAdvancedDigitizingDockWidget::setTool( QgsAdvancedDigitizingTool *tool ) mCurrentTool = nullptr; } + qDebug() << mCurrentTool; mCurrentTool = tool; if ( mCurrentTool ) @@ -681,22 +699,6 @@ QgsAdvancedDigitizingTool *QgsAdvancedDigitizingDockWidget::tool() const return mCurrentTool.data(); } -void QgsAdvancedDigitizingDockWidget::circlesIntersectionClicked() -{ - if ( mCurrentTool && dynamic_cast( mCurrentTool.data() ) ) - { - setTool( nullptr ); - mCirclesIntersectionAction->setChecked( false ); - } - else - { - QgsAdvancedDigitizingCirclesIntersectionTool *circlesIntersectionTool = new QgsAdvancedDigitizingCirclesIntersectionTool( mMapCanvas, this ); - connect( circlesIntersectionTool, &QObject::destroyed, this, [ = ] { mCirclesIntersectionAction->setChecked( false ); } ); - setTool( circlesIntersectionTool ); - mCirclesIntersectionAction->setChecked( true ); - } -} - void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked( bool activated ) { if ( !activated ) diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index acc11fc9145b..44d327fc4c8b 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -981,9 +981,6 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private //! Sets the between line constraint by clicking on the perpendicular/parallel buttons void betweenLineConstraintClicked( bool activated ); - //! Activate the circles intersection tool - void circlesIntersectionClicked(); - //! lock/unlock a constraint and set its value void lockConstraint( bool activate = true ); diff --git a/src/gui/qgsadvanceddigitizingtoolsregistry.cpp b/src/gui/qgsadvanceddigitizingtoolsregistry.cpp new file mode 100644 index 000000000000..77b7992b8c67 --- /dev/null +++ b/src/gui/qgsadvanceddigitizingtoolsregistry.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + qgsadvanceddigitizingtoolsregistry.cpp + ------------------- + begin : July 27 2024 + copyright : (C) 2024 by Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsadvanceddigitizingtoolsregistry.h" +#include "qgsadvanceddigitizingtools.h" +#include "qgsapplication.h" + +QgsAdvancedDigitizingTool *QgsAdvancedDigitizingToolAbstractMetadata::createTool( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * ) +{ + return nullptr; +} + +QgsAdvancedDigitizingTool *QgsAdvancedDigitizingToolMetadata::createTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) +{ + return mToolFunc ? mToolFunc( canvas, cadDockWidget ) : QgsAdvancedDigitizingToolAbstractMetadata::createTool( canvas, cadDockWidget ); +} + +QgsAdvancedDigitizingToolsRegistry::~QgsAdvancedDigitizingToolsRegistry() +{ + qDeleteAll( mTools ); +} + +void QgsAdvancedDigitizingToolsRegistry::addDefaultTools() +{ + addTool( new QgsAdvancedDigitizingToolMetadata( QStringLiteral( "circlesintersection" ), + QObject::tr( "2-Circle Point Intersection" ), + QgsApplication::getThemeIcon( QStringLiteral( "/cadtools/circlesintersection.svg" ) ), + [ = ]( QgsMapCanvas * canvas, QgsAdvancedDigitizingDockWidget * cadDockWidget )->QgsAdvancedDigitizingTool * + { + return new QgsAdvancedDigitizingCirclesIntersectionTool( canvas, cadDockWidget ); + } ) ); +} + +bool QgsAdvancedDigitizingToolsRegistry::addTool( QgsAdvancedDigitizingToolAbstractMetadata *toolMetaData ) +{ + if ( mTools.contains( toolMetaData->name() ) ) + return false; + + mTools[toolMetaData->name()] = toolMetaData; + return true; +} + + +bool QgsAdvancedDigitizingToolsRegistry::removeTool( const QString &name ) +{ + if ( !mTools.contains( name ) ) + return false; + + delete mTools.take( name ); + return true; +} + +QgsAdvancedDigitizingToolAbstractMetadata *QgsAdvancedDigitizingToolsRegistry::toolMetadata( const QString &name ) +{ + if ( !mTools.contains( name ) ) + return nullptr; + + return mTools[name]; +} + +const QStringList QgsAdvancedDigitizingToolsRegistry::toolMetadataNames() const +{ + return mTools.keys(); +} diff --git a/src/gui/qgsadvanceddigitizingtoolsregistry.h b/src/gui/qgsadvanceddigitizingtoolsregistry.h new file mode 100644 index 000000000000..3793cda98a58 --- /dev/null +++ b/src/gui/qgsadvanceddigitizingtoolsregistry.h @@ -0,0 +1,153 @@ +/*************************************************************************** + qgsadvanceddigitizingtoolsregistry.cpp + ------------------- + begin : July 27 2024 + copyright : (C) 2024 by Mathieu Pellerin + email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSADVANCEDDIGITIZINGTOOLSREGSITRY_H +#define QGSADVANCEDDIGITIZINGTOOLSREGSITRY_H + +#include "qgis_sip.h" +#include "qgis_gui.h" + +#include +#include + +class QgsAdvancedDigitizingDockWidget; +class QgsAdvancedDigitizingTool; +class QgsMapCanvas; + +/** + * \ingroup gui + * \brief Stores metadata about one advanced digitizing tool class. + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsAdvancedDigitizingToolAbstractMetadata +{ + public: + + /** + * Constructor for QgsAdvancedDigitizingToolAbstractMetadata with the specified tool \a name. + * + * \a visibleName should be set to a translated, user visible name identifying the corresponding annotation item. + * + * An optional \a icon can be set, which will be used by the advanced digitizing dock widget. + */ + QgsAdvancedDigitizingToolAbstractMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon() ) + : mName( name ) + , mVisibleName( visibleName ) + , mIcon( icon ) + {} + + virtual ~QgsAdvancedDigitizingToolAbstractMetadata() = default; + + //! Returns the tool's unique name + QString name() const { return mName; } + + //! Returns the tool's translatable user-friendly name + QString visibleName() const { return mVisibleName; } + + //! Returns the tool's icon + QIcon icon() const { return mIcon; } + + //! Returns new tool of this type. Return NULLPTR on error + virtual QgsAdvancedDigitizingTool *createTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) SIP_FACTORY; + + protected: + QString mName; + QString mVisibleName; + QIcon mIcon; +}; + +#ifndef SIP_RUN + +typedef std::function QgsAdvancedDigitizingToolFunc SIP_SKIP; + +/** + * \ingroup gui + * \brief Convenience metadata class that uses static functions to handle advanced digitizing tool creation + * \note not available in Python bindings + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsAdvancedDigitizingToolMetadata : public QgsAdvancedDigitizingToolAbstractMetadata +{ + public: + + /** + * Constructor for QgsAdvancedDigitizingToolAbstractMetadata with the specified tool \a name. + * + * \a visibleName should be set to a translated, user visible name identifying the corresponding annotation item. + * + * An optional \a icon can be set, which will be used by the advanced digitizing dock widget. + * + * A tool creation function can be declared through the \a toolFunction parameter. + */ + QgsAdvancedDigitizingToolMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon(), + const QgsAdvancedDigitizingToolFunc &toolFunction = nullptr ) + : QgsAdvancedDigitizingToolAbstractMetadata( name, visibleName, icon ) + , mToolFunc( toolFunction ) + {} + + //! Returns the tool creation function + QgsAdvancedDigitizingToolFunc toolFunction() const { return mToolFunc; } + + //! Sets the tool creation \a function. + void setToolFunction( const QgsAdvancedDigitizingToolFunc &function ) { mToolFunc = function; } + + QgsAdvancedDigitizingTool *createTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) override; + + protected: + QgsAdvancedDigitizingToolFunc mToolFunc = nullptr; +}; + +#endif + +/** + * \ingroup gui + * \brief Registry of available advanced digitizing tools. + * + * \since QGIS 3.40 + */ +class GUI_EXPORT QgsAdvancedDigitizingToolsRegistry +{ + public: + + QgsAdvancedDigitizingToolsRegistry() = default; + ~QgsAdvancedDigitizingToolsRegistry(); + + QgsAdvancedDigitizingToolsRegistry( const QgsAdvancedDigitizingToolsRegistry &rh ) = delete; + QgsAdvancedDigitizingToolsRegistry &operator=( const QgsAdvancedDigitizingToolsRegistry &rh ) = delete; + + //! Adds the default tools shipped in QGIS + void addDefaultTools(); + + //! Adds an advanced digitizing tool (take ownership) and return TRUE on success + bool addTool( QgsAdvancedDigitizingToolAbstractMetadata *toolMetaData SIP_TRANSFER ); + + //! Removes the advanced digitizing tool matching the provided \a name and return TRUE on success + bool removeTool( const QString &name ); + + //! Returns the advanced digitizing tool matching the provided \a name or NULLPTR when no match available + QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata( const QString &name ); + + //! Returns the list of registered tool names + const QStringList toolMetadataNames() const; + + private: +#ifdef SIP_RUN + QgsAdvancedDigitizingToolsRegistry( const QgsAdvancedDigitizingToolsRegistry &rh ); +#endif + + QMap mTools; +}; + +#endif // QGSADVANCEDDIGITIZINGTOOLSREGSITRY_H diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index 86404cfb4802..7daacba99b1b 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -26,6 +26,7 @@ #include "qgssourceselectproviderregistry.h" #include "qgslayoutitemguiregistry.h" #include "qgsannotationitemguiregistry.h" +#include "qgsadvanceddigitizingtoolsregistry.h" #include "qgscalloutsregistry.h" #include "callouts/qgscalloutwidget.h" #ifdef Q_OS_MACX @@ -144,6 +145,11 @@ QgsAnnotationItemGuiRegistry *QgsGui::annotationItemGuiRegistry() return instance()->mAnnotationItemGuiRegistry; } +QgsAdvancedDigitizingToolsRegistry *QgsGui::advancedDigitizingToolsRegistry() +{ + return instance()->mAdvancedDigitizingToolsRegistry; +} + QgsProcessingGuiRegistry *QgsGui::processingGuiRegistry() { return instance()->mProcessingGuiRegistry; @@ -243,6 +249,7 @@ QgsGui::~QgsGui() delete mProcessingRecentAlgorithmLog; delete mLayoutItemGuiRegistry; delete mAnnotationItemGuiRegistry; + delete mAdvancedDigitizingToolsRegistry; delete mLayerTreeEmbeddedWidgetRegistry; delete mEditorWidgetRegistry; delete mMapLayerActionRegistry; @@ -352,6 +359,9 @@ QgsGui::QgsGui() mAnnotationItemGuiRegistry = new QgsAnnotationItemGuiRegistry(); mAnnotationItemGuiRegistry->addDefaultItems(); + mAdvancedDigitizingToolsRegistry = new QgsAdvancedDigitizingToolsRegistry(); + mAdvancedDigitizingToolsRegistry->addDefaultTools(); + mWidgetStateHelper = new QgsWidgetStateHelper(); mProcessingFavoriteAlgorithmManager = new QgsProcessingFavoriteAlgorithmManager(); mProcessingRecentAlgorithmLog = new QgsProcessingRecentAlgorithmLog(); diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index 8fcf8afe71ee..d45056f4736f 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -32,6 +32,7 @@ class QgsSourceSelectProviderRegistry; class QgsNative; class QgsLayoutItemGuiRegistry; class QgsAnnotationItemGuiRegistry; +class QgsAdvancedDigitizingToolsRegistry; class QgsWidgetStateHelper; class QgsProcessingGuiRegistry; class QgsProcessingFavoriteAlgorithmManager; @@ -131,6 +132,13 @@ class GUI_EXPORT QgsGui : public QObject */ static QgsAnnotationItemGuiRegistry *annotationItemGuiRegistry() SIP_KEEPREFERENCE; + /** + * Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools. + * + * \since QGIS 3.40 + */ + static QgsAdvancedDigitizingToolsRegistry *advancedDigitizingToolsRegistry() SIP_KEEPREFERENCE; + /** * Returns the global processing gui registry, used for registering the GUI behavior of processing algorithms. * \since QGIS 3.2 @@ -334,6 +342,7 @@ class GUI_EXPORT QgsGui : public QObject QgsMapLayerActionRegistry *mMapLayerActionRegistry = nullptr; QgsLayoutItemGuiRegistry *mLayoutItemGuiRegistry = nullptr; QgsAnnotationItemGuiRegistry *mAnnotationItemGuiRegistry = nullptr; + QgsAdvancedDigitizingToolsRegistry *mAdvancedDigitizingToolsRegistry = nullptr; QgsProcessingGuiRegistry *mProcessingGuiRegistry = nullptr; QgsProcessingFavoriteAlgorithmManager *mProcessingFavoriteAlgorithmManager = nullptr; QgsProcessingRecentAlgorithmLog *mProcessingRecentAlgorithmLog = nullptr; diff --git a/src/ui/qgsadvanceddigitizingdockwidgetbase.ui b/src/ui/qgsadvanceddigitizingdockwidgetbase.ui index d97bc8ed333d..68b57cbccaa4 100644 --- a/src/ui/qgsadvanceddigitizingdockwidgetbase.ui +++ b/src/ui/qgsadvanceddigitizingdockwidgetbase.ui @@ -48,7 +48,7 @@ - + @@ -606,7 +606,7 @@ - + true From 9fab4fcf4fb5cc237a3fcc0b90b621cf9d9bede0 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Mon, 29 Jul 2024 10:25:20 +0700 Subject: [PATCH 4/7] Address review and add test --- .../qgsadvanceddigitizingdockwidget.sip.in | 12 +-- .../qgsadvanceddigitizingtools.sip.in | 38 ++++++-- .../qgsadvanceddigitizingtoolsregistry.sip.in | 3 + .../qgsadvanceddigitizingdockwidget.sip.in | 12 +-- .../qgsadvanceddigitizingtools.sip.in | 38 ++++++-- .../qgsadvanceddigitizingtoolsregistry.sip.in | 3 + src/gui/qgsadvanceddigitizingcanvasitem.cpp | 7 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 54 +++++++---- src/gui/qgsadvanceddigitizingdockwidget.h | 9 +- src/gui/qgsadvanceddigitizingtools.cpp | 67 ++++++++----- src/gui/qgsadvanceddigitizingtools.h | 35 ++++--- .../qgsadvanceddigitizingtoolsregistry.cpp | 2 + src/gui/qgsadvanceddigitizingtoolsregistry.h | 3 + src/gui/qgsmaptooladvanceddigitizing.cpp | 15 +-- tests/src/gui/CMakeLists.txt | 1 + ...testqgsadvanceddigitizingtoolsregistry.cpp | 95 +++++++++++++++++++ 16 files changed, 279 insertions(+), 115 deletions(-) create mode 100644 tests/src/gui/testqgsadvanceddigitizingtoolsregistry.cpp diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index d465295e75f9..fccd3de72a2d 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -236,25 +236,19 @@ align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. %End - bool processCanvasPressEvent( QgsMapMouseEvent *event ); + void processCanvasPressEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas press ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End - bool processCanvasMoveEvent( QgsMapMouseEvent *event ); + void processCanvasMoveEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas move ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End - bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); + void processCanvasReleaseEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas release ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End void setTool( QgsAdvancedDigitizingTool *tool ); diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in index c3511108cb71..b587dae0133b 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -11,7 +11,7 @@ -class QgsAdvancedDigitizingTool : QWidget +class QgsAdvancedDigitizingTool : QObject { %Docstring(signature="appended") An abstract class for advanced digitizing tools. @@ -42,27 +42,45 @@ Returns the map canvas associated with the tool. Returns the advanced digitizing widget associated with the tool. %End + virtual QWidget *createWidget(); +%Docstring +Returns a widget to control the tool. + +.. note:: + + The caller gets the ownership. +%End + virtual void paint( QPainter *painter ); %Docstring Paints tool content onto the advanced digitizing canvas item. %End - virtual bool canvasPressEvent( QgsMapMouseEvent *event ); + virtual void canvasPressEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas press event. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas press event. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End - virtual bool canvasMoveEvent( QgsMapMouseEvent *event ); + virtual void canvasMoveEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas press move. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas press move. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End - virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ); + virtual void canvasReleaseEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas release event. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas release event. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End signals: diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in index fb48ea403aaa..3f9d6284c8e4 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in @@ -63,6 +63,9 @@ class QgsAdvancedDigitizingToolsRegistry %Docstring(signature="appended") Registry of available advanced digitizing tools. +:py:class:`QgsAdvancedDigitizingToolsRegistry` is not usually directly created, but rather accessed through +:py:func:`QgsGui.advancedDigitizingToolsRegistry()`. + .. versionadded:: 3.40 %End diff --git a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index fc9a4d70c705..962a3a03f8a1 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -236,25 +236,19 @@ align to segment for between line constraint. If between line constraints are used, this will determine the angle to be locked depending on the snapped segment. %End - bool processCanvasPressEvent( QgsMapMouseEvent *event ); + void processCanvasPressEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas press ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End - bool processCanvasMoveEvent( QgsMapMouseEvent *event ); + void processCanvasMoveEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas move ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End - bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); + void processCanvasReleaseEvent( QgsMapMouseEvent *event ); %Docstring Processes the canvas release ``event``. - -:return: Returns ``True`` if the event has been 'eaten' and should not propagate further. %End void setTool( QgsAdvancedDigitizingTool *tool ); diff --git a/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in index c3511108cb71..b587dae0133b 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -11,7 +11,7 @@ -class QgsAdvancedDigitizingTool : QWidget +class QgsAdvancedDigitizingTool : QObject { %Docstring(signature="appended") An abstract class for advanced digitizing tools. @@ -42,27 +42,45 @@ Returns the map canvas associated with the tool. Returns the advanced digitizing widget associated with the tool. %End + virtual QWidget *createWidget(); +%Docstring +Returns a widget to control the tool. + +.. note:: + + The caller gets the ownership. +%End + virtual void paint( QPainter *painter ); %Docstring Paints tool content onto the advanced digitizing canvas item. %End - virtual bool canvasPressEvent( QgsMapMouseEvent *event ); + virtual void canvasPressEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas press event. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas press event. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End - virtual bool canvasMoveEvent( QgsMapMouseEvent *event ); + virtual void canvasMoveEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas press move. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas press move. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End - virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ); + virtual void canvasReleaseEvent( QgsMapMouseEvent *event ); %Docstring -Handles canvas release event. If ``True`` is returned, the tool will have -blocked the event for propagating. +Handles canvas release event. + +.. note:: + + To stop propagation, set the event's accepted property to ``False``. %End signals: diff --git a/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in index fb48ea403aaa..3f9d6284c8e4 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingtoolsregistry.sip.in @@ -63,6 +63,9 @@ class QgsAdvancedDigitizingToolsRegistry %Docstring(signature="appended") Registry of available advanced digitizing tools. +:py:class:`QgsAdvancedDigitizingToolsRegistry` is not usually directly created, but rather accessed through +:py:func:`QgsGui.advancedDigitizingToolsRegistry()`. + .. versionadded:: 3.40 %End diff --git a/src/gui/qgsadvanceddigitizingcanvasitem.cpp b/src/gui/qgsadvanceddigitizingcanvasitem.cpp index 571601be1613..fe67e6c7ab47 100644 --- a/src/gui/qgsadvanceddigitizingcanvasitem.cpp +++ b/src/gui/qgsadvanceddigitizingcanvasitem.cpp @@ -59,10 +59,11 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) } } - // Draw current intersection tool - if ( mAdvancedDigitizingDockWidget->tool() ) + // Draw current tool + if ( QgsAdvancedDigitizingTool *tool = mAdvancedDigitizingDockWidget->tool() ) { - mAdvancedDigitizingDockWidget->tool()->paint( painter ); + // if a tool is active in the dock, then delegate to that tool to handle decorating the canvas instead of using the default decorations + tool->paint( painter ); return; } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 52cebd0450e1..383c0e2d31f7 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -242,17 +242,20 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * // Tools QMenu *toolsMenu = new QMenu( this ); - const QStringList toolMetadataNames = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadataNames(); - for ( const QString &name : toolMetadataNames ) + connect( toolsMenu, &QMenu::aboutToShow, this, [ = ]() { - QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadata( name ); - QAction *toolAction = new QAction( toolMetadata->icon(), toolMetadata->visibleName(), this ); - connect( toolAction, &QAction::triggered, this, [ = ]() + const QStringList toolMetadataNames = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadataNames(); + for ( const QString &name : toolMetadataNames ) { - setTool( toolMetadata->createTool( mMapCanvas, this ) ); - } ); - toolsMenu->addAction( toolAction ); - } + QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadata( name ); + QAction *toolAction = new QAction( toolMetadata->icon(), toolMetadata->visibleName(), this ); + connect( toolAction, &QAction::triggered, this, [ = ]() + { + setTool( toolMetadata->createTool( mMapCanvas, this ) ); + } ); + toolsMenu->addAction( toolAction ); + } + } ); qobject_cast< QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup ); mToolsAction->setMenu( toolsMenu ); @@ -684,12 +687,15 @@ void QgsAdvancedDigitizingDockWidget::setTool( QgsAdvancedDigitizingTool *tool ) mCurrentTool = nullptr; } - qDebug() << mCurrentTool; mCurrentTool = tool; if ( mCurrentTool ) { - mUserInputWidget->addUserInputWidget( mCurrentTool.data() ); + if ( QWidget *toolWidget = mCurrentTool->createWidget() ) + { + toolWidget->setParent( mUserInputWidget ); + mUserInputWidget->addUserInputWidget( toolWidget ); + } connect( mCurrentTool.data(), &QgsAdvancedDigitizingTool::paintRequested, this, &QgsAdvancedDigitizingDockWidget::updateCadPaintItem ); } } @@ -1547,17 +1553,20 @@ QList QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const return segment; } -bool QgsAdvancedDigitizingDockWidget::processCanvasPressEvent( QgsMapMouseEvent *event ) +void QgsAdvancedDigitizingDockWidget::processCanvasPressEvent( QgsMapMouseEvent *event ) { if ( mCurrentTool ) { mCurrentTool->canvasPressEvent( event ); } - return constructionMode(); + if ( constructionMode() ) + { + event->setAccepted( false ); + } } -bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent *event ) +void QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent *event ) { // perpendicular/parallel constraint // do a soft lock when snapping to a segment @@ -1569,22 +1578,22 @@ bool QgsAdvancedDigitizingDockWidget::processCanvasMoveEvent( QgsMapMouseEvent * } updateCadPaintItem(); - - return false; } -bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEvent *event ) +void QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEvent *event ) { if ( alignToSegment( event ) ) { - return true; + event->setAccepted( false ); + return; } if ( mCurrentTool ) { - if ( mCurrentTool->canvasReleaseEvent( event ) ) + mCurrentTool->canvasReleaseEvent( event ); + if ( !event->isAccepted() ) { - return true; + return; } else { @@ -1608,7 +1617,10 @@ bool QgsAdvancedDigitizingDockWidget::processCanvasReleaseEvent( QgsMapMouseEven addPoint( event->mapPoint() ); releaseLocks( false ); - return constructionMode(); + if ( constructionMode() ) + { + event->setAccepted( false ); + } } bool QgsAdvancedDigitizingDockWidget::alignToSegment( QgsMapMouseEvent *e, CadConstraint::LockMode lockMode ) diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index 44d327fc4c8b..73871a705a8b 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -278,21 +278,18 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private /** * Processes the canvas press \a event. - * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. */ - bool processCanvasPressEvent( QgsMapMouseEvent *event ); + void processCanvasPressEvent( QgsMapMouseEvent *event ); /** * Processes the canvas move \a event. - * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. */ - bool processCanvasMoveEvent( QgsMapMouseEvent *event ); + void processCanvasMoveEvent( QgsMapMouseEvent *event ); /** * Processes the canvas release \a event. - * \returns Returns TRUE if the event has been 'eaten' and should not propagate further. */ - bool processCanvasReleaseEvent( QgsMapMouseEvent *event ); + void processCanvasReleaseEvent( QgsMapMouseEvent *event ); /** * Sets an advanced digitizing tool which will take over digitizing until the tool is close. diff --git a/src/gui/qgsadvanceddigitizingtools.cpp b/src/gui/qgsadvanceddigitizingtools.cpp index 23abb12ab125..b85e4d16cce6 100644 --- a/src/gui/qgsadvanceddigitizingtools.cpp +++ b/src/gui/qgsadvanceddigitizingtools.cpp @@ -23,7 +23,7 @@ #include "qgsmapcanvas.h" QgsAdvancedDigitizingTool::QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) - : QWidget( canvas->viewport() ) + : QObject( canvas ? canvas->viewport() : nullptr ) , mMapCanvas( canvas ) , mCadDockWidget( cadDockWidget ) { @@ -32,72 +32,78 @@ QgsAdvancedDigitizingTool::QgsAdvancedDigitizingTool( QgsMapCanvas *canvas, QgsA QgsAdvancedDigitizingCirclesIntersectionTool::QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ) : QgsAdvancedDigitizingTool( canvas, cadDockWidget ) { - QGridLayout *layout = new QGridLayout( this ); +} + +QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() +{ + QWidget *toolWidget = new QWidget(); + + QGridLayout *layout = new QGridLayout( toolWidget ); layout->setContentsMargins( 0, 0, 0, 0 ); - setLayout( layout ); + toolWidget->setLayout( layout ); - QLabel *label = new QLabel( QStringLiteral( "Circle #1" ), this ); + QLabel *label = new QLabel( QStringLiteral( "Circle #1" ), toolWidget ); layout->addWidget( label, 0, 0, 1, 3 ); - mCircle1Digitize = new QToolButton( this ); + mCircle1Digitize = new QToolButton( toolWidget ); mCircle1Digitize->setCheckable( true ); mCircle1Digitize->setChecked( false ); mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 ); - label = new QLabel( QStringLiteral( "X" ), this ); + label = new QLabel( QStringLiteral( "X" ), toolWidget ); layout->addWidget( label, 1, 0 ); - mCircle1X = new QgsDoubleSpinBox( this ); + mCircle1X = new QgsDoubleSpinBox( toolWidget ); mCircle1X->setMinimum( std::numeric_limits::min() ); mCircle1X->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle1X, 1, 1 ); - label = new QLabel( QStringLiteral( "Y" ), this ); + label = new QLabel( QStringLiteral( "Y" ), toolWidget ); layout->addWidget( label, 2, 0 ); - mCircle1Y = new QgsDoubleSpinBox( this ); + mCircle1Y = new QgsDoubleSpinBox( toolWidget ); mCircle1Y->setMinimum( std::numeric_limits::min() ); mCircle1Y->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle1Y, 2, 1 ); - label = new QLabel( QStringLiteral( "Distance" ), this ); + label = new QLabel( QStringLiteral( "Distance" ), toolWidget ); layout->addWidget( label, 3, 0 ); - mCircle1Distance = new QgsDoubleSpinBox( this ); + mCircle1Distance = new QgsDoubleSpinBox( toolWidget ); mCircle1Distance->setMinimum( 0 ); mCircle1Distance->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle1Distance, 3, 1 ); - label = new QLabel( QStringLiteral( "Circle #2" ), this ); + label = new QLabel( QStringLiteral( "Circle #2" ), toolWidget ); layout->addWidget( label, 4, 0, 1, 3 ); - mCircle2Digitize = new QToolButton( this ); + mCircle2Digitize = new QToolButton( toolWidget ); mCircle2Digitize->setCheckable( true ); mCircle2Digitize->setChecked( false ); mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 ); - label = new QLabel( QStringLiteral( "X" ), this ); + label = new QLabel( QStringLiteral( "X" ), toolWidget ); layout->addWidget( label, 5, 0 ); - mCircle2X = new QgsDoubleSpinBox( this ); + mCircle2X = new QgsDoubleSpinBox( toolWidget ); mCircle2X->setMinimum( std::numeric_limits::min() ); mCircle2X->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle2X, 5, 1 ); - label = new QLabel( QStringLiteral( "Y" ), this ); + label = new QLabel( QStringLiteral( "Y" ), toolWidget ); layout->addWidget( label, 6, 0 ); - mCircle2Y = new QgsDoubleSpinBox( this ); + mCircle2Y = new QgsDoubleSpinBox( toolWidget ); mCircle2Y->setMinimum( std::numeric_limits::min() ); mCircle2Y->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle2Y, 6, 1 ); - label = new QLabel( QStringLiteral( "Distance" ), this ); + label = new QLabel( QStringLiteral( "Distance" ), toolWidget ); layout->addWidget( label, 7, 0 ); - mCircle2Distance = new QgsDoubleSpinBox( this ); + mCircle2Distance = new QgsDoubleSpinBox( toolWidget ); mCircle2Distance->setMinimum( 0 ); mCircle2Distance->setMaximum( std::numeric_limits::max() ); layout->addWidget( mCircle2Distance, 7, 1 ); @@ -108,6 +114,9 @@ QgsAdvancedDigitizingCirclesIntersectionTool::QgsAdvancedDigitizingCirclesInters connect( mCircle2X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + + mToolWidget = toolWidget; + return toolWidget; } void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters() @@ -120,7 +129,7 @@ void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters() emit paintRequested(); } -bool QgsAdvancedDigitizingCirclesIntersectionTool::canvasMoveEvent( QgsMapMouseEvent *event ) +void QgsAdvancedDigitizingCirclesIntersectionTool::canvasMoveEvent( QgsMapMouseEvent *event ) { if ( mCircle1Digitize->isChecked() ) { @@ -138,35 +147,41 @@ bool QgsAdvancedDigitizingCirclesIntersectionTool::canvasMoveEvent( QgsMapMouseE mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) ); } - return true; + event->setAccepted( false ); } -bool QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMouseEvent *event ) +void QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMouseEvent *event ) { if ( mCircle1Digitize->isChecked() ) { mCircle1X->setValue( event->mapPoint().x() ); mCircle1Y->setValue( event->mapPoint().y() ); mCircle1Digitize->setChecked( false ); - return true; + event->setAccepted( false ); + return; } else if ( mCircle2Digitize->isChecked() ) { mCircle2X->setValue( event->mapPoint().x() ); mCircle2Y->setValue( event->mapPoint().y() ); mCircle2Digitize->setChecked( false ); - return true; + event->setAccepted( false ); + return; } if ( !mP1.isEmpty() ) { mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) ); event->setMapPoint( mP1Closest ? mP1 : mP2 ); + if ( mToolWidget ) + { + mToolWidget->deleteLater(); + } deleteLater(); - return false; + return; } - return true; + event->setAccepted( false ); } void QgsAdvancedDigitizingCirclesIntersectionTool::drawCircle( QPainter *painter, double x, double y, double distance ) diff --git a/src/gui/qgsadvanceddigitizingtools.h b/src/gui/qgsadvanceddigitizingtools.h index dd4275a91941..a8669f7a5f80 100644 --- a/src/gui/qgsadvanceddigitizingtools.h +++ b/src/gui/qgsadvanceddigitizingtools.h @@ -35,7 +35,7 @@ class QgsMapCanvas; * \brief An abstract class for advanced digitizing tools. * \since QGIS 3.40 */ -class GUI_EXPORT QgsAdvancedDigitizingTool : public QWidget +class GUI_EXPORT QgsAdvancedDigitizingTool : public QObject { Q_OBJECT @@ -58,39 +58,42 @@ class GUI_EXPORT QgsAdvancedDigitizingTool : public QWidget */ QgsAdvancedDigitizingDockWidget *cadDockWidget() const { return mCadDockWidget.data(); } + /** + * Returns a widget to control the tool. + * \note The caller gets the ownership. + */ + virtual QWidget *createWidget() { return nullptr; } + /** * Paints tool content onto the advanced digitizing canvas item. */ virtual void paint( QPainter *painter ) { Q_UNUSED( painter ) } /** - * Handles canvas press event. If TRUE is returned, the tool will have - * blocked the event for propagating. + * Handles canvas press event. + * \note To stop propagation, set the event's accepted property to FALSE. */ - virtual bool canvasPressEvent( QgsMapMouseEvent *event ) + virtual void canvasPressEvent( QgsMapMouseEvent *event ) { Q_UNUSED( event ) - return true; } /** - * Handles canvas press move. If TRUE is returned, the tool will have - * blocked the event for propagating. + * Handles canvas press move. + * \note To stop propagation, set the event's accepted property to FALSE. */ - virtual bool canvasMoveEvent( QgsMapMouseEvent *event ) + virtual void canvasMoveEvent( QgsMapMouseEvent *event ) { Q_UNUSED( event ) - return true; } /** - * Handles canvas release event. If TRUE is returned, the tool will have - * blocked the event for propagating. + * Handles canvas release event. + * \note To stop propagation, set the event's accepted property to FALSE. */ - virtual bool canvasReleaseEvent( QgsMapMouseEvent *event ) + virtual void canvasReleaseEvent( QgsMapMouseEvent *event ) { Q_UNUSED( event ) - return true; } signals: @@ -127,10 +130,11 @@ class GUI_EXPORT QgsAdvancedDigitizingCirclesIntersectionTool : public QgsAdvanc */ explicit QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); + QWidget *createWidget() override; void paint( QPainter *painter ) override; - bool canvasMoveEvent( QgsMapMouseEvent *event ) override; - bool canvasReleaseEvent( QgsMapMouseEvent *event ) override; + void canvasMoveEvent( QgsMapMouseEvent *event ) override; + void canvasReleaseEvent( QgsMapMouseEvent *event ) override; private: void processParameters(); @@ -138,6 +142,7 @@ class GUI_EXPORT QgsAdvancedDigitizingCirclesIntersectionTool : public QgsAdvanc void drawCircle( QPainter *painter, double x, double y, double distance ); void drawCandidate( QPainter *painter, double x, double y, bool closest ); + QPointer mToolWidget; QToolButton *mCircle1Digitize = nullptr; QgsDoubleSpinBox *mCircle1X = nullptr; QgsDoubleSpinBox *mCircle1Y = nullptr; diff --git a/src/gui/qgsadvanceddigitizingtoolsregistry.cpp b/src/gui/qgsadvanceddigitizingtoolsregistry.cpp index 77b7992b8c67..6f1b09a75f4b 100644 --- a/src/gui/qgsadvanceddigitizingtoolsregistry.cpp +++ b/src/gui/qgsadvanceddigitizingtoolsregistry.cpp @@ -49,6 +49,7 @@ bool QgsAdvancedDigitizingToolsRegistry::addTool( QgsAdvancedDigitizingToolAbstr return false; mTools[toolMetaData->name()] = toolMetaData; + return true; } @@ -59,6 +60,7 @@ bool QgsAdvancedDigitizingToolsRegistry::removeTool( const QString &name ) return false; delete mTools.take( name ); + return true; } diff --git a/src/gui/qgsadvanceddigitizingtoolsregistry.h b/src/gui/qgsadvanceddigitizingtoolsregistry.h index 3793cda98a58..e320822053d3 100644 --- a/src/gui/qgsadvanceddigitizingtoolsregistry.h +++ b/src/gui/qgsadvanceddigitizingtoolsregistry.h @@ -115,6 +115,9 @@ class GUI_EXPORT QgsAdvancedDigitizingToolMetadata : public QgsAdvancedDigitizin * \ingroup gui * \brief Registry of available advanced digitizing tools. * + * QgsAdvancedDigitizingToolsRegistry is not usually directly created, but rather accessed through + * QgsGui::advancedDigitizingToolsRegistry(). + * * \since QGIS 3.40 */ class GUI_EXPORT QgsAdvancedDigitizingToolsRegistry diff --git a/src/gui/qgsmaptooladvanceddigitizing.cpp b/src/gui/qgsmaptooladvanceddigitizing.cpp index 30e08a0c9d57..94f3d804a2e0 100644 --- a/src/gui/qgsmaptooladvanceddigitizing.cpp +++ b/src/gui/qgsmaptooladvanceddigitizing.cpp @@ -36,9 +36,10 @@ void QgsMapToolAdvancedDigitizing::canvasPressEvent( QgsMapMouseEvent *e ) if ( isAdvancedDigitizingAllowed() && mCadDockWidget->cadEnabled() ) { mCadDockWidget->applyConstraints( e ); // updates event's map point - if ( mCadDockWidget->processCanvasPressEvent( e ) ) + mCadDockWidget->processCanvasPressEvent( e ); + if ( !e->isAccepted() ) { - return; // The dock widget has eaten the event + return; // The dock widget has taken the event } } else if ( isAutoSnapEnabled() ) @@ -66,9 +67,10 @@ void QgsMapToolAdvancedDigitizing::canvasReleaseEvent( QgsMapMouseEvent *e ) else { mCadDockWidget->applyConstraints( e ); // updates event's map point - if ( mCadDockWidget->processCanvasReleaseEvent( e ) ) + mCadDockWidget->processCanvasReleaseEvent( e ); + if ( !e->isAccepted() ) { - return; // The dock widget has eaten the event + return; // The dock widget has taken the event } } } @@ -91,9 +93,10 @@ void QgsMapToolAdvancedDigitizing::canvasMoveEvent( QgsMapMouseEvent *e ) if ( isAdvancedDigitizingAllowed() && mCadDockWidget->cadEnabled() ) { mCadDockWidget->applyConstraints( e ); // updates event's map point - if ( mCadDockWidget->processCanvasMoveEvent( e ) ) + mCadDockWidget->processCanvasMoveEvent( e ); + if ( !e->isAccepted() ) { - return; // The dock widget has eaten the event + return; // The dock widget has taken the event } } else if ( isAutoSnapEnabled() ) diff --git a/tests/src/gui/CMakeLists.txt b/tests/src/gui/CMakeLists.txt index 94edb3c983fe..a1d151f974e0 100644 --- a/tests/src/gui/CMakeLists.txt +++ b/tests/src/gui/CMakeLists.txt @@ -25,6 +25,7 @@ endif() set(TESTS testqgsadvanceddigitizingdockwidget.cpp + testqgsadvanceddigitizingtoolsregistry.cpp testqgsannotationitemguiregistry.cpp testqgsmaptoolzoom.cpp testqgsmaptooledit.cpp diff --git a/tests/src/gui/testqgsadvanceddigitizingtoolsregistry.cpp b/tests/src/gui/testqgsadvanceddigitizingtoolsregistry.cpp new file mode 100644 index 000000000000..b04222632049 --- /dev/null +++ b/tests/src/gui/testqgsadvanceddigitizingtoolsregistry.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + testqgsadvanceddigitizingtoolsregistry.cpp + -------------------- + Date : July 2024 + Copyright : (C) 2024 Mathieu Pellerin + Email : mathieu at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstest.h" +#include "qgsadvanceddigitizingtools.h" +#include "qgsadvanceddigitizingtoolsregistry.h" +#include + +class TestQgsAdvancedDigitizingToolsRegistry: public QObject +{ + Q_OBJECT + private slots: + void initTestCase(); // will be called before the first testfunction is executed. + void cleanupTestCase(); // will be called after the last testfunction was executed. + void init(); // will be called before each testfunction is executed. + void cleanup(); // will be called after every testfunction. + void guiRegistry(); + + private: + +}; + +void TestQgsAdvancedDigitizingToolsRegistry::initTestCase() +{ +} + +void TestQgsAdvancedDigitizingToolsRegistry::cleanupTestCase() +{ +} + +void TestQgsAdvancedDigitizingToolsRegistry::init() +{ +} + +void TestQgsAdvancedDigitizingToolsRegistry::cleanup() +{ +} + +class DummyAdvancedDigitizingTool : public QgsAdvancedDigitizingTool +{ + Q_OBJECT + public: + DummyAdvancedDigitizingTool() + : QgsAdvancedDigitizingTool( nullptr, nullptr ) + {} +}; + +void TestQgsAdvancedDigitizingToolsRegistry::guiRegistry() +{ + // test QgsAnnotationItemGuiRegistry + QgsAdvancedDigitizingToolsRegistry registry; + + // empty registry + QVERIFY( !registry.toolMetadata( QString( "empty" ) ) ); + QVERIFY( registry.toolMetadataNames().isEmpty() ); + + auto createTool = []( QgsMapCanvas *, QgsAdvancedDigitizingDockWidget * )->QgsAdvancedDigitizingTool * + { + return new DummyAdvancedDigitizingTool(); + }; + + QgsAdvancedDigitizingToolMetadata *metadata = new QgsAdvancedDigitizingToolMetadata( QStringLiteral( "dummy" ), QStringLiteral( "My Dummy Tool" ), QIcon(), createTool ); + QVERIFY( registry.addTool( metadata ) ); + const QString name = registry.toolMetadataNames().value( 0 ); + QCOMPARE( name, QStringLiteral( "dummy" ) ); + + // duplicate name not allowed + metadata = new QgsAdvancedDigitizingToolMetadata( QStringLiteral( "dummy" ), QStringLiteral( "My Dummy Tool" ), QIcon(), createTool ); + QVERIFY( !registry.addTool( metadata ) ); + + QVERIFY( registry.toolMetadata( name ) ); + QCOMPARE( registry.toolMetadata( name )->visibleName(), QStringLiteral( "My Dummy Tool" ) ); + + QgsAdvancedDigitizingTool *tool = registry.toolMetadata( name )->createTool( nullptr, nullptr ); + QVERIFY( tool ); + + registry.removeTool( name ); + QVERIFY( registry.toolMetadataNames().isEmpty() ); +} + + +QGSTEST_MAIN( TestQgsAdvancedDigitizingToolsRegistry ) +#include "testqgsadvanceddigitizingtoolsregistry.moc" From fedac1dea26291e6ee319b0b211ff0beb20419ed Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Tue, 30 Jul 2024 09:56:52 +0700 Subject: [PATCH 5/7] Apply requested UX logic --- .../editorwidgets/qgsdoublespinbox.sip.in | 16 ++++++++++++++++ .../editorwidgets/qgsspinbox.sip.in | 16 ++++++++++++++++ .../editorwidgets/qgsdoublespinbox.sip.in | 16 ++++++++++++++++ .../editorwidgets/qgsspinbox.sip.in | 16 ++++++++++++++++ src/gui/editorwidgets/qgsdoublespinbox.cpp | 1 + src/gui/editorwidgets/qgsdoublespinbox.h | 14 ++++++++++++++ src/gui/editorwidgets/qgsspinbox.cpp | 2 ++ src/gui/editorwidgets/qgsspinbox.h | 14 ++++++++++++++ src/gui/qgsadvanceddigitizingdockwidget.cpp | 3 ++- src/gui/qgsadvanceddigitizingtools.cpp | 17 +++++++++++++++++ src/gui/qgsadvanceddigitizingtools.h | 1 + 11 files changed, 115 insertions(+), 1 deletion(-) diff --git a/python/PyQt6/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in b/python/PyQt6/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in index 2103aacc0641..b3106b79dfd2 100644 --- a/python/PyQt6/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in +++ b/python/PyQt6/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in @@ -138,6 +138,22 @@ is equal to :py:func:`~QgsDoubleSpinBox.minimum`. Typical use is to indicate tha virtual void stepBy( int steps ); + signals: + + void returnPressed(); +%Docstring +Emitted when the Return or Enter key is used in the line edit. + +.. versionadded:: 3.40 +%End + + void textEdited( const QString &text ); +%Docstring +Emitted when the the value has been manually edited via line edit. + +.. versionadded:: 3.40 +%End + protected: virtual void changeEvent( QEvent *event ); diff --git a/python/PyQt6/gui/auto_generated/editorwidgets/qgsspinbox.sip.in b/python/PyQt6/gui/auto_generated/editorwidgets/qgsspinbox.sip.in index 8e3fb06ba77e..7461c9ccd83e 100644 --- a/python/PyQt6/gui/auto_generated/editorwidgets/qgsspinbox.sip.in +++ b/python/PyQt6/gui/auto_generated/editorwidgets/qgsspinbox.sip.in @@ -136,6 +136,22 @@ is equal to :py:func:`~QgsSpinBox.minimum`. Typical use is to indicate that this virtual void stepBy( int steps ); + signals: + + void returnPressed(); +%Docstring +Emitted when the Return or Enter key is used in the line edit + +.. versionadded:: 3.40 +%End + + void textEdited( const QString &text ); +%Docstring +Emitted when the the value has been manually edited via line edit. + +.. versionadded:: 3.40 +%End + protected: virtual void changeEvent( QEvent *event ); diff --git a/python/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in b/python/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in index 592b70c3a898..e167a3500f26 100644 --- a/python/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in +++ b/python/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in @@ -138,6 +138,22 @@ is equal to :py:func:`~QgsDoubleSpinBox.minimum`. Typical use is to indicate tha virtual void stepBy( int steps ); + signals: + + void returnPressed(); +%Docstring +Emitted when the Return or Enter key is used in the line edit. + +.. versionadded:: 3.40 +%End + + void textEdited( const QString &text ); +%Docstring +Emitted when the the value has been manually edited via line edit. + +.. versionadded:: 3.40 +%End + protected: virtual void changeEvent( QEvent *event ); diff --git a/python/gui/auto_generated/editorwidgets/qgsspinbox.sip.in b/python/gui/auto_generated/editorwidgets/qgsspinbox.sip.in index 15de6adb253f..022beaa9ad9e 100644 --- a/python/gui/auto_generated/editorwidgets/qgsspinbox.sip.in +++ b/python/gui/auto_generated/editorwidgets/qgsspinbox.sip.in @@ -136,6 +136,22 @@ is equal to :py:func:`~QgsSpinBox.minimum`. Typical use is to indicate that this virtual void stepBy( int steps ); + signals: + + void returnPressed(); +%Docstring +Emitted when the Return or Enter key is used in the line edit + +.. versionadded:: 3.40 +%End + + void textEdited( const QString &text ); +%Docstring +Emitted when the the value has been manually edited via line edit. + +.. versionadded:: 3.40 +%End + protected: virtual void changeEvent( QEvent *event ); diff --git a/src/gui/editorwidgets/qgsdoublespinbox.cpp b/src/gui/editorwidgets/qgsdoublespinbox.cpp index 71d354f8e5dc..a4446cf16194 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.cpp +++ b/src/gui/editorwidgets/qgsdoublespinbox.cpp @@ -40,6 +40,7 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent ) : QDoubleSpinBox( parent ) { mLineEdit = new QgsSpinBoxLineEdit(); + connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsDoubleSpinBox::returnPressed ); // By default, group separator is off setLineEdit( mLineEdit ); diff --git a/src/gui/editorwidgets/qgsdoublespinbox.h b/src/gui/editorwidgets/qgsdoublespinbox.h index f1ad4ddf542f..7bf91175efa3 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.h +++ b/src/gui/editorwidgets/qgsdoublespinbox.h @@ -142,6 +142,20 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox void paintEvent( QPaintEvent *e ) override; void stepBy( int steps ) override; + signals: + + /** + * Emitted when the Return or Enter key is used in the line edit. + * \since QGIS 3.40 + */ + void returnPressed(); + + /** + * Emitted when the the value has been manually edited via line edit. + * \since QGIS 3.40 + */ + void textEdited( const QString &text ); + protected: void changeEvent( QEvent *event ) override; void wheelEvent( QWheelEvent *event ) override; diff --git a/src/gui/editorwidgets/qgsspinbox.cpp b/src/gui/editorwidgets/qgsspinbox.cpp index 920949936553..b9f273a9b8d6 100644 --- a/src/gui/editorwidgets/qgsspinbox.cpp +++ b/src/gui/editorwidgets/qgsspinbox.cpp @@ -39,6 +39,8 @@ QgsSpinBox::QgsSpinBox( QWidget *parent ) : QSpinBox( parent ) { mLineEdit = new QgsSpinBoxLineEdit(); + connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsSpinBox::returnPressed ); + connect( mLineEdit, &QLineEdit::textEdited, this, &QgsSpinBox::textEdited ); setLineEdit( mLineEdit ); const QSize msz = minimumSizeHint(); diff --git a/src/gui/editorwidgets/qgsspinbox.h b/src/gui/editorwidgets/qgsspinbox.h index d9fb3367b503..636215fd7dfa 100644 --- a/src/gui/editorwidgets/qgsspinbox.h +++ b/src/gui/editorwidgets/qgsspinbox.h @@ -141,6 +141,20 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox QValidator::State validate( QString &input, int &pos ) const override; void stepBy( int steps ) override; + signals: + + /** + * Emitted when the Return or Enter key is used in the line edit + * \since QGIS 3.40 + */ + void returnPressed(); + + /** + * Emitted when the the value has been manually edited via line edit. + * \since QGIS 3.40 + */ + void textEdited( const QString &text ); + protected: void changeEvent( QEvent *event ) override; diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 383c0e2d31f7..f4fb773a32d4 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -244,11 +244,12 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * QMenu *toolsMenu = new QMenu( this ); connect( toolsMenu, &QMenu::aboutToShow, this, [ = ]() { + toolsMenu->clear(); const QStringList toolMetadataNames = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadataNames(); for ( const QString &name : toolMetadataNames ) { QgsAdvancedDigitizingToolAbstractMetadata *toolMetadata = QgsGui::instance()->advancedDigitizingToolsRegistry()->toolMetadata( name ); - QAction *toolAction = new QAction( toolMetadata->icon(), toolMetadata->visibleName(), this ); + QAction *toolAction = new QAction( toolMetadata->icon(), toolMetadata->visibleName(), toolsMenu ); connect( toolAction, &QAction::triggered, this, [ = ]() { setTool( toolMetadata->createTool( mMapCanvas, this ) ); diff --git a/src/gui/qgsadvanceddigitizingtools.cpp b/src/gui/qgsadvanceddigitizingtools.cpp index b85e4d16cce6..968d1eea091e 100644 --- a/src/gui/qgsadvanceddigitizingtools.cpp +++ b/src/gui/qgsadvanceddigitizingtools.cpp @@ -34,6 +34,14 @@ QgsAdvancedDigitizingCirclesIntersectionTool::QgsAdvancedDigitizingCirclesInters { } +QgsAdvancedDigitizingCirclesIntersectionTool::~QgsAdvancedDigitizingCirclesIntersectionTool() +{ + if ( mToolWidget ) + { + mToolWidget->deleteLater(); + } +} + QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() { QWidget *toolWidget = new QWidget(); @@ -57,6 +65,7 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle1X = new QgsDoubleSpinBox( toolWidget ); mCircle1X->setMinimum( std::numeric_limits::min() ); mCircle1X->setMaximum( std::numeric_limits::max() ); + connect( mCircle1X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } ); layout->addWidget( mCircle1X, 1, 1 ); label = new QLabel( QStringLiteral( "Y" ), toolWidget ); @@ -65,6 +74,7 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle1Y = new QgsDoubleSpinBox( toolWidget ); mCircle1Y->setMinimum( std::numeric_limits::min() ); mCircle1Y->setMaximum( std::numeric_limits::max() ); + connect( mCircle1Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } ); layout->addWidget( mCircle1Y, 2, 1 ); label = new QLabel( QStringLiteral( "Distance" ), toolWidget ); @@ -73,6 +83,7 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle1Distance = new QgsDoubleSpinBox( toolWidget ); mCircle1Distance->setMinimum( 0 ); mCircle1Distance->setMaximum( std::numeric_limits::max() ); + connect( mCircle1Distance, &QgsDoubleSpinBox::returnPressed, this, [ = ]() { mCircle2Digitize->setChecked( true ); } ); layout->addWidget( mCircle1Distance, 3, 1 ); label = new QLabel( QStringLiteral( "Circle #2" ), toolWidget ); @@ -90,6 +101,7 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle2X = new QgsDoubleSpinBox( toolWidget ); mCircle2X->setMinimum( std::numeric_limits::min() ); mCircle2X->setMaximum( std::numeric_limits::max() ); + connect( mCircle2X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } ); layout->addWidget( mCircle2X, 5, 1 ); label = new QLabel( QStringLiteral( "Y" ), toolWidget ); @@ -98,6 +110,7 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle2Y = new QgsDoubleSpinBox( toolWidget ); mCircle2Y->setMinimum( std::numeric_limits::min() ); mCircle2Y->setMaximum( std::numeric_limits::max() ); + connect( mCircle2Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } ); layout->addWidget( mCircle2Y, 6, 1 ); label = new QLabel( QStringLiteral( "Distance" ), toolWidget ); @@ -115,6 +128,8 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); + mCircle1Digitize->setChecked( true ); + mToolWidget = toolWidget; return toolWidget; } @@ -157,6 +172,7 @@ void QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMou mCircle1X->setValue( event->mapPoint().x() ); mCircle1Y->setValue( event->mapPoint().y() ); mCircle1Digitize->setChecked( false ); + mCircle1Distance->setFocus(); event->setAccepted( false ); return; } @@ -165,6 +181,7 @@ void QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMou mCircle2X->setValue( event->mapPoint().x() ); mCircle2Y->setValue( event->mapPoint().y() ); mCircle2Digitize->setChecked( false ); + mCircle2Distance->setFocus(); event->setAccepted( false ); return; } diff --git a/src/gui/qgsadvanceddigitizingtools.h b/src/gui/qgsadvanceddigitizingtools.h index a8669f7a5f80..36fa14aba7c0 100644 --- a/src/gui/qgsadvanceddigitizingtools.h +++ b/src/gui/qgsadvanceddigitizingtools.h @@ -129,6 +129,7 @@ class GUI_EXPORT QgsAdvancedDigitizingCirclesIntersectionTool : public QgsAdvanc * \param cadDockWidget The cadDockWidget to which the floater belongs */ explicit QgsAdvancedDigitizingCirclesIntersectionTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget ); + ~QgsAdvancedDigitizingCirclesIntersectionTool(); QWidget *createWidget() override; void paint( QPainter *painter ) override; From e739843369372e831f3824a23b5b0d66ad6c6fcb Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 31 Jul 2024 10:23:28 +0700 Subject: [PATCH 6/7] [cad] Make 2-circle intersection tool's digitizing buttons mutually exclusive --- src/gui/qgsadvanceddigitizingtools.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/gui/qgsadvanceddigitizingtools.cpp b/src/gui/qgsadvanceddigitizingtools.cpp index 968d1eea091e..9dd11594786a 100644 --- a/src/gui/qgsadvanceddigitizingtools.cpp +++ b/src/gui/qgsadvanceddigitizingtools.cpp @@ -57,6 +57,13 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle1Digitize->setCheckable( true ); mCircle1Digitize->setChecked( false ); mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); + connect( mCircle1Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked ) + { + if ( checked ) + { + mCircle2Digitize->setChecked( false ); + } + } ); layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 ); label = new QLabel( QStringLiteral( "X" ), toolWidget ); @@ -93,6 +100,13 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() mCircle2Digitize->setCheckable( true ); mCircle2Digitize->setChecked( false ); mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) ); + connect( mCircle2Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked ) + { + if ( checked ) + { + mCircle1Digitize->setChecked( false ); + } + } ); layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 ); label = new QLabel( QStringLiteral( "X" ), toolWidget ); From 99c1c1d943ff73e8ece8998aa510dcf4b99af13b Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 31 Jul 2024 14:30:55 +0700 Subject: [PATCH 7/7] [cad] Implement locked distance logic for 2-circle intersection tool --- .../qgsadvanceddigitizingdockwidget.sip.in | 7 +++++ .../qgsadvanceddigitizingtools.sip.in | 2 ++ .../qgsadvanceddigitizingdockwidget.sip.in | 7 +++++ .../qgsadvanceddigitizingtools.sip.in | 2 ++ src/gui/qgsadvanceddigitizingdockwidget.cpp | 11 +++++-- src/gui/qgsadvanceddigitizingdockwidget.h | 6 ++++ src/gui/qgsadvanceddigitizingtools.cpp | 31 ++++++++++++++++++- src/gui/qgsadvanceddigitizingtools.h | 2 +- 8 files changed, 63 insertions(+), 5 deletions(-) diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index fccd3de72a2d..92574b8ea84d 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -403,6 +403,13 @@ Returns the X value of the X soft lock. The value is NaN is the constraint isn't double softLockY() const; %Docstring Returns the Y value of the Y soft lock. The value is NaN is the constraint isn't magnetized to a line +%End + + void toggleConstraintDistance(); +%Docstring +Toggles the distance constraint. + +.. versionadded:: 3.40 %End QgsPointLocator::Match mapPointMatch() const; diff --git a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in index b587dae0133b..aa95923f9a44 100644 --- a/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in +++ b/python/PyQt6/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -90,6 +90,8 @@ Handles canvas release event. Requests a new painting event to the advanced digitizing canvas item. %End + protected: + }; diff --git a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in index 962a3a03f8a1..5e35a45b3a59 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingdockwidget.sip.in @@ -403,6 +403,13 @@ Returns the X value of the X soft lock. The value is NaN is the constraint isn't double softLockY() const; %Docstring Returns the Y value of the Y soft lock. The value is NaN is the constraint isn't magnetized to a line +%End + + void toggleConstraintDistance(); +%Docstring +Toggles the distance constraint. + +.. versionadded:: 3.40 %End QgsPointLocator::Match mapPointMatch() const; diff --git a/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in index b587dae0133b..aa95923f9a44 100644 --- a/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in +++ b/python/gui/auto_generated/qgsadvanceddigitizingtools.sip.in @@ -90,6 +90,8 @@ Handles canvas release event. Requests a new painting event to the advanced digitizing canvas item. %End + protected: + }; diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index f4fb773a32d4..e0ba31247b23 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -1753,6 +1753,13 @@ void QgsAdvancedDigitizingDockWidget::setPoints( const QList &points } } +void QgsAdvancedDigitizingDockWidget::toggleConstraintDistance() +{ + mDistanceConstraint->toggleLocked(); + emit lockDistanceChanged( mDistanceConstraint->isLocked() ); + emit pointChangedV2( mCadPointList.value( 0 ) ); +} + bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event ) { if ( !cadEnabled() ) @@ -1965,9 +1972,7 @@ bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e ) { if ( mCapacities.testFlag( RelativeCoordinates ) && mCapacities.testFlag( Distance ) ) { - mDistanceConstraint->toggleLocked(); - emit lockDistanceChanged( mDistanceConstraint->isLocked() ); - emit pointChangedV2( mCadPointList.value( 0 ) ); + toggleConstraintDistance(); e->accept(); } } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index 73871a705a8b..5f501c56106b 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -410,6 +410,12 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private //! Returns the Y value of the Y soft lock. The value is NaN is the constraint isn't magnetized to a line double softLockY() const { return mSoftLockY; } + /** + * Toggles the distance constraint. + * \since QGIS 3.40 + */ + void toggleConstraintDistance(); + /** * Returns the point locator match * \since QGIS 3.4 diff --git a/src/gui/qgsadvanceddigitizingtools.cpp b/src/gui/qgsadvanceddigitizingtools.cpp index 9dd11594786a..7f9bdef73bba 100644 --- a/src/gui/qgsadvanceddigitizingtools.cpp +++ b/src/gui/qgsadvanceddigitizingtools.cpp @@ -142,7 +142,34 @@ QWidget *QgsAdvancedDigitizingCirclesIntersectionTool::createWidget() connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } ); - mCircle1Digitize->setChecked( true ); + + bool focusOnCircle2 = false; + if ( mCadDockWidget ) + { + if ( mCadDockWidget->constraintDistance()->isLocked() ) + { + QgsPoint point = mCadDockWidget->previousPointV2(); + if ( !point.isEmpty() ) + { + whileBlocking( mCircle1Distance )->setValue( mCadDockWidget->constraintDistance()->value() ); + whileBlocking( mCircle1X )->setValue( point.x() ); + whileBlocking( mCircle1Y )->setValue( point.y() ); + mP1 = point; + focusOnCircle2 = true; + + mCadDockWidget->toggleConstraintDistance(); + } + } + } + + if ( focusOnCircle2 ) + { + mCircle2Digitize->setChecked( true ); + } + else + { + mCircle1Digitize->setChecked( true ); + } mToolWidget = toolWidget; return toolWidget; @@ -187,6 +214,7 @@ void QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMou mCircle1Y->setValue( event->mapPoint().y() ); mCircle1Digitize->setChecked( false ); mCircle1Distance->setFocus(); + mCircle1Distance->selectAll(); event->setAccepted( false ); return; } @@ -196,6 +224,7 @@ void QgsAdvancedDigitizingCirclesIntersectionTool::canvasReleaseEvent( QgsMapMou mCircle2Y->setValue( event->mapPoint().y() ); mCircle2Digitize->setChecked( false ); mCircle2Distance->setFocus(); + mCircle2Distance->selectAll(); event->setAccepted( false ); return; } diff --git a/src/gui/qgsadvanceddigitizingtools.h b/src/gui/qgsadvanceddigitizingtools.h index 36fa14aba7c0..fa3a83467a0f 100644 --- a/src/gui/qgsadvanceddigitizingtools.h +++ b/src/gui/qgsadvanceddigitizingtools.h @@ -103,7 +103,7 @@ class GUI_EXPORT QgsAdvancedDigitizingTool : public QObject */ void paintRequested(); - private: + protected: QgsMapCanvas *mMapCanvas = nullptr; QPointer< QgsAdvancedDigitizingDockWidget > mCadDockWidget;