43#include <QActionGroup>
44#include <QCoreApplication>
49#include "moc_qgsadvanceddigitizingdockwidget.cpp"
51using namespace Qt::StringLiterals;
61 , mMapCanvas( canvas )
62 , mUserInputWidget( userInputWidget )
64 , mCommonAngleConstraint(
QgsSettings().value( u
"/Cad/CommonAngle"_s, 0.0 ).toDouble() )
70 mAngleConstraint = std::make_unique<CadConstraint>( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton );
72 mAngleConstraint->setMapCanvas( mMapCanvas );
73 mDistanceConstraint = std::make_unique<CadConstraint>( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton );
75 mDistanceConstraint->setMapCanvas( mMapCanvas );
76 mXConstraint = std::make_unique<CadConstraint>( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton );
78 mXConstraint->setMapCanvas( mMapCanvas );
79 mYConstraint = std::make_unique<CadConstraint>( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton );
81 mYConstraint->setMapCanvas( mMapCanvas );
82 mZConstraint = std::make_unique<CadConstraint>( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton );
84 mZConstraint->setMapCanvas( mMapCanvas );
85 mMConstraint = std::make_unique<CadConstraint>( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton );
87 mMConstraint->setMapCanvas( mMapCanvas );
89 mLineExtensionConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
90 mXyVertexConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
91 mXyVertexConstraint->setMapCanvas( mMapCanvas );
95 mMapCanvas->installEventFilter(
this );
96 mAngleLineEdit->installEventFilter(
this );
97 mDistanceLineEdit->installEventFilter(
this );
98 mXLineEdit->installEventFilter(
this );
99 mYLineEdit->installEventFilter(
this );
100 mZLineEdit->installEventFilter(
this );
101 mMLineEdit->installEventFilter(
this );
104 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
105 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
106 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
107 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
108 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
109 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
110 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
111 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
112 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
113 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
114 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
115 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
116 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
117 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
118 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
119 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
120 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
121 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
122 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
123 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
124 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
125 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
126 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
127 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
128 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
129 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
130 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
131 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
132 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
133 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
134 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
135 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
136 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
142 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
148 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
160 mCommonAngleActionsMenu =
new QMenu(
this );
162#ifndef __clang_analyzer__
163 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
165 QList<QPair<double, QString>> commonAngles;
166 const QList<double> anglesDouble( { 0.0, 0.1, 0.5, 1.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0 } );
167 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
173 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
174 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
175 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
176 featuresAction->setCheckable(
true );
177 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
178 anglesAction->setCheckable(
true );
179 snappingPriorityActionGroup->addAction( featuresAction );
180 snappingPriorityActionGroup->addAction( anglesAction );
181 snappingPriorityMenu->addAction( anglesAction );
182 snappingPriorityMenu->addAction( featuresAction );
183 connect( anglesAction, &QAction::changed,
this, [
this, featuresAction] {
184 mSnappingPrioritizeFeatures = featuresAction->isChecked();
185 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
187 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value() );
188 anglesAction->setChecked( !featuresAction->isChecked() );
189 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
192 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
194 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
195 action->setCheckable(
true );
196 action->setChecked( it->first == mCommonAngleConstraint );
197 mCommonAngleActionsMenu->addAction( action );
199#ifndef __clang_analyzer__
200 angleButtonGroup->addAction( action );
202 mCommonAngleActions.insert( it->first, action );
206 QMenu *constructionSettingsMenu =
new QMenu(
this );
208 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
209 mRecordConstructionGuides->setCheckable(
true );
210 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->value() );
211 constructionSettingsMenu->addAction( mRecordConstructionGuides );
212 connect( mRecordConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadRecordConstructionGuides->setValue( mRecordConstructionGuides->isChecked() ); } );
214 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
215 mShowConstructionGuides->setCheckable(
true );
216 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->value() );
217 constructionSettingsMenu->addAction( mShowConstructionGuides );
218 connect( mShowConstructionGuides, &QAction::triggered,
this, [
this]() {
219 settingsCadShowConstructionGuides->setValue( mShowConstructionGuides->isChecked() );
223 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
224 mSnapToConstructionGuides->setCheckable(
true );
225 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->value() );
226 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
227 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadSnapToConstructionGuides->setValue( mSnapToConstructionGuides->isChecked() ); } );
229 constructionSettingsMenu->addSeparator();
231 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
232 constructionSettingsMenu->addAction( mClearConstructionGuides );
233 connect( mClearConstructionGuides, &QAction::triggered,
this, [
this]() {
234 resetConstructionGuides();
238 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
239 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
240 constructionModeToolButton->setMenu( constructionSettingsMenu );
241 constructionModeToolButton->setObjectName( u
"ConstructionModeButton"_s );
244 QMenu *toolsMenu =
new QMenu(
this );
245 connect( toolsMenu, &QMenu::aboutToShow,
this, [
this, toolsMenu]() {
248 for (
const QString &name : toolMetadataNames )
251 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
252 connect( toolAction, &QAction::triggered,
this, [
this, toolMetadata]() {
255 toolsMenu->addAction( toolAction );
258 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
259 mToolsAction->setMenu( toolsMenu );
261 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
262 mSettingsAction->setMenu( mCommonAngleActionsMenu );
263 mSettingsAction->setCheckable(
true );
264 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
265 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
266 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
269 QMenu *constructionMenu =
new QMenu(
this );
271 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
272 mLineExtensionAction->setCheckable(
true );
273 constructionMenu->addAction( mLineExtensionAction );
274 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
276 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
277 mXyVertexAction->setCheckable(
true );
278 constructionMenu->addAction( mXyVertexAction );
279 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
281 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
282 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
283 mConstructionAction->setMenu( constructionMenu );
284 constructionToolBar->setObjectName( u
"ConstructionButton"_s );
285 mConstructionAction->setCheckable(
true );
286 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
289 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
290 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
291 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
292 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
294 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
295 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
296 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
297 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
299 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
300 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
301 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
302 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
304 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
305 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
306 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
307 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
309 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
310 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
311 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
312 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
314 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
315 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
316 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
317 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
328 mFloaterActionsMenu =
new QMenu(
this );
329 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
330 mFloaterAction->setMenu( mFloaterActionsMenu );
331 mFloaterAction->setCheckable(
true );
333 mFloaterAction->setChecked( mFloater->active() );
337 QAction *action =
new QAction( tr(
"Show floater" ), mFloaterActionsMenu );
338 action->setCheckable(
true );
339 action->setChecked( mFloater->active() );
340 mFloaterActionsMenu->addAction( action );
341 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
342 mFloater->setActive( checked );
343 mFloaterAction->setChecked( checked );
347 mFloaterActionsMenu->addSeparator();
350 QAction *action =
new QAction( tr(
"Show distance" ), mFloaterActionsMenu );
351 action->setCheckable(
true );
352 mFloaterActionsMenu->addAction( action );
353 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
356 const bool isDistanceChecked =
QgsSettings().
value( u
"/Cad/DistanceShowInFloater"_s,
true ).toBool();
357 action->setChecked( isDistanceChecked );
363 QAction *action =
new QAction( tr(
"Show angle" ), mFloaterActionsMenu );
364 action->setCheckable(
true );
365 mFloaterActionsMenu->addAction( action );
366 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
369 const bool isAngleChecked =
QgsSettings().
value( u
"/Cad/AngleShowInFloater"_s,
true ).toBool();
370 action->setChecked( isAngleChecked );
376 QAction *action =
new QAction( tr(
"Show XY coordinates" ), mFloaterActionsMenu );
377 action->setCheckable(
true );
378 mFloaterActionsMenu->addAction( action );
379 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
384 const bool isXCoordinateChecked =
QgsSettings().
value( u
"/Cad/XCoordinateShowInFloater"_s,
true ).toBool();
385 action->setChecked( isXCoordinateChecked );
393 QAction *action =
new QAction( tr(
"Show Z value" ), mFloaterActionsMenu );
394 action->setCheckable(
true );
395 mFloaterActionsMenu->addAction( action );
396 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
399 const bool isZCoordinateChecked =
QgsSettings().
value( u
"/Cad/ZCoordinateShowInFloater"_s,
true ).toBool();
400 action->setChecked( isZCoordinateChecked );
407 QAction *action =
new QAction( tr(
"Show M value" ), mFloaterActionsMenu );
408 action->setCheckable(
true );
409 mFloaterActionsMenu->addAction( action );
410 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
413 const bool isMCoordinateChecked =
QgsSettings().
value( u
"/Cad/MCoordinateShowInFloater"_s,
true ).toBool();
414 action->setChecked( isMCoordinateChecked );
421 QAction *action =
new QAction( tr(
"Show bearing/azimuth" ), mFloaterActionsMenu );
422 action->setCheckable(
true );
423 mFloaterActionsMenu->addAction( action );
424 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
427 const bool isBearingChecked =
QgsSettings().
value( u
"/Cad/BearingShowInFloater"_s,
false ).toBool();
428 action->setChecked( isBearingChecked );
435 QAction *action =
new QAction( tr(
"Show common snapping angle" ), mFloaterActionsMenu );
436 action->setCheckable(
true );
437 mFloaterActionsMenu->addAction( action );
438 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
441 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( u
"/Cad/CommonAngleSnappingShowInFloater"_s,
false ).toBool();
442 action->setChecked( isCommonAngleSnappingChecked );
449 QAction *action =
new QAction( tr(
"Show weight" ), mFloaterActionsMenu );
450 action->setCheckable(
true );
451 mFloaterActionsMenu->addAction( action );
452 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
455 const bool isWeightChecked =
QgsSettings().
value( u
"/Cad/WeightShowInFloater"_s,
true ).toBool();
456 action->setChecked( isWeightChecked );
461 updateCapacity(
true );
465 mConstructionGuidesLayer.reset();
476 mCurrentTool->deleteLater();
483 return tr(
"Do Not Snap to Common Angles" );
485 return QString( tr(
"%1, %2, %3, %4°…" ) ).arg( angle, 0,
'f', 1 ).arg( angle * 2, 0,
'f', 1 ).arg( angle * 3, 0,
'f', 1 ).arg( angle * 4, 0,
'f', 1 );
490 mXLineEdit->setText( value );
493 emit mXLineEdit->returnPressed();
497 QEvent *e =
new QEvent( QEvent::FocusOut );
498 QCoreApplication::postEvent( mXLineEdit, e );
502 emit mXLineEdit->textEdited( value );
507 mYLineEdit->setText( value );
510 emit mYLineEdit->returnPressed();
514 QEvent *e =
new QEvent( QEvent::FocusOut );
515 QCoreApplication::postEvent( mYLineEdit, e );
519 emit mYLineEdit->textEdited( value );
524 mZLineEdit->setText( value );
527 emit mZLineEdit->returnPressed();
531 QEvent *e =
new QEvent( QEvent::FocusOut );
532 QCoreApplication::postEvent( mZLineEdit, e );
536 emit mZLineEdit->textEdited( value );
541 mMLineEdit->setText( value );
544 emit mMLineEdit->returnPressed();
548 QEvent *e =
new QEvent( QEvent::FocusOut );
549 QCoreApplication::postEvent( mMLineEdit, e );
553 emit mMLineEdit->textEdited( value );
558 mAngleLineEdit->setText( value );
561 emit mAngleLineEdit->returnPressed();
565 emit mAngleLineEdit->textEdited( value );
570 mDistanceLineEdit->setText( value );
573 emit mDistanceLineEdit->returnPressed();
577 QEvent *e =
new QEvent( QEvent::FocusOut );
578 QCoreApplication::postEvent( mDistanceLineEdit, e );
582 emit mDistanceLineEdit->textEdited( value );
587void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
589 mCadEnabled = enabled;
590 mEnableAction->setChecked( enabled );
591 mConstructionModeAction->setEnabled( enabled );
592 mSettingsAction->setEnabled( enabled );
593 mInputWidgets->setEnabled( enabled );
594 mFloaterAction->setEnabled( enabled );
595 mConstructionAction->setEnabled( enabled );
596 mToolsAction->setEnabled( enabled );
601 mLineExtensionAction->setChecked(
false );
602 mXyVertexAction->setChecked(
false );
604 mParallelAction->setEnabled(
false );
605 mPerpendicularAction->setEnabled(
false );
608 mCurrentTool->deleteLater();
615 setConstructionMode(
false );
625 mLastSnapMatch = QgsPointLocator::Match();
631 bool enableZ =
false;
632 bool enableM =
false;
636 switch ( layer->type() )
649 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
665 mTargetLayerSupportsM = enableM;
666 mTargetLayerSupportsZ = enableZ;
674 if (
enable && !mTargetLayerSupportsZ )
678 mRelativeZButton->setEnabled(
enable );
679 mZLabel->setEnabled(
enable );
680 mZLineEdit->setEnabled(
enable );
681 if ( mZLineEdit->isEnabled() )
685 mLockZButton->setEnabled(
enable );
691 if (
enable && !mTargetLayerSupportsM )
695 mRelativeMButton->setEnabled(
enable );
696 mMLabel->setEnabled(
enable );
697 mMLineEdit->setEnabled(
enable );
698 if ( mMLineEdit->isEnabled() )
702 mLockMButton->setEnabled(
enable );
706void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
708 enabled &= mCurrentMapToolSupportsCad;
710 mSessionActive = enabled;
712 if ( enabled && !isVisible() )
717 setCadEnabled( enabled );
724 mCurrentTool->deleteLater();
725 mCurrentTool =
nullptr;
732 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
734 toolWidget->setParent( mUserInputWidget );
735 mUserInputWidget->addUserInputWidget( toolWidget );
743 return mCurrentTool.data();
746void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
752 else if ( sender() == mParallelAction )
756 else if ( sender() == mPerpendicularAction )
762void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
764 if ( sender() == mRelativeAngleButton )
766 mAngleConstraint->setRelative( activate );
769 else if ( sender() == mRelativeXButton )
771 mXConstraint->setRelative( activate );
774 else if ( sender() == mRelativeYButton )
776 mYConstraint->setRelative( activate );
779 else if ( sender() == mRelativeZButton )
781 mZConstraint->setRelative( activate );
784 else if ( sender() == mRelativeMButton )
786 mMConstraint->setRelative( activate );
791void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
793 if ( sender() == mRepeatingLockDistanceButton )
795 mDistanceConstraint->setRepeatingLock( activate );
797 else if ( sender() == mRepeatingLockAngleButton )
799 mAngleConstraint->setRepeatingLock( activate );
801 else if ( sender() == mRepeatingLockXButton )
803 mXConstraint->setRepeatingLock( activate );
805 else if ( sender() == mRepeatingLockYButton )
807 mYConstraint->setRepeatingLock( activate );
809 else if ( sender() == mRepeatingLockZButton )
811 mZConstraint->setRepeatingLock( activate );
813 else if ( sender() == mRepeatingLockMButton )
815 mMConstraint->setRepeatingLock( activate );
819void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
821 mConstructionMode = enabled;
822 mConstructionModeAction->setChecked( enabled );
826 if ( enabled && mCadPointList.size() > 1 )
828 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
833void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
836 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
838 if ( it.value() == action )
840 it.value()->setChecked(
true );
841 mCommonAngleConstraint = it.key();
842 QgsSettings().setValue( u
"/Cad/CommonAngle"_s, it.key() );
843 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
850QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
852 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast<QgsMapToolAdvancedDigitizing *>( mMapCanvas->mapTool() ) )
854 return advancedTool->layer();
858 return mMapCanvas->currentLayer();
868 if ( releaseRepeatingLocks )
870 mXyVertexAction->setChecked(
false );
874 mLineExtensionAction->setChecked(
false );
881 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
886 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
891 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
896 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
901 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
906 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
912 if ( !mCadPointList.empty() )
914 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
916 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
918 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
920 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
922 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
924 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
926 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
928 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
934void QgsAdvancedDigitizingDockWidget::emit pointChanged()
937 QPoint globalPos = mMapCanvas->cursor().pos();
938 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
939 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
940 mCurrentMapTool->canvasMoveEvent( e );
947 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
949 constraint = mAngleConstraint.get();
951 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
953 constraint = mDistanceConstraint.get();
955 else if ( obj == mXLineEdit || obj == mLockXButton )
957 constraint = mXConstraint.get();
959 else if ( obj == mYLineEdit || obj == mLockYButton )
961 constraint = mYConstraint.get();
963 else if ( obj == mZLineEdit || obj == mLockZButton )
965 constraint = mZConstraint.get();
967 else if ( obj == mMLineEdit || obj == mLockMButton )
969 constraint = mMConstraint.get();
971 else if ( obj == mLineExtensionAction )
973 constraint = mLineExtensionConstraint.get();
975 else if ( obj == mXyVertexAction )
977 constraint = mXyVertexConstraint.get();
982double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
992 QgsExpression expr( inputValue );
993 const QVariant result = expr.evaluate();
994 if ( expr.hasEvalError() )
997 QString inputValueC { inputValue };
1000 if ( inputValue.contains( QLocale().groupSeparator() ) )
1002 inputValueC.remove( QLocale().groupSeparator() );
1003 QgsExpression exprC( inputValueC );
1004 const QVariant resultC = exprC.evaluate();
1005 if ( !exprC.hasEvalError() )
1007 value = resultC.toDouble( &ok );
1012 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
1014 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
1015 const QVariant resultC = exprC.evaluate();
1016 if ( !exprC.hasEvalError() )
1018 value = resultC.toDouble( &ok );
1024 value = result.toDouble( &ok );
1039void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1041 if ( !constraint || textValue.isEmpty() )
1050 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1054 constraint->setValue( value, convertExpression );
1059void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1069 const QString textValue = constraint->lineEdit()->text();
1070 if ( !textValue.isEmpty() )
1073 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1076 constraint->setValue( value );
1090 if ( constraint == mXConstraint.get() )
1094 else if ( constraint == mYConstraint.get() )
1098 else if ( constraint == mZConstraint.get() )
1102 else if ( constraint == mMConstraint.get() )
1106 else if ( constraint == mDistanceConstraint.get() )
1110 else if ( constraint == mAngleConstraint.get() )
1118 if ( constraint == mAngleConstraint.get() )
1128void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1136 updateConstraintValue( constraint, textValue,
false );
1139void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1141 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1151 updateConstraintValue( constraint, lineEdit->text(),
true );
1156 mBetweenLineConstraint = constraint;
1161void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1171 if ( constraint == mXyVertexConstraint.get() )
1175 else if ( constraint == mLineExtensionConstraint.get() )
1189void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1192 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1195 if ( mCadPointList.count() > 1 )
1198 if ( !isGeographic )
1204 if ( mCadPointList.count() > 2 )
1206 if ( !isGeographic )
1209 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1219 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1220 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1221 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1222 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1224 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1225 mParallelAction->setEnabled( distance && snappingEnabled );
1227 mLineExtensionAction->setEnabled( snappingEnabled );
1228 mXyVertexAction->setEnabled( snappingEnabled );
1232 if ( !snappingEnabled )
1234 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1235 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1236 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1237 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1239 else if ( mCadPointList.count() <= 1 )
1241 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1242 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1244 else if ( isGeographic )
1246 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1247 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1251 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1252 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1256 if ( !absoluteAngle )
1262 mLockAngleButton->setEnabled( absoluteAngle );
1263 mRelativeAngleButton->setEnabled( relativeAngle );
1264 mAngleLineEdit->setEnabled( absoluteAngle );
1266 if ( !absoluteAngle )
1270 if ( !relativeAngle )
1272 mAngleConstraint->setRelative(
false );
1275 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1278 mAngleConstraint->setRelative(
true );
1283 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1284 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1286 if ( !( distance && relativeCoordinates ) )
1291 mRelativeXButton->setEnabled( relativeCoordinates );
1292 mRelativeYButton->setEnabled( relativeCoordinates );
1293 mRelativeZButton->setEnabled( relativeCoordinates );
1294 mRelativeMButton->setEnabled( relativeCoordinates );
1297 mCapacities = newCapacities;
1298 mCadPaintItem->updatePosition();
1305 constr.
locked =
c->isLocked();
1307 constr.
value =
c->value();
1314 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1320 const int lastIndex = mLockedSnapVertices.length() - 1;
1321 for (
int i = lastIndex; i >= 0; --i )
1323 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1325 if ( snapMatch.
point() != previouslySnap.
point() )
1327 mLockedSnapVertices.removeAt( i );
1333 if ( snapMatch.
point() != previouslySnap.
point() )
1335 mLockedSnapVertices.enqueue( snapMatch );
1338 if ( mLockedSnapVertices.count() > 3 )
1340 mLockedSnapVertices.dequeue();
1349 context.
xConstraint = _constraint( mXConstraint.get() );
1350 context.
yConstraint = _constraint( mYConstraint.get() );
1351 context.
zConstraint = _constraint( mZConstraint.get() );
1352 context.
mConstraint = _constraint( mMConstraint.get() );
1379 const bool res = output.
valid;
1381 mSnappedSegment.clear();
1386 mSnappedSegment << edgePt0 << edgePt1;
1407 mSnapIndicator->setMatch( output.
snapMatch );
1408 mSnapIndicator->setVisible(
true );
1412 mSnapIndicator->setVisible(
false );
1434 if ( mSnapMatch.layer() )
1438 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && (
QgsPointXY( point ) == mSnapMatch.point() ) )
1442 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1448 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1450 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1451 mLastSnapMatch = mSnapMatch;
1461 if ( mLockZButton->isChecked() )
1463 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1465 if ( mLockMButton->isChecked() )
1467 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1473 updateUnlockedConstraintValues( point );
1481 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1488void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1490 bool previousPointExist, penulPointExist;
1495 if ( previousPointExist )
1497 double prevAngle = 0.0;
1499 if ( penulPointExist && mAngleConstraint->relative() )
1502 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1505 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1508 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1509 if ( !mAngleConstraint->isLocked() )
1511 mAngleConstraint->setValue( angle );
1515 double bearing { std::fmod( xAngle, 360.0 ) };
1516 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1517 const QgsNumericFormatContext context;
1522 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1524 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1527 if ( !mXConstraint->isLocked() )
1529 if ( previousPointExist && mXConstraint->relative() )
1531 mXConstraint->setValue( point.
x() - previousPt.
x() );
1535 mXConstraint->setValue( point.
x() );
1539 if ( !mYConstraint->isLocked() )
1541 if ( previousPointExist && mYConstraint->relative() )
1543 mYConstraint->setValue( point.
y() - previousPt.
y() );
1547 mYConstraint->setValue( point.
y() );
1551 if ( !mZConstraint->isLocked() )
1553 if ( previousPointExist && mZConstraint->relative() )
1555 mZConstraint->setValue( point.
z() - previousPt.
z() );
1559 mZConstraint->setValue( point.
z() );
1563 if ( !mMConstraint->isLocked() )
1565 if ( previousPointExist && mMConstraint->relative() )
1567 mMConstraint->setValue( point.
m() - previousPt.
m() );
1571 mMConstraint->setValue( point.
m() );
1577QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1580 QgsPointXY pt1, pt2;
1581 QgsPointLocator::Match match;
1583 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1585 const QgsSnappingConfig canvasConfig = snappingUtils->
config();
1586 QgsSnappingConfig localConfig = snappingUtils->
config();
1590 snappingUtils->
setConfig( localConfig );
1592 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1594 snappingUtils->
setConfig( canvasConfig );
1604 *snapped =
segment.count() == 2;
1614 mCurrentTool->canvasPressEvent( event );
1619 event->setAccepted(
false );
1631 mCurrentTool->canvasMoveEvent( event );
1639 if ( event->button() == Qt::RightButton )
1643 mCurrentTool->canvasReleaseEvent( event );
1644 if ( !event->isAccepted() )
1656 event->setAccepted(
false );
1662 mCurrentTool->canvasReleaseEvent( event );
1663 if ( !event->isAccepted() )
1674 if ( mLockZButton->isChecked() )
1676 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1678 if ( mLockMButton->isChecked() )
1680 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1691 event->setAccepted(
false );
1703 bool previousPointExist, penulPointExist, snappedSegmentExist;
1706 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1708 if ( !previousPointExist || !snappedSegmentExist )
1713 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1715 if ( mAngleConstraint->relative() && penulPointExist )
1717 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1725 angle *= 180 / M_PI;
1727 mAngleConstraint->setValue( angle );
1728 mAngleConstraint->setLockMode( lockMode );
1746 case Qt::Key_Backspace:
1747 case Qt::Key_Delete:
1753 case Qt::Key_Escape:
1772 mCurrentTool->deleteLater();
1775 if ( !mConstructionGuideLine.isEmpty() )
1777 mConstructionGuideLine.clear();
1793 case Qt::Key_Backspace:
1794 case Qt::Key_Delete:
1800 case Qt::Key_Escape:
1804 if ( mConstructionGuideLine.numPoints() >= 2 )
1806 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1807 mConstructionGuideLine.clear();
1812 mCurrentTool->deleteLater();
1819 filterKeyPress( e );
1828 const auto constPoints = points;
1837 mDistanceConstraint->toggleLocked();
1842bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1846 return QgsDockWidget::eventFilter( obj, event );
1854 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1856 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1858 return filterKeyPress( keyEvent );
1861 return QgsDockWidget::eventFilter( obj, event );
1864bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1870 const QEvent::Type type = e->type();
1873 case Qt::Key_Escape:
1875 if ( type == QEvent::KeyPress && mCurrentTool )
1877 mCurrentTool->deleteLater();
1879 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.numPoints() >= 2 )
1881 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1882 mConstructionGuideLine.clear();
1884 if ( mCadPointList.size() > 1 )
1886 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
1901 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1903 mXConstraint->toggleLocked();
1908 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1912 mXConstraint->toggleRelative();
1919 else if ( type == QEvent::KeyPress )
1921 mXLineEdit->setFocus();
1922 mXLineEdit->selectAll();
1931 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1933 mYConstraint->toggleLocked();
1938 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1942 mYConstraint->toggleRelative();
1949 else if ( type == QEvent::KeyPress )
1951 mYLineEdit->setFocus();
1952 mYLineEdit->selectAll();
1961 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1963 mZConstraint->toggleLocked();
1968 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1972 mZConstraint->toggleRelative();
1979 else if ( type == QEvent::KeyPress )
1981 mZLineEdit->setFocus();
1982 mZLineEdit->selectAll();
1991 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1993 mMConstraint->toggleLocked();
1998 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2002 mMConstraint->toggleRelative();
2009 else if ( type == QEvent::KeyPress )
2011 mMLineEdit->setFocus();
2012 mMLineEdit->selectAll();
2021 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2025 mAngleConstraint->toggleLocked();
2031 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2035 mAngleConstraint->toggleRelative();
2042 else if ( type == QEvent::KeyPress )
2044 mAngleLineEdit->setFocus();
2045 mAngleLineEdit->selectAll();
2054 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2063 else if ( type == QEvent::KeyPress )
2065 mDistanceLineEdit->setFocus();
2066 mDistanceLineEdit->selectAll();
2074 if ( type == QEvent::KeyPress )
2076 setConstructionMode( !mConstructionMode );
2083 if ( type == QEvent::KeyPress )
2085 const bool parallel = mParallelAction->isChecked();
2086 const bool perpendicular = mPerpendicularAction->isChecked();
2088 if ( !parallel && !perpendicular )
2092 else if ( perpendicular )
2109 if ( type == QEvent::ShortcutOverride )
2111 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2112 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2113 const QList<QAction *> constActions { mCommonAngleActions.values() };
2114 QAction *nextAngleAction;
2115 if ( e->modifiers() == Qt::ShiftModifier )
2117 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2121 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2123 nextAngleAction->trigger();
2133 return e->isAccepted();
2140 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2142 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2143 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2145 mLabelX->setText( tr(
"Long" ) );
2146 mLabelY->setText( tr(
"Lat" ) );
2148 mXConstraint->setPrecision( 8 );
2149 mYConstraint->setPrecision( 8 );
2153 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2154 mAngleLineEdit->setToolTip( QString() );
2156 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2158 mLabelX->setText( tr(
"x" ) );
2159 mLabelY->setText( tr(
"y" ) );
2161 mXConstraint->setPrecision( 6 );
2162 mYConstraint->setPrecision( 6 );
2167 mEnableAction->setEnabled(
true );
2168 mErrorLabel->hide();
2171 mCurrentMapToolSupportsCad =
true;
2173 if ( mSessionActive && !isVisible() )
2178 setCadEnabled( mSessionActive );
2180 if ( !mConstructionGuidesLayer )
2182 resetConstructionGuides();
2185 if ( mDeferredUpdateConstructionGuidesCrs )
2187 updateConstructionGuidesCrs();
2197 mEnableAction->setEnabled(
false );
2198 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2199 mErrorLabel->show();
2202 mCurrentMapToolSupportsCad =
false;
2204 mSnapIndicator->setVisible(
false );
2206 setCadEnabled(
false );
2211 mCadPaintItem->update();
2216 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2221 mLockedSnapVertices.clear();
2226 QgsPoint pt = pointXYToPoint( point );
2229 mCadPointList << pt;
2233 mCadPointList.insert( 0, pt );
2240 mConstructionGuideLine.addVertex( pt );
2242 if ( mConstructionGuideLine.numPoints() == 2 )
2245 QgsGeometry geom( mConstructionGuideLine.clone() );
2247 ( void ) mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2248 mConstructionGuideId = feature.
id();
2250 else if ( mConstructionGuideLine.numPoints() > 2 )
2252 QgsGeometry geom( mConstructionGuideLine.clone() );
2253 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2258 if ( !mConstructionGuideLine.isEmpty() )
2260 mConstructionGuideLine.addVertex( pt );
2262 QgsGeometry geom( mConstructionGuideLine.clone() );
2263 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2264 mConstructionGuideLine.clear();
2279 mCadPointList.removeAt( i );
2286 mCadPointList.clear();
2287 mSnappedSegment.clear();
2297 mCadPointList << point;
2302 mCadPointList[0] = point;
2309 if ( mode == mLockMode )
2314 mLockerButton->setChecked( mode ==
HardLock );
2315 if ( mRepeatingLockButton )
2319 mRepeatingLockButton->setEnabled(
true );
2323 mRepeatingLockButton->setChecked(
false );
2324 mRepeatingLockButton->setEnabled(
false );
2336 mRepeatingLock = repeating;
2337 if ( mRepeatingLockButton )
2338 mRepeatingLockButton->setChecked( repeating );
2344 if ( mRelativeButton )
2346 mRelativeButton->setChecked(
relative );
2353 if ( updateWidget && mLineEdit->isEnabled() )
2359 switch ( mCadConstraintType )
2363 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2368 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2370 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2374 return QLocale().toString( mValue,
'f', mPrecision );
2391 return QLocale().toString( mValue,
'f', mPrecision );
2407 if ( mLineEdit->isEnabled() )
2413 return mCadConstraintType;
2418 mCadConstraintType = constraintType;
2423 mMapCanvas = mapCanvas;
2428 QString
value { text.trimmed() };
2429 switch ( constraintType )
2435 if (
value.endsWith( distanceUnit ) )
2437 value.chop( distanceUnit.length() );
2444 const QString angleUnit { tr(
"°" ) };
2445 if (
value.endsWith( angleUnit ) )
2447 value.chop( angleUnit.length() );
2454 return value.trimmed();
2462 return mCadPointList.value( 0 );
2471 QgsPoint res = mCadPointList.value( 0 );
2472 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2473 res.
setX( layerCoordinates.
x() );
2474 res.
setY( layerCoordinates.
y() );
2485 return mCadPointList.value( 1 );
2495 return mCadPointList.value( 2 );
2500QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2507 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2512 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2517 mWeightValue = value;
2518 const bool wasEnabled = mWeightEnabled;
2519 mWeightEnabled = enabled;
2522 if ( wasEnabled != enabled )
2530 return mWeightValue;
2535 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2540 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2545 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2548void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2550 if ( !mConstructionGuidesLayer )
2557 mDeferredUpdateConstructionGuidesCrs =
true;
2560 QgsCoordinateTransform transform = QgsCoordinateTransform( mConstructionGuidesLayer->crs(), mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->
transformContext() );
2561 mConstructionGuidesLayer->setCrs( mMapCanvas->mapSettings().destinationCrs() );
2562 QgsFeatureIterator it = mConstructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes() );
2566 QgsGeometry geom = feature.
geometry();
2568 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2571 mDeferredUpdateConstructionGuidesCrs =
false;
2574void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2576 if ( mConstructionGuidesLayer )
2578 mConstructionGuidesLayer.reset();
2581 const QgsVectorLayer::LayerOptions options(
QgsProject::instance()->transformContext(),
false,
false );
2582 mConstructionGuidesLayer = std::make_unique<QgsVectorLayer>( u
"LineString?crs=%1"_s.arg( mMapCanvas->mapSettings().destinationCrs().authid() ), u
"constructionGuides"_s, u
"memory"_s, options );
DistanceUnit
Units of distance.
CadConstraintType
Advanced digitizing constraint type.
@ Distance
Distance value.
@ YCoordinate
Y Coordinate value.
@ XCoordinate
X Coordinate value.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ AllLayers
On all vector layers.
BetweenLineConstraint
Between line constraints which can be enabled.
@ NoConstraint
No additional constraint.
@ Perpendicular
Perpendicular.
WkbType
The WKB type describes the number of dimensions a geometry has.
Draws the graphical elements of the CAD tools (.
A widget that floats next to the mouse pointer, and allows interaction with the AdvancedDigitizing fe...
@ Weight
Weight for NURBSCurve.
Structure with details of one constraint.
bool locked
Whether the constraint is active, i.e. should be considered.
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees).
bool relative
Whether the value is relative to previous value.
Defines constraints for the QgsCadUtils::alignMapPoint() method.
QgsCadUtils::AlignMapPointConstraint xyVertexConstraint
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
void setCadPoints(const QList< QgsPoint > &points)
Sets the list of recent CAD points (in map coordinates).
void setLockedSnapVertices(const QQueue< QgsPointLocator::Match > &lockedSnapVertices)
Sets the queue of locked vertices.
QgsCadUtils::AlignMapPointConstraint mConstraint
Constraint for M coordinate.
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
bool snappingToFeaturesOverridesCommonAngle
Flag to set snapping to features priority over common angle.
QgsCadUtils::AlignMapPointConstraint zConstraint
Constraint for Z coordinate.
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
QgsCadUtils::AlignMapPointConstraint lineExtensionConstraint
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Structure returned from alignMapPoint() method.
Qgis::LineExtensionSide softLockLineExtension
QgsPointXY finalMapPoint
map point aligned according to the constraints
bool valid
Whether the combination of constraints is actually valid.
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1).
static QgsCadUtils::AlignMapPointOutput alignMapPoint(const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx)
Applies X/Y/angle/distance constraints from the given context to a map point.
static QString formatDistance(double distance, int decimals, Qgis::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
An event filter for watching for focus events on a parent object.
void focusIn()
Emitted when parent object gains focus.
void focusOut()
Emitted when parent object loses focus.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGui * instance()
Returns a pointer to the singleton instance.
static QgsAdvancedDigitizingToolsRegistry * advancedDigitizingToolsRegistry()
Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools...
Map canvas is a class for displaying all GIS data types on a canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
Base class for all map layer types.
A mouse event which is the result of a user interaction with a QgsMapCanvas.
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
void setMapPoint(const QgsPointXY &point)
Set the (snapped) point this event points to in map coordinates.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
Represents a mesh layer supporting display of data on structured or unstructured meshes.
bool isEditable() const override
Returns true if the layer can be edited.
Point geometry type, with support for z-dimension and m-values.
void setY(double y)
Sets the point's y-coordinate.
void setX(double x)
Sets the point's x-coordinate.
void setM(double m)
Sets the point's m-value.
void setZ(double z)
Sets the point's z-coordinate.
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
const QgsBearingNumericFormat * bearingFormat() const
Returns the project bearing's format, which controls how bearings associated with the project are dis...
Qgis::DistanceUnit distanceUnits
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
QgsProjectDisplaySettings * displaySettings
QgsCoordinateTransformContext transformContext
A boolean settings entry.
static QgsSettingsTreeNode * sTreeDigitizing
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Shows a snapping marker on map canvas for the current snapping match.
void setTypeFlag(Qgis::SnappingTypes type)
define the type of snapping
void setMode(Qgis::SnappingMode mode)
define the mode of snapping
void addExtraSnapLayer(QgsVectorLayer *vl)
Supply an extra snapping layer (typically a memory layer).
void removeExtraSnapLayer(QgsVectorLayer *vl)
Removes an extra snapping layer.
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QString toAbbreviatedString(Qgis::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
QSet< QgsFeatureId > QgsFeatureIds
QLineF segment(int index, QRectF rect, double radius)
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.