18 #include <QCoreApplication>
43 #include <QActionGroup>
48 , mMapCanvas( canvas )
50 , mCommonAngleConstraint(
QgsSettings().value( QStringLiteral(
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
56 mAngleConstraint.reset(
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
57 mDistanceConstraint.reset(
new CadConstraint( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton ) );
58 mXConstraint.reset(
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
59 mYConstraint.reset(
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
60 mZConstraint.reset(
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
61 mMConstraint.reset(
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
63 mLineExtensionConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
64 mXyVertexConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
68 mMapCanvas->installEventFilter(
this );
69 mAngleLineEdit->installEventFilter(
this );
70 mDistanceLineEdit->installEventFilter(
this );
71 mXLineEdit->installEventFilter(
this );
72 mYLineEdit->installEventFilter(
this );
73 mZLineEdit->installEventFilter(
this );
74 mMLineEdit->installEventFilter(
this );
77 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
78 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
79 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
80 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
81 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
82 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
83 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
84 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
85 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
86 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
87 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
88 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
89 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
90 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
91 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
92 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
93 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
94 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
95 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
96 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
97 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
98 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
99 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
100 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
101 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
102 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
103 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
104 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
105 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
106 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
107 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
108 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
109 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
125 QMenu *menu =
new QMenu(
this );
127 QActionGroup *angleButtonGroup =
new QActionGroup( menu );
128 mCommonAngleActions = QMap<QAction *, double>();
129 QList< QPair< double, QString > > commonAngles;
131 const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
132 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
135 menuText = tr(
"Do Not Snap to Common Angles" );
137 menuText = QString( tr(
"%1, %2, %3, %4°…" ) ).arg( *it, 0,
'f', 1 ).arg( *it * 2, 0,
'f', 1 ).arg( *it * 3, 0,
'f', 1 ).arg( *it * 4, 0,
'f', 1 );
138 commonAngles << QPair<double, QString>( *it, menuText );
140 for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
142 QAction *action =
new QAction( it->second, menu );
143 action->setCheckable(
true );
144 action->setChecked( it->first == mCommonAngleConstraint );
145 menu->addAction( action );
146 angleButtonGroup->addAction( action );
147 mCommonAngleActions.insert( action, it->first );
150 qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
151 mSettingsAction->setMenu( menu );
152 mSettingsAction->setCheckable(
true );
153 mSettingsAction->setToolTip( tr(
"Snap to common angles" ) );
154 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
155 connect( menu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
158 QMenu *constructionMenu =
new QMenu(
this );
160 mLineExtensionAction =
new QAction(
"Line Extension", constructionMenu );
161 mLineExtensionAction->setCheckable(
true );
162 constructionMenu->addAction( mLineExtensionAction );
163 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
165 mXyVertexAction =
new QAction(
"X/Y Point", constructionMenu );
166 mXyVertexAction->setCheckable(
true );
167 constructionMenu->addAction( mXyVertexAction );
168 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
170 auto constructionToolBar = qobject_cast< QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
171 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
172 constructionToolBar->setMenu( constructionMenu );
173 constructionToolBar->setObjectName( QStringLiteral(
"ConstructionButton" ) );
175 mConstructionAction->setMenu( menu );
176 mConstructionAction->setCheckable(
true );
177 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
181 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
182 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
183 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
184 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
186 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
187 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
188 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
189 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
191 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
192 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
193 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
194 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
196 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
197 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
198 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
199 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
201 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
202 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
203 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
204 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
206 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
207 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
208 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
209 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
222 mToggleFloaterAction->setChecked( mFloater->
active() );
224 updateCapacity(
true );
232 mXLineEdit->setText( value );
233 if ( mode == WidgetSetMode::ReturnPressed )
235 emit mXLineEdit->returnPressed();
237 else if ( mode == WidgetSetMode::FocusOut )
239 QEvent *e =
new QEvent( QEvent::FocusOut );
240 QCoreApplication::postEvent( mXLineEdit, e );
242 else if ( mode == WidgetSetMode::TextEdited )
244 emit mXLineEdit->textEdited( value );
249 mYLineEdit->setText( value );
250 if ( mode == WidgetSetMode::ReturnPressed )
252 emit mYLineEdit->returnPressed();
254 else if ( mode == WidgetSetMode::FocusOut )
256 QEvent *e =
new QEvent( QEvent::FocusOut );
257 QCoreApplication::postEvent( mYLineEdit, e );
259 else if ( mode == WidgetSetMode::TextEdited )
261 emit mYLineEdit->textEdited( value );
266 mZLineEdit->setText( value );
267 if ( mode == WidgetSetMode::ReturnPressed )
269 emit mZLineEdit->returnPressed();
271 else if ( mode == WidgetSetMode::FocusOut )
273 QEvent *e =
new QEvent( QEvent::FocusOut );
274 QCoreApplication::postEvent( mZLineEdit, e );
276 else if ( mode == WidgetSetMode::TextEdited )
278 emit mZLineEdit->textEdited( value );
283 mMLineEdit->setText( value );
284 if ( mode == WidgetSetMode::ReturnPressed )
286 emit mMLineEdit->returnPressed();
288 else if ( mode == WidgetSetMode::FocusOut )
290 QEvent *e =
new QEvent( QEvent::FocusOut );
291 QCoreApplication::postEvent( mMLineEdit, e );
293 else if ( mode == WidgetSetMode::TextEdited )
295 emit mMLineEdit->textEdited( value );
300 mAngleLineEdit->setText( value );
301 if ( mode == WidgetSetMode::ReturnPressed )
303 emit mAngleLineEdit->returnPressed();
305 else if ( mode == WidgetSetMode::FocusOut )
307 emit mAngleLineEdit->textEdited( value );
312 mDistanceLineEdit->setText( value );
313 if ( mode == WidgetSetMode::ReturnPressed )
315 emit mDistanceLineEdit->returnPressed();
317 else if ( mode == WidgetSetMode::FocusOut )
319 QEvent *e =
new QEvent( QEvent::FocusOut );
320 QCoreApplication::postEvent( mDistanceLineEdit, e );
322 else if ( mode == WidgetSetMode::TextEdited )
324 emit mDistanceLineEdit->textEdited( value );
329 void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
331 mCadEnabled = enabled;
332 mEnableAction->setChecked( enabled );
333 mConstructionModeAction->setEnabled( enabled );
334 mSettingsAction->setEnabled( enabled );
335 mInputWidgets->setEnabled( enabled );
336 mToggleFloaterAction->setEnabled( enabled );
337 mConstructionAction->setEnabled( enabled );
342 mLineExtensionAction->setChecked(
false );
343 mXyVertexAction->setChecked(
false );
345 mParallelAction->setEnabled(
false );
346 mPerpendicularAction->setEnabled(
false );
352 setConstructionMode(
false );
363 bool enableZ =
false;
364 bool enableM =
false;
368 switch ( layer->type() )
381 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
402 mRelativeZButton->setEnabled(
enable );
403 mZLabel->setEnabled(
enable );
404 mZLineEdit->setEnabled(
enable );
405 if ( mZLineEdit->isEnabled() )
409 mLockZButton->setEnabled(
enable );
415 mRelativeMButton->setEnabled(
enable );
416 mMLabel->setEnabled(
enable );
417 mMLineEdit->setEnabled(
enable );
418 if ( mMLineEdit->isEnabled() )
422 mLockMButton->setEnabled(
enable );
426 void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
428 enabled &= mCurrentMapToolSupportsCad;
430 mSessionActive = enabled;
432 if ( enabled && !isVisible() )
437 setCadEnabled( enabled );
440 void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
446 else if ( sender() == mParallelAction )
450 else if ( sender() == mPerpendicularAction )
456 void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
458 if ( sender() == mRelativeAngleButton )
460 mAngleConstraint->setRelative( activate );
463 else if ( sender() == mRelativeXButton )
465 mXConstraint->setRelative( activate );
468 else if ( sender() == mRelativeYButton )
470 mYConstraint->setRelative( activate );
473 else if ( sender() == mRelativeZButton )
475 mZConstraint->setRelative( activate );
478 else if ( sender() == mRelativeMButton )
480 mMConstraint->setRelative( activate );
485 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
487 if ( sender() == mRepeatingLockDistanceButton )
489 mDistanceConstraint->setRepeatingLock( activate );
491 else if ( sender() == mRepeatingLockAngleButton )
493 mAngleConstraint->setRepeatingLock( activate );
495 else if ( sender() == mRepeatingLockXButton )
497 mXConstraint->setRepeatingLock( activate );
499 else if ( sender() == mRepeatingLockYButton )
501 mYConstraint->setRepeatingLock( activate );
503 else if ( sender() == mRepeatingLockZButton )
505 mZConstraint->setRepeatingLock( activate );
507 else if ( sender() == mRepeatingLockMButton )
509 mMConstraint->setRepeatingLock( activate );
513 void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
515 mConstructionMode = enabled;
516 mConstructionModeAction->setChecked( enabled );
519 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
522 const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
523 if ( ica != mCommonAngleActions.constEnd() )
525 ica.key()->setChecked(
true );
526 mCommonAngleConstraint = ica.value();
528 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
533 QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
537 return advancedTool->layer();
551 if ( releaseRepeatingLocks )
553 mXyVertexAction->setChecked(
false );
557 mLineExtensionAction->setChecked(
false );
564 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
569 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
574 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
579 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
584 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
589 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
595 if ( !mCadPointList.empty() )
597 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
599 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
601 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
603 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
605 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
607 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
609 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
611 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
618 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
621 QPoint globalPos = mMapCanvas->cursor().pos();
622 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
623 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
624 mCurrentMapTool->canvasMoveEvent( e );
630 CadConstraint *constraint =
nullptr;
631 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
633 constraint = mAngleConstraint.get();
635 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
637 constraint = mDistanceConstraint.get();
639 else if ( obj == mXLineEdit || obj == mLockXButton )
641 constraint = mXConstraint.get();
643 else if ( obj == mYLineEdit || obj == mLockYButton )
645 constraint = mYConstraint.get();
647 else if ( obj == mZLineEdit || obj == mLockZButton )
649 constraint = mZConstraint.get();
651 else if ( obj == mMLineEdit || obj == mLockMButton )
653 constraint = mMConstraint.get();
655 else if ( obj == mLineExtensionAction )
657 constraint = mLineExtensionConstraint.get();
659 else if ( obj == mXyVertexAction )
661 constraint = mXyVertexConstraint.get();
666 double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
bool &ok )
const
678 const QVariant result = expr.evaluate();
679 if ( expr.hasEvalError() )
682 QString inputValueC { inputValue };
685 if ( inputValue.contains( QLocale().groupSeparator() ) )
687 inputValueC.remove( QLocale().groupSeparator() );
689 const QVariant resultC = exprC.evaluate();
690 if ( ! exprC.hasEvalError() )
692 value = resultC.toDouble( &ok );
697 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
699 QgsExpression exprC( inputValueC .replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
700 const QVariant resultC = exprC.evaluate();
701 if ( ! exprC.hasEvalError() )
703 value = resultC.toDouble( &ok );
709 value = result.toDouble( &ok );
715 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
717 if ( !constraint || textValue.isEmpty() )
726 const double value = parseUserInput( textValue, ok );
730 constraint->setValue( value, convertExpression );
735 void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
737 CadConstraint *constraint = objectToConstraint( sender() );
745 const QString textValue = constraint->lineEdit()->text();
746 if ( !textValue.isEmpty() )
749 const double value = parseUserInput( textValue, ok );
752 constraint->setValue( value );
766 if ( constraint == mXConstraint.get() )
770 else if ( constraint == mYConstraint.get() )
774 else if ( constraint == mZConstraint.get() )
778 else if ( constraint == mMConstraint.get() )
782 else if ( constraint == mDistanceConstraint.get() )
786 else if ( constraint == mAngleConstraint.get() )
794 if ( constraint == mAngleConstraint.get() )
804 void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
806 CadConstraint *constraint = objectToConstraint( sender() );
812 updateConstraintValue( constraint, textValue,
false );
815 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
817 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
821 CadConstraint *constraint = objectToConstraint( lineEdit );
827 updateConstraintValue( constraint, lineEdit->text(),
true );
832 mBetweenLineConstraint = constraint;
837 void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
839 CadConstraint *constraint = objectToConstraint( sender() );
847 if ( constraint == mXyVertexConstraint.get() )
851 else if ( constraint == mLineExtensionConstraint.get() )
865 void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
867 CadCapacities newCapacities = CadCapacities();
871 if ( mCadPointList.count() > 1 )
880 if ( mCadPointList.count() > 2 )
885 if ( !updateUIwithoutChange && newCapacities == mCapacities )
895 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
896 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
897 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
898 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
900 mPerpendicularAction->setEnabled( distance && snappingEnabled );
901 mParallelAction->setEnabled( distance && snappingEnabled );
903 mLineExtensionAction->setEnabled( snappingEnabled );
904 mXyVertexAction->setEnabled( snappingEnabled );
908 if ( !snappingEnabled )
910 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
911 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
912 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
913 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
915 else if ( mCadPointList.count() <= 1 )
917 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
918 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
920 else if ( isGeographic )
922 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
923 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
927 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
928 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
932 if ( !absoluteAngle )
938 mLockAngleButton->setEnabled( absoluteAngle );
939 mRelativeAngleButton->setEnabled( relativeAngle );
940 mAngleLineEdit->setEnabled( absoluteAngle );
942 if ( !absoluteAngle )
946 if ( !relativeAngle )
948 mAngleConstraint->setRelative(
false );
951 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
954 mAngleConstraint->setRelative(
true );
959 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
960 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
962 if ( !( distance && relativeCoordinates ) )
967 mRelativeXButton->setEnabled( relativeCoordinates );
968 mRelativeYButton->setEnabled( relativeCoordinates );
969 mRelativeZButton->setEnabled( relativeCoordinates );
970 mRelativeMButton->setEnabled( relativeCoordinates );
973 mCapacities = newCapacities;
983 constr.
value =
c->value();
990 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
996 const int lastIndex = mLockedSnapVertices.length() - 1;
997 for (
int i = lastIndex ; i >= 0; --i )
999 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1001 if ( snapMatch.
point() != previouslySnap.
point() )
1003 mLockedSnapVertices.removeAt( i );
1009 if ( snapMatch.
point() != previouslySnap.
point() )
1011 mLockedSnapVertices.enqueue( snapMatch );
1014 if ( mLockedSnapVertices.count() > 3 )
1016 mLockedSnapVertices.dequeue();
1025 context.
xConstraint = _constraint( mXConstraint.get() );
1026 context.
yConstraint = _constraint( mYConstraint.get() );
1027 context.
zConstraint = _constraint( mZConstraint.get() );
1028 context.
mConstraint = _constraint( mMConstraint.get() );
1044 const bool res = output.
valid;
1046 mSnappedSegment.clear();
1051 mSnappedSegment << edgePt0 << edgePt1;
1072 mSnapIndicator->setMatch( output.
snapMatch );
1073 mSnapIndicator->setVisible(
true );
1077 mSnapIndicator->setVisible(
false );
1098 if ( mSnapMatch.
layer() )
1110 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1111 mLastSnapMatch = mSnapMatch;
1121 if ( mLockZButton->isChecked() )
1123 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1125 if ( mLockMButton->isChecked() )
1127 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1131 updateCurrentPoint( point );
1133 updateUnlockedConstraintValues( point );
1141 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1148 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1150 bool previousPointExist, penulPointExist;
1155 if ( !mAngleConstraint->isLocked() && previousPointExist )
1158 if ( penulPointExist && mAngleConstraint->relative() )
1161 angle = std::atan2( previousPt.
y() - penultimatePt.
y(),
1162 previousPt.
x() - penultimatePt.
x() );
1164 angle = ( std::atan2( point.
y() - previousPt.
y(),
1165 point.
x() - previousPt.
x()
1166 ) -
angle ) * 180 / M_PI;
1169 mAngleConstraint->setValue(
angle );
1172 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1174 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1177 if ( !mXConstraint->isLocked() )
1179 if ( previousPointExist && mXConstraint->relative() )
1181 mXConstraint->setValue( point.
x() - previousPt.
x() );
1185 mXConstraint->setValue( point.
x() );
1189 if ( !mYConstraint->isLocked() )
1191 if ( previousPointExist && mYConstraint->relative() )
1193 mYConstraint->setValue( point.
y() - previousPt.
y() );
1197 mYConstraint->setValue( point.
y() );
1201 if ( !mZConstraint->isLocked() )
1203 if ( previousPointExist && mZConstraint->relative() )
1205 mZConstraint->setValue( point.
z() - previousPt.
z() );
1209 mZConstraint->setValue( point.
z() );
1213 if ( !mMConstraint->isLocked() )
1215 if ( previousPointExist && mMConstraint->relative() )
1217 mMConstraint->setValue( point.
m() - previousPt.
m() );
1221 mMConstraint->setValue( point.
m() );
1227 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1239 localConfig.
setTypeFlag( Qgis::SnappingType::Segment );
1240 snappingUtils->
setConfig( localConfig );
1242 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1244 snappingUtils->
setConfig( canvasConfig );
1254 *snapped =
segment.count() == 2;
1267 bool previousPointExist, penulPointExist, snappedSegmentExist;
1270 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1272 if ( !previousPointExist || !snappedSegmentExist )
1277 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1279 if ( mAngleConstraint->relative() && penulPointExist )
1281 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1289 angle *= 180 / M_PI;
1291 mAngleConstraint->setValue(
angle );
1292 mAngleConstraint->setLockMode( lockMode );
1310 case Qt::Key_Backspace:
1311 case Qt::Key_Delete:
1317 case Qt::Key_Escape:
1347 case Qt::Key_Backspace:
1348 case Qt::Key_Delete:
1354 case Qt::Key_Escape:
1361 filterKeyPress( e );
1370 const auto constPoints = points;
1377 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1381 return QgsDockWidget::eventFilter( obj, event );
1389 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1391 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1393 return filterKeyPress( keyEvent );
1396 return QgsDockWidget::eventFilter( obj, event );
1399 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1405 const QEvent::Type type = e->type();
1411 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1413 mXConstraint->toggleLocked();
1418 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1422 mXConstraint->toggleRelative();
1429 else if ( type == QEvent::KeyPress )
1431 mXLineEdit->setFocus();
1432 mXLineEdit->selectAll();
1441 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1443 mYConstraint->toggleLocked();
1448 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1452 mYConstraint->toggleRelative();
1459 else if ( type == QEvent::KeyPress )
1461 mYLineEdit->setFocus();
1462 mYLineEdit->selectAll();
1471 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1473 mZConstraint->toggleLocked();
1478 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1482 mZConstraint->toggleRelative();
1489 else if ( type == QEvent::KeyPress )
1491 mZLineEdit->setFocus();
1492 mZLineEdit->selectAll();
1501 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1503 mMConstraint->toggleLocked();
1508 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1512 mMConstraint->toggleRelative();
1519 else if ( type == QEvent::KeyPress )
1521 mMLineEdit->setFocus();
1522 mMLineEdit->selectAll();
1531 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1535 mAngleConstraint->toggleLocked();
1541 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1545 mAngleConstraint->toggleRelative();
1552 else if ( type == QEvent::KeyPress )
1554 mAngleLineEdit->setFocus();
1555 mAngleLineEdit->selectAll();
1564 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1568 mDistanceConstraint->toggleLocked();
1575 else if ( type == QEvent::KeyPress )
1577 mDistanceLineEdit->setFocus();
1578 mDistanceLineEdit->selectAll();
1586 if ( type == QEvent::KeyPress )
1588 setConstructionMode( !mConstructionMode );
1595 if ( type == QEvent::KeyPress )
1597 const bool parallel = mParallelAction->isChecked();
1598 const bool perpendicular = mPerpendicularAction->isChecked();
1600 if ( !parallel && !perpendicular )
1604 else if ( perpendicular )
1624 return e->isAccepted();
1633 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1634 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1636 mLabelX->setText( tr(
"Long" ) );
1637 mLabelY->setText( tr(
"Lat" ) );
1639 mXConstraint->setPrecision( 8 );
1640 mYConstraint->setPrecision( 8 );
1644 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
1645 mAngleLineEdit->setToolTip( QString() );
1647 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
1649 mLabelX->setText( tr(
"x" ) );
1650 mLabelY->setText( tr(
"y" ) );
1652 mXConstraint->setPrecision( 6 );
1653 mYConstraint->setPrecision( 6 );
1658 mEnableAction->setEnabled(
true );
1659 mErrorLabel->hide();
1662 mCurrentMapToolSupportsCad =
true;
1664 if ( mSessionActive && !isVisible() )
1668 setCadEnabled( mSessionActive );
1675 mEnableAction->setEnabled(
false );
1676 mErrorLabel->setText( tr(
"CAD tools are not enabled for the current map tool" ) );
1677 mErrorLabel->show();
1680 mCurrentMapToolSupportsCad =
false;
1682 mSnapIndicator->setVisible(
false );
1684 setCadEnabled(
false );
1689 mCadPaintItem->update();
1694 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
1699 mLockedSnapVertices.clear();
1705 QgsPoint pt = pointXYToPoint( point );
1708 mCadPointList << pt;
1712 mCadPointList.insert( 0, pt );
1725 mCadPointList.removeAt( i );
1732 mCadPointList.clear();
1733 mSnappedSegment.clear();
1739 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint(
const QgsPoint &point )
1743 mCadPointList << point;
1748 mCadPointList[0] = point;
1757 mLockerButton->setChecked( mode ==
HardLock );
1758 if ( mRepeatingLockButton )
1762 mRepeatingLockButton->setEnabled(
true );
1766 mRepeatingLockButton->setChecked(
false );
1767 mRepeatingLockButton->setEnabled(
false );
1780 mRepeatingLock = repeating;
1781 if ( mRepeatingLockButton )
1782 mRepeatingLockButton->setChecked( repeating );
1787 mRelative = relative;
1788 if ( mRelativeButton )
1790 mRelativeButton->setChecked( relative );
1797 if ( updateWidget && mLineEdit->isEnabled() )
1798 mLineEdit->setText( QLocale().toString( value,
'f', mPrecision ) );
1803 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1808 setRelative( !mRelative );
1814 if ( mLineEdit->isEnabled() )
1815 mLineEdit->setText( QLocale().toString( mValue,
'f', mPrecision ) );
1823 return mCadPointList.value( 0 );
1832 QgsPoint res = mCadPointList.value( 0 );
1834 res.
setX( layerCoordinates.
x() );
1835 res.
setY( layerCoordinates.
y() );
1846 return mCadPointList.value( 1 );
1856 return mCadPointList.value( 2 );
1861 QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
1868 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1873 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();