QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgslayoutitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitem.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 "qgslayoutitem.h"
18
20#include "qgsimageoperation.h"
21#include "qgslayout.h"
22#include "qgslayoutitemgroup.h"
23#include "qgslayoutitempage.h"
25#include "qgslayoutmodel.h"
28#include "qgslayoutundostack.h"
29#include "qgslayoututils.h"
30#include "qgspagesizeregistry.h"
31#include "qgspainting.h"
32#include "qgssvgcache.h"
33#include "qgssymbollayerutils.h"
34
35#include <QPainter>
36#include <QString>
37#include <QStyleOptionGraphicsItem>
38#include <QUuid>
39
40#include "moc_qgslayoutitem.cpp"
41
42using namespace Qt::StringLiterals;
43
44#define CACHE_SIZE_LIMIT 5000
45
47 : mRenderContext( context )
48 , mViewScaleFactor( viewScaleFactor )
49{
50}
51
52
53
56 , QGraphicsRectItem( nullptr )
57 , mUuid( QUuid::createUuid().toString() )
58{
59 setZValue( QgsLayout::ZItem );
60
61 // needed to access current view transform during paint operations
62 setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption | QGraphicsItem::ItemIsSelectable );
63
64 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
65
66 //record initial position
67 const Qgis::LayoutUnit initialUnits = layout ? layout->units() : Qgis::LayoutUnit::Millimeters;
68 mItemPosition = QgsLayoutPoint( scenePos().x(), scenePos().y(), initialUnits );
69 mItemSize = QgsLayoutSize( rect().width(), rect().height(), initialUnits );
70
71 // required to initially setup background/frame style
73 refreshFrame( false );
74
75 initConnectionsToLayout();
76
77 //let z-Value be managed by layout
78 if ( mLayout && manageZValue )
79 {
80 mLayoutManagesZValue = true;
81 mLayout->itemsModel()->addItemAtTop( this );
82 }
83 else
84 {
85 mLayoutManagesZValue = false;
86 }
87}
88
93
95{
96 if ( mLayout && mLayoutManagesZValue )
97 {
98 mLayout->itemsModel()->removeItem( this );
99 }
100}
101
103{
104 //return id, if it's not empty
105 if ( !id().isEmpty() )
106 {
107 return id();
108 }
109
110 //for unnamed items, default to item type
111 if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( type() ) )
112 {
113 return tr( "<%1>" ).arg( metadata->visibleName() );
114 }
115
116 return tr( "<item>" );
117}
118
123
125{
126 return QgsApplication::getThemeIcon( u"/mLayoutItem.svg"_s );
127}
128
133
134void QgsLayoutItem::setId( const QString &id )
135{
136 if ( id == mId )
137 {
138 return;
139 }
140
141 if ( !shouldBlockUndoCommands() )
142 mLayout->undoStack()->beginCommand( this, tr( "Change Item ID" ) );
143
144 mId = id;
145
146 if ( !shouldBlockUndoCommands() )
147 mLayout->undoStack()->endCommand();
148
149 setToolTip( id );
150
151 //inform model that id data has changed
152 if ( mLayout )
153 {
154 mLayout->itemsModel()->updateItemDisplayName( this );
155 }
156
157 emit changed();
158}
159
160void QgsLayoutItem::setSelected( bool selected )
161{
162 QGraphicsRectItem::setSelected( selected );
163 //inform model that id data has changed
164 if ( mLayout )
165 {
166 mLayout->itemsModel()->updateItemSelectStatus( this );
167 }
168}
169
170void QgsLayoutItem::setVisibility( const bool visible )
171{
172 if ( visible == isVisible() )
173 {
174 //nothing to do
175 return;
176 }
177
178 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
179 if ( !shouldBlockUndoCommands() )
180 {
181 command.reset( createCommand( visible ? tr( "Show Item" ) : tr( "Hide Item" ), 0 ) );
182 command->saveBeforeState();
183 }
184
185 QGraphicsItem::setVisible( visible );
186
187 if ( command )
188 {
189 command->saveAfterState();
190 mLayout->undoStack()->push( command.release() );
191 }
192
193 //inform model that visibility has changed
194 if ( mLayout )
195 {
196 mLayout->itemsModel()->updateItemVisibility( this );
197 }
198}
199
201{
202 if ( locked == mIsLocked )
203 {
204 return;
205 }
206
207 if ( !shouldBlockUndoCommands() )
208 mLayout->undoStack()->beginCommand( this, locked ? tr( "Lock Item" ) : tr( "Unlock Item" ) );
209
210 mIsLocked = locked;
211
212 if ( !shouldBlockUndoCommands() )
213 mLayout->undoStack()->endCommand();
214
215 //inform model that id data has changed
216 if ( mLayout )
217 {
218 mLayout->itemsModel()->updateItemLockStatus( this );
219 }
220
221 update();
222 emit lockChanged();
223}
224
226{
227 return !mParentGroupUuid.isEmpty() && mLayout && static_cast< bool >( mLayout->itemByUuid( mParentGroupUuid ) );
228}
229
231{
232 if ( !mLayout || mParentGroupUuid.isEmpty() )
233 return nullptr;
234
235 return qobject_cast< QgsLayoutItemGroup * >( mLayout->itemByUuid( mParentGroupUuid ) );
236}
237
239{
240 if ( !group )
241 mParentGroupUuid.clear();
242 else
243 mParentGroupUuid = group->uuid();
244 setFlag( QGraphicsItem::ItemIsSelectable, !static_cast< bool>( group ) ); //item in groups cannot be selected
245}
246
251
253{
254 return 0;
255}
256
261
266
268{
270 if ( !mLayout || mLayout->renderContext().currentExportLayer() == -1 )
271 return false;
272
273 // QGIS 5- return false from base class implementation
274
275 const int layers = numberExportLayers();
276 return mLayout->renderContext().currentExportLayer() < layers;
278}
279
284
285void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
286{
287 if ( !painter || !painter->device() || !shouldDrawItem() )
288 {
289 return;
290 }
291
292 if ( shouldDrawDebugRect() )
293 {
294 drawDebugRect( painter );
295 return;
296 }
297
298 const bool previewRender = !mLayout || mLayout->renderContext().isPreviewRender();
299 double destinationDpi = previewRender ? QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter ) * 25.4 : mLayout->renderContext().dpi();
300 const bool useImageCache = false;
301 bool forceRasterOutput = containsAdvancedEffects();
302 QPainter::CompositionMode blendMode = blendModeForRender();
303 if ( mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector )
304 {
305 // the FlagForceVectorOutput flag overrides everything, and absolutely DISABLES rasterisation
306 // even when we need it to get correct rendering of opacity/blend modes/etc
307 forceRasterOutput = false;
308 }
309 else if ( blendMode != QPainter::CompositionMode_SourceOver )
310 {
311 // we have to rasterize content in order to show it with alternative blend modes
312 forceRasterOutput = true;
313 }
314
315 if ( useImageCache || forceRasterOutput )
316 {
317 double widthInPixels = 0;
318 double heightInPixels = 0;
319
320 if ( previewRender )
321 {
322 widthInPixels = boundingRect().width() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
323 heightInPixels = boundingRect().height() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
324 }
325 else
326 {
327 const double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, Qgis::LayoutUnit::Pixels ).length() : destinationDpi / 25.4;
328 widthInPixels = boundingRect().width() * layoutUnitsToPixels;
329 heightInPixels = boundingRect().height() * layoutUnitsToPixels;
330 }
331
332 // limit size of image for better performance
333 if ( previewRender && ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT ) )
334 {
335 double scale = 1.0;
336 if ( widthInPixels > heightInPixels )
337 {
338 scale = widthInPixels / CACHE_SIZE_LIMIT;
339 widthInPixels = CACHE_SIZE_LIMIT;
340 heightInPixels /= scale;
341 }
342 else
343 {
344 scale = heightInPixels / CACHE_SIZE_LIMIT;
345 heightInPixels = CACHE_SIZE_LIMIT;
346 widthInPixels /= scale;
347 }
348 destinationDpi = destinationDpi / scale;
349 }
350
351 if ( previewRender && !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
352 {
353 // can reuse last cached image
354 const QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
355 const QgsScopedQPainterState painterState( painter );
356 preparePainter( painter );
357 const double cacheScale = destinationDpi / mItemCacheDpi;
358 painter->scale( cacheScale / context.scaleFactor(), cacheScale / context.scaleFactor() );
359 painter->setCompositionMode( blendMode );
360 painter->drawImage( boundingRect().x() * context.scaleFactor() / cacheScale,
361 boundingRect().y() * context.scaleFactor() / cacheScale, mItemCachedImage );
362 return;
363 }
364 else
365 {
366 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
367 image.fill( Qt::transparent );
368 image.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
369 image.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
370 QPainter p( &image );
371
372 preparePainter( &p );
375 // painter is already scaled to dots
376 // need to translate so that item origin is at 0,0 in painter coordinates (not bounding rect origin)
377 p.translate( -boundingRect().x() * context.scaleFactor(), -boundingRect().y() * context.scaleFactor() );
378 // scale to layout units for background and frame rendering
379 p.scale( context.scaleFactor(), context.scaleFactor() );
380 drawBackground( context );
381 p.scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
382 const double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
383 QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
384 draw( itemRenderContext );
385 p.scale( context.scaleFactor(), context.scaleFactor() );
386 drawFrame( context );
387 p.scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
388 p.end();
389
390 QgsImageOperation::multiplyOpacity( image, mEvaluatedOpacity );
391
392 const QgsScopedQPainterState painterState( painter );
393 // scale painter from mm to dots
394 painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
395 painter->setCompositionMode( blendMode );
396 painter->drawImage( boundingRect().x() * context.scaleFactor(),
397 boundingRect().y() * context.scaleFactor(), image );
398
399 if ( previewRender )
400 {
401 mItemCacheDpi = destinationDpi;
402 mItemCachedImage = image;
403 }
404 }
405 }
406 else
407 {
408 // no caching or flattening
409 const QgsScopedQPainterState painterState( painter );
410 preparePainter( painter );
411 QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
413 drawBackground( context );
414
415 const double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
416
417 // scale painter from mm to dots
418 painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
419 QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
420 draw( itemRenderContext );
421
422 painter->scale( context.scaleFactor(), context.scaleFactor() );
423 drawFrame( context );
424 }
425
426 if ( isRefreshing() && previewRender )
427 {
428 drawRefreshingOverlay( painter, itemStyle );
429 }
430}
431
433{
434 if ( point == mReferencePoint )
435 {
436 return;
437 }
438
439 mReferencePoint = point;
440
441 //also need to adjust stored position
442 updateStoredItemPosition();
444}
445
446void QgsLayoutItem::attemptResize( const QgsLayoutSize &s, bool includesFrame )
447{
448 if ( !mLayout )
449 {
450 mItemSize = s;
451 setRect( 0, 0, s.width(), s.height() );
452 return;
453 }
454
455 QgsLayoutSize size = s;
456
457 if ( includesFrame )
458 {
459 //adjust position to account for frame size
460 const double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), size.units() ).length();
461 size.setWidth( size.width() - 2 * bleed );
462 size.setHeight( size.height() - 2 * bleed );
463 }
464
465 const QgsLayoutSize evaluatedSize = applyDataDefinedSize( size );
466 const QSizeF targetSizeLayoutUnits = mLayout->convertToLayoutUnits( evaluatedSize );
467 QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
468 actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
469 actualSizeLayoutUnits = applyItemSizeConstraint( actualSizeLayoutUnits );
470
471 if ( actualSizeLayoutUnits == rect().size() )
472 {
473 return;
474 }
475
476 const QgsLayoutSize actualSizeTargetUnits = mLayout->convertFromLayoutUnits( actualSizeLayoutUnits, size.units() );
477 mItemSize = actualSizeTargetUnits;
478
479 setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
481 emit sizePositionChanged();
482}
483
484void QgsLayoutItem::attemptMove( const QgsLayoutPoint &p, bool useReferencePoint, bool includesFrame, int page )
485{
486 if ( !mLayout )
487 {
488 mItemPosition = p;
489 setPos( p.toQPointF() );
490 return;
491 }
492
493 QgsLayoutPoint point = p;
494 if ( page >= 0 )
495 {
496 point = mLayout->pageCollection()->pagePositionToAbsolute( page, p );
497 }
498
499 if ( includesFrame )
500 {
501 //adjust position to account for frame size
502 const double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), point.units() ).length();
503 point.setX( point.x() + bleed );
504 point.setY( point.y() + bleed );
505 }
506
507 QgsLayoutPoint evaluatedPoint = point;
508 if ( !useReferencePoint )
509 {
510 evaluatedPoint = topLeftToReferencePoint( point );
511 }
512
513 evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
514 const QPointF evaluatedPointLayoutUnits = mLayout->convertToLayoutUnits( evaluatedPoint );
515 const QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size(), mReferencePoint );
516 if ( topLeftPointLayoutUnits == scenePos() && point.units() == mItemPosition.units() )
517 {
518 //TODO - add test for second condition
519 return;
520 }
521
522 const QgsLayoutPoint referencePointTargetUnits = mLayout->convertFromLayoutUnits( evaluatedPointLayoutUnits, point.units() );
523 mItemPosition = referencePointTargetUnits;
524 setScenePos( topLeftPointLayoutUnits );
525 emit sizePositionChanged();
526}
527
528void QgsLayoutItem::attemptSetSceneRect( const QRectF &rect, bool includesFrame )
529{
530 const QPointF newPos = rect.topLeft();
531
532 blockSignals( true );
533 // translate new size to current item units
534 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( rect.size(), mItemSize.units() );
535 attemptResize( newSize, includesFrame );
536
537 // translate new position to current item units
538 const QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, mItemPosition.units() );
539 attemptMove( itemPos, false, includesFrame );
540 blockSignals( false );
541 emit sizePositionChanged();
542}
543
544void QgsLayoutItem::attemptMoveBy( double deltaX, double deltaY )
545{
546 if ( !mLayout )
547 {
548 moveBy( deltaX, deltaY );
549 return;
550 }
551
553 const QgsLayoutPoint deltaPos = mLayout->convertFromLayoutUnits( QPointF( deltaX, deltaY ), itemPos.units() );
554 itemPos.setX( itemPos.x() + deltaPos.x() );
555 itemPos.setY( itemPos.y() + deltaPos.y() );
556 attemptMove( itemPos );
557}
558
560{
561 if ( !mLayout )
562 return -1;
563
564 return mLayout->pageCollection()->pageNumberForPoint( pos() );
565}
566
568{
569 QPointF p = positionAtReferencePoint( mReferencePoint );
570
571 if ( !mLayout )
572 return p;
573
574 // try to get page
575 QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page() );
576 if ( !pageItem )
577 return p;
578
579 p.ry() -= pageItem->pos().y();
580 return p;
581}
582
584{
585 const QPointF p = pagePos();
586 if ( !mLayout )
587 return QgsLayoutPoint( p );
588
589 return mLayout->convertFromLayoutUnits( p, mItemPosition.units() );
590}
591
592void QgsLayoutItem::setScenePos( const QPointF destinationPos )
593{
594 //since setPos does not account for item rotation, use difference between
595 //current scenePos (which DOES account for rotation) and destination pos
596 //to calculate how much the item needs to move
597 if ( auto *lParentItem = parentItem() )
598 setPos( pos() + ( destinationPos - scenePos() ) + lParentItem->scenePos() );
599 else
600 setPos( pos() + ( destinationPos - scenePos() ) );
601}
602
603bool QgsLayoutItem::shouldBlockUndoCommands() const
604{
605 return !mLayout || mLayout != scene() || mBlockUndoCommands;
606}
607
609{
611 return false;
612
613 if ( !mLayout || mLayout->renderContext().isPreviewRender() )
614 {
615 //preview mode so OK to draw item
616 return true;
617 }
618
619 //exporting layout, so check if item is excluded from exports
620 return !mEvaluatedExcludeFromExports;
621}
622
624{
625 return mItemRotation;
626}
627
628bool QgsLayoutItem::writeXml( QDomElement &parentElement, QDomDocument &doc, const QgsReadWriteContext &context ) const
629{
630 QDomElement element = doc.createElement( u"LayoutItem"_s );
631 element.setAttribute( u"type"_s, QString::number( type() ) );
632
633 element.setAttribute( u"uuid"_s, mUuid );
634 element.setAttribute( u"templateUuid"_s, mUuid );
635 element.setAttribute( u"id"_s, mId );
636 element.setAttribute( u"referencePoint"_s, QString::number( static_cast< int >( mReferencePoint ) ) );
637 element.setAttribute( u"position"_s, mItemPosition.encodePoint() );
638 element.setAttribute( u"positionOnPage"_s, pagePositionWithUnits().encodePoint() );
639 element.setAttribute( u"size"_s, mItemSize.encodeSize() );
640 element.setAttribute( u"itemRotation"_s, QString::number( mItemRotation ) );
641 element.setAttribute( u"groupUuid"_s, mParentGroupUuid );
642
643 element.setAttribute( u"zValue"_s, QString::number( zValue() ) );
644 element.setAttribute( u"visibility"_s, isVisible() );
645 //position lock for mouse moves/resizes
646 if ( mIsLocked )
647 {
648 element.setAttribute( u"positionLock"_s, u"true"_s );
649 }
650 else
651 {
652 element.setAttribute( u"positionLock"_s, u"false"_s );
653 }
654
655 //frame
656 if ( mFrame )
657 {
658 element.setAttribute( u"frame"_s, u"true"_s );
659 }
660 else
661 {
662 element.setAttribute( u"frame"_s, u"false"_s );
663 }
664
665 //background
666 if ( mBackground )
667 {
668 element.setAttribute( u"background"_s, u"true"_s );
669 }
670 else
671 {
672 element.setAttribute( u"background"_s, u"false"_s );
673 }
674
675 //frame color
676 QDomElement frameColorElem = doc.createElement( u"FrameColor"_s );
677 frameColorElem.setAttribute( u"red"_s, QString::number( mFrameColor.red() ) );
678 frameColorElem.setAttribute( u"green"_s, QString::number( mFrameColor.green() ) );
679 frameColorElem.setAttribute( u"blue"_s, QString::number( mFrameColor.blue() ) );
680 frameColorElem.setAttribute( u"alpha"_s, QString::number( mFrameColor.alpha() ) );
681 element.appendChild( frameColorElem );
682 element.setAttribute( u"outlineWidthM"_s, mFrameWidth.encodeMeasurement() );
683 element.setAttribute( u"frameJoinStyle"_s, QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );
684
685 //background color
686 QDomElement bgColorElem = doc.createElement( u"BackgroundColor"_s );
687 bgColorElem.setAttribute( u"red"_s, QString::number( mBackgroundColor.red() ) );
688 bgColorElem.setAttribute( u"green"_s, QString::number( mBackgroundColor.green() ) );
689 bgColorElem.setAttribute( u"blue"_s, QString::number( mBackgroundColor.blue() ) );
690 bgColorElem.setAttribute( u"alpha"_s, QString::number( mBackgroundColor.alpha() ) );
691 element.appendChild( bgColorElem );
692
693 //blend mode
694 element.setAttribute( u"blendMode"_s, static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
695
696 //opacity
697 element.setAttribute( u"opacity"_s, QString::number( mOpacity ) );
698
699 element.setAttribute( u"excludeFromExports"_s, mExcludeFromExports );
700
701 writeObjectPropertiesToElement( element, doc, context );
702
703 writePropertiesToElement( element, doc, context );
704 parentElement.appendChild( element );
705
706 return true;
707}
708
709bool QgsLayoutItem::readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
710{
711 if ( element.nodeName() != "LayoutItem"_L1 )
712 {
713 return false;
714 }
715
716 readObjectPropertiesFromElement( element, doc, context );
717
718 mBlockUndoCommands = true;
719 mUuid = element.attribute( u"uuid"_s, QUuid::createUuid().toString() );
720 setId( element.attribute( u"id"_s ) );
721 mReferencePoint = static_cast< ReferencePoint >( element.attribute( u"referencePoint"_s ).toInt() );
722 setItemRotation( element.attribute( u"itemRotation"_s, u"0"_s ).toDouble() );
723 attemptMove( QgsLayoutPoint::decodePoint( element.attribute( u"position"_s ) ) );
724 attemptResize( QgsLayoutSize::decodeSize( element.attribute( u"size"_s ) ) );
725
726 mParentGroupUuid = element.attribute( u"groupUuid"_s );
727 if ( !mParentGroupUuid.isEmpty() )
728 {
729 if ( QgsLayoutItemGroup *group = parentGroup() )
730 {
731 group->addItem( this );
732 }
733 }
734 mTemplateUuid = element.attribute( u"templateUuid"_s );
735
736 //position lock for mouse moves/resizes
737 const QString positionLock = element.attribute( u"positionLock"_s );
738 if ( positionLock.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
739 {
740 setLocked( true );
741 }
742 else
743 {
744 setLocked( false );
745 }
746 //visibility
747 setVisibility( element.attribute( u"visibility"_s, u"1"_s ) != "0"_L1 );
748 setZValue( element.attribute( u"zValue"_s ).toDouble() );
749
750 //frame
751 const QString frame = element.attribute( u"frame"_s );
752 if ( frame.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
753 {
754 mFrame = true;
755 }
756 else
757 {
758 mFrame = false;
759 }
760
761 //frame
762 const QString background = element.attribute( u"background"_s );
763 if ( background.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
764 {
765 mBackground = true;
766 }
767 else
768 {
769 mBackground = false;
770 }
771
772 //pen
773 mFrameWidth = QgsLayoutMeasurement::decodeMeasurement( element.attribute( u"outlineWidthM"_s ) );
774 mFrameJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( element.attribute( u"frameJoinStyle"_s, u"miter"_s ) );
775 const QDomNodeList frameColorList = element.elementsByTagName( u"FrameColor"_s );
776 if ( !frameColorList.isEmpty() )
777 {
778 const QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
779 bool redOk = false;
780 bool greenOk = false;
781 bool blueOk = false;
782 bool alphaOk = false;
783 int penRed, penGreen, penBlue, penAlpha;
784
785 penRed = frameColorElem.attribute( u"red"_s ).toDouble( &redOk );
786 penGreen = frameColorElem.attribute( u"green"_s ).toDouble( &greenOk );
787 penBlue = frameColorElem.attribute( u"blue"_s ).toDouble( &blueOk );
788 penAlpha = frameColorElem.attribute( u"alpha"_s ).toDouble( &alphaOk );
789
790 if ( redOk && greenOk && blueOk && alphaOk )
791 {
792 mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
793 }
794 }
795 refreshFrame( false );
796
797 //brush
798 const QDomNodeList bgColorList = element.elementsByTagName( u"BackgroundColor"_s );
799 if ( !bgColorList.isEmpty() )
800 {
801 const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
802 bool redOk, greenOk, blueOk, alphaOk;
803 int bgRed, bgGreen, bgBlue, bgAlpha;
804 bgRed = bgColorElem.attribute( u"red"_s ).toDouble( &redOk );
805 bgGreen = bgColorElem.attribute( u"green"_s ).toDouble( &greenOk );
806 bgBlue = bgColorElem.attribute( u"blue"_s ).toDouble( &blueOk );
807 bgAlpha = bgColorElem.attribute( u"alpha"_s ).toDouble( &alphaOk );
808 if ( redOk && greenOk && blueOk && alphaOk )
809 {
810 mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
811 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
812 }
813 //apply any data defined settings
814 refreshBackgroundColor( false );
815 }
816
817 //blend mode
818 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( element.attribute( u"blendMode"_s, u"0"_s ).toUInt() ) ) );
819
820 //opacity
821 if ( element.hasAttribute( u"opacity"_s ) )
822 {
823 setItemOpacity( element.attribute( u"opacity"_s, u"1"_s ).toDouble() );
824 }
825 else
826 {
827 setItemOpacity( 1.0 - element.attribute( u"transparency"_s, u"0"_s ).toInt() / 100.0 );
828 }
829
830 mExcludeFromExports = element.attribute( u"excludeFromExports"_s, u"0"_s ).toInt();
831 mEvaluatedExcludeFromExports = mExcludeFromExports;
832
833 const bool result = readPropertiesFromElement( element, doc, context );
834
835 mBlockUndoCommands = false;
836
837 emit changed();
838 update();
839 return result;
840}
841
845
846QgsAbstractLayoutUndoCommand *QgsLayoutItem::createCommand( const QString &text, int id, QUndoCommand *parent )
847{
848 return new QgsLayoutItemUndoCommand( this, text, id, parent );
849}
850
852{
853 if ( drawFrame == mFrame )
854 {
855 //no change
856 return;
857 }
858
859 mFrame = drawFrame;
860 refreshFrame( true );
861 emit frameChanged();
862}
863
864void QgsLayoutItem::setFrameStrokeColor( const QColor &color )
865{
866 if ( mFrameColor == color )
867 {
868 //no change
869 return;
870 }
871 mFrameColor = color;
872 // apply any datadefined overrides
873 refreshFrame( true );
874 emit frameChanged();
875}
876
878{
879 if ( mFrameWidth == width )
880 {
881 //no change
882 return;
883 }
884 mFrameWidth = width;
885 refreshFrame();
886 emit frameChanged();
887}
888
889void QgsLayoutItem::setFrameJoinStyle( const Qt::PenJoinStyle style )
890{
891 if ( mFrameJoinStyle == style )
892 {
893 //no change
894 return;
895 }
896 mFrameJoinStyle = style;
897
898 QPen itemPen = pen();
899 itemPen.setJoinStyle( mFrameJoinStyle );
900 setPen( itemPen );
901 emit frameChanged();
902}
903
905{
906 mBackground = drawBackground;
907 update();
908}
909
910void QgsLayoutItem::setBackgroundColor( const QColor &color )
911{
912 mBackgroundColor = color;
913 // apply any datadefined overrides
915}
916
917void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
918{
919 mBlendMode = mode;
920 // Update the item effect to use the new blend mode
922 update();
923}
924
925void QgsLayoutItem::setItemOpacity( double opacity )
926{
927 mOpacity = opacity;
928 refreshOpacity( mItemCachedImage.isNull() );
929 if ( !mItemCachedImage.isNull() )
931}
932
934{
935 return mExcludeFromExports;
936}
937
943
945{
946 return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
947}
948
950{
951 return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) ||
952 blendMode() != QPainter::CompositionMode_SourceOver;
953}
954
956{
957 if ( !frameEnabled() )
958 {
959 return 0;
960 }
961
962 return pen().widthF() / 2.0;
963}
964
966{
967 const double frameBleed = estimatedFrameBleed();
968 return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
969}
970
971void QgsLayoutItem::moveContent( double, double )
972{
973
974}
975
977{
978
979}
980
981void QgsLayoutItem::zoomContent( double, QPointF )
982{
983
984}
985
986void QgsLayoutItem::beginCommand( const QString &commandText, UndoCommand command )
987{
988 if ( !mLayout )
989 return;
990
991 mLayout->undoStack()->beginCommand( this, commandText, command );
992}
993
995{
996 if ( mLayout )
997 mLayout->undoStack()->endCommand();
998}
999
1001{
1002 if ( mLayout )
1003 mLayout->undoStack()->cancelCommand();
1004}
1005
1006QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
1007{
1008 if ( !mLayout )
1009 {
1010 return position;
1011 }
1012
1013 const QgsExpressionContext context = createExpressionContext();
1014 const double evaluatedX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::PositionX, context, position.x() );
1015 const double evaluatedY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::PositionY, context, position.y() );
1016 return QgsLayoutPoint( evaluatedX, evaluatedY, position.units() );
1017}
1018
1019void QgsLayoutItem::applyDataDefinedOrientation( double &width, double &height, const QgsExpressionContext &context )
1020{
1021 bool ok = false;
1022 const QString orientationString = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::PaperOrientation, context, QString(), &ok );
1023 if ( ok && !orientationString.isEmpty() )
1024 {
1025 const QgsLayoutItemPage::Orientation orientation = QgsLayoutUtils::decodePaperOrientation( orientationString, ok );
1026 if ( ok )
1027 {
1028 double heightD = 0.0, widthD = 0.0;
1029 switch ( orientation )
1030 {
1032 {
1033 heightD = std::max( height, width );
1034 widthD = std::min( height, width );
1035 break;
1036 }
1038 {
1039 heightD = std::min( height, width );
1040 widthD = std::max( height, width );
1041 break;
1042 }
1043 }
1044 width = widthD;
1045 height = heightD;
1046 }
1047 }
1048}
1049
1051{
1052 if ( !mLayout )
1053 {
1054 return size;
1055 }
1056
1061 return size;
1062
1063
1065
1066 // lowest priority is page size
1067 const QString pageSize = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::PresetPaperSize, context );
1068 QgsPageSize matchedSize;
1069 double evaluatedWidth = size.width();
1070 double evaluatedHeight = size.height();
1071 if ( QgsApplication::pageSizeRegistry()->decodePageSize( pageSize, matchedSize ) )
1072 {
1073 const QgsLayoutSize convertedSize = mLayout->renderContext().measurementConverter().convert( matchedSize.size, size.units() );
1074 evaluatedWidth = convertedSize.width();
1075 evaluatedHeight = convertedSize.height();
1076 }
1077
1078 // highest priority is dd width/height
1079 evaluatedWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemWidth, context, evaluatedWidth );
1080 evaluatedHeight = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemHeight, context, evaluatedHeight );
1081
1082 //which is finally overwritten by data defined orientation
1083 applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1084
1085 return QgsLayoutSize( evaluatedWidth, evaluatedHeight, size.units() );
1086}
1087
1088QPainter::CompositionMode QgsLayoutItem::blendModeForRender() const
1089{
1090 QPainter::CompositionMode mode = mEvaluatedBlendMode;
1091 if ( mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector )
1092 {
1093 // this policy overrides everything, and absolutely DISABLES rasterisation
1094 // even when we need it to get correct rendering of opacity/blend modes/etc
1095 mode = QPainter::CompositionMode_SourceOver;
1096 }
1097 return mode;
1098}
1099
1100double QgsLayoutItem::applyDataDefinedRotation( const double rotation )
1101{
1102 if ( !mLayout )
1103 {
1104 return rotation;
1105 }
1106
1107 const QgsExpressionContext context = createExpressionContext();
1108 const double evaluatedRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemRotation, context, rotation );
1109 return evaluatedRotation;
1110}
1111
1113{
1114 //update data defined properties and update item to match
1115
1116 //evaluate width and height first, since they may affect position if non-top-left reference point set
1119 {
1121 }
1124 {
1126 }
1128 {
1130 }
1132 {
1133 refreshOpacity( false );
1134 }
1136 {
1137 refreshFrame( false );
1138 }
1140 {
1141 refreshBackgroundColor( false );
1142 }
1144 {
1146 }
1148 {
1149 const bool exclude = mExcludeFromExports;
1150 //data defined exclude from exports set?
1152 }
1153
1154 update();
1155}
1156
1157void QgsLayoutItem::setItemRotation( double angle, const bool adjustPosition )
1158{
1159 if ( angle >= 360.0 || angle <= -360.0 )
1160 {
1161 angle = std::fmod( angle, 360.0 );
1162 }
1163
1164 const QPointF point = adjustPosition ? positionAtReferencePoint( QgsLayoutItem::Middle )
1165 : pos();
1166 const double rotationRequired = angle - rotation();
1167 rotateItem( rotationRequired, point );
1168
1169 mItemRotation = angle;
1170}
1171
1172void QgsLayoutItem::updateStoredItemPosition()
1173{
1174 const QPointF layoutPosReferencePoint = positionAtReferencePoint( mReferencePoint );
1175 mItemPosition = mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.units() );
1176}
1177
1178void QgsLayoutItem::rotateItem( const double angle, const QPointF transformOrigin )
1179{
1180 double evaluatedAngle = angle + rotation();
1181 evaluatedAngle = QgsLayoutUtils::normalizedAngle( evaluatedAngle, true );
1182 mItemRotation = evaluatedAngle;
1183
1184 QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1185
1186 refreshItemRotation( &itemTransformOrigin );
1187}
1188
1190{
1191 return false;
1192}
1193
1200
1202{
1203 Q_UNUSED( visitor );
1204 return true;
1205}
1206
1208{
1209 return QgsGeometry();
1210}
1211
1219
1221{
1222 if ( !mItemCachedImage.isNull() )
1223 {
1224 mItemCachedImage = QImage();
1225 mItemCacheDpi = -1;
1226 update();
1227 }
1228}
1229
1231{
1232 update();
1233}
1234
1235void QgsLayoutItem::drawDebugRect( QPainter *painter )
1236{
1237 if ( !painter )
1238 {
1239 return;
1240 }
1241
1242 const QgsScopedQPainterState painterState( painter );
1243 painter->setRenderHint( QPainter::Antialiasing, false );
1244 painter->setPen( Qt::NoPen );
1245 painter->setBrush( QColor( 100, 255, 100, 200 ) );
1246 painter->drawRect( rect() );
1247}
1248
1249QPainterPath QgsLayoutItem::framePath() const
1250{
1251 QPainterPath path;
1252 path.addRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1253 return path;
1254}
1255
1257{
1258 if ( !mFrame || !context.painter() )
1259 return;
1260
1261 QPainter *p = context.painter();
1262
1263 const QgsScopedQPainterState painterState( p );
1264
1265 p->setPen( pen() );
1266 p->setBrush( Qt::NoBrush );
1267 context.setPainterFlagsUsingContext( p );
1268
1269 p->drawPath( framePath() );
1270}
1271
1273{
1274 if ( !mBackground || !context.painter() )
1275 return;
1276
1277 const QgsScopedQPainterState painterState( context.painter() );
1278
1279 QPainter *p = context.painter();
1280 p->setBrush( brush() );
1281 p->setPen( Qt::NoPen );
1282 context.setPainterFlagsUsingContext( p );
1283
1284 p->drawPath( framePath() );
1285}
1286
1287void QgsLayoutItem::drawRefreshingOverlay( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle )
1288{
1289 const QgsScopedQPainterState painterState( painter );
1290 bool fitsInCache = false;
1291 const int xSize = std::floor( static_cast<double>( QFontMetrics( QFont() ).horizontalAdvance( 'X' ) ) * painter->device()->devicePixelRatioF() );
1292 const QImage refreshingImage = QgsApplication::svgCache()->svgAsImage( u":/images/composer/refreshing_item.svg"_s, xSize * 3, QColor(), QColor(), 1, 1, fitsInCache );
1293
1294 const double previewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
1295 painter->scale( 1.0 / previewScaleFactor / painter->device()->devicePixelRatioF(), 1.0 / previewScaleFactor / painter->device()->devicePixelRatioF() );
1296 painter->drawImage( xSize, xSize, refreshingImage );
1297}
1298
1300{
1301 mFixedSize = size;
1303}
1304
1306{
1307 mMinimumSize = size;
1309}
1310
1311QSizeF QgsLayoutItem::applyItemSizeConstraint( const QSizeF targetSize )
1312{
1313 return targetSize;
1314}
1315
1317{
1318 attemptResize( mItemSize );
1319}
1320
1322{
1323 attemptMove( mItemPosition );
1324}
1325
1326QPointF QgsLayoutItem::itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF size ) const
1327{
1328 switch ( reference )
1329 {
1330 case UpperMiddle:
1331 return QPointF( size.width() / 2.0, 0 );
1332 case UpperRight:
1333 return QPointF( size.width(), 0 );
1334 case MiddleLeft:
1335 return QPointF( 0, size.height() / 2.0 );
1336 case Middle:
1337 return QPointF( size.width() / 2.0, size.height() / 2.0 );
1338 case MiddleRight:
1339 return QPointF( size.width(), size.height() / 2.0 );
1340 case LowerLeft:
1341 return QPointF( 0, size.height() );
1342 case LowerMiddle:
1343 return QPointF( size.width() / 2.0, size.height() );
1344 case LowerRight:
1345 return QPointF( size.width(), size.height() );
1346 case UpperLeft:
1347 return QPointF( 0, 0 );
1348 }
1349 // no warnings
1350 return QPointF( 0, 0 );
1351}
1352
1353QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF position, const QSizeF size, const ReferencePoint reference ) const
1354{
1355 const QPointF itemPosition = mapFromScene( position ); //need to map from scene to handle item rotation
1356 const QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1357 return mapToScene( adjustedPointInsideItem );
1358}
1359
1361{
1362 const QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1363 return mapToScene( pointWithinItem );
1364}
1365
1367{
1368 const QPointF topLeft = mLayout->convertToLayoutUnits( point );
1369 const QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1370 return mLayout->convertFromLayoutUnits( refPoint, point.units() );
1371}
1372
1373bool QgsLayoutItem::writePropertiesToElement( QDomElement &, QDomDocument &, const QgsReadWriteContext & ) const
1374{
1375 return true;
1376}
1377
1378bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &, const QDomDocument &, const QgsReadWriteContext & )
1379{
1380
1381 return true;
1382}
1383
1384void QgsLayoutItem::initConnectionsToLayout()
1385{
1386 if ( !mLayout )
1387 return;
1388
1389}
1390
1391void QgsLayoutItem::preparePainter( QPainter *painter )
1392{
1393 if ( !painter || !painter->device() )
1394 {
1395 return;
1396 }
1397
1398 painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1399
1400 painter->setRenderHint( QPainter::LosslessImageRendering, mLayout && mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::LosslessImageRendering ) );
1401}
1402
1403bool QgsLayoutItem::shouldDrawAntialiased() const
1404{
1405 if ( !mLayout )
1406 {
1407 return true;
1408 }
1409 return mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Antialiasing ) && !mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Debug );
1410}
1411
1412bool QgsLayoutItem::shouldDrawDebugRect() const
1413{
1414 return mLayout && mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Debug );
1415}
1416
1417QSizeF QgsLayoutItem::applyMinimumSize( const QSizeF targetSize )
1418{
1419 if ( !mLayout || minimumSize().isEmpty() )
1420 {
1421 return targetSize;
1422 }
1423 const QSizeF minimumSizeLayoutUnits = mLayout->convertToLayoutUnits( minimumSize() );
1424 return targetSize.expandedTo( minimumSizeLayoutUnits );
1425}
1426
1427QSizeF QgsLayoutItem::applyFixedSize( const QSizeF targetSize )
1428{
1429 if ( !mLayout || fixedSize().isEmpty() )
1430 {
1431 return targetSize;
1432 }
1433
1434 QSizeF size = targetSize;
1435 const QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
1436 if ( fixedSizeLayoutUnits.width() > 0 )
1437 size.setWidth( fixedSizeLayoutUnits.width() );
1438 if ( fixedSizeLayoutUnits.height() > 0 )
1439 size.setHeight( fixedSizeLayoutUnits.height() );
1440
1441 return size;
1442}
1443
1445{
1446 double r = mItemRotation;
1447
1448 //data defined rotation set?
1450
1451 if ( qgsDoubleNear( r, rotation() ) && !origin )
1452 {
1453 return;
1454 }
1455
1456 const QPointF transformPoint = origin ? *origin : mapFromScene( positionAtReferencePoint( QgsLayoutItem::Middle ) );
1457
1458 if ( !transformPoint.isNull() )
1459 {
1460 //adjustPosition set, so shift the position of the item so that rotation occurs around item center
1461 //create a line from the transform point to the item's origin, in scene coordinates
1462 QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1463 //rotate this line by the current rotation angle
1464 refLine.setAngle( refLine.angle() - r + rotation() );
1465 //get new end point of line - this is the new item position
1466 const QPointF rotatedReferencePoint = refLine.p2();
1467 setPos( rotatedReferencePoint );
1468 }
1469
1470 setTransformOriginPoint( 0, 0 );
1471 QGraphicsItem::setRotation( r );
1472
1473 //adjust stored position of item to match scene pos of reference point
1474 updateStoredItemPosition();
1475 emit sizePositionChanged();
1476
1477 emit rotationChanged( r );
1478
1479 //update bounds of scene, since rotation may affect this
1480 mLayout->updateBounds();
1481}
1482
1483void QgsLayoutItem::refreshOpacity( bool updateItem )
1484{
1485 //data defined opacity set?
1486 const double opacity = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::Opacity, createExpressionContext(), mOpacity * 100.0 );
1487
1488 // Set the QGraphicItem's opacity
1489 mEvaluatedOpacity = opacity / 100.0;
1490
1492 {
1493 // item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
1494 // we have to rely on QGraphicsItem opacity to handle this
1495 setOpacity( mEvaluatedOpacity );
1496 }
1497
1498 if ( updateItem )
1499 {
1500 update();
1501 }
1502}
1503
1504void QgsLayoutItem::refreshFrame( bool updateItem )
1505{
1506 if ( !mFrame )
1507 {
1508 setPen( Qt::NoPen );
1509 return;
1510 }
1511
1512 //data defined stroke color set?
1513 bool ok = false;
1514 const QColor frameColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::DataDefinedProperty::FrameColor, createExpressionContext(), mFrameColor, &ok );
1515 QPen itemPen;
1516 if ( ok )
1517 {
1518 itemPen = QPen( frameColor );
1519 }
1520 else
1521 {
1522 itemPen = QPen( mFrameColor );
1523 }
1524 itemPen.setJoinStyle( mFrameJoinStyle );
1525
1526 if ( mLayout )
1527 itemPen.setWidthF( mLayout->convertToLayoutUnits( mFrameWidth ) );
1528 else
1529 itemPen.setWidthF( mFrameWidth.length() );
1530
1531 setPen( itemPen );
1532
1533 if ( updateItem )
1534 {
1535 update();
1536 }
1537}
1538
1539QColor QgsLayoutItem::backgroundColor( bool useDataDefined ) const
1540{
1541 return useDataDefined ? brush().color() : mBackgroundColor;
1542}
1543
1544
1546{
1547 //data defined fill color set?
1548 bool ok = false;
1550 if ( ok )
1551 {
1552 setBrush( QBrush( backgroundColor, Qt::SolidPattern ) );
1553 }
1554 else
1555 {
1556 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1557 }
1558 if ( updateItem )
1559 {
1560 update();
1561 }
1562}
1563
1565{
1566 QPainter::CompositionMode blendMode = mBlendMode;
1567
1568 //data defined blend mode set?
1569 bool ok = false;
1570 const QString blendStr = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::BlendMode, createExpressionContext(), QString(), &ok );
1571 if ( ok && !blendStr.isEmpty() )
1572 {
1573 const QString blendstr = blendStr.trimmed();
1574 const QPainter::CompositionMode blendModeD = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1575 blendMode = blendModeD;
1576 }
1577
1578 mEvaluatedBlendMode = blendMode;
1579
1580 // we can only enable caching if no blend mode is set -- otherwise
1581 // we need to redraw the item every time it is painted
1582 if ( mEvaluatedBlendMode == QPainter::CompositionMode_Source && !( itemFlags() & QgsLayoutItem::FlagDisableSceneCaching ) )
1583 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
1584 else
1585 setCacheMode( QGraphicsItem::NoCache );
1586
1587 update();
1588}
1589
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2764
LayoutUnit
Layout measurement units.
Definition qgis.h:5275
@ Millimeters
Millimeters.
Definition qgis.h:5276
@ Pixels
Pixels.
Definition qgis.h:5283
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5002
@ Debug
Debug/testing mode, items are drawn as solid rectangles.
Definition qgis.h:5310
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
Definition qgis.h:5319
@ Antialiasing
Use antialiasing when drawing items.
Definition qgis.h:5312
Base class for commands to undo/redo layout and layout object changes.
static QgsPageSizeRegistry * pageSizeRegistry()
Returns the application's page size registry, used for managing layout page sizes.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsExpressionContextScope * layoutItemScope(const QgsLayoutItem *item)
Creates a new scope which contains variables and functions relating to a QgsLayoutItem.
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.
A geometry is the spatial representation of a feature.
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
Stores metadata about one layout item class.
Item representing the paper in a layout.
Orientation
Page orientation.
@ Landscape
Landscape orientation.
@ Portrait
Portrait orientation.
@ LayoutItem
Base class for items.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsLayoutItemRenderContext(QgsRenderContext &context, double viewScaleFactor=1.0)
Constructor for QgsLayoutItemRenderContext.
double viewScaleFactor() const
Returns the current view zoom (scale factor).
virtual void drawDebugRect(QPainter *painter)
Draws a debugging rectangle of the item's current bounds within the specified painter.
virtual void drawFrame(QgsRenderContext &context)
Draws the frame around the item.
friend class QgsLayoutItemGroup
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.
friend class QgsLayout
virtual QgsGeometry clipPath() const
Returns the clipping path generated by this item, in layout coordinates.
virtual QPainterPath framePath() const
Returns the path to use when drawing the item's frame or background.
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
bool isGroupMember() const
Returns true if the item is part of a QgsLayoutItemGroup group.
QColor backgroundColor(bool useDataDefined=true) const
Returns the background color for this item.
void drawRefreshingOverlay(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle)
Draws a "refreshing" overlay icon on the item.
bool writeXml(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores the item state in a DOM element.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
virtual void setFrameStrokeWidth(QgsLayoutMeasurement width)
Sets the frame stroke width.
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
void refreshItemRotation(QPointF *origin=nullptr)
Refreshes an item's rotation by rechecking it against any possible overrides such as data defined rot...
UndoCommand
Layout item undo commands, used for collapsing undo commands.
QgsLayoutItemGroup * parentGroup() const
Returns the item's parent group, if the item is part of a QgsLayoutItemGroup group.
double itemRotation() const
Returns the current rotation for the item, in degrees clockwise.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
virtual void setSelected(bool selected)
Sets whether the item should be selected.
QPointF adjustPointForReferencePosition(QPointF point, QSizeF size, ReferencePoint reference) const
Adjusts the specified point at which a reference position of the item sits and returns the top left c...
virtual void zoomContent(double factor, QPointF point)
Zooms content of item.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item's rotation, in degrees clockwise.
void rotationChanged(double newRotation)
Emitted on item rotation change.
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const
Returns the details for the specified current export layer.
void setBackgroundColor(const QColor &color)
Sets the background color for this item.
virtual QIcon icon() const
Returns the item's icon.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item's content.
bool excludeFromExports() const
Returns whether the item should be excluded from layout exports and prints.
virtual bool nextExportPart()
Moves to the next export part for a multi-layered export item, during a multi-layered export.
double itemOpacity() const
Returns the item's opacity.
virtual QRectF rectWithFrame() const
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
void beginCommand(const QString &commandText, UndoCommand command=UndoNone)
Starts new undo command for this item.
void cancelCommand()
Cancels the current item command and discards it.
void setItemOpacity(double opacity)
Sets the item's opacity.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsLayoutPoint positionWithUnits() const
Returns the item's current position, including units.
QgsLayoutPoint topLeftToReferencePoint(const QgsLayoutPoint &point) const
Returns the position for the reference point of the item, if the top-left of the item was placed at t...
ReferencePoint
Fixed position reference point.
@ LowerMiddle
Lower center of item.
@ MiddleLeft
Middle left of item.
@ Middle
Center of item.
@ UpperRight
Upper right corner of item.
@ LowerLeft
Lower left corner of item.
@ UpperLeft
Upper left corner of item.
@ UpperMiddle
Upper center of item.
@ MiddleRight
Middle right of item.
@ LowerRight
Lower right corner of item.
virtual void startLayeredExport()
Starts a multi-layer export operation.
virtual bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores item state within an XML DOM element.
void endCommand()
Completes the current item command and push it onto the layout's undo stack.
void refreshItemSize()
Refreshes an item's size by rechecking it against any possible item fixed or minimum sizes.
int page() const
Returns the page the item is currently on, with the first page returning 0.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void setId(const QString &id)
Set the item's id name.
void setFrameStrokeColor(const QColor &color)
Sets the frame stroke color.
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
void setFrameJoinStyle(Qt::PenJoinStyle style)
Sets the join style used when drawing the item's frame.
void refreshBackgroundColor(bool updateItem=true)
Refresh item's background color, considering data defined colors.
virtual void rotateItem(double angle, QPointF transformOrigin)
Rotates the item by a specified angle in degrees clockwise around a specified reference point.
bool readXml(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context)
Sets the item state from a DOM element.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
void setLocked(bool locked)
Sets whether the item is locked, preventing mouse interactions with the item.
~QgsLayoutItem() override
int type() const override
Returns a unique graphics item type identifier.
virtual void drawBackground(QgsRenderContext &context)
Draws the background for the item.
virtual QString displayName() const
Gets item display name.
virtual QgsLayoutSize minimumSize() const
Returns the minimum allowed size of the item, if applicable, or an empty size if item can be freely r...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
virtual bool requiresRasterization() const
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void moveContent(double dx, double dy)
Moves the content of the item, by a specified dx and dy in layout units.
@ FlagOverridesPaint
Item overrides the default layout item painting method.
@ FlagDisableSceneCaching
Item should not have QGraphicsItem caching enabled.
virtual void stopLayeredExport()
Stops a multi-layer export operation.
virtual bool containsAdvancedEffects() const
Returns true if the item contains contents with blend modes or transparency effects which can only be...
virtual QgsLayoutSize fixedSize() const
Returns the fixed size of the item, if applicable, or an empty size if item can be freely resized.
virtual void setMoveContentPreviewOffset(double dx, double dy)
Sets temporary offset for the item, by a specified dx and dy in layout units.
void setExcludeFromExports(bool exclude)
Sets whether the item should be excluded from layout exports and prints.
void sizePositionChanged()
Emitted when the item's size or position changes.
void lockChanged()
Emitted if the item's lock status changes.
virtual QSizeF applyItemSizeConstraint(QSizeF targetSize)
Applies any item-specific size constraint handling to a given targetSize in layout units.
virtual void invalidateCache()
Forces a deferred update of any cached image the item uses.
void refreshFrame(bool updateItem=true)
Refresh item's frame, considering data defined colors and frame size.
virtual QString uuid() const
Returns the item identification string.
virtual void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1)
Attempts to move the item to a specified point.
QPointF pagePos() const
Returns the item's position (in layout units) relative to the top left corner of its current page.
QString id() const
Returns the item's ID name.
void setBlendMode(QPainter::CompositionMode mode)
Sets the item's composition blending mode.
bool frameEnabled() const
Returns true if the item includes a frame.
void frameChanged()
Emitted if the item's frame style changes.
virtual Flags itemFlags() const
Returns the item's flags, which indicate how the item behaves.
void attemptMoveBy(double deltaX, double deltaY)
Attempts to shift the item's position by a specified deltaX and deltaY, in layout units.
void setReferencePoint(ReferencePoint point)
Sets the reference point for positioning of the layout item.
virtual bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context)
Sets item state from a DOM element.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ CanGroupWithAnyOtherItem
Item can be placed on a layer with any other item (default behavior).
virtual Q_DECL_DEPRECATED int numberExportLayers() const
Returns the number of layers that this item requires for exporting during layered exports (e....
virtual bool isRefreshing() const
Returns true if the item is currently refreshing content in the background.
void refreshBlendMode()
Refresh item's blend mode, considering data defined blend mode.
void setParentGroup(QgsLayoutItemGroup *group)
Sets the item's parent group.
QPointF positionAtReferencePoint(ReferencePoint reference) const
Returns the current position (in layout units) of a reference point for the item.
void refreshOpacity(bool updateItem=true)
Refresh item's opacity, considering data defined opacity.
QgsLayoutSize applyDataDefinedSize(const QgsLayoutSize &size)
Applies any present data defined size overrides to the specified layout size.
virtual void setMinimumSize(const QgsLayoutSize &size)
Sets the minimum allowed size for the layout item.
QFlags< Flag > Flags
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
virtual double estimatedFrameBleed() const
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
virtual ExportLayerBehavior exportLayerBehavior() const
Returns the behavior of this item during exporting to layered exports (e.g.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
void refreshItemPosition()
Refreshes an item's position by rechecking it against any possible overrides such as data defined pos...
virtual void setFixedSize(const QgsLayoutSize &size)
Sets a fixed size for the layout item, which prevents it from being freely resized.
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
virtual void draw(QgsLayoutItemRenderContext &context)=0
Draws the item's contents using the specified item render context.
QgsLayoutPoint pagePositionWithUnits() const
Returns the item's position (in item units) relative to the top left corner of its current page.
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
QgsPropertyCollection mDataDefinedProperties
bool readObjectPropertiesFromElement(const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context)
Sets object properties from a DOM element.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
virtual void refresh()
Refreshes the object, causing a recalculation of any property overrides.
QPointer< QgsLayout > mLayout
QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects' current state.
DataDefinedProperty
Data defined properties for different item types.
@ ExcludeFromExports
Exclude item from exports.
@ BackgroundColor
Item background color.
@ PresetPaperSize
Preset paper size for composition.
@ AllProperties
All properties for item.
QgsLayoutObject(QgsLayout *layout)
Constructor for QgsLayoutObject, with the specified parent layout.
bool writeObjectPropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores object properties within an XML DOM element.
Provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
double x() const
Returns x coordinate of point.
QPointF toQPointF() const
Converts the layout point to a QPointF.
void setX(const double x)
Sets the x coordinate of point.
static QgsLayoutPoint decodePoint(const QString &string)
Decodes a point from a string.
double y() const
Returns y coordinate of point.
Qgis::LayoutUnit units() const
Returns the units for the point.
void setY(const double y)
Sets y coordinate of point.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
static QgsLayoutSize decodeSize(const QString &string)
Decodes a size from a string.
double height() const
Returns the height of the size.
void setWidth(const double width)
Sets the width for the size.
Qgis::LayoutUnit units() const
Returns the units for the size.
double width() const
Returns the width of the size.
void setHeight(const double height)
Sets the height for the size.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static bool itemIsAClippingSource(const QgsLayoutItem *item)
Returns true if an item is a clipping item for another layout item.
static QgsLayoutItemPage::Orientation decodePaperOrientation(const QString &string, bool &ok)
Decodes a string representing a paper orientation and returns the decoded orientation.
static double normalizedAngle(double angle, bool allowNegative=false)
Ensures that an angle (in degrees) is in the range 0 <= angle < 360.
static Q_DECL_DEPRECATED double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
@ ZItem
Minimum z value for items.
Definition qgslayout.h:59
A named page size for layouts.
QgsLayoutSize size
Page size.
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
A container for the context for various read/write operations on objects.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QPainter::CompositionMode decodeBlendMode(const QString &s)
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7451
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
#define CACHE_SIZE_LIMIT
Contains details of a particular export layer relating to a layout item.