QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgslayout.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayout.cpp
3 -------------------
4 begin : June 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
17#include "qgslayout.h"
18
19#include <memory>
20
23#include "qgslayoutframe.h"
25#include "qgslayoutitem.h"
26#include "qgslayoutitemgroup.h"
28#include "qgslayoutitemhtml.h"
29#include "qgslayoutitemlabel.h"
30#include "qgslayoutitemmap.h"
32#include "qgslayoutmodel.h"
33#include "qgslayoutmultiframe.h"
37#include "qgslayoutundostack.h"
38#include "qgsproject.h"
39#include "qgsreadwritecontext.h"
40#include "qgsruntimeprofiler.h"
42#include "qgssettingstree.h"
44#include "qgsunittypes.h"
45
46#include <QString>
47
48#include "moc_qgslayout.cpp"
49
50using namespace Qt::StringLiterals;
51
53 = new QgsSettingsEntryStringList( u"search-paths-for-templates"_s, QgsSettingsTree::sTreeLayout, QStringList(), QObject::tr( "Search path for templates" ) );
54
56 : mProject( project )
57 , mRenderContext( new QgsLayoutRenderContext( this ) )
58 , mReportContext( new QgsLayoutReportContext( this ) )
59 , mSnapper( QgsLayoutSnapper( this ) )
60 , mGridSettings( this )
61 , mPageCollection( new QgsLayoutPageCollection( this ) )
62 , mUndoStack( new QgsLayoutUndoStack( this ) )
63{
64 // just to make sure - this should be the default, but maybe it'll change in some future Qt version...
65 setBackgroundBrush( Qt::NoBrush );
66 mItemsModel = std::make_unique<QgsLayoutModel>( this );
67}
68
70{
71 // no need for undo commands when we're destroying the layout
72 mUndoStack->blockCommands( true );
73
74 deleteAndRemoveMultiFrames();
75
76 // make sure that all layout items are removed before
77 // this class is deconstructed - to avoid segfaults
78 // when layout items access in destructor layout that isn't valid anymore
79
80 // since deletion of some item types (e.g. groups) trigger deletion
81 // of other items, we have to do this careful, one at a time...
82 QList<QGraphicsItem *> itemList = items();
83 bool deleted = true;
84 while ( deleted )
85 {
86 deleted = false;
87 for ( QGraphicsItem *item : std::as_const( itemList ) )
88 {
89 if ( dynamic_cast< QgsLayoutItem * >( item ) && !dynamic_cast< QgsLayoutItemPage *>( item ) )
90 {
91 delete item;
92 deleted = true;
93 break;
94 }
95 }
96 itemList = items();
97 }
98
99 mItemsModel.reset(); // manually delete, so we can control order of destruction
100}
101
103{
104 QDomDocument currentDoc;
105
106 QgsReadWriteContext context;
107 QDomElement elem = writeXml( currentDoc, context );
108 currentDoc.appendChild( elem );
109
110 auto newLayout = std::make_unique< QgsLayout >( mProject );
111 bool ok = false;
112 newLayout->loadFromTemplate( currentDoc, context, true, &ok );
113 if ( !ok )
114 {
115 return nullptr;
116 }
117
118 return newLayout.release();
119}
120
122{
123 // default to a A4 landscape page
124 QgsLayoutItemPage *page = new QgsLayoutItemPage( this );
126 mPageCollection->addPage( page );
127 mUndoStack->stack()->clear();
128}
129
131{
132 deleteAndRemoveMultiFrames();
133
134 //delete all non paper items
135 const QList<QGraphicsItem *> itemList = items();
136 for ( QGraphicsItem *item : itemList )
137 {
138 QgsLayoutItem *cItem = dynamic_cast<QgsLayoutItem *>( item );
139 QgsLayoutItemPage *pItem = dynamic_cast<QgsLayoutItemPage *>( item );
140 if ( cItem && !pItem )
141 {
142 removeLayoutItemPrivate( cItem );
143 }
144 }
145 mItemsModel->clear();
146
147 mPageCollection->clear();
148 mUndoStack->stack()->clear();
149}
150
152{
153 return mProject;
154}
155
157{
158 return mItemsModel.get();
159}
160
161QList<QgsLayoutItem *> QgsLayout::selectedLayoutItems( const bool includeLockedItems )
162{
163 QList<QgsLayoutItem *> layoutItemList;
164
165 const QList<QGraphicsItem *> graphicsItemList = selectedItems();
166 for ( QGraphicsItem *item : graphicsItemList )
167 {
168 QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
169 if ( layoutItem && ( includeLockedItems || !layoutItem->isLocked() ) )
170 {
171 layoutItemList.push_back( layoutItem );
172 }
173 }
174
175 return layoutItemList;
176}
177
179{
180 whileBlocking( this )->deselectAll();
181 if ( item )
182 {
183 item->setSelected( true );
184 }
185 emit selectedItemChanged( item );
186}
187
189{
190 //we can't use QGraphicsScene::clearSelection, as that emits no signals
191 //and we don't know which items are being deselected
192 //accordingly, we can't inform the layout model of selection changes
193 //instead, do the clear selection manually...
194 const QList<QGraphicsItem *> selectedItemList = selectedItems();
195 for ( QGraphicsItem *item : selectedItemList )
196 {
197 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
198 {
199 layoutItem->setSelected( false );
200 }
201 }
202 emit selectedItemChanged( nullptr );
203}
204
205bool QgsLayout::raiseItem( QgsLayoutItem *item, bool deferUpdate )
206{
207 //model handles reordering items
208 bool result = mItemsModel->reorderItemUp( item );
209 if ( result && !deferUpdate )
210 {
211 //update all positions
213 update();
214 }
215 return result;
216}
217
218bool QgsLayout::lowerItem( QgsLayoutItem *item, bool deferUpdate )
219{
220 //model handles reordering items
221 bool result = mItemsModel->reorderItemDown( item );
222 if ( result && !deferUpdate )
223 {
224 //update all positions
226 update();
227 }
228 return result;
229}
230
231bool QgsLayout::moveItemToTop( QgsLayoutItem *item, bool deferUpdate )
232{
233 //model handles reordering items
234 bool result = mItemsModel->reorderItemToTop( item );
235 if ( result && !deferUpdate )
236 {
237 //update all positions
239 update();
240 }
241 return result;
242}
243
244bool QgsLayout::moveItemToBottom( QgsLayoutItem *item, bool deferUpdate )
245{
246 //model handles reordering items
247 bool result = mItemsModel->reorderItemToBottom( item );
248 if ( result && !deferUpdate )
249 {
250 //update all positions
252 update();
253 }
254 return result;
255}
256
257QgsLayoutItem *QgsLayout::itemByUuid( const QString &uuid, bool includeTemplateUuids ) const
258{
259 QList<QgsLayoutItem *> itemList;
260 layoutItems( itemList );
261 for ( QgsLayoutItem *item : std::as_const( itemList ) )
262 {
263 if ( item->uuid() == uuid )
264 return item;
265 else if ( includeTemplateUuids && item->mTemplateUuid == uuid )
266 return item;
267 }
268
269 return nullptr;
270}
271
272QgsLayoutItem *QgsLayout::itemByTemplateUuid( const QString &uuid ) const
273{
274 QList<QgsLayoutItem *> itemList;
275 layoutItems( itemList );
276 for ( QgsLayoutItem *item : std::as_const( itemList ) )
277 {
278 if ( item->mTemplateUuid == uuid )
279 return item;
280 }
281
282 return nullptr;
283}
284
285QgsLayoutItem *QgsLayout::itemById( const QString &id ) const
286{
287 const QList<QGraphicsItem *> itemList = items();
288 for ( QGraphicsItem *item : itemList )
289 {
290 QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
291 if ( layoutItem && layoutItem->id() == id )
292 {
293 return layoutItem;
294 }
295 }
296 return nullptr;
297}
298
299QgsLayoutMultiFrame *QgsLayout::multiFrameByUuid( const QString &uuid, bool includeTemplateUuids ) const
300{
301 for ( QgsLayoutMultiFrame *mf : mMultiFrames )
302 {
303 if ( mf->uuid() == uuid )
304 return mf;
305 else if ( includeTemplateUuids && mf->mTemplateUuid == uuid )
306 return mf;
307 }
308
309 return nullptr;
310}
311
312QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const bool ignoreLocked, double searchTolerance ) const
313{
314 return layoutItemAt( position, nullptr, ignoreLocked, searchTolerance );
315}
316
317QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const QgsLayoutItem *belowItem, const bool ignoreLocked, double searchTolerance ) const
318{
319 //get a list of items which intersect the specified position, in descending z order
320 QList<QGraphicsItem *> itemList;
321 if ( searchTolerance == 0 )
322 {
323 itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
324 }
325 else
326 {
327 itemList = items( QRectF( position.x() - searchTolerance, position.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance ), Qt::IntersectsItemShape, Qt::DescendingOrder );
328 }
329
330 bool foundBelowItem = false;
331 for ( QGraphicsItem *graphicsItem : std::as_const( itemList ) )
332 {
333 QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( graphicsItem );
334 QgsLayoutItemPage *paperItem = dynamic_cast<QgsLayoutItemPage *>( layoutItem );
335 if ( layoutItem && !paperItem )
336 {
337 // If we are not checking for a an item below a specified item, or if we've
338 // already found that item, then we've found our target
339 if ( ( !belowItem || foundBelowItem ) && ( !ignoreLocked || !layoutItem->isLocked() ) )
340 {
341 // If ignoreLocked and item is part of a locked group, return the next item below
342 if ( ignoreLocked && layoutItem->parentGroup() && layoutItem->parentGroup()->isLocked() )
343 {
344 return layoutItemAt( position, layoutItem, ignoreLocked, searchTolerance );
345 }
346
347 return layoutItem;
348 }
349 else
350 {
351 if ( layoutItem == belowItem )
352 {
353 //Target item is next in list
354 foundBelowItem = true;
355 }
356 }
357 }
358 }
359 return nullptr;
360}
361
363{
364 return mRenderContext->measurementConverter().convert( measurement, mUnits ).length();
365}
366
368{
369 return mRenderContext->measurementConverter().convert( size, mUnits ).toQSizeF();
370}
371
373{
374 return mRenderContext->measurementConverter().convert( point, mUnits ).toQPointF();
375}
376
378{
379 return mRenderContext->measurementConverter().convert( QgsLayoutMeasurement( length, mUnits ), unit );
380}
381
383{
384 return mRenderContext->measurementConverter().convert( QgsLayoutSize( size.width(), size.height(), mUnits ), unit );
385}
386
388{
389 return mRenderContext->measurementConverter().convert( QgsLayoutPoint( point.x(), point.y(), mUnits ), unit );
390}
391
393{
394 return *mRenderContext;
395}
396
398{
399 return *mRenderContext;
400}
401
403{
404 return *mReportContext;
405}
406
408{
409 return *mReportContext;
410}
411
413{
414 mGridSettings.loadFromSettings();
415 mPageCollection->redraw();
416}
417
419{
420 return mPageCollection->guides();
421}
422
424{
425 return mPageCollection->guides();
426}
427
429{
433 if ( mReportContext->layer() )
434 context.appendScope( QgsExpressionContextUtils::layerScope( mReportContext->layer() ) );
435
437 return context;
438}
439
440void QgsLayout::setCustomProperty( const QString &key, const QVariant &value )
441{
442 mCustomProperties.setValue( key, value );
443
444 if ( key.startsWith( "variable"_L1 ) )
445 emit variablesChanged();
446}
447
448QVariant QgsLayout::customProperty( const QString &key, const QVariant &defaultValue ) const
449{
450 return mCustomProperties.value( key, defaultValue );
451}
452
453void QgsLayout::removeCustomProperty( const QString &key )
454{
455 mCustomProperties.remove( key );
456}
457
459{
460 return mCustomProperties.keys();
461}
462
464{
465 // prefer explicitly set reference map
466 if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( itemByUuid( mWorldFileMapId ) ) )
467 return map;
468
469 // else try to find largest map
470 QList< QgsLayoutItemMap * > maps;
471 layoutItems( maps );
472 QgsLayoutItemMap *largestMap = nullptr;
473 double largestMapArea = 0;
474 for ( QgsLayoutItemMap *map : std::as_const( maps ) )
475 {
476 double area = map->rect().width() * map->rect().height();
477 if ( area > largestMapArea )
478 {
479 largestMapArea = area;
480 largestMap = map;
481 }
482 }
483 return largestMap;
484}
485
487{
488 mWorldFileMapId = map ? map->uuid() : QString();
489 mProject->setDirty( true );
490}
491
493{
494 return mPageCollection.get();
495}
496
498{
499 return mPageCollection.get();
500}
501
502QRectF QgsLayout::layoutBounds( bool ignorePages, double margin ) const
503{
504 //start with an empty rectangle
505 QRectF bounds;
506
507 //add all layout items and pages which are in the layout
508 const auto constItems = items();
509 for ( const QGraphicsItem *item : constItems )
510 {
511 const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
512 if ( !layoutItem )
513 continue;
514
515 bool isPage = layoutItem->type() == QgsLayoutItemRegistry::LayoutPage;
516 if ( !isPage || !ignorePages )
517 {
518 //expand bounds with current item's bounds
519 QRectF itemBounds;
520 if ( isPage )
521 {
522 // for pages we only consider the item's rect - not the bounding rect
523 // as the bounding rect contains extra padding
524 itemBounds = layoutItem->mapToScene( layoutItem->rect() ).boundingRect();
525 }
526 else
527 itemBounds = item->sceneBoundingRect();
528
529 if ( bounds.isValid() )
530 bounds = bounds.united( itemBounds );
531 else
532 bounds = itemBounds;
533 }
534 }
535
536 if ( bounds.isValid() && margin > 0.0 )
537 {
538 //finally, expand bounds out by specified margin of page size
539 double maxWidth = mPageCollection->maximumPageWidth();
540 bounds.adjust( -maxWidth * margin, -maxWidth * margin, maxWidth * margin, maxWidth * margin );
541 }
542
543 return bounds;
544}
545
546QRectF QgsLayout::pageItemBounds( int page, bool visibleOnly ) const
547{
548 //start with an empty rectangle
549 QRectF bounds;
550
551 //add all QgsLayoutItems on page
552 const QList<QGraphicsItem *> itemList = items();
553 for ( QGraphicsItem *item : itemList )
554 {
555 const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
556 if ( layoutItem && layoutItem->type() != QgsLayoutItemRegistry::LayoutPage && layoutItem->page() == page )
557 {
558 if ( visibleOnly && !layoutItem->isVisible() )
559 continue;
560
561 //expand bounds with current item's bounds
562 if ( bounds.isValid() )
563 bounds = bounds.united( item->sceneBoundingRect() );
564 else
565 bounds = item->sceneBoundingRect();
566 }
567 }
568
569 return bounds;
570}
571
573{
574 addLayoutItemPrivate( item );
575 QString undoText;
576 if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( item->type() ) )
577 {
578 undoText = tr( "Create %1" ).arg( metadata->visibleName() );
579 }
580 else
581 {
582 undoText = tr( "Create Item" );
583 }
584 if ( !mUndoStack->isBlocked() )
585 mUndoStack->push( new QgsLayoutItemAddItemCommand( item, undoText ) );
586}
587
589{
590 std::unique_ptr< QgsLayoutItemDeleteUndoCommand > deleteCommand;
591 if ( !mUndoStack->isBlocked() )
592 {
593 mUndoStack->beginMacro( tr( "Delete Items" ) );
594 deleteCommand = std::make_unique<QgsLayoutItemDeleteUndoCommand>( item, tr( "Delete Item" ) );
595 }
596 removeLayoutItemPrivate( item );
597 if ( deleteCommand )
598 {
599 mUndoStack->push( deleteCommand.release() );
600 mUndoStack->endMacro();
601 }
602}
603
605{
606 if ( !multiFrame )
607 return;
608
609 if ( !mMultiFrames.contains( multiFrame ) )
610 mMultiFrames << multiFrame;
611}
612
614{
615 mMultiFrames.removeAll( multiFrame );
616}
617
618QList<QgsLayoutMultiFrame *> QgsLayout::multiFrames() const
619{
620 return mMultiFrames;
621}
622
623bool QgsLayout::saveAsTemplate( const QString &path, const QgsReadWriteContext &context ) const
624{
625 QFile templateFile( path );
626 if ( !templateFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
627 {
628 return false;
629 }
630
631 QDomDocument saveDocument;
632 QDomElement elem = writeXml( saveDocument, context );
633 saveDocument.appendChild( elem );
634
635 if ( templateFile.write( saveDocument.toByteArray() ) == -1 )
636 return false;
637
638 return true;
639}
640
641QList< QgsLayoutItem * > QgsLayout::loadFromTemplate( const QDomDocument &document, const QgsReadWriteContext &context, bool clearExisting, bool *ok )
642{
643 if ( ok )
644 *ok = false;
645
646 QList< QgsLayoutItem * > result;
647
648 if ( clearExisting )
649 {
650 clear();
651 }
652
653 QDomDocument doc;
654
655 // If this is a 2.x composition template, convert it to a layout template
657 {
658 doc = QgsCompositionConverter::convertCompositionTemplate( document, mProject );
659 }
660 else
661 {
662 doc = document;
663 }
664
665 // remove all uuid attributes since we don't want duplicates UUIDS
666 QDomNodeList itemsNodes = doc.elementsByTagName( u"LayoutItem"_s );
667 for ( int i = 0; i < itemsNodes.count(); ++i )
668 {
669 QDomNode itemNode = itemsNodes.at( i );
670 if ( itemNode.isElement() )
671 {
672 itemNode.toElement().removeAttribute( u"uuid"_s );
673 }
674 }
675 QDomNodeList multiFrameNodes = doc.elementsByTagName( u"LayoutMultiFrame"_s );
676 for ( int i = 0; i < multiFrameNodes.count(); ++i )
677 {
678 QDomNode multiFrameNode = multiFrameNodes.at( i );
679 if ( multiFrameNode.isElement() )
680 {
681 multiFrameNode.toElement().removeAttribute( u"uuid"_s );
682 QDomNodeList frameNodes = multiFrameNode.toElement().elementsByTagName( u"childFrame"_s );
683 QDomNode itemNode = frameNodes.at( i );
684 if ( itemNode.isElement() )
685 {
686 itemNode.toElement().removeAttribute( u"uuid"_s );
687 }
688 }
689 }
690
691 //read general settings
692 if ( clearExisting )
693 {
694 QDomElement layoutElem = doc.documentElement();
695 if ( layoutElem.isNull() )
696 {
697 return result;
698 }
699
700 bool loadOk = readXml( layoutElem, doc, context );
701 if ( !loadOk )
702 {
703 return result;
704 }
705 layoutItems( result );
706 }
707 else
708 {
709 result = addItemsFromXml( doc.documentElement(), doc, context );
710 }
711
712 if ( ok )
713 *ok = true;
714
715 return result;
716}
717
719{
720 return mUndoStack.get();
721}
722
724{
725 return mUndoStack.get();
726}
727
729class QgsLayoutUndoCommand : public QgsAbstractLayoutUndoCommand
730{
731 public:
732 QgsLayoutUndoCommand( QgsLayout *layout, const QString &text, int id, QUndoCommand *parent SIP_TRANSFERTHIS = nullptr )
733 : QgsAbstractLayoutUndoCommand( text, id, parent )
734 , mLayout( layout )
735 {}
736
737 protected:
738 void saveState( QDomDocument &stateDoc ) const override
739 {
740 stateDoc.clear();
741 QDomElement documentElement = stateDoc.createElement( u"UndoState"_s );
742 mLayout->writeXmlLayoutSettings( documentElement, stateDoc, QgsReadWriteContext() );
743 stateDoc.appendChild( documentElement );
744 }
745
746 void restoreState( QDomDocument &stateDoc ) override
747 {
748 if ( !mLayout )
749 {
750 return;
751 }
752
753 mLayout->readXmlLayoutSettings( stateDoc.documentElement(), stateDoc, QgsReadWriteContext() );
754 mLayout->project()->setDirty( true );
755 }
756
757 private:
758 QgsLayout *mLayout = nullptr;
759};
761
762QgsAbstractLayoutUndoCommand *QgsLayout::createCommand( const QString &text, int id, QUndoCommand *parent )
763{
764 return new QgsLayoutUndoCommand( this, text, id, parent );
765}
766
767QgsLayoutItemGroup *QgsLayout::groupItems( const QList<QgsLayoutItem *> &items )
768{
769 if ( items.size() < 2 )
770 {
771 //not enough items for a group
772 return nullptr;
773 }
774
775 mUndoStack->beginMacro( tr( "Group Items" ) );
776 auto itemGroup = std::make_unique<QgsLayoutItemGroup>( this );
777 for ( QgsLayoutItem *item : items )
778 {
779 itemGroup->addItem( item );
780 }
781 QgsLayoutItemGroup *returnGroup = itemGroup.get();
782 addLayoutItem( itemGroup.release() );
783
784 auto c = std::make_unique<QgsLayoutItemGroupUndoCommand>( QgsLayoutItemGroupUndoCommand::Grouped, returnGroup, this, tr( "Group Items" ) );
785 mUndoStack->push( c.release() );
786 mProject->setDirty( true );
787
788 mUndoStack->endMacro();
789
790 // cppcheck-suppress returnDanglingLifetime
791 return returnGroup;
792}
793
794QList<QgsLayoutItem *> QgsLayout::ungroupItems( QgsLayoutItemGroup *group )
795{
796 QList<QgsLayoutItem *> ungroupedItems;
797 if ( !group )
798 {
799 return ungroupedItems;
800 }
801
802 mUndoStack->beginMacro( tr( "Ungroup Items" ) );
803 // Call this before removing group items so it can keep note
804 // of contents
805 auto c = std::make_unique<QgsLayoutItemGroupUndoCommand>( QgsLayoutItemGroupUndoCommand::Ungrouped, group, this, tr( "Ungroup Items" ) );
806 mUndoStack->push( c.release() );
807
808 mProject->setDirty( true );
809
810 ungroupedItems = group->items();
811 group->removeItems();
812
813 removeLayoutItem( group );
814 mUndoStack->endMacro();
815
816 return ungroupedItems;
817}
818
820{
821 const QList< QGraphicsItem * > constItems = items();
822 for ( const QGraphicsItem *item : constItems )
823 {
824 const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
825 if ( !layoutItem )
826 continue;
827
828 if ( !layoutItem->accept( visitor ) )
829 return false;
830 }
831 return true;
832}
833
835{
836 mUndoStack->blockCommands( true );
837 mPageCollection->beginPageSizeChange();
838 emit refreshed();
839 mPageCollection->reflow();
840 mPageCollection->endPageSizeChange();
841 mUndoStack->blockCommands( false );
842 update();
843}
844
845void QgsLayout::writeXmlLayoutSettings( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
846{
847 mCustomProperties.writeXml( element, document );
848 element.setAttribute( u"units"_s, QgsUnitTypes::encodeUnit( mUnits ) );
849 element.setAttribute( u"worldFileMap"_s, mWorldFileMapId );
850 element.setAttribute( u"printResolution"_s, mRenderContext->dpi() );
851}
852
853QDomElement QgsLayout::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
854{
855 QDomElement element = document.createElement( u"Layout"_s );
856 auto save = [&]( const QgsLayoutSerializableObject *object ) -> bool { return object->writeXml( element, document, context ); };
857 save( &mSnapper );
858 save( &mGridSettings );
859 save( mPageCollection.get() );
860
861 //save items except paper items and frame items (they are saved with the corresponding multiframe)
862 const QList<QGraphicsItem *> itemList = items();
863 for ( const QGraphicsItem *graphicsItem : itemList )
864 {
865 if ( const QgsLayoutItem *item = dynamic_cast< const QgsLayoutItem *>( graphicsItem ) )
866 {
867 if ( item->type() == QgsLayoutItemRegistry::LayoutPage )
868 continue;
869
870 item->writeXml( element, document, context );
871 }
872 }
873
874 //save multiframes
875 for ( QgsLayoutMultiFrame *mf : mMultiFrames )
876 {
877 if ( mf->frameCount() > 0 )
878 mf->writeXml( element, document, context );
879 }
880
881 writeXmlLayoutSettings( element, document, context );
882 return element;
883}
884
885bool QgsLayout::readXmlLayoutSettings( const QDomElement &layoutElement, const QDomDocument &, const QgsReadWriteContext & )
886{
887 mCustomProperties.readXml( layoutElement );
888 setUnits( QgsUnitTypes::decodeLayoutUnit( layoutElement.attribute( u"units"_s ) ) );
889 mWorldFileMapId = layoutElement.attribute( u"worldFileMap"_s );
890 mRenderContext->setDpi( layoutElement.attribute( u"printResolution"_s, u"300"_s ).toDouble() );
891 emit changed();
892
893 return true;
894}
895
896void QgsLayout::addLayoutItemPrivate( QgsLayoutItem *item )
897{
898 addItem( item );
899 updateBounds();
900 mItemsModel->rebuildZList();
901 connect( item, &QgsLayoutItem::backgroundTaskCountChanged, this, &QgsLayout::itemBackgroundTaskCountChanged );
902 emit itemAdded( item );
903}
904
905void QgsLayout::removeLayoutItemPrivate( QgsLayoutItem *item )
906{
907 mItemsModel->setItemRemoved( item );
908 // small chance that item is still in a scene - the model may have
909 // rejected the removal for some reason. This is probably not necessary,
910 // but can't hurt...
911 if ( item->scene() )
912 removeItem( item );
913#if 0 //TODO
914 emit itemRemoved( item );
915#endif
916 item->cleanup();
917 item->deleteLater();
918}
919
920void QgsLayout::deleteAndRemoveMultiFrames()
921{
922 qDeleteAll( mMultiFrames );
923 mMultiFrames.clear();
924}
925
926QPointF QgsLayout::minPointFromXml( const QDomElement &elem ) const
927{
928 double minX = std::numeric_limits<double>::max();
929 double minY = std::numeric_limits<double>::max();
930 const QDomNodeList itemList = elem.elementsByTagName( u"LayoutItem"_s );
931 bool found = false;
932 for ( int i = 0; i < itemList.size(); ++i )
933 {
934 const QDomElement currentItemElem = itemList.at( i ).toElement();
935
936 QgsLayoutPoint pos = QgsLayoutPoint::decodePoint( currentItemElem.attribute( u"position"_s ) );
937 QPointF layoutPoint = convertToLayoutUnits( pos );
938
939 minX = std::min( minX, layoutPoint.x() );
940 minY = std::min( minY, layoutPoint.y() );
941 found = true;
942 }
943 return found ? QPointF( minX, minY ) : QPointF( 0, 0 );
944}
945
946void QgsLayout::updateZValues( const bool addUndoCommands )
947{
948 int counter = mItemsModel->zOrderListSize();
949 const QList<QgsLayoutItem *> zOrderList = mItemsModel->zOrderList();
950
951 if ( addUndoCommands )
952 {
953 mUndoStack->beginMacro( tr( "Change Item Stacking" ) );
954 }
955 for ( QgsLayoutItem *currentItem : zOrderList )
956 {
957 if ( currentItem )
958 {
959 if ( addUndoCommands )
960 {
961 mUndoStack->beginCommand( currentItem, QString() );
962 }
963 currentItem->setZValue( counter );
964 if ( addUndoCommands )
965 {
966 mUndoStack->endCommand();
967 }
968 }
969 --counter;
970 }
971 if ( addUndoCommands )
972 {
973 mUndoStack->endMacro();
974 }
975}
976
977bool QgsLayout::readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context )
978{
979 if ( layoutElement.nodeName() != "Layout"_L1 )
980 {
981 return false;
982 }
983
984 auto restore = [&]( QgsLayoutSerializableObject *object ) -> bool { return object->readXml( layoutElement, document, context ); };
985
986 std::unique_ptr< QgsScopedRuntimeProfile > profile;
987 if ( QgsApplication::profiler()->groupIsActive( u"projectload"_s ) )
988 profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Read layout settings" ), u"projectload"_s );
989
990 blockSignals( true ); // defer changed signal to end
991 readXmlLayoutSettings( layoutElement, document, context );
992 blockSignals( false );
993
994 if ( profile )
995 profile->switchTask( tr( "Load pages" ) );
996 restore( mPageCollection.get() );
997 if ( profile )
998 profile->switchTask( tr( "Load snapping settings" ) );
999 restore( &mSnapper );
1000 if ( profile )
1001 profile->switchTask( tr( "Load grid settings" ) );
1002 restore( &mGridSettings );
1003
1004 if ( profile )
1005 profile->switchTask( tr( "Restore items" ) );
1006 addItemsFromXml( layoutElement, document, context );
1007
1008 emit changed();
1009
1010 return true;
1011}
1012
1013QList< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context, QPointF *position, bool pasteInPlace )
1014{
1015 QList< QgsLayoutItem * > newItems;
1016 QList< QgsLayoutMultiFrame * > newMultiFrames;
1017
1018 //if we are adding items to a layout which already contains items, we need to make sure
1019 //these items are placed at the top of the layout and that zValues are not duplicated
1020 //so, calculate an offset which needs to be added to the zValue of created items
1021 int zOrderOffset = mItemsModel->zOrderListSize();
1022
1023 QPointF pasteShiftPos;
1024 int pageNumber = -1;
1025 if ( position )
1026 {
1027 //If we are placing items relative to a certain point, then calculate how much we need
1028 //to shift the items by so that they are placed at this point
1029 //First, calculate the minimum position from the xml
1030 QPointF minItemPos = minPointFromXml( parentElement );
1031 //next, calculate how much each item needs to be shifted from its original position
1032 //so that it's placed at the correct relative position
1033 pasteShiftPos = *position - minItemPos;
1034 if ( pasteInPlace )
1035 {
1036 pageNumber = mPageCollection->pageNumberForPoint( *position );
1037 }
1038 }
1039
1040 std::unique_ptr< QgsScopedRuntimeProfile > profile;
1041 if ( QgsApplication::profiler()->groupIsActive( u"projectload"_s ) )
1042 profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Read items" ), u"projectload"_s );
1043
1044 // multiframes
1045
1046 //TODO - fix this. pasting multiframe frame items has no effect
1047 const QDomNodeList multiFrameList = parentElement.elementsByTagName( u"LayoutMultiFrame"_s );
1048 for ( int i = 0; i < multiFrameList.size(); ++i )
1049 {
1050 const QDomElement multiFrameElem = multiFrameList.at( i ).toElement();
1051 const int itemType = multiFrameElem.attribute( u"type"_s ).toInt();
1052
1053 if ( profile )
1054 {
1055 if ( QgsLayoutMultiFrameAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->multiFrameMetadata( itemType ) )
1056 {
1057 profile->switchTask( tr( "Load %1" ).arg( metadata->visibleName() ) );
1058 }
1059 }
1060
1061 std::unique_ptr< QgsLayoutMultiFrame > mf( QgsApplication::layoutItemRegistry()->createMultiFrame( itemType, this ) );
1062 if ( !mf )
1063 {
1064 // e.g. plugin based item which is no longer available
1065 continue;
1066 }
1067 mf->readXml( multiFrameElem, document, context );
1068
1069#if 0 //TODO?
1070 mf->setCreateUndoCommands( true );
1071#endif
1072
1073 QgsLayoutMultiFrame *m = mf.get();
1074 this->addMultiFrame( mf.release() );
1075
1076 //offset z values for frames
1077 //TODO - fix this after fixing multiframe item paste
1078 /*for ( int frameIdx = 0; frameIdx < mf->frameCount(); ++frameIdx )
1079 {
1080 QgsLayoutItemFrame * frame = mf->frame( frameIdx );
1081 frame->setZValue( frame->zValue() + zOrderOffset );
1082
1083 // also need to shift frames according to position/pasteInPlacePt
1084 }*/
1085 newMultiFrames << m;
1086 }
1087
1088 const QDomNodeList layoutItemList = parentElement.childNodes();
1089 for ( int i = 0; i < layoutItemList.size(); ++i )
1090 {
1091 const QDomElement currentItemElem = layoutItemList.at( i ).toElement();
1092 if ( currentItemElem.nodeName() != "LayoutItem"_L1 )
1093 continue;
1094
1095 const int itemType = currentItemElem.attribute( u"type"_s ).toInt();
1096
1097 if ( profile )
1098 {
1099 if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( itemType ) )
1100 {
1101 profile->switchTask( tr( "Load %1" ).arg( metadata->visibleName() ) );
1102 }
1103 }
1104
1105 std::unique_ptr< QgsLayoutItem > item( QgsApplication::layoutItemRegistry()->createItem( itemType, this ) );
1106 if ( !item )
1107 {
1108 // e.g. plugin based item which is no longer available
1109 continue;
1110 }
1111
1112 item->readXml( currentItemElem, document, context );
1113 if ( position )
1114 {
1115 if ( pasteInPlace )
1116 {
1117 QgsLayoutPoint posOnPage = QgsLayoutPoint::decodePoint( currentItemElem.attribute( u"positionOnPage"_s ) );
1118 item->attemptMove( posOnPage, true, false, pageNumber );
1119 }
1120 else
1121 {
1122 item->attemptMoveBy( pasteShiftPos.x(), pasteShiftPos.y() );
1123 }
1124 }
1125
1126 // When restoring items on project load saved with QGIS < 3.32, convert HTML-enabled labels into HTML items
1127 if ( !position && QgsProjectVersion( 3, 31, 0 ) > mProject->lastSaveVersion() )
1128 {
1129 if ( QgsLayoutItemLabel *label = qobject_cast<QgsLayoutItemLabel *>( item.get() ) )
1130 {
1131 if ( label->mode() == QgsLayoutItemLabel::ModeHtml )
1132 {
1133 QgsTextFormat textFormat = label->textFormat();
1134 if ( textFormat.lineHeightUnit() == Qgis::RenderUnit::Percentage )
1135 {
1136 // The line-height property handles height differently in webkit, adjust accordingly
1137 textFormat.setLineHeight( textFormat.lineHeight() + 0.22 );
1138 label->setTextFormat( textFormat );
1139 }
1141 addMultiFrame( html );
1142 if ( item->isGroupMember() )
1143 {
1144 if ( QgsLayoutItemGroup *group = item->parentGroup() )
1145 {
1146 QList<QgsLayoutItem *> groupItems = group->items();
1147 groupItems.removeAll( item.get() );
1148 group->removeItems();
1149 for ( QgsLayoutItem *groupItem : std::as_const( groupItems ) )
1150 {
1151 group->addItem( groupItem );
1152 }
1153 group->addItem( html->frame( 0 ) );
1154 }
1155 }
1156 newMultiFrames << html;
1157 continue;
1158 }
1159 }
1160 }
1161
1162 QgsLayoutItem *layoutItem = item.get();
1163 addLayoutItem( item.release() );
1164 layoutItem->setZValue( layoutItem->zValue() + zOrderOffset );
1165 newItems << layoutItem;
1166 }
1167
1168 // we now allow items to "post-process", e.g. if they need to setup connections
1169 // to other items in the layout, which may not have existed at the time the
1170 // item's state was restored. E.g. a scalebar may have been restored before the map
1171 // it is linked to
1172 std::unique_ptr< QgsScopedRuntimeProfile > itemProfile;
1173 if ( profile )
1174 {
1175 profile->switchTask( tr( "Finalize restore" ) );
1176 }
1177 for ( QgsLayoutItem *item : std::as_const( newItems ) )
1178 {
1179 if ( profile )
1180 itemProfile = std::make_unique< QgsScopedRuntimeProfile >( item->displayName(), u"projectload"_s );
1181 item->finalizeRestoreFromXml();
1182 if ( itemProfile )
1183 itemProfile.reset();
1184 }
1185 for ( QgsLayoutMultiFrame *mf : std::as_const( newMultiFrames ) )
1186 {
1187 if ( profile )
1188 itemProfile = std::make_unique< QgsScopedRuntimeProfile >( mf->displayName(), u"projectload"_s );
1189 mf->finalizeRestoreFromXml();
1190 if ( itemProfile )
1191 itemProfile.reset();
1192 }
1193
1194 for ( QgsLayoutItem *item : std::as_const( newItems ) )
1195 {
1196 item->mTemplateUuid.clear();
1197 }
1198 for ( QgsLayoutMultiFrame *mf : std::as_const( newMultiFrames ) )
1199 {
1200 mf->mTemplateUuid.clear();
1201 }
1202
1203 //Since this function adds items in an order which isn't the z-order, and each item is added to end of
1204 //z order list in turn, it will now be inconsistent with the actual order of items in the scene.
1205 //Make sure z order list matches the actual order of items in the scene.
1206
1207 if ( profile )
1208 profile->switchTask( tr( "Update model" ) );
1209 mItemsModel->rebuildZList();
1210
1211 return newItems;
1212}
1213
1215{
1216 setSceneRect( layoutBounds( false, 0.05 ) );
1217}
1218
1219void QgsLayout::itemBackgroundTaskCountChanged( int count )
1220{
1221 QgsLayoutItem *item = qobject_cast<QgsLayoutItem *>( sender() );
1222 if ( !item )
1223 return;
1224
1225 if ( count > 0 )
1226 mBackgroundTaskCount.insert( item, count );
1227 else
1228 mBackgroundTaskCount.remove( item );
1229
1230 // sum up new count of background tasks
1231 int total = 0;
1232 for ( auto it = mBackgroundTaskCount.constBegin(); it != mBackgroundTaskCount.constEnd(); ++it )
1233 {
1234 total += it.value();
1235 }
1236
1237 emit backgroundTaskCountChanged( total );
1238}
LayoutUnit
Layout measurement units.
Definition qgis.h:5360
@ Millimeters
Millimeters.
Definition qgis.h:5361
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5344
Base class for commands to undo/redo layout and layout object changes.
virtual void saveState(QDomDocument &stateDoc) const =0
Saves the state of the object to the specified stateDoc.
virtual void restoreState(QDomDocument &stateDoc)=0
Restores the state of the object from the specified stateDoc.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
static bool isCompositionTemplate(const QDomDocument &document)
Check if the given document is a composition template.
static QDomDocument convertCompositionTemplate(const QDomDocument &document, QgsProject *project)
Convert a composition template document to a layout template.
static QgsExpressionContextScope * layoutScope(const QgsLayout *layout)
Creates a new scope which contains variables and functions relating to a QgsLayout layout.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Stores and manages the snap guides used by a layout.
QList< QgsLayoutGuide * > guides()
Returns a list of all guides contained in the collection.
Stores metadata about one layout item class.
A container for grouping several QgsLayoutItems.
void removeItems()
Removes all items from the group (but does not delete them).
QList< QgsLayoutItem * > items() const
Returns a list of items contained by the group.
static QgsLayoutItemHtml * createFromLabel(QgsLayoutItemLabel *label)
Returns a new QgsLayoutItemHtml matching the content and rendering of a given label.
A layout item subclass for text labels.
@ ModeHtml
Label displays rendered HTML content.
Layout graphical items for displaying a map.
Item representing the paper in a layout.
void setPageSize(const QgsLayoutSize &size)
Sets the size of the page.
Base class for graphical items within a QgsLayout.
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QgsLayoutItemGroup * parentGroup() const
Returns the item's parent group, if the item is part of a QgsLayoutItemGroup group.
virtual void setSelected(bool selected)
Sets whether the item should be selected.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
int page() const
Returns the page the item is currently on, with the first page returning 0.
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
int type() const override
Returns a unique graphics item type identifier.
virtual QString displayName() const
Gets item display name.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
void backgroundTaskCountChanged(int count)
Emitted whenever the number of background tasks an item is executing changes.
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
Stores metadata about one layout multiframe class.
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
A manager for a collection of pages in a layout.
Provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
static QgsLayoutPoint decodePoint(const QString &string)
Decodes a point from a string.
Stores information relating to the current rendering settings for a layout.
void setDpi(double dpi)
Sets the dpi for outputting the layout.
double dpi() const
Returns the dpi for outputting the layout.
Stores information relating to the current reporting context for a layout.
An interface for layout objects which can be stored and read from DOM elements.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
Manages snapping grids and preset snap lines in a layout, and handles snapping points to the nearest ...
An undo stack for QgsLayouts.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:50
QgsLayoutItem * itemById(const QString &id) const
Returns a layout item given its id.
void removeMultiFrame(QgsLayoutMultiFrame *multiFrame)
Removes a multiFrame from the layout (but does not delete it).
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the layout.
void updateBounds()
Updates the scene bounds of the layout.
bool saveAsTemplate(const QString &path, const QgsReadWriteContext &context) const
Saves the layout as a template at the given file path.
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id=0, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.
void initializeDefaults()
Initializes an empty layout, e.g.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
void removeCustomProperty(const QString &key)
Remove a custom property from the layout.
QgsLayoutModel * itemsModel()
Returns the items model attached to the layout.
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
void variablesChanged()
Emitted whenever the expression variables stored in the layout have been changed.
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
Definition qgslayout.h:120
QgsLayoutItem * itemByUuid(const QString &uuid, bool includeTemplateUuids=false) const
Returns the layout item with matching uuid unique identifier, or nullptr if a matching item could not...
void addMultiFrame(QgsLayoutMultiFrame *multiFrame)
Adds a multiFrame to the layout.
void setUnits(Qgis::LayoutUnit units)
Sets the native measurement units for the layout.
Definition qgslayout.h:322
QgsLayoutGuideCollection & guides()
Returns a reference to the layout's guide collection, which manages page snap guides.
QList< QgsLayoutItem * > ungroupItems(QgsLayoutItemGroup *group)
Ungroups items by removing them from an item group and removing the group from the layout.
bool moveItemToTop(QgsLayoutItem *item, bool deferUpdate=false)
Raises an item up to the top of the z-order.
QgsLayoutItem * layoutItemAt(QPointF position, bool ignoreLocked=false, double searchTolerance=0) const
Returns the topmost layout item at a specified position.
QgsLayoutItemMap * referenceMap() const
Returns the map item which will be used to generate corresponding world files when the layout is expo...
void changed()
Emitted when properties of the layout change.
friend class QgsLayoutMultiFrame
Definition qgslayout.h:776
bool moveItemToBottom(QgsLayoutItem *item, bool deferUpdate=false)
Lowers an item down to the bottom of the z-order.
QgsLayoutItemGroup * groupItems(const QList< QgsLayoutItem * > &items)
Creates a new group from a list of layout items and adds the group to the layout.
QList< QgsLayoutItem * > addItemsFromXml(const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context, QPointF *position=nullptr, bool pasteInPlace=false)
Add items from an XML representation to the layout.
friend class QgsLayoutItemAddItemCommand
Definition qgslayout.h:770
void reloadSettings()
Refreshes the layout when global layout related options change.
double convertToLayoutUnits(QgsLayoutMeasurement measurement) const
Converts a measurement into the layout's native units.
virtual bool readXml(const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context)
Sets the collection's state from a DOM element.
QStringList customProperties() const
Returns list of keys stored in custom properties for the layout.
QgsLayout * clone() const
Creates a clone of the layout.
void clear()
Clears the layout.
~QgsLayout() override
Definition qgslayout.cpp:69
QgsLayoutItem * itemByTemplateUuid(const QString &uuid) const
Returns the layout item with matching template uuid unique identifier, or nullptr if a matching item ...
friend class QgsLayoutUndoCommand
Definition qgslayout.h:773
bool lowerItem(QgsLayoutItem *item, bool deferUpdate=false)
Lowers an item down the z-order.
QgsLayoutMeasurement convertFromLayoutUnits(double length, Qgis::LayoutUnit unit) const
Converts a length measurement from the layout's native units to a specified target unit.
friend class QgsLayoutModel
Definition qgslayout.h:775
QList< QgsLayoutItem * > loadFromTemplate(const QDomDocument &document, const QgsReadWriteContext &context, bool clearExisting=true, bool *ok=nullptr)
Load a layout template document.
static const QgsSettingsEntryStringList * settingsSearchPathForTemplates
Settings entry search path for templates.
Definition qgslayout.h:661
bool raiseItem(QgsLayoutItem *item, bool deferUpdate=false)
Raises an item up the z-order.
QList< QgsLayoutItem * > selectedLayoutItems(bool includeLockedItems=true)
Returns list of selected layout items.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from the layout.
void selectedItemChanged(QgsLayoutItem *selected)
Emitted whenever the selected item changes.
void deselectAll()
Clears any selected items in the layout.
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
QRectF layoutBounds(bool ignorePages=false, double margin=0.0) const
Calculates the bounds of all non-gui items in the layout.
void refresh()
Forces the layout, and all items contained within it, to refresh.
void backgroundTaskCountChanged(int total)
Emitted whenever the total number of background tasks running in items from the layout changes.
void removeLayoutItem(QgsLayoutItem *item)
Removes an item from the layout.
void refreshed()
Emitted when the layout has been refreshed and items should also be refreshed and updated.
void updateZValues(bool addUndoCommands=true)
Resets the z-values of items based on their position in the internal z order list.
QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the layout's current state.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
virtual QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns the layout's state encapsulated in a DOM element.
void setReferenceMap(QgsLayoutItemMap *map)
Sets the map item which will be used to generate corresponding world files when the layout is exporte...
QList< QgsLayoutMultiFrame * > multiFrames() const
Returns a list of multi frames contained in the layout.
void addLayoutItem(QgsLayoutItem *item)
Adds an item to the layout.
QgsLayoutMultiFrame * multiFrameByUuid(const QString &uuid, bool includeTemplateUuids=false) const
Returns the layout multiframe with matching uuid unique identifier, or nullptr if a matching multifra...
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QgsProject * project() const
The project associated with the layout.
void itemAdded(QgsLayoutItem *item)
Emitted when an item was added to the layout.
QRectF pageItemBounds(int page, bool visibleOnly=false) const
Returns the bounding box of the items contained on a specified page.
QgsLayout(QgsProject *project)
Construct a new layout linked to the specified project.
Definition qgslayout.cpp:55
QgsLayoutUndoStack * undoStack()
Returns a pointer to the layout's undo stack, which manages undo/redo states for the layout and it's ...
void writeXml(QDomNode &parentNode, QDomDocument &doc) const
Writes the store contents to an XML node.
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from an XML node.
Describes the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
A container for the context for various read/write operations on objects.
A string list settings entry.
static QgsSettingsTreeNode * sTreeLayout
An interface for classes which can visit style entity (e.g.
Container for all settings relating to text rendering.
double lineHeight() const
Returns the line height for text.
Qgis::RenderUnit lineHeightUnit() const
Returns the units for the line height for text.
void setLineHeight(double height)
Sets the line height for text.
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.
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
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6880
#define SIP_TRANSFERTHIS
Definition qgis_sip.h:52