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