QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsadvanceddigitizingdockwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsadvanceddigitizingdockwidget.cpp - dock for CAD tools
3  ----------------------
4  begin : October 2014
5  copyright : (C) Denis Rouzaud
6  email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QMenu>
17 
18 #include <cmath>
19 
22 #include "qgsapplication.h"
23 #include "qgscadutils.h"
24 #include "qgsexpression.h"
25 #include "qgslogger.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsmaptoolcapture.h"
29 #include "qgsmessagebaritem.h"
30 #include "qgslinestring.h"
31 #include "qgsfocuswatcher.h"
32 #include "qgssettings.h"
33 #include "qgssnappingutils.h"
34 #include "qgsproject.h"
35 #include "qgsmapmouseevent.h"
36 
37 
39  : QgsDockWidget( parent )
40  , mMapCanvas( canvas )
41  , mSnapIndicator( qgis::make_unique< QgsSnapIndicator>( canvas ) )
42  , mCommonAngleConstraint( QgsSettings().value( QStringLiteral( "/Cad/CommonAngle" ), 90 ).toDouble() )
43 {
44  setupUi( this );
45 
46  mCadPaintItem = new QgsAdvancedDigitizingCanvasItem( canvas, this );
47 
48  mAngleConstraint.reset( new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
49  mDistanceConstraint.reset( new CadConstraint( mDistanceLineEdit, mLockDistanceButton, nullptr, mRepeatingLockDistanceButton ) );
50  mXConstraint.reset( new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
51  mYConstraint.reset( new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
52  mAdditionalConstraint = NoConstraint;
53 
54  mMapCanvas->installEventFilter( this );
55  mAngleLineEdit->installEventFilter( this );
56  mDistanceLineEdit->installEventFilter( this );
57  mXLineEdit->installEventFilter( this );
58  mYLineEdit->installEventFilter( this );
59 
60  // Connect the UI to the event filter to update constraints
61  connect( mEnableAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::activateCad );
62  connect( mConstructionModeAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
63  connect( mParallelAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
64  connect( mPerpendicularAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
65  connect( mLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
66  connect( mLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
67  connect( mLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
68  connect( mLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
69  connect( mRelativeAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
70  connect( mRelativeXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
71  connect( mRelativeYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
72  connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
73  connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
74  connect( mRepeatingLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
75  connect( mRepeatingLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
76  connect( mAngleLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
77  connect( mDistanceLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
78  connect( mXLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
79  connect( mYLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
80  connect( mAngleLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
81  connect( mDistanceLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
82  connect( mXLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
83  connect( mYLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
84  //also watch for focus out events on these widgets
85  QgsFocusWatcher *angleWatcher = new QgsFocusWatcher( mAngleLineEdit );
86  connect( angleWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
87  QgsFocusWatcher *distanceWatcher = new QgsFocusWatcher( mDistanceLineEdit );
88  connect( distanceWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
89  QgsFocusWatcher *xWatcher = new QgsFocusWatcher( mXLineEdit );
90  connect( xWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
91  QgsFocusWatcher *yWatcher = new QgsFocusWatcher( mYLineEdit );
92  connect( yWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
93 
94  // config menu
95  QMenu *menu = new QMenu( this );
96  // common angles
97  QActionGroup *angleButtonGroup = new QActionGroup( menu ); // actions are exclusive for common angles
98  mCommonAngleActions = QMap<QAction *, double>();
99  QList< QPair< double, QString > > commonAngles;
100  QString menuText;
101  QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
102  for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
103  {
104  if ( *it == 0 )
105  menuText = tr( "Do Not Snap to Common Angles" );
106  else
107  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 );
108  commonAngles << QPair<double, QString>( *it, menuText );
109  }
110  for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
111  {
112  QAction *action = new QAction( it->second, menu );
113  action->setCheckable( true );
114  action->setChecked( it->first == mCommonAngleConstraint );
115  menu->addAction( action );
116  angleButtonGroup->addAction( action );
117  mCommonAngleActions.insert( action, it->first );
118  }
119 
120  qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
121  mSettingsAction->setMenu( menu );
122  connect( menu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
123 
124  // set tooltips
125  mConstructionModeAction->setToolTip( "<b>" + tr( "Construction mode" ) + "</b><br>(" + tr( "press c to toggle on/off" ) + ")" );
126  mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
127  mLockDistanceButton->setToolTip( "<b>" + tr( "Lock distance" ) + "</b><br>(" + tr( "press Ctrl + d for quick access" ) + ")" );
128  mRepeatingLockDistanceButton->setToolTip( "<b>" + tr( "Continuously lock distance" ) + "</b>" );
129 
130  mRelativeAngleButton->setToolTip( "<b>" + tr( "Toggles relative angle to previous segment" ) + "</b><br>(" + tr( "press Shift + a for quick access" ) + ")" );
131  mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
132  mLockAngleButton->setToolTip( "<b>" + tr( "Lock angle" ) + "</b><br>(" + tr( "press Ctrl + a for quick access" ) + ")" );
133  mRepeatingLockAngleButton->setToolTip( "<b>" + tr( "Continuously lock angle" ) + "</b>" );
134 
135  mRelativeXButton->setToolTip( "<b>" + tr( "Toggles relative x to previous node" ) + "</b><br>(" + tr( "press Shift + x for quick access" ) + ")" );
136  mXLineEdit->setToolTip( "<b>" + tr( "X coordinate" ) + "</b><br>(" + tr( "press x for quick access" ) + ")" );
137  mLockXButton->setToolTip( "<b>" + tr( "Lock x coordinate" ) + "</b><br>(" + tr( "press Ctrl + x for quick access" ) + ")" );
138  mRepeatingLockXButton->setToolTip( "<b>" + tr( "Continuously lock x coordinate" ) + "</b>" );
139 
140  mRelativeYButton->setToolTip( "<b>" + tr( "Toggles relative y to previous node" ) + "</b><br>(" + tr( "press Shift + y for quick access" ) + ")" );
141  mYLineEdit->setToolTip( "<b>" + tr( "Y coordinate" ) + "</b><br>(" + tr( "press y for quick access" ) + ")" );
142  mLockYButton->setToolTip( "<b>" + tr( "Lock y coordinate" ) + "</b><br>(" + tr( "press Ctrl + y for quick access" ) + ")" );
143  mRepeatingLockYButton->setToolTip( "<b>" + tr( "Continuously lock y coordinate" ) + "</b>" );
144 
145 
146  updateCapacity( true );
147  connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, [ = ] { updateCapacity( true ); } );
148 
149  disable();
150 }
151 
153 {
154  // disable CAD but do not unset map event filter
155  // so it will be reactivated whenever the map tool is show again
156  setCadEnabled( false );
157 }
158 
159 void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled )
160 {
161  mCadEnabled = enabled;
162  mEnableAction->setChecked( enabled );
163  mConstructionModeAction->setEnabled( enabled );
164  mParallelAction->setEnabled( enabled );
165  mPerpendicularAction->setEnabled( enabled );
166  mSettingsAction->setEnabled( enabled );
167  mInputWidgets->setEnabled( enabled );
168 
169  clear();
170  setConstructionMode( false );
171 }
172 
173 void QgsAdvancedDigitizingDockWidget::activateCad( bool enabled )
174 {
175  enabled &= mCurrentMapToolSupportsCad;
176 
177  mSessionActive = enabled;
178 
179  if ( enabled && !isVisible() )
180  {
181  show();
182  }
183 
184  setCadEnabled( enabled );
185 }
186 
187 void QgsAdvancedDigitizingDockWidget::additionalConstraintClicked( bool activated )
188 {
189  if ( !activated )
190  {
191  lockAdditionalConstraint( NoConstraint );
192  }
193  if ( sender() == mParallelAction )
194  {
195  lockAdditionalConstraint( Parallel );
196  }
197  else if ( sender() == mPerpendicularAction )
198  {
199  lockAdditionalConstraint( Perpendicular );
200  }
201 }
202 
203 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( bool activate )
204 {
205  if ( sender() == mRelativeAngleButton )
206  {
207  mAngleConstraint->setRelative( activate );
208  }
209  else if ( sender() == mRelativeXButton )
210  {
211  mXConstraint->setRelative( activate );
212  }
213  else if ( sender() == mRelativeYButton )
214  {
215  mYConstraint->setRelative( activate );
216  }
217 }
218 
219 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( bool activate )
220 {
221  if ( sender() == mRepeatingLockDistanceButton )
222  {
223  mDistanceConstraint->setRepeatingLock( activate );
224  }
225  else if ( sender() == mRepeatingLockAngleButton )
226  {
227  mAngleConstraint->setRepeatingLock( activate );
228  }
229  else if ( sender() == mRepeatingLockXButton )
230  {
231  mXConstraint->setRepeatingLock( activate );
232  }
233  else if ( sender() == mRepeatingLockYButton )
234  {
235  mYConstraint->setRepeatingLock( activate );
236  }
237 }
238 
239 void QgsAdvancedDigitizingDockWidget::setConstructionMode( bool enabled )
240 {
241  mConstructionMode = enabled;
242  mConstructionModeAction->setChecked( enabled );
243 }
244 
245 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
246 {
247  // common angles
248  QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
249  if ( ica != mCommonAngleActions.constEnd() )
250  {
251  ica.key()->setChecked( true );
252  mCommonAngleConstraint = ica.value();
253  QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngle" ), ica.value() );
254  return;
255  }
256 }
257 
258 void QgsAdvancedDigitizingDockWidget::releaseLocks( bool releaseRepeatingLocks )
259 {
260  // release all locks except construction mode
261 
262  lockAdditionalConstraint( NoConstraint );
263 
264  if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
265  mAngleConstraint->setLockMode( CadConstraint::NoLock );
266  if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
267  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
268  if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
269  mXConstraint->setLockMode( CadConstraint::NoLock );
270  if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
271  mYConstraint->setLockMode( CadConstraint::NoLock );
272 }
273 
274 #if 0
275 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
276 {
277  // run a fake map mouse event to update the paint item
278  QPoint globalPos = mMapCanvas->cursor().pos();
279  QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
280  QMouseEvent *e = new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
281  mCurrentMapTool->canvasMoveEvent( e );
282 }
283 #endif
284 
285 QgsAdvancedDigitizingDockWidget::CadConstraint *QgsAdvancedDigitizingDockWidget::objectToConstraint( const QObject *obj ) const
286 {
287  CadConstraint *constraint = nullptr;
288  if ( obj == mAngleLineEdit || obj == mLockAngleButton )
289  {
290  constraint = mAngleConstraint.get();
291  }
292  else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
293  {
294  constraint = mDistanceConstraint.get();
295  }
296  else if ( obj == mXLineEdit || obj == mLockXButton )
297  {
298  constraint = mXConstraint.get();
299  }
300  else if ( obj == mYLineEdit || obj == mLockYButton )
301  {
302  constraint = mYConstraint.get();
303  }
304  return constraint;
305 }
306 
307 double QgsAdvancedDigitizingDockWidget::parseUserInput( const QString &inputValue, bool &ok ) const
308 {
309  ok = false;
310  double value = qgsPermissiveToDouble( inputValue, ok );
311  if ( ok )
312  {
313  return value;
314  }
315  else
316  {
317  // try to evaluate expression
318  QgsExpression expr( inputValue );
319  QVariant result = expr.evaluate();
320  if ( expr.hasEvalError() )
321  ok = false;
322  else
323  value = result.toDouble( &ok );
324  return value;
325  }
326 }
327 
328 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, const QString &textValue, bool convertExpression )
329 {
330  if ( !constraint || textValue.isEmpty() )
331  {
332  return;
333  }
334 
335  if ( constraint->lockMode() == CadConstraint::NoLock )
336  return;
337 
338  bool ok;
339  double value = parseUserInput( textValue, ok );
340  if ( !ok )
341  return;
342 
343  constraint->setValue( value, convertExpression );
344  // run a fake map mouse event to update the paint item
345  emit pointChanged( mCadPointList.value( 0 ) );
346 }
347 
348 void QgsAdvancedDigitizingDockWidget::lockConstraint( bool activate /* default true */ )
349 {
350  CadConstraint *constraint = objectToConstraint( sender() );
351  if ( !constraint )
352  {
353  return;
354  }
355 
356  if ( activate )
357  {
358  QString textValue = constraint->lineEdit()->text();
359  if ( !textValue.isEmpty() )
360  {
361  bool ok;
362  double value = parseUserInput( textValue, ok );
363  if ( ok )
364  {
365  constraint->setValue( value );
366  }
367  else
368  {
369  activate = false;
370  }
371  }
372  else
373  {
374  activate = false;
375  }
376  }
377  constraint->setLockMode( activate ? CadConstraint::HardLock : CadConstraint::NoLock );
378 
379  if ( activate )
380  {
381  // deactivate perpendicular/parallel if angle has been activated
382  if ( constraint == mAngleConstraint.get() )
383  {
384  lockAdditionalConstraint( NoConstraint );
385  }
386 
387  // run a fake map mouse event to update the paint item
388  emit pointChanged( mCadPointList.value( 0 ) );
389  }
390 }
391 
392 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( const QString &textValue )
393 {
394  CadConstraint *constraint = objectToConstraint( sender() );
395  if ( !constraint )
396  {
397  return;
398  }
399 
400  updateConstraintValue( constraint, textValue, false );
401 }
402 
403 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
404 {
405  QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
406  if ( !lineEdit )
407  return;
408 
409  CadConstraint *constraint = objectToConstraint( lineEdit );
410  if ( !constraint )
411  {
412  return;
413  }
414 
415  updateConstraintValue( constraint, lineEdit->text(), true );
416 }
417 
418 void QgsAdvancedDigitizingDockWidget::lockAdditionalConstraint( AdditionalConstraint constraint )
419 {
420  mAdditionalConstraint = constraint;
421  mPerpendicularAction->setChecked( constraint == Perpendicular );
422  mParallelAction->setChecked( constraint == Parallel );
423 }
424 
425 void QgsAdvancedDigitizingDockWidget::updateCapacity( bool updateUIwithoutChange )
426 {
427  CadCapacities newCapacities = nullptr;
428  // first point is the mouse point (it doesn't count)
429  if ( mCadPointList.count() > 1 )
430  {
431  newCapacities |= AbsoluteAngle | RelativeCoordinates;
432  }
433  if ( mCadPointList.count() > 2 )
434  {
435  newCapacities |= RelativeAngle;
436  }
437  if ( !updateUIwithoutChange && newCapacities == mCapacities )
438  {
439  return;
440  }
441 
442  bool snappingEnabled = QgsProject::instance()->snappingConfig().enabled();
443 
444  // update the UI according to new capacities
445  // still keep the old to compare
446 
447  bool relativeAngle = mCadEnabled && newCapacities.testFlag( RelativeAngle );
448  bool absoluteAngle = mCadEnabled && newCapacities.testFlag( AbsoluteAngle );
449  bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( RelativeCoordinates );
450 
451  mPerpendicularAction->setEnabled( absoluteAngle && snappingEnabled );
452  mParallelAction->setEnabled( absoluteAngle && snappingEnabled );
453 
454  //update tooltips on buttons
455  if ( !snappingEnabled )
456  {
457  mPerpendicularAction->setToolTip( tr( "Snapping must be enabled to utilize perpendicular mode" ) );
458  mParallelAction->setToolTip( tr( "Snapping must be enabled to utilize parallel mode" ) );
459  }
460  else
461  {
462  mPerpendicularAction->setToolTip( "<b>" + tr( "Perpendicular" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
463  mParallelAction->setToolTip( "<b>" + tr( "Parallel" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
464  }
465 
466 
467  if ( !absoluteAngle )
468  {
469  lockAdditionalConstraint( NoConstraint );
470  }
471 
472  // absolute angle = azimuth, relative = from previous line
473  mLockAngleButton->setEnabled( absoluteAngle );
474  mRelativeAngleButton->setEnabled( relativeAngle );
475  mAngleLineEdit->setEnabled( absoluteAngle );
476  if ( !absoluteAngle )
477  {
478  mAngleConstraint->setLockMode( CadConstraint::NoLock );
479  }
480  if ( !relativeAngle )
481  {
482  mAngleConstraint->setRelative( false );
483  }
484  else if ( relativeAngle && !mCapacities.testFlag( RelativeAngle ) )
485  {
486  // set angle mode to relative if can do and wasn't available before
487  mAngleConstraint->setRelative( true );
488  }
489 
490  // distance is always relative
491  mLockDistanceButton->setEnabled( relativeCoordinates );
492  mDistanceLineEdit->setEnabled( relativeCoordinates );
493  if ( !relativeCoordinates )
494  {
495  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
496  }
497 
498  mRelativeXButton->setEnabled( relativeCoordinates );
499  mRelativeYButton->setEnabled( relativeCoordinates );
500 
501  // update capacities
502  mCapacities = newCapacities;
503 }
504 
505 
507 {
510  constr.relative = c->relative();
511  constr.value = c->value();
512  return constr;
513 }
514 
516 {
518  context.snappingUtils = mMapCanvas->snappingUtils();
519  context.mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
520  context.xConstraint = _constraint( mXConstraint.get() );
521  context.yConstraint = _constraint( mYConstraint.get() );
522  context.distanceConstraint = _constraint( mDistanceConstraint.get() );
523  context.angleConstraint = _constraint( mAngleConstraint.get() );
524  context.cadPointList = mCadPointList;
525 
526  context.commonAngleConstraint.locked = true;
528  context.commonAngleConstraint.value = mCommonAngleConstraint;
529 
531 
532  bool res = output.valid;
533  QgsPointXY point = output.finalMapPoint;
534  mSnappedSegment.clear();
535  if ( output.edgeMatch.hasEdge() )
536  {
537  QgsPointXY edgePt0, edgePt1;
538  output.edgeMatch.edgePoints( edgePt0, edgePt1 );
539  mSnappedSegment << edgePt0 << edgePt1;
540  }
541  if ( mAngleConstraint->lockMode() != CadConstraint::HardLock )
542  {
543  if ( output.softLockCommonAngle != -1 )
544  {
545  mAngleConstraint->setLockMode( CadConstraint::SoftLock );
546  mAngleConstraint->setValue( output.softLockCommonAngle );
547  }
548  else
549  {
550  mAngleConstraint->setLockMode( CadConstraint::NoLock );
551  }
552  }
553 
554  // set the point coordinates in the map event
555  e->setMapPoint( point );
556 
557  mSnapMatch = context.snappingUtils->snapToMap( point );
558 
559  if ( mSnapMatch.isValid() )
560  {
561  mSnapIndicator->setMatch( mSnapMatch );
562  mSnapIndicator->setVisible( true );
563  }
564  else
565  {
566  mSnapIndicator->setVisible( false );
567  }
568 
569  /*
570  * Constraints are applied in 2D, they are always called when using the tool
571  * but they do not take into account if when you snap on a vertex it has
572  * a Z value.
573  * To get the value we use the snapPoint method. However, we only apply it
574  * when the snapped point corresponds to the constrained point.
575  */
576  if ( mSnapMatch.hasVertex() && ( point == mSnapMatch.point() ) )
577  {
578  e->snapPoint();
579  }
580  // update the point list
581  updateCurrentPoint( point );
582 
583  updateUnlockedConstraintValues( point );
584 
585  if ( res )
586  {
587  emit popWarning();
588  }
589  else
590  {
591  emit pushWarning( tr( "Some constraints are incompatible. Resulting point might be incorrect." ) );
592  }
593 
594  return res;
595 }
596 
597 
598 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( const QgsPointXY &point )
599 {
600  bool previousPointExist, penulPointExist;
601  QgsPointXY previousPt = previousPoint( &previousPointExist );
602  QgsPointXY penultimatePt = penultimatePoint( &penulPointExist );
603 
604  // --- angle
605  if ( !mAngleConstraint->isLocked() && previousPointExist )
606  {
607  double angle = 0.0;
608  if ( penulPointExist && mAngleConstraint->relative() )
609  {
610  // previous angle
611  angle = std::atan2( previousPt.y() - penultimatePt.y(),
612  previousPt.x() - penultimatePt.x() );
613  }
614  angle = ( std::atan2( point.y() - previousPt.y(),
615  point.x() - previousPt.x()
616  ) - angle ) * 180 / M_PI;
617  // modulus
618  angle = std::fmod( angle, 360.0 );
619  mAngleConstraint->setValue( angle );
620  }
621  // --- distance
622  if ( !mDistanceConstraint->isLocked() && previousPointExist )
623  {
624  mDistanceConstraint->setValue( std::sqrt( previousPt.sqrDist( point ) ) );
625  }
626  // --- X
627  if ( !mXConstraint->isLocked() )
628  {
629  if ( previousPointExist && mXConstraint->relative() )
630  {
631  mXConstraint->setValue( point.x() - previousPt.x() );
632  }
633  else
634  {
635  mXConstraint->setValue( point.x() );
636  }
637  }
638  // --- Y
639  if ( !mYConstraint->isLocked() )
640  {
641  if ( previousPointExist && mYConstraint->relative() )
642  {
643  mYConstraint->setValue( point.y() - previousPt.y() );
644  }
645  else
646  {
647  mYConstraint->setValue( point.y() );
648  }
649  }
650 }
651 
652 
653 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const QgsPointXY &originalMapPoint, bool *snapped ) const
654 {
655  QList<QgsPointXY> segment;
656  QgsPointXY pt1, pt2;
658 
659  QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
660 
661  QgsSnappingConfig canvasConfig = snappingUtils->config();
662  QgsSnappingConfig localConfig = snappingUtils->config();
663 
664  localConfig.setMode( QgsSnappingConfig::AllLayers );
665  localConfig.setType( QgsSnappingConfig::Segment );
666  snappingUtils->setConfig( localConfig );
667 
668  match = snappingUtils->snapToMap( originalMapPoint );
669 
670  snappingUtils->setConfig( canvasConfig );
671 
672  if ( match.isValid() && match.hasEdge() )
673  {
674  match.edgePoints( pt1, pt2 );
675  segment << pt1 << pt2;
676  }
677 
678  if ( snapped )
679  {
680  *snapped = segment.count() == 2;
681  }
682 
683  return segment;
684 }
685 
687 {
688  if ( mAdditionalConstraint == NoConstraint )
689  {
690  return false;
691  }
692 
693  bool previousPointExist, penulPointExist, snappedSegmentExist;
694  QgsPointXY previousPt = previousPoint( &previousPointExist );
695  QgsPointXY penultimatePt = penultimatePoint( &penulPointExist );
696  mSnappedSegment = snapSegmentToAllLayers( e->originalMapPoint(), &snappedSegmentExist );
697 
698  if ( !previousPointExist || !snappedSegmentExist )
699  {
700  return false;
701  }
702 
703  double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
704 
705  if ( mAngleConstraint->relative() && penulPointExist )
706  {
707  angle -= std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() );
708  }
709 
710  if ( mAdditionalConstraint == Perpendicular )
711  {
712  angle += M_PI_2;
713  }
714 
715  angle *= 180 / M_PI;
716 
717  mAngleConstraint->setValue( angle );
718  mAngleConstraint->setLockMode( lockMode );
719  if ( lockMode == CadConstraint::HardLock )
720  {
721  mAdditionalConstraint = NoConstraint;
722  }
723 
724  return true;
725 }
726 
728 {
729  // event on map tool
730 
731  if ( !mCadEnabled )
732  return false;
733 
734  switch ( e->key() )
735  {
736  case Qt::Key_Backspace:
737  case Qt::Key_Delete:
738  {
740  releaseLocks( false );
741  break;
742  }
743  case Qt::Key_Escape:
744  {
745  releaseLocks();
746  break;
747  }
748  default:
749  {
750  keyPressEvent( e );
751  break;
752  }
753  }
754  // for map tools, continues with key press in any case
755  return false;
756 }
757 
759 {
760  clearPoints();
761  releaseLocks();
762 }
763 
765 {
766  // event on dock (this)
767 
768  if ( !mCadEnabled )
769  return;
770 
771  switch ( e->key() )
772  {
773  case Qt::Key_Backspace:
774  case Qt::Key_Delete:
775  {
777  releaseLocks( false );
778  break;
779  }
780  case Qt::Key_Escape:
781  {
782  releaseLocks();
783  break;
784  }
785  default:
786  {
787  filterKeyPress( e );
788  break;
789  }
790  }
791 }
792 
793 void QgsAdvancedDigitizingDockWidget::setPoints( const QList<QgsPointXY> &points )
794 {
795  clearPoints();
796  Q_FOREACH ( const QgsPointXY &pt, points )
797  {
798  addPoint( pt );
799  }
800 }
801 
802 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
803 {
804  if ( !cadEnabled() )
805  {
806  return QgsDockWidget::eventFilter( obj, event );
807  }
808 
809  // event for line edits and map canvas
810  // we have to catch both KeyPress events and ShortcutOverride events. This is because
811  // the Ctrl+D and Ctrl+A shortcuts for locking distance/angle clash with the global
812  // "remove layer" and "select all" shortcuts. Catching ShortcutOverride events allows
813  // us to intercept these keystrokes before they are caught by the global shortcuts
814  if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
815  {
816  if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
817  {
818  return filterKeyPress( keyEvent );
819  }
820  }
821  return QgsDockWidget::eventFilter( obj, event );
822 }
823 
824 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
825 {
826  // we need to be careful here -- because this method is called on both KeyPress events AND
827  // ShortcutOverride events, we have to take care that we don't trigger the handling for BOTH
828  // these event types for a single key press. I.e. pressing "A" may first call trigger a
829  // ShortcutOverride event (sometimes, not always!) followed immediately by a KeyPress event.
830  QEvent::Type type = e->type();
831  switch ( e->key() )
832  {
833  case Qt::Key_X:
834  {
835  // modifier+x ONLY caught for ShortcutOverride events...
836  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
837  {
838  mXConstraint->toggleLocked();
839  emit pointChanged( mCadPointList.value( 0 ) );
840  e->accept();
841  }
842  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
843  {
844  if ( mCapacities.testFlag( RelativeCoordinates ) )
845  {
846  mXConstraint->toggleRelative();
847  emit pointChanged( mCadPointList.value( 0 ) );
848  e->accept();
849  }
850  }
851  // .. but "X" alone ONLY caught for KeyPress events (see comment at start of function)
852  else if ( type == QEvent::KeyPress )
853  {
854  mXLineEdit->setFocus();
855  mXLineEdit->selectAll();
856  e->accept();
857  }
858  break;
859  }
860  case Qt::Key_Y:
861  {
862  // modifier+y ONLY caught for ShortcutOverride events...
863  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
864  {
865  mYConstraint->toggleLocked();
866  emit pointChanged( mCadPointList.value( 0 ) );
867  e->accept();
868  }
869  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
870  {
871  if ( mCapacities.testFlag( RelativeCoordinates ) )
872  {
873  mYConstraint->toggleRelative();
874  emit pointChanged( mCadPointList.value( 0 ) );
875  e->accept();
876  }
877  }
878  // .. but "y" alone ONLY caught for KeyPress events (see comment at start of function)
879  else if ( type == QEvent::KeyPress )
880  {
881  mYLineEdit->setFocus();
882  mYLineEdit->selectAll();
883  e->accept();
884  }
885  break;
886  }
887  case Qt::Key_A:
888  {
889  // modifier+a ONLY caught for ShortcutOverride events...
890  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
891  {
892  if ( mCapacities.testFlag( AbsoluteAngle ) )
893  {
894  mAngleConstraint->toggleLocked();
895  emit pointChanged( mCadPointList.value( 0 ) );
896  e->accept();
897  }
898  }
899  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
900  {
901  if ( mCapacities.testFlag( RelativeAngle ) )
902  {
903  mAngleConstraint->toggleRelative();
904  emit pointChanged( mCadPointList.value( 0 ) );
905  e->accept();
906  }
907  }
908  // .. but "a" alone ONLY caught for KeyPress events (see comment at start of function)
909  else if ( type == QEvent::KeyPress )
910  {
911  mAngleLineEdit->setFocus();
912  mAngleLineEdit->selectAll();
913  e->accept();
914  }
915  break;
916  }
917  case Qt::Key_D:
918  {
919  // modifier+d ONLY caught for ShortcutOverride events...
920  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
921  {
922  if ( mCapacities.testFlag( RelativeCoordinates ) )
923  {
924  mDistanceConstraint->toggleLocked();
925  emit pointChanged( mCadPointList.value( 0 ) );
926  e->accept();
927  }
928  }
929  // .. but "d" alone ONLY caught for KeyPress events (see comment at start of function)
930  else if ( type == QEvent::KeyPress )
931  {
932  mDistanceLineEdit->setFocus();
933  mDistanceLineEdit->selectAll();
934  e->accept();
935  }
936  break;
937  }
938  case Qt::Key_C:
939  {
940  if ( type == QEvent::KeyPress )
941  {
942  setConstructionMode( !mConstructionMode );
943  e->accept();
944  }
945  break;
946  }
947  case Qt::Key_P:
948  {
949  if ( type == QEvent::KeyPress )
950  {
951  bool parallel = mParallelAction->isChecked();
952  bool perpendicular = mPerpendicularAction->isChecked();
953 
954  if ( !parallel && !perpendicular )
955  {
956  lockAdditionalConstraint( Perpendicular );
957  }
958  else if ( perpendicular )
959  {
960  lockAdditionalConstraint( Parallel );
961  }
962  else
963  {
964  lockAdditionalConstraint( NoConstraint );
965  }
966  e->accept();
967  }
968  break;
969  }
970  default:
971  {
972  return false; // continues
973  }
974  }
975  return e->isAccepted();
976 }
977 
979 {
980  connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsAdvancedDigitizingDockWidget::enable, Qt::UniqueConnection );
981  if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
982  {
983  mErrorLabel->setText( tr( "CAD tools can not be used on geographic coordinates. Change the coordinates system in the project properties." ) );
984  mErrorLabel->show();
985  mEnableAction->setEnabled( false );
986  setCadEnabled( false );
987  }
988  else
989  {
990  mEnableAction->setEnabled( true );
991  mErrorLabel->hide();
992  mCadWidget->show();
993 
994  mCurrentMapToolSupportsCad = true;
995 
996  if ( mSessionActive && !isVisible() )
997  {
998  show();
999  }
1000  setCadEnabled( mSessionActive );
1001  }
1002 }
1003 
1005 {
1007 
1008  mEnableAction->setEnabled( false );
1009  mErrorLabel->setText( tr( "CAD tools are not enabled for the current map tool" ) );
1010  mErrorLabel->show();
1011  mCadWidget->hide();
1012 
1013  mCurrentMapToolSupportsCad = false;
1014 
1015  setCadEnabled( false );
1016 }
1017 
1019 {
1020  mCadPaintItem->update();
1021 }
1022 
1024 {
1025  if ( !pointsCount() )
1026  {
1027  mCadPointList << point;
1028  }
1029  else
1030  {
1031  mCadPointList.insert( 0, point );
1032  }
1033 
1034  updateCapacity();
1036 }
1037 
1039 {
1040  if ( !pointsCount() )
1041  return;
1042 
1043  int i = pointsCount() > 1 ? 1 : 0;
1044  mCadPointList.removeAt( i );
1045  updateCapacity();
1047 }
1048 
1050 {
1051  mCadPointList.clear();
1052  mSnappedSegment.clear();
1053 
1054  updateCapacity();
1056 }
1057 
1058 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( const QgsPointXY &point )
1059 {
1060  if ( !pointsCount() )
1061  {
1062  mCadPointList << point;
1063  updateCapacity();
1064  }
1065  else
1066  {
1067  mCadPointList[0] = point;
1068  }
1070 }
1071 
1072 
1074 {
1075  mLockMode = mode;
1076  mLockerButton->setChecked( mode == HardLock );
1077  if ( mRepeatingLockButton )
1078  {
1079  if ( mode == HardLock )
1080  {
1081  mRepeatingLockButton->setEnabled( true );
1082  }
1083  else
1084  {
1085  mRepeatingLockButton->setChecked( false );
1086  mRepeatingLockButton->setEnabled( false );
1087  }
1088  }
1089 
1090  if ( mode == NoLock )
1091  {
1092  mLineEdit->clear();
1093  }
1094 }
1095 
1097 {
1098  mRepeatingLock = repeating;
1099  if ( mRepeatingLockButton )
1100  mRepeatingLockButton->setChecked( repeating );
1101 }
1102 
1104 {
1105  mRelative = relative;
1106  if ( mRelativeButton )
1107  {
1108  mRelativeButton->setChecked( relative );
1109  }
1110 }
1111 
1112 void QgsAdvancedDigitizingDockWidget::CadConstraint::setValue( double value, bool updateWidget )
1113 {
1114  mValue = value;
1115  if ( updateWidget )
1116  mLineEdit->setText( QLocale().toString( value, 'f', 6 ) );
1117 }
1118 
1120 {
1121  setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1122 }
1123 
1125 {
1126  setRelative( !mRelative );
1127 }
1128 
1130 {
1131  if ( exist )
1132  *exist = pointsCount() > 0;
1133  if ( pointsCount() > 0 )
1134  return mCadPointList.value( 0 );
1135  else
1136  return QgsPointXY();
1137 }
1138 
1140 {
1141  if ( exist )
1142  *exist = pointsCount() > 1;
1143  if ( pointsCount() > 1 )
1144  return mCadPointList.value( 1 );
1145  else
1146  return QgsPointXY();
1147 }
1148 
1150 {
1151  if ( exist )
1152  *exist = pointsCount() > 2;
1153  if ( pointsCount() > 2 )
1154  return mCadPointList.value( 2 );
1155  else
1156  return QgsPointXY();
1157 }
This corresponds to distance and relative coordinates.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
The CadConstraint is an abstract class for all basic constraints (angle/distance/x/y).
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords...
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
Definition: qgscadutils.h:64
void clearPoints()
Removes all points from the CAD point list.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
LockMode lockMode() const
The current lock mode of this constraint.
void focusOut()
Emitted when parent object loses focus.
A event filter for watching for focus events on a parent object.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsPointXY currentPoint(bool *exists=nullptr) const
The last point.
Structure with details of one constraint.
Definition: qgscadutils.h:37
bool relative() const
Is the constraint in relative mode.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
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...
Definition: qgis.cpp:97
QVariant evaluate()
Evaluate the feature and return the result.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:169
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
bool applyConstraints(QgsMapMouseEvent *e)
apply the CAD constraints.
On all vector layers.
void setRelative(bool relative)
Set if the constraint should be treated relative.
void clear()
Clear any cached previous clicks and helper lines.
void setMapPoint(const QgsPointXY &point)
Set the (snapped) point this event points to in map coordinates.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:74
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)
Definition: MathUtils.cpp:786
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
QgsPointLocator::Match edgeMatch
Snapped segment - only valid if actually used for something.
Definition: qgscadutils.h:96
void releaseLocks(bool releaseRepeatingLocks=true)
unlock all constraints
bool canvasKeyPressEventFilter(QKeyEvent *e)
Filter key events to e.g.
bool locked
Whether the constraint is active, i.e. should be considered.
Definition: qgscadutils.h:46
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
void updateCadPaintItem()
Updates canvas item that displays constraints on the ma.
int pointsCount() const
The number of points in the CAD point helper list.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void setRepeatingLock(bool repeating)
Sets whether a repeating lock is set for the constraint.
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
Definition: qgscadutils.h:66
QLineEdit * lineEdit() const
The line edit that manages the value of the constraint.
void addPoint(const QgsPointXY &point)
Adds point to the CAD point list.
void hideEvent(QHideEvent *) override
Disables the CAD tools when hiding the dock.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
QgsDockWidget subclass with more fine-grained control over how the widget is closed or opened...
Definition: qgsdockwidget.h:31
Structure defining all constraints for alignMapPoint() method.
Definition: qgscadutils.h:54
QgsAdvancedDigitizingDockWidget(QgsMapCanvas *canvas, QWidget *parent=nullptr)
Create an advanced digitizing dock widget.
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
Definition: qgscadutils.h:70
QgsPointXY penultimatePoint(bool *exists=nullptr) const
The penultimate point.
void destinationCrsChanged()
Emitted when map CRS has changed.
bool enabled() const
Returns if snapping is enabled.
void enable()
Enables the tool (call this when an appropriate map tool is set and in the condition to make use of c...
QgsPointXY finalMapPoint
map point aligned according to the constraints
Definition: qgscadutils.h:93
void setPoints(const QList< QgsPointXY > &points)
Configures list of current CAD points.
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees) ...
Definition: qgscadutils.h:50
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
Definition: qgscadutils.h:99
double x
Definition: qgspointxy.h:47
Structure returned from alignMapPoint() method.
Definition: qgscadutils.h:87
double mapUnitsPerPixel
Map units/pixel ratio from map canvas. Needed for.
Definition: qgscadutils.h:59
void pointChanged(const QgsPointXY &point)
Sometimes a constraint may change the current point out of a mouse event.
void popWarning()
Remove any previously emitted warnings (if any)
void setMode(SnappingMode mode)
define the mode of snapping
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
Definition: qgscadutils.h:62
QgsPointXY previousPoint(bool *exists=nullptr) const
The previous point.
QgsSnappingConfig config
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Definition: qgscadutils.h:68
bool valid
Whether the combination of constraints is actually valid.
Definition: qgscadutils.h:90
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
void setType(SnappingType type)
define the type of snapping
This class has all the configuration of snapping and can return answers to snapping queries...
Class that shows snapping marker on map canvas for the current snapping match.
void pushWarning(const QString &message)
Push a warning.
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be null.
Definition: qgscadutils.h:57
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
This is a container for configuration of the snapping of the project.
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.
Definition: qgscadutils.cpp:37
void setValue(double value, bool updateWidget=true)
Set the value of the constraint.
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to map according to the current configuration. Optional filter allows discarding unwanted matche...
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
bool relative
Whether the value is relative to previous value.
Definition: qgscadutils.h:48
bool isGeographic() const
Returns whether the CRS is a geographic CRS (using lat/lon coordinates)
bool alignToSegment(QgsMapMouseEvent *e, QgsAdvancedDigitizingDockWidget::CadConstraint::LockMode lockMode=QgsAdvancedDigitizingDockWidget::CadConstraint::HardLock)
align to segment for additional constraint.
QgsSnappingConfig snappingConfig
Definition: qgsproject.h:99
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
AdditionalConstraint
Additional constraints which can be enabled.
double value() const
The value of the constraint.
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (...
void removePreviousPoint()
Remove previous point in the CAD point list.
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
QList< QgsPointXY > cadPointList
List of recent CAD points in map coordinates.
Definition: qgscadutils.h:77