QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
qgslayoutguidecollection.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutguidecollection.cpp
3 ----------------------------
4 begin : July 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18#include "moc_qgslayoutguidecollection.cpp"
19#include "qgslayout.h"
20#include "qgsreadwritecontext.h"
22#include "qgslayoutundostack.h"
23#include "qgsunittypes.h"
24
25#include <QGraphicsLineItem>
26
27
28//
29// QgsLayoutGuide
30//
31
32QgsLayoutGuide::QgsLayoutGuide( Qt::Orientation orientation, QgsLayoutMeasurement position, QgsLayoutItemPage *page )
33 : QObject( nullptr )
34 , mOrientation( orientation )
35 , mPosition( position )
36 , mPage( page )
37{}
38
40{
41 if ( mLayout && mLineItem )
42 {
43 mLayout->removeItem( mLineItem );
44 delete mLineItem;
45 }
46}
47
49{
50 return mPosition;
51}
52
54{
55 mPosition = position;
56 update();
57 emit positionChanged();
58}
59
61{
62 return mPage;
63}
64
66{
67 mPage = page;
68 update();
69}
70
72{
73 if ( !mLayout || !mLineItem )
74 return;
75
76 // first find matching page
77 if ( !mPage )
78 {
79 mLineItem->hide();
80 return;
81 }
82
83 double layoutPos = mLayout->convertToLayoutUnits( mPosition );
84 bool showGuide = mLayout->guides().visible();
85 switch ( mOrientation )
86 {
87 case Qt::Horizontal:
88 if ( layoutPos > mPage->rect().height() )
89 {
90 mLineItem->hide();
91 }
92 else
93 {
94 mLineItem->setLine( 0, layoutPos + mPage->y(), mPage->rect().width(), layoutPos + mPage->y() );
95 mLineItem->setVisible( showGuide );
96 }
97
98 break;
99
100 case Qt::Vertical:
101 if ( layoutPos > mPage->rect().width() )
102 {
103 mLineItem->hide();
104 }
105 else
106 {
107 mLineItem->setLine( layoutPos, mPage->y(), layoutPos, mPage->y() + mPage->rect().height() );
108 mLineItem->setVisible( showGuide );
109 }
110
111 break;
112 }
113}
114
115QGraphicsLineItem *QgsLayoutGuide::item()
116{
117 return mLineItem;
118}
119
121{
122 if ( !mLineItem )
123 return -999;
124
125 switch ( mOrientation )
126 {
127 case Qt::Horizontal:
128 return mLineItem->mapToScene( mLineItem->line().p1() ).y();
129
130 case Qt::Vertical:
131 return mLineItem->mapToScene( mLineItem->line().p1() ).x();
132 }
133 return -999; // avoid warning
134}
135
137{
138 if ( !mLayout )
139 return;
140
141 double p = 0;
142 switch ( mOrientation )
143 {
144 case Qt::Horizontal:
145 p = mPage->mapFromScene( QPointF( 0, position ) ).y();
146 break;
147
148 case Qt::Vertical:
149 p = mPage->mapFromScene( QPointF( position, 0 ) ).x();
150 break;
151 }
152 mPosition = mLayout->convertFromLayoutUnits( p, mPosition.units() );
153 update();
154 emit positionChanged();
155}
156
158{
159 return mLayout;
160}
161
163{
164 mLayout = layout;
165
166 if ( !mLineItem )
167 {
168 mLineItem = new QGraphicsLineItem();
169 mLineItem->hide();
170 mLineItem->setZValue( QgsLayout::ZGuide );
171 QPen linePen( Qt::DotLine );
172 linePen.setColor( Qt::red );
173 // use a pen width of 0, since this activates a cosmetic pen
174 // which doesn't scale with the layout and keeps a constant size
175 linePen.setWidthF( 0 );
176 mLineItem->setPen( linePen );
177 }
178
179 mLayout->addItem( mLineItem );
180 update();
181}
182
183Qt::Orientation QgsLayoutGuide::orientation() const
184{
185 return mOrientation;
186}
187
188
189
190//
191// QgsLayoutGuideCollection
192//
193
195 : QAbstractTableModel( layout )
196 , mLayout( layout )
197 , mPageCollection( pageCollection )
198{
199 QFont f;
200 mHeaderSize = QFontMetrics( f ).boundingRect( QStringLiteral( "XX" ) ).width();
201
202 connect( mPageCollection, &QgsLayoutPageCollection::pageAboutToBeRemoved, this, &QgsLayoutGuideCollection::pageAboutToBeRemoved );
203}
204
206{
207 qDeleteAll( mGuides );
208}
209
211{
212 return mLayout;
213}
214
215int QgsLayoutGuideCollection::rowCount( const QModelIndex & ) const
216{
217 return mGuides.count();
218}
219
220int QgsLayoutGuideCollection::columnCount( const QModelIndex &parent ) const
221{
222 if ( parent.isValid() )
223 return 0;
224
225 return 2;
226}
227
228QVariant QgsLayoutGuideCollection::data( const QModelIndex &index, int role ) const
229{
230 if ( !index.isValid() )
231 return QVariant();
232
233 if ( index.row() >= mGuides.count() || index.row() < 0 )
234 return QVariant();
235
236 QgsLayoutGuide *guide = mGuides.at( index.row() );
237 switch ( role )
238 {
239 case Qt::DisplayRole:
240 case Qt::EditRole:
241 {
242 if ( index.column() == 0 )
243 return guide->position().length();
244 else
246 }
247
248 case static_cast< int >( CustomRole::Orientation ):
249 return QVariant::fromValue( guide->orientation() );
250
251 case static_cast< int >( CustomRole::Position ):
252 return guide->position().length();
253
254 case static_cast< int >( CustomRole::Units ):
255 return static_cast< int >( guide->position().units() );
256
257 case static_cast< int >( CustomRole::Page ):
258 return mPageCollection->pageNumber( guide->page() );
259
260 case static_cast< int >( CustomRole::LayoutPosition ):
261 return guide->layoutPosition();
262
263 default:
264 return QVariant();
265 }
266}
267
268bool QgsLayoutGuideCollection::setData( const QModelIndex &index, const QVariant &value, int role )
269{
270 if ( !index.isValid() )
271 return false;
272
273 if ( index.row() >= mGuides.count() || index.row() < 0 )
274 return false;
275
276 QgsLayoutGuide *guide = mGuides.at( index.row() );
277
278 switch ( role )
279 {
280 case Qt::EditRole:
281 {
282 bool ok = false;
283 double newPos = value.toDouble( &ok );
284 if ( !ok )
285 return false;
286
287 QgsLayoutMeasurement m = guide->position();
288 m.setLength( newPos );
289 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
290 whileBlocking( guide )->setPosition( m );
291 guide->update();
292 mLayout->undoStack()->endCommand();
293 emit dataChanged( index, index, QVector<int>() << role );
294 return true;
295 }
296 case static_cast< int >( CustomRole::Position ):
297 {
298 bool ok = false;
299 double newPos = value.toDouble( &ok );
300 if ( !ok )
301 return false;
302
303 QgsLayoutMeasurement m = guide->position();
304 if ( qgsDoubleNear( m.length(), newPos ) )
305 return true;
306
307 m.setLength( newPos );
308 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
309 whileBlocking( guide )->setPosition( m );
310 guide->update();
311 mLayout->undoStack()->endCommand();
312 emit dataChanged( index, index, QVector<int>() << role );
313 return true;
314 }
315
316 case static_cast< int >( CustomRole::LayoutPosition ):
317 {
318 bool ok = false;
319 double newPos = value.toDouble( &ok );
320 if ( !ok )
321 return false;
322
323 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
324 whileBlocking( guide )->setLayoutPosition( newPos );
325 mLayout->undoStack()->endCommand();
326 emit dataChanged( index, index, QVector<int>() << role );
327 return true;
328 }
329
330 case static_cast< int >( CustomRole::Units ):
331 {
332 bool ok = false;
333 int units = value.toInt( &ok );
334 if ( !ok )
335 return false;
336
337 QgsLayoutMeasurement m = guide->position();
338 m.setUnits( static_cast< Qgis::LayoutUnit >( units ) );
339 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
340 whileBlocking( guide )->setPosition( m );
341 guide->update();
342 mLayout->undoStack()->endCommand();
343 emit dataChanged( index, index, QVector<int>() << role );
344 return true;
345 }
346
347 default:
348 break;
349 }
350
351 return false;
352}
353
354Qt::ItemFlags QgsLayoutGuideCollection::flags( const QModelIndex &index ) const
355{
356 if ( !index.isValid() )
357 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
358 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
359}
360
361QVariant QgsLayoutGuideCollection::headerData( int section, Qt::Orientation orientation, int role ) const
362{
363 if ( role == Qt::DisplayRole )
364 return QVariant();
365 else if ( role == Qt::SizeHintRole )
366 {
367 return QSize( mHeaderSize, mHeaderSize );
368 }
369 return QAbstractTableModel::headerData( section, orientation, role );
370}
371
372bool QgsLayoutGuideCollection::removeRows( int row, int count, const QModelIndex &parent )
373{
374 if ( parent.isValid() )
375 return false;
376
377 if ( !mBlockUndoCommands )
378 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Remove Guide(s)" ), Remove + row );
379 beginRemoveRows( parent, row, row + count - 1 );
380 for ( int i = 0; i < count; ++ i )
381 {
382 delete mGuides.takeAt( row );
383 }
384 endRemoveRows();
385 if ( !mBlockUndoCommands )
386 mLayout->undoStack()->endCommand();
387 return true;
388}
389
391{
392 if ( guide->layout() != mLayout )
393 guide->setLayout( mLayout );
394
395 if ( !mBlockUndoCommands )
396 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Create Guide" ) );
397 beginInsertRows( QModelIndex(), mGuides.count(), mGuides.count() );
398 mGuides.append( guide );
399 endInsertRows();
400 if ( !mBlockUndoCommands )
401 mLayout->undoStack()->endCommand();
402
403 QModelIndex index = createIndex( mGuides.length() - 1, 0 );
404 connect( guide, &QgsLayoutGuide::positionChanged, this, [ this, index ]
405 {
406 emit dataChanged( index, index );
407 } );
408}
409
411{
412 int row = mGuides.indexOf( guide );
413 if ( row < 0 )
414 return;
415
416 removeRow( row );
417}
418
420{
421 int row = mGuides.indexOf( guide );
422 if ( row < 0 )
423 return;
424
425 setData( index( row, 0 ), position, static_cast< int >( CustomRole::LayoutPosition ) );
426}
427
429{
430 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Clear Guides" ) );
431 beginResetModel();
432 qDeleteAll( mGuides );
433 mGuides.clear();
434 endResetModel();
435 mLayout->undoStack()->endCommand();
436}
437
439{
440 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Apply Guides" ) );
441 mBlockUndoCommands = true;
442 QgsLayoutItemPage *page = mPageCollection->page( sourcePage );
443 // remove other page's guides
444 const auto constMGuides = mGuides;
445 for ( QgsLayoutGuide *guide : constMGuides )
446 {
447 if ( guide->page() != page )
448 removeGuide( guide );
449 }
450
451 // remaining guides belong to source page - clone them to other pages
452 const auto constMGuidesNew = mGuides;
453 for ( QgsLayoutGuide *guide : constMGuidesNew )
454 {
455 for ( int p = 0; p < mPageCollection->pageCount(); ++p )
456 {
457 if ( p == sourcePage )
458 continue;
459
460 std::unique_ptr< QgsLayoutGuide> newGuide( new QgsLayoutGuide( guide->orientation(), guide->position(), mPageCollection->page( p ) ) );
461 newGuide->setLayout( mLayout );
462 if ( newGuide->item()->isVisible() )
463 {
464 // if invisible, new guide is outside of page bounds
465 addGuide( newGuide.release() );
466 }
467 }
468 }
469 mLayout->undoStack()->endCommand();
470 mBlockUndoCommands = false;
471}
472
474{
475 const auto constMGuides = mGuides;
476 for ( QgsLayoutGuide *guide : constMGuides )
477 {
478 guide->update();
479 }
480}
481
482QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guides()
483{
484 return mGuides;
485}
486
487QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guides( Qt::Orientation orientation, int page )
488{
489 QList<QgsLayoutGuide *> res;
490 const auto constMGuides = mGuides;
491 for ( QgsLayoutGuide *guide : constMGuides )
492 {
493 if ( guide->orientation() == orientation && guide->item()->isVisible() &&
494 ( page < 0 || mPageCollection->page( page ) == guide->page() ) )
495 res << guide;
496 }
497 return res;
498}
499
500QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guidesOnPage( int page )
501{
502 QList<QgsLayoutGuide *> res;
503 const auto constMGuides = mGuides;
504 for ( QgsLayoutGuide *guide : constMGuides )
505 {
506 if ( mPageCollection->page( page ) == guide->page() )
507 res << guide;
508 }
509 return res;
510}
511
513{
514 return mGuidesVisible;
515}
516
518{
519 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Change Guide Visibility" ) );
520 mGuidesVisible = visible;
521 mLayout->undoStack()->endCommand();
522 update();
523}
524
525void QgsLayoutGuideCollection::pageAboutToBeRemoved( int pageNumber )
526{
527 mBlockUndoCommands = true;
528 const auto constGuidesOnPage = guidesOnPage( pageNumber );
529 for ( QgsLayoutGuide *guide : constGuidesOnPage )
530 {
531 removeGuide( guide );
532 }
533 mBlockUndoCommands = false;
534}
535
536bool QgsLayoutGuideCollection::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & ) const
537{
538 QDomElement element = document.createElement( QStringLiteral( "GuideCollection" ) );
539 element.setAttribute( QStringLiteral( "visible" ), mGuidesVisible );
540 const auto constMGuides = mGuides;
541 for ( QgsLayoutGuide *guide : constMGuides )
542 {
543 QDomElement guideElement = document.createElement( QStringLiteral( "Guide" ) );
544 guideElement.setAttribute( QStringLiteral( "orientation" ), guide->orientation() );
545 guideElement.setAttribute( QStringLiteral( "page" ), mPageCollection->pageNumber( guide->page() ) );
546 guideElement.setAttribute( QStringLiteral( "position" ), guide->position().length() );
547 guideElement.setAttribute( QStringLiteral( "units" ), QgsUnitTypes::encodeUnit( guide->position().units() ) );
548 element.appendChild( guideElement );
549 }
550
551 parentElement.appendChild( element );
552 return true;
553}
554
555bool QgsLayoutGuideCollection::readXml( const QDomElement &e, const QDomDocument &, const QgsReadWriteContext & )
556{
557 QDomElement element = e;
558 if ( element.nodeName() != QLatin1String( "GuideCollection" ) )
559 {
560 element = element.firstChildElement( QStringLiteral( "GuideCollection" ) );
561 }
562
563 if ( element.nodeName() != QLatin1String( "GuideCollection" ) )
564 {
565 return false;
566 }
567
568 mBlockUndoCommands = true;
569 beginResetModel();
570 qDeleteAll( mGuides );
571 mGuides.clear();
572
573 mGuidesVisible = element.attribute( QStringLiteral( "visible" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
574 QDomNodeList guideNodeList = element.elementsByTagName( QStringLiteral( "Guide" ) );
575 for ( int i = 0; i < guideNodeList.size(); ++i )
576 {
577 QDomElement element = guideNodeList.at( i ).toElement();
578 Qt::Orientation orientation = static_cast< Qt::Orientation >( element.attribute( QStringLiteral( "orientation" ), QStringLiteral( "1" ) ).toInt() );
579 double pos = element.attribute( QStringLiteral( "position" ), QStringLiteral( "0" ) ).toDouble();
580 Qgis::LayoutUnit unit = QgsUnitTypes::decodeLayoutUnit( element.attribute( QStringLiteral( "units" ) ) );
581 int page = element.attribute( QStringLiteral( "page" ), QStringLiteral( "0" ) ).toInt();
582 std::unique_ptr< QgsLayoutGuide > guide( new QgsLayoutGuide( orientation, QgsLayoutMeasurement( pos, unit ), mPageCollection->page( page ) ) );
583 guide->update();
584 addGuide( guide.release() );
585 }
586
587 endResetModel();
588 mBlockUndoCommands = false;
589 return true;
590}
591
592//
593// QgsLayoutGuideProxyModel
594//
595
596QgsLayoutGuideProxyModel::QgsLayoutGuideProxyModel( QObject *parent, Qt::Orientation orientation, int page )
597 : QSortFilterProxyModel( parent )
598 , mOrientation( orientation )
599 , mPage( page )
600{
601 setDynamicSortFilter( true );
602 sort( 0 );
603}
604
606{
607 mPage = page;
608 invalidateFilter();
609}
610
611bool QgsLayoutGuideProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
612{
613 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
614 const Qt::Orientation orientation = static_cast< Qt::Orientation>( sourceModel()->data( index, static_cast< int >( QgsLayoutGuideCollection::CustomRole::Orientation ) ).value< Qt::Orientation >() );
615 if ( orientation != mOrientation )
616 return false;
617
618 int page = sourceModel()->data( index, static_cast< int >( QgsLayoutGuideCollection::CustomRole::Page ) ).toInt();
619 return page == mPage;
620}
621
622bool QgsLayoutGuideProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
623{
624 double leftPos = sourceModel()->data( left, static_cast< int >( QgsLayoutGuideCollection::CustomRole::LayoutPosition ) ).toDouble();
625 double rightPos = sourceModel()->data( right, static_cast< int >( QgsLayoutGuideCollection::CustomRole::LayoutPosition ) ).toDouble();
626 return leftPos < rightPos;
627}
LayoutUnit
Layout measurement units.
Definition qgis.h:4859
bool writeXml(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores the collection's state in a DOM element.
int columnCount(const QModelIndex &) const override
void addGuide(QgsLayoutGuide *guide)
Adds a guide to the collection.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QgsLayout * layout() override
Returns the layout the object belongs to.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
QgsLayoutGuideCollection(QgsLayout *layout, QgsLayoutPageCollection *pageCollection)
Constructor for QgsLayoutGuideCollection belonging to the specified layout, and linked to the specifi...
bool readXml(const QDomElement &collectionElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets the collection's state from a DOM element.
@ LayoutPosition
Guide position in layout coordinates.
@ Orientation
Guide orientation role.
@ Units
Guide position units role.
QVariant data(const QModelIndex &index, int role) const override
void applyGuidesToAllOtherPages(int sourcePage)
Resets all other pages' guides to match the guides from the specified sourcePage.
void removeGuide(QgsLayoutGuide *guide)
Removes the specified guide, and deletes it.
QList< QgsLayoutGuide * > guidesOnPage(int page)
Returns the list of guides contained on a matching page.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
void setGuideLayoutPosition(QgsLayoutGuide *guide, double position)
Sets the absolute position (in layout coordinates) for guide within the layout.
QList< QgsLayoutGuide * > guides()
Returns a list of all guides contained in the collection.
int rowCount(const QModelIndex &) const override
void clear()
Removes all guides from the collection.
Qt::ItemFlags flags(const QModelIndex &index) const override
bool visible() const
Returns true if the guide lines should be drawn.
void update()
Updates the position (and visibility) of all guide line items.
void setVisible(bool visible)
Sets whether the guide lines should be visible.
void setPage(int page)
Sets the current page for filtering matching guides.
QgsLayoutGuideProxyModel(QObject *parent, Qt::Orientation orientation, int page)
Constructor for QgsLayoutGuideProxyModel, filtered to guides of the specified orientation and page on...
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
Contains the configuration for a single snap guide used by a layout.
QgsLayoutMeasurement position() const
Returns the guide's position within the page.
QgsLayoutItemPage * page()
Returns the page the guide is contained within.
Qt::Orientation orientation() const
Returns the guide's orientation.
QgsLayout * layout() const
Returns the layout the guide belongs to.
void setLayout(QgsLayout *layout)
Sets the layout the guide belongs to.
void setLayoutPosition(double position)
Sets the guide's position in absolute layout units.
void setPage(QgsLayoutItemPage *page)
Sets the page the guide is contained within.
void setPosition(QgsLayoutMeasurement position)
Sets the guide's position within the page.
QgsLayoutGuide(Qt::Orientation orientation, QgsLayoutMeasurement position, QgsLayoutItemPage *page)
Constructor for a new guide with the specified orientation and initial position.
double layoutPosition() const
Returns the guide's position in absolute layout units.
void positionChanged()
Emitted when the guide's position is changed.
void update()
Updates the position of the guide's line item.
QGraphicsLineItem * item()
Returns the guide's line item.
Item representing the paper in a layout.
int page() const
Returns the page the item is currently on, with the first page returning 0.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
void setLength(const double length)
Sets the length of the measurement.
Qgis::LayoutUnit units() const
Returns the units for the measurement.
void setUnits(const Qgis::LayoutUnit units)
Sets the units for the measurement.
double length() const
Returns the length of the measurement.
A manager for a collection of pages in a layout.
void pageAboutToBeRemoved(int pageNumber)
Emitted just before a page is removed from the collection.
int pageCount() const
Returns the number of pages in the collection.
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
int pageNumber(QgsLayoutItemPage *page) const
Returns the page number for the specified page, or -1 if the page is not contained in the collection.
void endCommand()
Saves final state of an object and pushes the active command to the undo history.
void beginCommand(QgsLayoutUndoObjectInterface *object, const QString &commandText, int id=0)
Begins a new undo command for the specified object.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
@ ZGuide
Z-value for page guides.
Definition qgslayout.h:60
QgsLayoutUndoStack * undoStack()
Returns a pointer to the layout's undo stack, which manages undo/redo states for the layout and it's ...
The class is used as a container of context for various read/write operations on other objects.
static Q_INVOKABLE QString toAbbreviatedString(Qgis::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
static Q_INVOKABLE Qgis::LayoutUnit decodeLayoutUnit(const QString &string, bool *ok=nullptr)
Decodes a layout unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5917
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5821