44#include <QActionGroup>
45#include <QCoreApplication>
50#include "moc_qgsadvanceddigitizingdockwidget.cpp"
52using namespace Qt::StringLiterals;
62 , mMapCanvas( canvas )
63 , mUserInputWidget( userInputWidget )
65 , mCommonAngleConstraint(
QgsSettings().value( u
"/Cad/CommonAngle"_s, 0.0 ).toDouble() )
71 mAngleConstraint = std::make_unique<CadConstraint>( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton );
73 mAngleConstraint->setMapCanvas( mMapCanvas );
74 mDistanceConstraint = std::make_unique<CadConstraint>( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton );
76 mDistanceConstraint->setMapCanvas( mMapCanvas );
77 mXConstraint = std::make_unique<CadConstraint>( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton );
79 mXConstraint->setMapCanvas( mMapCanvas );
80 mYConstraint = std::make_unique<CadConstraint>( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton );
82 mYConstraint->setMapCanvas( mMapCanvas );
83 mZConstraint = std::make_unique<CadConstraint>( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton );
85 mZConstraint->setMapCanvas( mMapCanvas );
86 mMConstraint = std::make_unique<CadConstraint>( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton );
88 mMConstraint->setMapCanvas( mMapCanvas );
90 mLineExtensionConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
91 mXyVertexConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
92 mXyVertexConstraint->setMapCanvas( mMapCanvas );
96 mMapCanvas->installEventFilter(
this );
97 mAngleLineEdit->installEventFilter(
this );
98 mDistanceLineEdit->installEventFilter(
this );
99 mXLineEdit->installEventFilter(
this );
100 mYLineEdit->installEventFilter(
this );
101 mZLineEdit->installEventFilter(
this );
102 mMLineEdit->installEventFilter(
this );
105 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
106 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
107 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
108 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
109 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
110 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
111 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
112 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
113 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
114 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
115 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
116 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
117 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
118 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
119 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
120 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
121 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
122 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
123 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
124 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
125 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
126 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
127 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
128 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
129 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
130 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
131 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
132 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
133 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
134 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
135 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
136 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
137 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
143 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
149 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
161 mCommonAngleActionsMenu =
new QMenu(
this );
163#ifndef __clang_analyzer__
164 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
166 QList<QPair<double, QString>> commonAngles;
167 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 } );
168 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
174 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
175 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
176 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
177 featuresAction->setCheckable(
true );
178 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
179 anglesAction->setCheckable(
true );
180 snappingPriorityActionGroup->addAction( featuresAction );
181 snappingPriorityActionGroup->addAction( anglesAction );
182 snappingPriorityMenu->addAction( anglesAction );
183 snappingPriorityMenu->addAction( featuresAction );
184 connect( anglesAction, &QAction::changed,
this, [
this, featuresAction] {
185 mSnappingPrioritizeFeatures = featuresAction->isChecked();
186 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
188 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value() );
189 anglesAction->setChecked( !featuresAction->isChecked() );
190 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
193 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
195 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
196 action->setCheckable(
true );
197 action->setChecked( it->first == mCommonAngleConstraint );
198 mCommonAngleActionsMenu->addAction( action );
200#ifndef __clang_analyzer__
201 angleButtonGroup->addAction( action );
203 mCommonAngleActions.insert( it->first, action );
207 QMenu *constructionSettingsMenu =
new QMenu(
this );
209 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
210 mRecordConstructionGuides->setCheckable(
true );
211 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->value() );
212 constructionSettingsMenu->addAction( mRecordConstructionGuides );
213 connect( mRecordConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadRecordConstructionGuides->setValue( mRecordConstructionGuides->isChecked() ); } );
215 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
216 mShowConstructionGuides->setCheckable(
true );
217 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->value() );
218 constructionSettingsMenu->addAction( mShowConstructionGuides );
219 connect( mShowConstructionGuides, &QAction::triggered,
this, [
this]() {
220 settingsCadShowConstructionGuides->setValue( mShowConstructionGuides->isChecked() );
224 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
225 mSnapToConstructionGuides->setCheckable(
true );
226 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->value() );
227 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
228 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadSnapToConstructionGuides->setValue( mSnapToConstructionGuides->isChecked() ); } );
230 constructionSettingsMenu->addSeparator();
232 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
233 constructionSettingsMenu->addAction( mClearConstructionGuides );
234 connect( mClearConstructionGuides, &QAction::triggered,
this, [
this]() {
235 resetConstructionGuides();
239 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
240 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
241 constructionModeToolButton->setMenu( constructionSettingsMenu );
242 constructionModeToolButton->setObjectName( u
"ConstructionModeButton"_s );
245 QMenu *toolsMenu =
new QMenu(
this );
246 connect( toolsMenu, &QMenu::aboutToShow,
this, [
this, toolsMenu]() {
249 for (
const QString &name : toolMetadataNames )
252 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
253 connect( toolAction, &QAction::triggered,
this, [
this, toolMetadata]() {
256 toolsMenu->addAction( toolAction );
259 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
260 mToolsAction->setMenu( toolsMenu );
262 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
263 mSettingsAction->setMenu( mCommonAngleActionsMenu );
264 mSettingsAction->setCheckable(
true );
265 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
266 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
267 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
270 QMenu *constructionMenu =
new QMenu(
this );
272 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
273 mLineExtensionAction->setCheckable(
true );
274 constructionMenu->addAction( mLineExtensionAction );
275 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
277 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
278 mXyVertexAction->setCheckable(
true );
279 constructionMenu->addAction( mXyVertexAction );
280 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
282 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
283 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
284 mConstructionAction->setMenu( constructionMenu );
285 constructionToolBar->setObjectName( u
"ConstructionButton"_s );
286 mConstructionAction->setCheckable(
true );
287 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
290 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
291 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
292 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
293 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
295 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
296 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
297 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
298 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
300 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
301 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
302 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
303 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
305 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
306 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
307 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
308 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
310 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
311 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
312 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
313 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
315 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
316 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
317 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
318 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
329 mFloaterActionsMenu =
new QMenu(
this );
330 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
331 mFloaterAction->setMenu( mFloaterActionsMenu );
332 mFloaterAction->setCheckable(
true );
334 mFloaterAction->setChecked( mFloater->active() );
338 QAction *action =
new QAction( tr(
"Show Floater" ), mFloaterActionsMenu );
339 action->setCheckable(
true );
340 action->setChecked( mFloater->active() );
341 mFloaterActionsMenu->addAction( action );
342 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
343 mFloater->setActive( checked );
344 mFloaterAction->setChecked( checked );
348 mFloaterActionsMenu->addSeparator();
351 QAction *action =
new QAction( tr(
"Show Distance" ), mFloaterActionsMenu );
352 action->setCheckable(
true );
353 mFloaterActionsMenu->addAction( action );
354 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
357 const bool isDistanceChecked =
QgsSettings().
value( u
"/Cad/DistanceShowInFloater"_s,
true ).toBool();
358 action->setChecked( isDistanceChecked );
364 QAction *action =
new QAction( tr(
"Show Angle" ), mFloaterActionsMenu );
365 action->setCheckable(
true );
366 mFloaterActionsMenu->addAction( action );
367 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
370 const bool isAngleChecked =
QgsSettings().
value( u
"/Cad/AngleShowInFloater"_s,
true ).toBool();
371 action->setChecked( isAngleChecked );
377 QAction *action =
new QAction( tr(
"Show XY Coordinates" ), mFloaterActionsMenu );
378 action->setCheckable(
true );
379 mFloaterActionsMenu->addAction( action );
380 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
385 const bool isXCoordinateChecked =
QgsSettings().
value( u
"/Cad/XCoordinateShowInFloater"_s,
true ).toBool();
386 action->setChecked( isXCoordinateChecked );
394 QAction *action =
new QAction( tr(
"Show Z Value" ), mFloaterActionsMenu );
395 action->setCheckable(
true );
396 mFloaterActionsMenu->addAction( action );
397 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
400 const bool isZCoordinateChecked =
QgsSettings().
value( u
"/Cad/ZCoordinateShowInFloater"_s,
true ).toBool();
401 action->setChecked( isZCoordinateChecked );
408 QAction *action =
new QAction( tr(
"Show M Value" ), mFloaterActionsMenu );
409 action->setCheckable(
true );
410 mFloaterActionsMenu->addAction( action );
411 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
414 const bool isMCoordinateChecked =
QgsSettings().
value( u
"/Cad/MCoordinateShowInFloater"_s,
true ).toBool();
415 action->setChecked( isMCoordinateChecked );
422 QAction *action =
new QAction( tr(
"Show Bearing/Azimuth" ), mFloaterActionsMenu );
423 action->setCheckable(
true );
424 mFloaterActionsMenu->addAction( action );
425 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
428 const bool isBearingChecked =
QgsSettings().
value( u
"/Cad/BearingShowInFloater"_s,
false ).toBool();
429 action->setChecked( isBearingChecked );
436 QAction *action =
new QAction( tr(
"Show Common Snapping Angle" ), mFloaterActionsMenu );
437 action->setCheckable(
true );
438 mFloaterActionsMenu->addAction( action );
439 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
442 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( u
"/Cad/CommonAngleSnappingShowInFloater"_s,
false ).toBool();
443 action->setChecked( isCommonAngleSnappingChecked );
450 QMenu *menu =
new QMenu( tr(
"Show Area" ), mFloaterActionsMenu );
451 mFloaterActionsMenu->addMenu( menu );
453 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
455 actionHidden->setCheckable(
true );
456 menu->addAction( actionHidden );
458 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Area" ), mFloaterActionsMenu );
460 actionEllipsoidal->setCheckable(
true );
461 menu->addAction( actionEllipsoidal );
463 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Area" ), mFloaterActionsMenu );
465 actionCartesian->setCheckable(
true );
466 menu->addAction( actionCartesian );
468 QActionGroup *group =
new QActionGroup( menu );
469 group->addAction( actionHidden );
470 group->addAction( actionEllipsoidal );
471 group->addAction( actionCartesian );
474 for ( QAction *action : group->actions() )
478 action->setChecked(
true );
480 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
491 QMenu *menu =
new QMenu( tr(
"Show Total Length/Perimeter" ), mFloaterActionsMenu );
492 mFloaterActionsMenu->addMenu( menu );
494 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
496 actionHidden->setCheckable(
true );
497 menu->addAction( actionHidden );
499 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Lengths" ), mFloaterActionsMenu );
501 actionEllipsoidal->setCheckable(
true );
502 menu->addAction( actionEllipsoidal );
504 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Lengths" ), mFloaterActionsMenu );
506 actionCartesian->setCheckable(
true );
507 menu->addAction( actionCartesian );
509 QActionGroup *group =
new QActionGroup( menu );
510 group->addAction( actionHidden );
511 group->addAction( actionEllipsoidal );
512 group->addAction( actionCartesian );
515 for ( QAction *action : group->actions() )
519 action->setChecked(
true );
521 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
532 QAction *action =
new QAction( tr(
"Show Weight" ), mFloaterActionsMenu );
533 action->setCheckable(
true );
534 mFloaterActionsMenu->addAction( action );
535 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
538 const bool isWeightChecked =
QgsSettings().
value( u
"/Cad/WeightShowInFloater"_s,
true ).toBool();
539 action->setChecked( isWeightChecked );
544 updateCapacity(
true );
548 mConstructionGuidesLayer.reset();
559 mCurrentTool->deleteLater();
566 return tr(
"Do Not Snap to Common Angles" );
568 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 );
587 QString totalLengthString;
597 mXLineEdit->setText( value );
600 emit mXLineEdit->returnPressed();
604 QEvent *e =
new QEvent( QEvent::FocusOut );
605 QCoreApplication::postEvent( mXLineEdit, e );
609 emit mXLineEdit->textEdited( value );
614 mYLineEdit->setText( value );
617 emit mYLineEdit->returnPressed();
621 QEvent *e =
new QEvent( QEvent::FocusOut );
622 QCoreApplication::postEvent( mYLineEdit, e );
626 emit mYLineEdit->textEdited( value );
631 mZLineEdit->setText( value );
634 emit mZLineEdit->returnPressed();
638 QEvent *e =
new QEvent( QEvent::FocusOut );
639 QCoreApplication::postEvent( mZLineEdit, e );
643 emit mZLineEdit->textEdited( value );
648 mMLineEdit->setText( value );
651 emit mMLineEdit->returnPressed();
655 QEvent *e =
new QEvent( QEvent::FocusOut );
656 QCoreApplication::postEvent( mMLineEdit, e );
660 emit mMLineEdit->textEdited( value );
665 mAngleLineEdit->setText( value );
668 emit mAngleLineEdit->returnPressed();
672 emit mAngleLineEdit->textEdited( value );
677 mDistanceLineEdit->setText( value );
680 emit mDistanceLineEdit->returnPressed();
684 QEvent *e =
new QEvent( QEvent::FocusOut );
685 QCoreApplication::postEvent( mDistanceLineEdit, e );
689 emit mDistanceLineEdit->textEdited( value );
694void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
696 mCadEnabled = enabled;
697 mEnableAction->setChecked( enabled );
698 mConstructionModeAction->setEnabled( enabled );
699 mSettingsAction->setEnabled( enabled );
700 mInputWidgets->setEnabled( enabled );
701 mFloaterAction->setEnabled( enabled );
702 mConstructionAction->setEnabled( enabled );
703 mToolsAction->setEnabled( enabled );
708 mLineExtensionAction->setChecked(
false );
709 mXyVertexAction->setChecked(
false );
711 mParallelAction->setEnabled(
false );
712 mPerpendicularAction->setEnabled(
false );
715 mCurrentTool->deleteLater();
722 setConstructionMode(
false );
732 mLastSnapMatch = QgsPointLocator::Match();
738 bool enableZ =
false;
739 bool enableM =
false;
743 switch ( layer->type() )
756 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
772 mTargetLayerSupportsM = enableM;
773 mTargetLayerSupportsZ = enableZ;
781 if (
enable && !mTargetLayerSupportsZ )
785 mRelativeZButton->setEnabled(
enable );
786 mZLabel->setEnabled(
enable );
787 mZLineEdit->setEnabled(
enable );
788 if ( mZLineEdit->isEnabled() )
792 mLockZButton->setEnabled(
enable );
798 if (
enable && !mTargetLayerSupportsM )
802 mRelativeMButton->setEnabled(
enable );
803 mMLabel->setEnabled(
enable );
804 mMLineEdit->setEnabled(
enable );
805 if ( mMLineEdit->isEnabled() )
809 mLockMButton->setEnabled(
enable );
813void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
815 enabled &= mCurrentMapToolSupportsCad;
817 mSessionActive = enabled;
819 if ( enabled && !isVisible() )
824 setCadEnabled( enabled );
831 mCurrentTool->deleteLater();
832 mCurrentTool =
nullptr;
839 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
841 toolWidget->setParent( mUserInputWidget );
842 mUserInputWidget->addUserInputWidget( toolWidget );
850 return mCurrentTool.data();
853void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
859 else if ( sender() == mParallelAction )
863 else if ( sender() == mPerpendicularAction )
869void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
871 if ( sender() == mRelativeAngleButton )
873 mAngleConstraint->setRelative( activate );
876 else if ( sender() == mRelativeXButton )
878 mXConstraint->setRelative( activate );
881 else if ( sender() == mRelativeYButton )
883 mYConstraint->setRelative( activate );
886 else if ( sender() == mRelativeZButton )
888 mZConstraint->setRelative( activate );
891 else if ( sender() == mRelativeMButton )
893 mMConstraint->setRelative( activate );
898void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
900 if ( sender() == mRepeatingLockDistanceButton )
902 mDistanceConstraint->setRepeatingLock( activate );
904 else if ( sender() == mRepeatingLockAngleButton )
906 mAngleConstraint->setRepeatingLock( activate );
908 else if ( sender() == mRepeatingLockXButton )
910 mXConstraint->setRepeatingLock( activate );
912 else if ( sender() == mRepeatingLockYButton )
914 mYConstraint->setRepeatingLock( activate );
916 else if ( sender() == mRepeatingLockZButton )
918 mZConstraint->setRepeatingLock( activate );
920 else if ( sender() == mRepeatingLockMButton )
922 mMConstraint->setRepeatingLock( activate );
926void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
928 mConstructionMode = enabled;
929 mConstructionModeAction->setChecked( enabled );
933 if ( enabled && mCadPointList.size() > 1 )
935 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
940void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
943 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
945 if ( it.value() == action )
947 it.value()->setChecked(
true );
948 mCommonAngleConstraint = it.key();
949 QgsSettings().setValue( u
"/Cad/CommonAngle"_s, it.key() );
950 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
957QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
959 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast<QgsMapToolAdvancedDigitizing *>( mMapCanvas->mapTool() ) )
961 return advancedTool->layer();
965 return mMapCanvas->currentLayer();
975 if ( releaseRepeatingLocks )
977 mXyVertexAction->setChecked(
false );
981 mLineExtensionAction->setChecked(
false );
988 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
993 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
998 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
1003 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
1008 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
1013 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
1019 if ( !mCadPointList.empty() )
1021 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
1023 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
1025 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
1027 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
1029 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
1031 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
1033 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
1035 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
1041void QgsAdvancedDigitizingDockWidget::emit pointChanged()
1044 QPoint globalPos = mMapCanvas->cursor().pos();
1045 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
1046 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
1047 mCurrentMapTool->canvasMoveEvent( e );
1054 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
1056 constraint = mAngleConstraint.get();
1058 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
1060 constraint = mDistanceConstraint.get();
1062 else if ( obj == mXLineEdit || obj == mLockXButton )
1064 constraint = mXConstraint.get();
1066 else if ( obj == mYLineEdit || obj == mLockYButton )
1068 constraint = mYConstraint.get();
1070 else if ( obj == mZLineEdit || obj == mLockZButton )
1072 constraint = mZConstraint.get();
1074 else if ( obj == mMLineEdit || obj == mLockMButton )
1076 constraint = mMConstraint.get();
1078 else if ( obj == mLineExtensionAction )
1080 constraint = mLineExtensionConstraint.get();
1082 else if ( obj == mXyVertexAction )
1084 constraint = mXyVertexConstraint.get();
1089double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
1099 QgsExpression expr( inputValue );
1100 const QVariant result = expr.evaluate();
1101 if ( expr.hasEvalError() )
1104 QString inputValueC { inputValue };
1107 if ( inputValue.contains( QLocale().groupSeparator() ) )
1109 inputValueC.remove( QLocale().groupSeparator() );
1110 QgsExpression exprC( inputValueC );
1111 const QVariant resultC = exprC.evaluate();
1112 if ( !exprC.hasEvalError() )
1114 value = resultC.toDouble( &ok );
1119 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
1121 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
1122 const QVariant resultC = exprC.evaluate();
1123 if ( !exprC.hasEvalError() )
1125 value = resultC.toDouble( &ok );
1131 value = result.toDouble( &ok );
1146void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1148 if ( !constraint || textValue.isEmpty() )
1157 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1161 constraint->setValue( value, convertExpression );
1166void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1176 const QString textValue = constraint->lineEdit()->text();
1177 if ( !textValue.isEmpty() )
1180 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1183 constraint->setValue( value );
1197 if ( constraint == mXConstraint.get() )
1201 else if ( constraint == mYConstraint.get() )
1205 else if ( constraint == mZConstraint.get() )
1209 else if ( constraint == mMConstraint.get() )
1213 else if ( constraint == mDistanceConstraint.get() )
1217 else if ( constraint == mAngleConstraint.get() )
1225 if ( constraint == mAngleConstraint.get() )
1235void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1243 updateConstraintValue( constraint, textValue,
false );
1246void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1248 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1258 updateConstraintValue( constraint, lineEdit->text(),
true );
1263 mBetweenLineConstraint = constraint;
1268void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1278 if ( constraint == mXyVertexConstraint.get() )
1282 else if ( constraint == mLineExtensionConstraint.get() )
1296void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1299 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1302 if ( mCadPointList.count() > 1 )
1305 if ( !isGeographic )
1311 if ( mCadPointList.count() > 2 )
1313 if ( !isGeographic )
1316 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1326 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1327 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1328 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1329 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1331 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1332 mParallelAction->setEnabled( distance && snappingEnabled );
1334 mLineExtensionAction->setEnabled( snappingEnabled );
1335 mXyVertexAction->setEnabled( snappingEnabled );
1339 if ( !snappingEnabled )
1341 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1342 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1343 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1344 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1346 else if ( mCadPointList.count() <= 1 )
1348 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1349 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1351 else if ( isGeographic )
1353 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1354 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1358 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1359 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1363 if ( !absoluteAngle )
1369 mLockAngleButton->setEnabled( absoluteAngle );
1370 mRelativeAngleButton->setEnabled( relativeAngle );
1371 mAngleLineEdit->setEnabled( absoluteAngle );
1373 if ( !absoluteAngle )
1377 if ( !relativeAngle )
1379 mAngleConstraint->setRelative(
false );
1382 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1385 mAngleConstraint->setRelative(
true );
1390 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1391 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1393 if ( !( distance && relativeCoordinates ) )
1398 mRelativeXButton->setEnabled( relativeCoordinates );
1399 mRelativeYButton->setEnabled( relativeCoordinates );
1400 mRelativeZButton->setEnabled( relativeCoordinates );
1401 mRelativeMButton->setEnabled( relativeCoordinates );
1404 mCapacities = newCapacities;
1405 mCadPaintItem->updatePosition();
1412 constr.
locked =
c->isLocked();
1414 constr.
value =
c->value();
1421 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1427 const int lastIndex = mLockedSnapVertices.length() - 1;
1428 for (
int i = lastIndex; i >= 0; --i )
1430 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1432 if ( snapMatch.
point() != previouslySnap.
point() )
1434 mLockedSnapVertices.removeAt( i );
1440 if ( snapMatch.
point() != previouslySnap.
point() )
1442 mLockedSnapVertices.enqueue( snapMatch );
1445 if ( mLockedSnapVertices.count() > 3 )
1447 mLockedSnapVertices.dequeue();
1456 context.
xConstraint = _constraint( mXConstraint.get() );
1457 context.
yConstraint = _constraint( mYConstraint.get() );
1458 context.
zConstraint = _constraint( mZConstraint.get() );
1459 context.
mConstraint = _constraint( mMConstraint.get() );
1486 const bool res = output.
valid;
1488 mSnappedSegment.clear();
1493 mSnappedSegment << edgePt0 << edgePt1;
1514 mSnapIndicator->setMatch( output.
snapMatch );
1515 mSnapIndicator->setVisible(
true );
1519 mSnapIndicator->setVisible(
false );
1541 if ( mSnapMatch.layer() )
1545 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && (
QgsPointXY( point ) == mSnapMatch.point() ) )
1549 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1555 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1557 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1558 mLastSnapMatch = mSnapMatch;
1568 if ( mLockZButton->isChecked() )
1570 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1572 if ( mLockMButton->isChecked() )
1574 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1580 updateUnlockedConstraintValues( point );
1588 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1595void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1597 bool previousPointExist, penulPointExist;
1602 if ( previousPointExist )
1604 double prevAngle = 0.0;
1606 if ( penulPointExist && mAngleConstraint->relative() )
1609 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1612 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1615 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1616 if ( !mAngleConstraint->isLocked() )
1618 mAngleConstraint->setValue( angle );
1622 double bearing { std::fmod( xAngle, 360.0 ) };
1623 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1624 const QgsNumericFormatContext context;
1629 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1631 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1634 if ( !mXConstraint->isLocked() )
1636 if ( previousPointExist && mXConstraint->relative() )
1638 mXConstraint->setValue( point.
x() - previousPt.
x() );
1642 mXConstraint->setValue( point.
x() );
1646 if ( !mYConstraint->isLocked() )
1648 if ( previousPointExist && mYConstraint->relative() )
1650 mYConstraint->setValue( point.
y() - previousPt.
y() );
1654 mYConstraint->setValue( point.
y() );
1658 if ( !mZConstraint->isLocked() )
1660 if ( previousPointExist && mZConstraint->relative() )
1662 mZConstraint->setValue( point.
z() - previousPt.
z() );
1666 mZConstraint->setValue( point.
z() );
1670 if ( !mMConstraint->isLocked() )
1672 if ( previousPointExist && mMConstraint->relative() )
1674 mMConstraint->setValue( point.
m() - previousPt.
m() );
1678 mMConstraint->setValue( point.
m() );
1684QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1687 QgsPointXY pt1, pt2;
1688 QgsPointLocator::Match match;
1690 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1692 const QgsSnappingConfig canvasConfig = snappingUtils->
config();
1693 QgsSnappingConfig localConfig = snappingUtils->
config();
1697 snappingUtils->
setConfig( localConfig );
1699 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1701 snappingUtils->
setConfig( canvasConfig );
1711 *snapped =
segment.count() == 2;
1721 mCurrentTool->canvasPressEvent( event );
1726 event->setAccepted(
false );
1738 mCurrentTool->canvasMoveEvent( event );
1746 if ( event->button() == Qt::RightButton )
1750 mCurrentTool->canvasReleaseEvent( event );
1751 if ( !event->isAccepted() )
1763 event->setAccepted(
false );
1769 mCurrentTool->canvasReleaseEvent( event );
1770 if ( !event->isAccepted() )
1781 if ( mLockZButton->isChecked() )
1783 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1785 if ( mLockMButton->isChecked() )
1787 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1798 event->setAccepted(
false );
1810 bool previousPointExist, penulPointExist, snappedSegmentExist;
1813 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1815 if ( !previousPointExist || !snappedSegmentExist )
1820 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1822 if ( mAngleConstraint->relative() && penulPointExist )
1824 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1832 angle *= 180 / M_PI;
1834 mAngleConstraint->setValue( angle );
1835 mAngleConstraint->setLockMode( lockMode );
1853 case Qt::Key_Backspace:
1854 case Qt::Key_Delete:
1860 case Qt::Key_Escape:
1879 mCurrentTool->deleteLater();
1882 if ( !mConstructionGuideLine.isEmpty() )
1884 mConstructionGuideLine.clear();
1900 case Qt::Key_Backspace:
1901 case Qt::Key_Delete:
1907 case Qt::Key_Escape:
1911 if ( mConstructionGuideLine.numPoints() >= 2 )
1913 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1914 mConstructionGuideLine.clear();
1919 mCurrentTool->deleteLater();
1926 filterKeyPress( e );
1935 const auto constPoints = points;
1944 mDistanceConstraint->toggleLocked();
1949bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1953 return QgsDockWidget::eventFilter( obj, event );
1961 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1963 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1965 return filterKeyPress( keyEvent );
1968 return QgsDockWidget::eventFilter( obj, event );
1971bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1977 const QEvent::Type type = e->type();
1980 case Qt::Key_Escape:
1982 if ( type == QEvent::KeyPress && mCurrentTool )
1984 mCurrentTool->deleteLater();
1986 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.numPoints() >= 2 )
1988 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1989 mConstructionGuideLine.clear();
1991 if ( mCadPointList.size() > 1 )
1993 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
2008 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2010 mXConstraint->toggleLocked();
2015 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2019 mXConstraint->toggleRelative();
2026 else if ( type == QEvent::KeyPress )
2028 mXLineEdit->setFocus();
2029 mXLineEdit->selectAll();
2038 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2040 mYConstraint->toggleLocked();
2045 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2049 mYConstraint->toggleRelative();
2056 else if ( type == QEvent::KeyPress )
2058 mYLineEdit->setFocus();
2059 mYLineEdit->selectAll();
2068 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
2070 mZConstraint->toggleLocked();
2075 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2079 mZConstraint->toggleRelative();
2086 else if ( type == QEvent::KeyPress )
2088 mZLineEdit->setFocus();
2089 mZLineEdit->selectAll();
2098 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2100 mMConstraint->toggleLocked();
2105 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2109 mMConstraint->toggleRelative();
2116 else if ( type == QEvent::KeyPress )
2118 mMLineEdit->setFocus();
2119 mMLineEdit->selectAll();
2128 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2132 mAngleConstraint->toggleLocked();
2138 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2142 mAngleConstraint->toggleRelative();
2149 else if ( type == QEvent::KeyPress )
2151 mAngleLineEdit->setFocus();
2152 mAngleLineEdit->selectAll();
2161 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2170 else if ( type == QEvent::KeyPress )
2172 mDistanceLineEdit->setFocus();
2173 mDistanceLineEdit->selectAll();
2181 if ( type == QEvent::KeyPress )
2183 setConstructionMode( !mConstructionMode );
2190 if ( type == QEvent::KeyPress )
2192 const bool parallel = mParallelAction->isChecked();
2193 const bool perpendicular = mPerpendicularAction->isChecked();
2195 if ( !parallel && !perpendicular )
2199 else if ( perpendicular )
2216 if ( type == QEvent::ShortcutOverride )
2218 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2219 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2220 const QList<QAction *> constActions { mCommonAngleActions.values() };
2221 QAction *nextAngleAction;
2222 if ( e->modifiers() == Qt::ShiftModifier )
2224 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2228 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2230 nextAngleAction->trigger();
2240 return e->isAccepted();
2247 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2249 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2250 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2252 mLabelX->setText( tr(
"Long" ) );
2253 mLabelY->setText( tr(
"Lat" ) );
2255 mXConstraint->setPrecision( 8 );
2256 mYConstraint->setPrecision( 8 );
2260 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2261 mAngleLineEdit->setToolTip( QString() );
2263 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2265 mLabelX->setText( tr(
"x" ) );
2266 mLabelY->setText( tr(
"y" ) );
2268 mXConstraint->setPrecision( 6 );
2269 mYConstraint->setPrecision( 6 );
2274 mEnableAction->setEnabled(
true );
2275 mErrorLabel->hide();
2278 mCurrentMapToolSupportsCad =
true;
2280 if ( mSessionActive && !isVisible() )
2285 setCadEnabled( mSessionActive );
2287 if ( !mConstructionGuidesLayer )
2289 resetConstructionGuides();
2292 if ( mDeferredUpdateConstructionGuidesCrs )
2294 updateConstructionGuidesCrs();
2304 mEnableAction->setEnabled(
false );
2305 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2306 mErrorLabel->show();
2309 mCurrentMapToolSupportsCad =
false;
2311 mSnapIndicator->setVisible(
false );
2313 setCadEnabled(
false );
2318 mCadPaintItem->update();
2323 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2328 mLockedSnapVertices.clear();
2333 QgsPoint pt = pointXYToPoint( point );
2336 mCadPointList << pt;
2340 mCadPointList.insert( 0, pt );
2347 mConstructionGuideLine.addVertex( pt );
2349 if ( mConstructionGuideLine.numPoints() == 2 )
2352 QgsGeometry geom( mConstructionGuideLine.clone() );
2354 ( void ) mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2355 mConstructionGuideId = feature.
id();
2357 else if ( mConstructionGuideLine.numPoints() > 2 )
2359 QgsGeometry geom( mConstructionGuideLine.clone() );
2360 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2365 if ( !mConstructionGuideLine.isEmpty() )
2367 mConstructionGuideLine.addVertex( pt );
2369 QgsGeometry geom( mConstructionGuideLine.clone() );
2370 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2371 mConstructionGuideLine.clear();
2386 mCadPointList.removeAt( i );
2393 mCadPointList.clear();
2394 mSnappedSegment.clear();
2404 mCadPointList << point;
2409 mCadPointList[0] = point;
2416 if ( mode == mLockMode )
2421 mLockerButton->setChecked( mode ==
HardLock );
2422 if ( mRepeatingLockButton )
2426 mRepeatingLockButton->setEnabled(
true );
2430 mRepeatingLockButton->setChecked(
false );
2431 mRepeatingLockButton->setEnabled(
false );
2443 mRepeatingLock = repeating;
2444 if ( mRepeatingLockButton )
2445 mRepeatingLockButton->setChecked( repeating );
2451 if ( mRelativeButton )
2453 mRelativeButton->setChecked(
relative );
2460 if ( updateWidget && mLineEdit->isEnabled() )
2466 switch ( mCadConstraintType )
2470 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2475 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2477 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2481 return QLocale().toString( mValue,
'f', mPrecision );
2498 return QLocale().toString( mValue,
'f', mPrecision );
2514 if ( mLineEdit->isEnabled() )
2520 return mCadConstraintType;
2525 mCadConstraintType = constraintType;
2530 mMapCanvas = mapCanvas;
2535 QString
value { text.trimmed() };
2536 switch ( constraintType )
2542 if (
value.endsWith( distanceUnit ) )
2544 value.chop( distanceUnit.length() );
2551 const QString angleUnit { tr(
"°" ) };
2552 if (
value.endsWith( angleUnit ) )
2554 value.chop( angleUnit.length() );
2561 return value.trimmed();
2569 return mCadPointList.value( 0 );
2578 QgsPoint res = mCadPointList.value( 0 );
2579 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2580 res.
setX( layerCoordinates.
x() );
2581 res.
setY( layerCoordinates.
y() );
2592 return mCadPointList.value( 1 );
2602 return mCadPointList.value( 2 );
2607QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2614 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2619 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2624 mWeightValue = value;
2625 const bool wasEnabled = mWeightEnabled;
2626 mWeightEnabled = enabled;
2629 if ( wasEnabled != enabled )
2637 return mWeightValue;
2642 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2647 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2652 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2655void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2657 if ( !mConstructionGuidesLayer )
2664 mDeferredUpdateConstructionGuidesCrs =
true;
2667 QgsCoordinateTransform transform = QgsCoordinateTransform( mConstructionGuidesLayer->crs(), mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->
transformContext() );
2668 mConstructionGuidesLayer->setCrs( mMapCanvas->mapSettings().destinationCrs() );
2669 QgsFeatureIterator it = mConstructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes() );
2673 QgsGeometry geom = feature.
geometry();
2675 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2678 mDeferredUpdateConstructionGuidesCrs =
false;
2681void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2683 if ( mConstructionGuidesLayer )
2685 mConstructionGuidesLayer.reset();
2688 const QgsVectorLayer::LayerOptions options(
QgsProject::instance()->transformContext(),
false,
false );
2689 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.
CadMeasurementDisplayType
Advanced digitizing measurement display types.
@ Hidden
Hide measurement.
@ Cartesian
Use Cartesian measurements.
@ Ellipsoidal
Use Ellipsoidal measurements.
@ 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...
@ Distance
Distance (segment length).
@ TotalLength
Total length (or perimeter).
@ MCoordinate
M coordinate.
@ Angle
Angle between segments.
@ Bearing
Segment bearing.
@ Weight
Weight for NURBSCurve.
@ ZCoordinate
Z coordinate.
@ YCoordinate
Y coordinate.
@ XCoordinate
X coordinate.
@ CommonAngleSnapping
Common angles.
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.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
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 QgsGeometry with associated coordinate reference system.
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.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
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.