QGIS API Documentation 3.37.0-Master (684a802617f)
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 "qgslayout.h"
19#include "qgsreadwritecontext.h"
21#include "qgslayoutundostack.h"
22#include "qgsunittypes.h"
23
24#include <QGraphicsLineItem>
25
26
27//
28// QgsLayoutGuide
29//
30
31QgsLayoutGuide::QgsLayoutGuide( Qt::Orientation orientation, QgsLayoutMeasurement position, QgsLayoutItemPage *page )
32 : QObject( nullptr )
33 , mOrientation( orientation )
34 , mPosition( position )
35 , mPage( page )
36{}
37
39{
40 if ( mLayout && mLineItem )
41 {
42 mLayout->removeItem( mLineItem );
43 delete mLineItem;
44 }
45}
46
48{
49 return mPosition;
50}
51
53{
54 mPosition = position;
55 update();
56 emit positionChanged();
57}
58
60{
61 return mPage;
62}
63
65{
66 mPage = page;
67 update();
68}
69
71{
72 if ( !mLayout || !mLineItem )
73 return;
74
75 // first find matching page
76 if ( !mPage )
77 {
78 mLineItem->hide();
79 return;
80 }
81
82 double layoutPos = mLayout->convertToLayoutUnits( mPosition );
83 bool showGuide = mLayout->guides().visible();
84 switch ( mOrientation )
85 {
86 case Qt::Horizontal:
87 if ( layoutPos > mPage->rect().height() )
88 {
89 mLineItem->hide();
90 }
91 else
92 {
93 mLineItem->setLine( 0, layoutPos + mPage->y(), mPage->rect().width(), layoutPos + mPage->y() );
94 mLineItem->setVisible( showGuide );
95 }
96
97 break;
98
99 case Qt::Vertical:
100 if ( layoutPos > mPage->rect().width() )
101 {
102 mLineItem->hide();
103 }
104 else
105 {
106 mLineItem->setLine( layoutPos, mPage->y(), layoutPos, mPage->y() + mPage->rect().height() );
107 mLineItem->setVisible( showGuide );
108 }
109
110 break;
111 }
112}
113
114QGraphicsLineItem *QgsLayoutGuide::item()
115{
116 return mLineItem;
117}
118
120{
121 if ( !mLineItem )
122 return -999;
123
124 switch ( mOrientation )
125 {
126 case Qt::Horizontal:
127 return mLineItem->mapToScene( mLineItem->line().p1() ).y();
128
129 case Qt::Vertical:
130 return mLineItem->mapToScene( mLineItem->line().p1() ).x();
131 }
132 return -999; // avoid warning
133}
134
136{
137 if ( !mLayout )
138 return;
139
140 double p = 0;
141 switch ( mOrientation )
142 {
143 case Qt::Horizontal:
144 p = mPage->mapFromScene( QPointF( 0, position ) ).y();
145 break;
146
147 case Qt::Vertical:
148 p = mPage->mapFromScene( QPointF( position, 0 ) ).x();
149 break;
150 }
151 mPosition = mLayout->convertFromLayoutUnits( p, mPosition.units() );
152 update();
153 emit positionChanged();
154}
155
157{
158 return mLayout;
159}
160
162{
163 mLayout = layout;
164
165 if ( !mLineItem )
166 {
167 mLineItem = new QGraphicsLineItem();
168 mLineItem->hide();
169 mLineItem->setZValue( QgsLayout::ZGuide );
170 QPen linePen( Qt::DotLine );
171 linePen.setColor( Qt::red );
172 // use a pen width of 0, since this activates a cosmetic pen
173 // which doesn't scale with the layout and keeps a constant size
174 linePen.setWidthF( 0 );
175 mLineItem->setPen( linePen );
176 }
177
178 mLayout->addItem( mLineItem );
179 update();
180}
181
182Qt::Orientation QgsLayoutGuide::orientation() const
183{
184 return mOrientation;
185}
186
187
188
189//
190// QgsLayoutGuideCollection
191//
192
194 : QAbstractTableModel( layout )
195 , mLayout( layout )
196 , mPageCollection( pageCollection )
197{
198 QFont f;
199 mHeaderSize = QFontMetrics( f ).boundingRect( QStringLiteral( "XX" ) ).width();
200
201 connect( mPageCollection, &QgsLayoutPageCollection::pageAboutToBeRemoved, this, &QgsLayoutGuideCollection::pageAboutToBeRemoved );
202}
203
205{
206 qDeleteAll( mGuides );
207}
208
210{
211 return mLayout;
212}
213
214int QgsLayoutGuideCollection::rowCount( const QModelIndex & ) const
215{
216 return mGuides.count();
217}
218
219int QgsLayoutGuideCollection::columnCount( const QModelIndex &parent ) const
220{
221 if ( parent.isValid() )
222 return 0;
223
224 return 2;
225}
226
227QVariant QgsLayoutGuideCollection::data( const QModelIndex &index, int role ) const
228{
229 if ( !index.isValid() )
230 return QVariant();
231
232 if ( index.row() >= mGuides.count() || index.row() < 0 )
233 return QVariant();
234
235 QgsLayoutGuide *guide = mGuides.at( index.row() );
236 switch ( role )
237 {
238 case Qt::DisplayRole:
239 case Qt::EditRole:
240 {
241 if ( index.column() == 0 )
242 return guide->position().length();
243 else
245 }
246
247 case static_cast< int >( CustomRole::Orientation ):
248 return QVariant::fromValue( guide->orientation() );
249
250 case static_cast< int >( CustomRole::Position ):
251 return guide->position().length();
252
253 case static_cast< int >( CustomRole::Units ):
254 return static_cast< int >( guide->position().units() );
255
256 case static_cast< int >( CustomRole::Page ):
257 return mPageCollection->pageNumber( guide->page() );
258
259 case static_cast< int >( CustomRole::LayoutPosition ):
260 return guide->layoutPosition();
261
262 default:
263 return QVariant();
264 }
265}
266
267bool QgsLayoutGuideCollection::setData( const QModelIndex &index, const QVariant &value, int role )
268{
269 if ( !index.isValid() )
270 return false;
271
272 if ( index.row() >= mGuides.count() || index.row() < 0 )
273 return false;
274
275 QgsLayoutGuide *guide = mGuides.at( index.row() );
276
277 switch ( role )
278 {
279 case Qt::EditRole:
280 {
281 bool ok = false;
282 double newPos = value.toDouble( &ok );
283 if ( !ok )
284 return false;
285
286 QgsLayoutMeasurement m = guide->position();
287 m.setLength( newPos );
288 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
289 whileBlocking( guide )->setPosition( m );
290 guide->update();
291 mLayout->undoStack()->endCommand();
292 emit dataChanged( index, index, QVector<int>() << role );
293 return true;
294 }
295 case static_cast< int >( CustomRole::Position ):
296 {
297 bool ok = false;
298 double newPos = value.toDouble( &ok );
299 if ( !ok )
300 return false;
301
302 QgsLayoutMeasurement m = guide->position();
303 if ( qgsDoubleNear( m.length(), newPos ) )
304 return true;
305
306 m.setLength( newPos );
307 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
308 whileBlocking( guide )->setPosition( m );
309 guide->update();
310 mLayout->undoStack()->endCommand();
311 emit dataChanged( index, index, QVector<int>() << role );
312 return true;
313 }
314
315 case static_cast< int >( CustomRole::LayoutPosition ):
316 {
317 bool ok = false;
318 double newPos = value.toDouble( &ok );
319 if ( !ok )
320 return false;
321
322 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
323 whileBlocking( guide )->setLayoutPosition( newPos );
324 mLayout->undoStack()->endCommand();
325 emit dataChanged( index, index, QVector<int>() << role );
326 return true;
327 }
328
329 case static_cast< int >( CustomRole::Units ):
330 {
331 bool ok = false;
332 int units = value.toInt( &ok );
333 if ( !ok )
334 return false;
335
336 QgsLayoutMeasurement m = guide->position();
337 m.setUnits( static_cast< Qgis::LayoutUnit >( units ) );
338 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Move Guide" ), Move + index.row() );
339 whileBlocking( guide )->setPosition( m );
340 guide->update();
341 mLayout->undoStack()->endCommand();
342 emit dataChanged( index, index, QVector<int>() << role );
343 return true;
344 }
345
346 default:
347 break;
348 }
349
350 return false;
351}
352
353Qt::ItemFlags QgsLayoutGuideCollection::flags( const QModelIndex &index ) const
354{
355 if ( !index.isValid() )
356 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
357 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
358}
359
360QVariant QgsLayoutGuideCollection::headerData( int section, Qt::Orientation orientation, int role ) const
361{
362 if ( role == Qt::DisplayRole )
363 return QVariant();
364 else if ( role == Qt::SizeHintRole )
365 {
366 return QSize( mHeaderSize, mHeaderSize );
367 }
368 return QAbstractTableModel::headerData( section, orientation, role );
369}
370
371bool QgsLayoutGuideCollection::removeRows( int row, int count, const QModelIndex &parent )
372{
373 if ( parent.isValid() )
374 return false;
375
376 if ( !mBlockUndoCommands )
377 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Remove Guide(s)" ), Remove + row );
378 beginRemoveRows( parent, row, row + count - 1 );
379 for ( int i = 0; i < count; ++ i )
380 {
381 delete mGuides.takeAt( row );
382 }
383 endRemoveRows();
384 if ( !mBlockUndoCommands )
385 mLayout->undoStack()->endCommand();
386 return true;
387}
388
390{
391 if ( guide->layout() != mLayout )
392 guide->setLayout( mLayout );
393
394 if ( !mBlockUndoCommands )
395 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Create Guide" ) );
396 beginInsertRows( QModelIndex(), mGuides.count(), mGuides.count() );
397 mGuides.append( guide );
398 endInsertRows();
399 if ( !mBlockUndoCommands )
400 mLayout->undoStack()->endCommand();
401
402 QModelIndex index = createIndex( mGuides.length() - 1, 0 );
403 connect( guide, &QgsLayoutGuide::positionChanged, this, [ this, index ]
404 {
405 emit dataChanged( index, index );
406 } );
407}
408
410{
411 int row = mGuides.indexOf( guide );
412 if ( row < 0 )
413 return;
414
415 removeRow( row );
416}
417
419{
420 int row = mGuides.indexOf( guide );
421 if ( row < 0 )
422 return;
423
424 setData( index( row, 0 ), position, static_cast< int >( CustomRole::LayoutPosition ) );
425}
426
428{
429 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Clear Guides" ) );
430 beginResetModel();
431 qDeleteAll( mGuides );
432 mGuides.clear();
433 endResetModel();
434 mLayout->undoStack()->endCommand();
435}
436
438{
439 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Apply Guides" ) );
440 mBlockUndoCommands = true;
441 QgsLayoutItemPage *page = mPageCollection->page( sourcePage );
442 // remove other page's guides
443 const auto constMGuides = mGuides;
444 for ( QgsLayoutGuide *guide : constMGuides )
445 {
446 if ( guide->page() != page )
447 removeGuide( guide );
448 }
449
450 // remaining guides belong to source page - clone them to other pages
451 const auto constMGuidesNew = mGuides;
452 for ( QgsLayoutGuide *guide : constMGuidesNew )
453 {
454 for ( int p = 0; p < mPageCollection->pageCount(); ++p )
455 {
456 if ( p == sourcePage )
457 continue;
458
459 std::unique_ptr< QgsLayoutGuide> newGuide( new QgsLayoutGuide( guide->orientation(), guide->position(), mPageCollection->page( p ) ) );
460 newGuide->setLayout( mLayout );
461 if ( newGuide->item()->isVisible() )
462 {
463 // if invisible, new guide is outside of page bounds
464 addGuide( newGuide.release() );
465 }
466 }
467 }
468 mLayout->undoStack()->endCommand();
469 mBlockUndoCommands = false;
470}
471
473{
474 const auto constMGuides = mGuides;
475 for ( QgsLayoutGuide *guide : constMGuides )
476 {
477 guide->update();
478 }
479}
480
481QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guides()
482{
483 return mGuides;
484}
485
486QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guides( Qt::Orientation orientation, int page )
487{
488 QList<QgsLayoutGuide *> res;
489 const auto constMGuides = mGuides;
490 for ( QgsLayoutGuide *guide : constMGuides )
491 {
492 if ( guide->orientation() == orientation && guide->item()->isVisible() &&
493 ( page < 0 || mPageCollection->page( page ) == guide->page() ) )
494 res << guide;
495 }
496 return res;
497}
498
499QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guidesOnPage( int page )
500{
501 QList<QgsLayoutGuide *> res;
502 const auto constMGuides = mGuides;
503 for ( QgsLayoutGuide *guide : constMGuides )
504 {
505 if ( mPageCollection->page( page ) == guide->page() )
506 res << guide;
507 }
508 return res;
509}
510
512{
513 return mGuidesVisible;
514}
515
517{
518 mLayout->undoStack()->beginCommand( mPageCollection, tr( "Change Guide Visibility" ) );
519 mGuidesVisible = visible;
520 mLayout->undoStack()->endCommand();
521 update();
522}
523
524void QgsLayoutGuideCollection::pageAboutToBeRemoved( int pageNumber )
525{
526 mBlockUndoCommands = true;
527 const auto constGuidesOnPage = guidesOnPage( pageNumber );
528 for ( QgsLayoutGuide *guide : constGuidesOnPage )
529 {
530 removeGuide( guide );
531 }
532 mBlockUndoCommands = false;
533}
534
535bool QgsLayoutGuideCollection::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & ) const
536{
537 QDomElement element = document.createElement( QStringLiteral( "GuideCollection" ) );
538 element.setAttribute( QStringLiteral( "visible" ), mGuidesVisible );
539 const auto constMGuides = mGuides;
540 for ( QgsLayoutGuide *guide : constMGuides )
541 {
542 QDomElement guideElement = document.createElement( QStringLiteral( "Guide" ) );
543 guideElement.setAttribute( QStringLiteral( "orientation" ), guide->orientation() );
544 guideElement.setAttribute( QStringLiteral( "page" ), mPageCollection->pageNumber( guide->page() ) );
545 guideElement.setAttribute( QStringLiteral( "position" ), guide->position().length() );
546 guideElement.setAttribute( QStringLiteral( "units" ), QgsUnitTypes::encodeUnit( guide->position().units() ) );
547 element.appendChild( guideElement );
548 }
549
550 parentElement.appendChild( element );
551 return true;
552}
553
554bool QgsLayoutGuideCollection::readXml( const QDomElement &e, const QDomDocument &, const QgsReadWriteContext & )
555{
556 QDomElement element = e;
557 if ( element.nodeName() != QLatin1String( "GuideCollection" ) )
558 {
559 element = element.firstChildElement( QStringLiteral( "GuideCollection" ) );
560 }
561
562 if ( element.nodeName() != QLatin1String( "GuideCollection" ) )
563 {
564 return false;
565 }
566
567 mBlockUndoCommands = true;
568 beginResetModel();
569 qDeleteAll( mGuides );
570 mGuides.clear();
571
572 mGuidesVisible = element.attribute( QStringLiteral( "visible" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
573 QDomNodeList guideNodeList = element.elementsByTagName( QStringLiteral( "Guide" ) );
574 for ( int i = 0; i < guideNodeList.size(); ++i )
575 {
576 QDomElement element = guideNodeList.at( i ).toElement();
577 Qt::Orientation orientation = static_cast< Qt::Orientation >( element.attribute( QStringLiteral( "orientation" ), QStringLiteral( "1" ) ).toInt() );
578 double pos = element.attribute( QStringLiteral( "position" ), QStringLiteral( "0" ) ).toDouble();
579 Qgis::LayoutUnit unit = QgsUnitTypes::decodeLayoutUnit( element.attribute( QStringLiteral( "units" ) ) );
580 int page = element.attribute( QStringLiteral( "page" ), QStringLiteral( "0" ) ).toInt();
581 std::unique_ptr< QgsLayoutGuide > guide( new QgsLayoutGuide( orientation, QgsLayoutMeasurement( pos, unit ), mPageCollection->page( page ) ) );
582 guide->update();
583 addGuide( guide.release() );
584 }
585
586 endResetModel();
587 mBlockUndoCommands = false;
588 return true;
589}
590
591//
592// QgsLayoutGuideProxyModel
593//
594
595QgsLayoutGuideProxyModel::QgsLayoutGuideProxyModel( QObject *parent, Qt::Orientation orientation, int page )
596 : QSortFilterProxyModel( parent )
597 , mOrientation( orientation )
598 , mPage( page )
599{
600 setDynamicSortFilter( true );
601 sort( 0 );
602}
603
605{
606 mPage = page;
607 invalidateFilter();
608}
609
610bool QgsLayoutGuideProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
611{
612 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
613 const Qt::Orientation orientation = static_cast< Qt::Orientation>( sourceModel()->data( index, static_cast< int >( QgsLayoutGuideCollection::CustomRole::Orientation ) ).value< Qt::Orientation >() );
614 if ( orientation != mOrientation )
615 return false;
616
617 int page = sourceModel()->data( index, static_cast< int >( QgsLayoutGuideCollection::CustomRole::Page ) ).toInt();
618 return page == mPage;
619}
620
621bool QgsLayoutGuideProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
622{
623 double leftPos = sourceModel()->data( left, static_cast< int >( QgsLayoutGuideCollection::CustomRole::LayoutPosition ) ).toDouble();
624 double rightPos = sourceModel()->data( right, static_cast< int >( QgsLayoutGuideCollection::CustomRole::LayoutPosition ) ).toDouble();
625 return leftPos < rightPos;
626}
LayoutUnit
Layout measurement units.
Definition qgis.h:4509
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:5445
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5349