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