QGIS API Documentation 4.1.0-Master (60fea48833c)
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
54 , QGraphicsRectItem( nullptr )
55 , mUuid( QUuid::createUuid().toString() )
56{
57 setZValue( QgsLayout::ZItem );
58
59 // needed to access current view transform during paint operations
60 setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption | QGraphicsItem::ItemIsSelectable );
61
62 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
63
64 //record initial position
65 const Qgis::LayoutUnit initialUnits = layout ? layout->units() : Qgis::LayoutUnit::Millimeters;
66 mItemPosition = QgsLayoutPoint( scenePos().x(), scenePos().y(), initialUnits );
67 mItemSize = QgsLayoutSize( rect().width(), rect().height(), initialUnits );
68
69 // required to initially setup background/frame style
71 refreshFrame( false );
72
73 initConnectionsToLayout();
74
75 //let z-Value be managed by layout
76 if ( mLayout && manageZValue )
77 {
78 mLayoutManagesZValue = true;
79 mLayout->itemsModel()->addItemAtTop( this );
80 }
81 else
82 {
83 mLayoutManagesZValue = false;
84 }
85}
86
91
93{
94 if ( mLayout && mLayoutManagesZValue )
95 {
96 mLayout->itemsModel()->removeItem( this );
97 }
98}
99
101{
102 //return id, if it's not empty
103 if ( !id().isEmpty() )
104 {
105 return id();
106 }
107
108 //for unnamed items, default to item type
109 if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( type() ) )
110 {
111 return tr( "<%1>" ).arg( metadata->visibleName() );
112 }
113
114 return tr( "<item>" );
115}
116
121
123{
124 return QgsApplication::getThemeIcon( u"/mLayoutItem.svg"_s );
125}
126
131
132void QgsLayoutItem::setId( const QString &id )
133{
134 if ( id == mId )
135 {
136 return;
137 }
138
139 if ( !shouldBlockUndoCommands() )
140 mLayout->undoStack()->beginCommand( this, tr( "Change Item ID" ) );
141
142 mId = id;
143
144 if ( !shouldBlockUndoCommands() )
145 mLayout->undoStack()->endCommand();
146
147 setToolTip( id );
148
149 //inform model that id data has changed
150 if ( mLayout )
151 {
152 mLayout->itemsModel()->updateItemDisplayName( this );
153 }
154
155 emit changed();
156}
157
158void QgsLayoutItem::setSelected( bool selected )
159{
160 QGraphicsRectItem::setSelected( selected );
161 //inform model that id data has changed
162 if ( mLayout )
163 {
164 mLayout->itemsModel()->updateItemSelectStatus( this );
165 }
166}
167
168void QgsLayoutItem::setVisibility( const bool visible )
169{
170 if ( visible == isVisible() )
171 {
172 //nothing to do
173 return;
174 }
175
176 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
177 if ( !shouldBlockUndoCommands() )
178 {
179 command.reset( createCommand( visible ? tr( "Show Item" ) : tr( "Hide Item" ), 0 ) );
180 command->saveBeforeState();
181 }
182
183 QGraphicsItem::setVisible( visible );
184
185 if ( command )
186 {
187 command->saveAfterState();
188 mLayout->undoStack()->push( command.release() );
189 }
190
191 //inform model that visibility has changed
192 if ( mLayout )
193 {
194 mLayout->itemsModel()->updateItemVisibility( this );
195 }
196}
197
199{
200 if ( locked == mIsLocked )
201 {
202 return;
203 }
204
205 if ( !shouldBlockUndoCommands() )
206 mLayout->undoStack()->beginCommand( this, locked ? tr( "Lock Item" ) : tr( "Unlock Item" ) );
207
208 mIsLocked = locked;
209
210 if ( !shouldBlockUndoCommands() )
211 mLayout->undoStack()->endCommand();
212
213 //inform model that id data has changed
214 if ( mLayout )
215 {
216 mLayout->itemsModel()->updateItemLockStatus( this );
217 }
218
219 update();
220 emit lockChanged();
221}
222
224{
225 return !mParentGroupUuid.isEmpty() && mLayout && static_cast< bool >( mLayout->itemByUuid( mParentGroupUuid ) );
226}
227
229{
230 if ( !mLayout || mParentGroupUuid.isEmpty() )
231 return nullptr;
232
233 return qobject_cast< QgsLayoutItemGroup * >( mLayout->itemByUuid( mParentGroupUuid ) );
234}
235
237{
238 if ( !group )
239 mParentGroupUuid.clear();
240 else
241 mParentGroupUuid = group->uuid();
242 setFlag( QGraphicsItem::ItemIsSelectable, !static_cast< bool>( group ) ); //item in groups cannot be selected
243}
244
249
251{
252 return 0;
253}
254
257
260
262{
264 if ( !mLayout || mLayout->renderContext().currentExportLayer() == -1 )
265 return false;
266
267 // QGIS 5- return false from base class implementation
268
269 const int layers = numberExportLayers();
270 return mLayout->renderContext().currentExportLayer() < layers;
272}
273
278
279void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
280{
281 if ( !painter || !painter->device() || !shouldDrawItem() )
282 {
283 return;
284 }
285
286 if ( shouldDrawDebugRect() )
287 {
288 drawDebugRect( painter );
289 return;
290 }
291
292 const bool previewRender = !mLayout || mLayout->renderContext().isPreviewRender();
293 double destinationDpi = previewRender ? QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter ) * 25.4 : mLayout->renderContext().dpi();
294 const bool useImageCache = false;
295 bool forceRasterOutput = containsAdvancedEffects();
296 QPainter::CompositionMode blendMode = blendModeForRender();
297 if ( mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector )
298 {
299 // the FlagForceVectorOutput flag overrides everything, and absolutely DISABLES rasterisation
300 // even when we need it to get correct rendering of opacity/blend modes/etc
301 forceRasterOutput = false;
302 }
303 else if ( blendMode != QPainter::CompositionMode_SourceOver )
304 {
305 // we have to rasterize content in order to show it with alternative blend modes
306 forceRasterOutput = true;
307 }
308
309 if ( useImageCache || forceRasterOutput )
310 {
311 double widthInPixels = 0;
312 double heightInPixels = 0;
313
314 if ( previewRender )
315 {
316 widthInPixels = boundingRect().width() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
317 heightInPixels = boundingRect().height() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
318 }
319 else
320 {
321 const double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, Qgis::LayoutUnit::Pixels ).length() : destinationDpi / 25.4;
322 widthInPixels = boundingRect().width() * layoutUnitsToPixels;
323 heightInPixels = boundingRect().height() * layoutUnitsToPixels;
324 }
325
326 // limit size of image for better performance
327 if ( previewRender && ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT ) )
328 {
329 double scale = 1.0;
330 if ( widthInPixels > heightInPixels )
331 {
332 scale = widthInPixels / CACHE_SIZE_LIMIT;
333 widthInPixels = CACHE_SIZE_LIMIT;
334 heightInPixels /= scale;
335 }
336 else
337 {
338 scale = heightInPixels / CACHE_SIZE_LIMIT;
339 heightInPixels = CACHE_SIZE_LIMIT;
340 widthInPixels /= scale;
341 }
342 destinationDpi = destinationDpi / scale;
343 }
344
345 if ( previewRender && !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
346 {
347 // can reuse last cached image
348 const QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
349 const QgsScopedQPainterState painterState( painter );
350 preparePainter( painter );
351 const double cacheScale = destinationDpi / mItemCacheDpi;
352 painter->scale( cacheScale / context.scaleFactor(), cacheScale / context.scaleFactor() );
353 painter->setCompositionMode( blendMode );
354 painter->drawImage( boundingRect().x() * context.scaleFactor() / cacheScale, boundingRect().y() * context.scaleFactor() / cacheScale, mItemCachedImage );
355 return;
356 }
357 else
358 {
359 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
360 image.fill( Qt::transparent );
361 image.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
362 image.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
363 QPainter p( &image );
364
365 preparePainter( &p );
368 // painter is already scaled to dots
369 // need to translate so that item origin is at 0,0 in painter coordinates (not bounding rect origin)
370 p.translate( -boundingRect().x() * context.scaleFactor(), -boundingRect().y() * context.scaleFactor() );
371 // scale to layout units for background and frame rendering
372 p.scale( context.scaleFactor(), context.scaleFactor() );
373 drawBackground( context );
374 p.scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
375 const double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
376 QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
377 draw( itemRenderContext );
378 p.scale( context.scaleFactor(), context.scaleFactor() );
379 drawFrame( context );
380 p.scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
381 p.end();
382
383 QgsImageOperation::multiplyOpacity( image, mEvaluatedOpacity );
384
385 const QgsScopedQPainterState painterState( painter );
386 // scale painter from mm to dots
387 painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
388 painter->setCompositionMode( blendMode );
389 painter->drawImage( boundingRect().x() * context.scaleFactor(), boundingRect().y() * context.scaleFactor(), image );
390
391 if ( previewRender )
392 {
393 mItemCacheDpi = destinationDpi;
394 mItemCachedImage = image;
395 }
396 }
397 }
398 else
399 {
400 // no caching or flattening
401 const QgsScopedQPainterState painterState( painter );
402 preparePainter( painter );
403 QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
405 drawBackground( context );
406
407 const double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
408
409 // scale painter from mm to dots
410 painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
411 QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
412 draw( itemRenderContext );
413
414 painter->scale( context.scaleFactor(), context.scaleFactor() );
415 drawFrame( context );
416 }
417
418 if ( isRefreshing() && previewRender )
419 {
420 drawRefreshingOverlay( painter, itemStyle );
421 }
422}
423
425{
426 if ( point == mReferencePoint )
427 {
428 return;
429 }
430
431 mReferencePoint = point;
432
433 //also need to adjust stored position
434 updateStoredItemPosition();
436}
437
438void QgsLayoutItem::attemptResize( const QgsLayoutSize &s, bool includesFrame )
439{
440 if ( !mLayout )
441 {
442 mItemSize = s;
443 setRect( 0, 0, s.width(), s.height() );
444 return;
445 }
446
447 QgsLayoutSize size = s;
448
449 if ( includesFrame )
450 {
451 //adjust position to account for frame size
452 const double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), size.units() ).length();
453 size.setWidth( size.width() - 2 * bleed );
454 size.setHeight( size.height() - 2 * bleed );
455 }
456
457 const QgsLayoutSize evaluatedSize = applyDataDefinedSize( size );
458 const QSizeF targetSizeLayoutUnits = mLayout->convertToLayoutUnits( evaluatedSize );
459 QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
460 actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
461 actualSizeLayoutUnits = applyItemSizeConstraint( actualSizeLayoutUnits );
462
463 if ( actualSizeLayoutUnits == rect().size() )
464 {
465 return;
466 }
467
468 const QgsLayoutSize actualSizeTargetUnits = mLayout->convertFromLayoutUnits( actualSizeLayoutUnits, size.units() );
469 mItemSize = actualSizeTargetUnits;
470
471 setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
473 emit sizePositionChanged();
474}
475
476void QgsLayoutItem::attemptMove( const QgsLayoutPoint &p, bool useReferencePoint, bool includesFrame, int page )
477{
478 if ( !mLayout )
479 {
480 mItemPosition = p;
481 setPos( p.toQPointF() );
482 return;
483 }
484
485 QgsLayoutPoint point = p;
486 if ( page >= 0 )
487 {
488 point = mLayout->pageCollection()->pagePositionToAbsolute( page, p );
489 }
490
491 if ( includesFrame )
492 {
493 //adjust position to account for frame size
494 const double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), point.units() ).length();
495 point.setX( point.x() + bleed );
496 point.setY( point.y() + bleed );
497 }
498
499 QgsLayoutPoint evaluatedPoint = point;
500 if ( !useReferencePoint )
501 {
502 evaluatedPoint = topLeftToReferencePoint( point );
503 }
504
505 evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
506 const QPointF evaluatedPointLayoutUnits = mLayout->convertToLayoutUnits( evaluatedPoint );
507 const QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size(), mReferencePoint );
508 if ( topLeftPointLayoutUnits == scenePos() && point.units() == mItemPosition.units() )
509 {
510 //TODO - add test for second condition
511 return;
512 }
513
514 const QgsLayoutPoint referencePointTargetUnits = mLayout->convertFromLayoutUnits( evaluatedPointLayoutUnits, point.units() );
515 mItemPosition = referencePointTargetUnits;
516 setScenePos( topLeftPointLayoutUnits );
517 emit sizePositionChanged();
518}
519
520void QgsLayoutItem::attemptSetSceneRect( const QRectF &rect, bool includesFrame )
521{
522 const QPointF newPos = rect.topLeft();
523
524 blockSignals( true );
525 // translate new size to current item units
526 const QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( rect.size(), mItemSize.units() );
527 attemptResize( newSize, includesFrame );
528
529 // translate new position to current item units
530 const QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, mItemPosition.units() );
531 attemptMove( itemPos, false, includesFrame );
532 blockSignals( false );
533 emit sizePositionChanged();
534}
535
536void QgsLayoutItem::attemptMoveBy( double deltaX, double deltaY )
537{
538 if ( !mLayout )
539 {
540 moveBy( deltaX, deltaY );
541 return;
542 }
543
545 const QgsLayoutPoint deltaPos = mLayout->convertFromLayoutUnits( QPointF( deltaX, deltaY ), itemPos.units() );
546 itemPos.setX( itemPos.x() + deltaPos.x() );
547 itemPos.setY( itemPos.y() + deltaPos.y() );
548 attemptMove( itemPos );
549}
550
552{
553 if ( !mLayout )
554 return -1;
555
556 return mLayout->pageCollection()->pageNumberForPoint( pos() );
557}
558
560{
561 QPointF p = positionAtReferencePoint( mReferencePoint );
562
563 if ( !mLayout )
564 return p;
565
566 // try to get page
567 QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page() );
568 if ( !pageItem )
569 return p;
570
571 p.ry() -= pageItem->pos().y();
572 return p;
573}
574
576{
577 const QPointF p = pagePos();
578 if ( !mLayout )
579 return QgsLayoutPoint( p );
580
581 return mLayout->convertFromLayoutUnits( p, mItemPosition.units() );
582}
583
584void QgsLayoutItem::setScenePos( const QPointF destinationPos )
585{
586 //since setPos does not account for item rotation, use difference between
587 //current scenePos (which DOES account for rotation) and destination pos
588 //to calculate how much the item needs to move
589 if ( auto *lParentItem = parentItem() )
590 setPos( pos() + ( destinationPos - scenePos() ) + lParentItem->scenePos() );
591 else
592 setPos( pos() + ( destinationPos - scenePos() ) );
593}
594
595bool QgsLayoutItem::shouldBlockUndoCommands() const
596{
597 return !mLayout || mLayout != scene() || mBlockUndoCommands;
598}
599
601{
603 return false;
604
605 if ( !mLayout || mLayout->renderContext().isPreviewRender() )
606 {
607 //preview mode so OK to draw item
608 return true;
609 }
610
611 //exporting layout, so check if item is excluded from exports
612 return !mEvaluatedExcludeFromExports;
613}
614
616{
617 return mItemRotation;
618}
619
620bool QgsLayoutItem::writeXml( QDomElement &parentElement, QDomDocument &doc, const QgsReadWriteContext &context ) const
621{
622 QDomElement element = doc.createElement( u"LayoutItem"_s );
623 element.setAttribute( u"type"_s, QString::number( type() ) );
624
625 element.setAttribute( u"uuid"_s, mUuid );
626 element.setAttribute( u"templateUuid"_s, mUuid );
627 element.setAttribute( u"id"_s, mId );
628 element.setAttribute( u"referencePoint"_s, QString::number( static_cast< int >( mReferencePoint ) ) );
629 element.setAttribute( u"position"_s, mItemPosition.encodePoint() );
630 element.setAttribute( u"positionOnPage"_s, pagePositionWithUnits().encodePoint() );
631 element.setAttribute( u"size"_s, mItemSize.encodeSize() );
632 element.setAttribute( u"itemRotation"_s, QString::number( mItemRotation ) );
633 element.setAttribute( u"groupUuid"_s, mParentGroupUuid );
634
635 element.setAttribute( u"zValue"_s, QString::number( zValue() ) );
636 element.setAttribute( u"visibility"_s, isVisible() );
637 //position lock for mouse moves/resizes
638 if ( mIsLocked )
639 {
640 element.setAttribute( u"positionLock"_s, u"true"_s );
641 }
642 else
643 {
644 element.setAttribute( u"positionLock"_s, u"false"_s );
645 }
646
647 //frame
648 if ( mFrame )
649 {
650 element.setAttribute( u"frame"_s, u"true"_s );
651 }
652 else
653 {
654 element.setAttribute( u"frame"_s, u"false"_s );
655 }
656
657 //background
658 if ( mBackground )
659 {
660 element.setAttribute( u"background"_s, u"true"_s );
661 }
662 else
663 {
664 element.setAttribute( u"background"_s, u"false"_s );
665 }
666
667 //frame color
668 QDomElement frameColorElem = doc.createElement( u"FrameColor"_s );
669 frameColorElem.setAttribute( u"red"_s, QString::number( mFrameColor.red() ) );
670 frameColorElem.setAttribute( u"green"_s, QString::number( mFrameColor.green() ) );
671 frameColorElem.setAttribute( u"blue"_s, QString::number( mFrameColor.blue() ) );
672 frameColorElem.setAttribute( u"alpha"_s, QString::number( mFrameColor.alpha() ) );
673 element.appendChild( frameColorElem );
674 element.setAttribute( u"outlineWidthM"_s, mFrameWidth.encodeMeasurement() );
675 element.setAttribute( u"frameJoinStyle"_s, QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );
676
677 //background color
678 QDomElement bgColorElem = doc.createElement( u"BackgroundColor"_s );
679 bgColorElem.setAttribute( u"red"_s, QString::number( mBackgroundColor.red() ) );
680 bgColorElem.setAttribute( u"green"_s, QString::number( mBackgroundColor.green() ) );
681 bgColorElem.setAttribute( u"blue"_s, QString::number( mBackgroundColor.blue() ) );
682 bgColorElem.setAttribute( u"alpha"_s, QString::number( mBackgroundColor.alpha() ) );
683 element.appendChild( bgColorElem );
684
685 //blend mode
686 element.setAttribute( u"blendMode"_s, static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
687
688 //opacity
689 element.setAttribute( u"opacity"_s, QString::number( mOpacity ) );
690
691 element.setAttribute( u"excludeFromExports"_s, mExcludeFromExports );
692
693 writeObjectPropertiesToElement( element, doc, context );
694
695 writePropertiesToElement( element, doc, context );
696 parentElement.appendChild( element );
697
698 return true;
699}
700
701bool QgsLayoutItem::readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
702{
703 if ( element.nodeName() != "LayoutItem"_L1 )
704 {
705 return false;
706 }
707
708 readObjectPropertiesFromElement( element, doc, context );
709
710 mBlockUndoCommands = true;
711 mUuid = element.attribute( u"uuid"_s, QUuid::createUuid().toString() );
712 setId( element.attribute( u"id"_s ) );
713 mReferencePoint = static_cast< ReferencePoint >( element.attribute( u"referencePoint"_s ).toInt() );
714 setItemRotation( element.attribute( u"itemRotation"_s, u"0"_s ).toDouble() );
715 attemptMove( QgsLayoutPoint::decodePoint( element.attribute( u"position"_s ) ) );
716 attemptResize( QgsLayoutSize::decodeSize( element.attribute( u"size"_s ) ) );
717
718 mParentGroupUuid = element.attribute( u"groupUuid"_s );
719 if ( !mParentGroupUuid.isEmpty() )
720 {
721 if ( QgsLayoutItemGroup *group = parentGroup() )
722 {
723 group->addItem( this );
724 }
725 }
726 mTemplateUuid = element.attribute( u"templateUuid"_s );
727
728 //position lock for mouse moves/resizes
729 const QString positionLock = element.attribute( u"positionLock"_s );
730 if ( positionLock.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
731 {
732 setLocked( true );
733 }
734 else
735 {
736 setLocked( false );
737 }
738 //visibility
739 setVisibility( element.attribute( u"visibility"_s, u"1"_s ) != "0"_L1 );
740 setZValue( element.attribute( u"zValue"_s ).toDouble() );
741
742 //frame
743 const QString frame = element.attribute( u"frame"_s );
744 if ( frame.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
745 {
746 mFrame = true;
747 }
748 else
749 {
750 mFrame = false;
751 }
752
753 //frame
754 const QString background = element.attribute( u"background"_s );
755 if ( background.compare( "true"_L1, Qt::CaseInsensitive ) == 0 )
756 {
757 mBackground = true;
758 }
759 else
760 {
761 mBackground = false;
762 }
763
764 //pen
765 mFrameWidth = QgsLayoutMeasurement::decodeMeasurement( element.attribute( u"outlineWidthM"_s ) );
766 mFrameJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( element.attribute( u"frameJoinStyle"_s, u"miter"_s ) );
767 const QDomNodeList frameColorList = element.elementsByTagName( u"FrameColor"_s );
768 if ( !frameColorList.isEmpty() )
769 {
770 const QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
771 bool redOk = false;
772 bool greenOk = false;
773 bool blueOk = false;
774 bool alphaOk = false;
775 int penRed, penGreen, penBlue, penAlpha;
776
777 penRed = frameColorElem.attribute( u"red"_s ).toDouble( &redOk );
778 penGreen = frameColorElem.attribute( u"green"_s ).toDouble( &greenOk );
779 penBlue = frameColorElem.attribute( u"blue"_s ).toDouble( &blueOk );
780 penAlpha = frameColorElem.attribute( u"alpha"_s ).toDouble( &alphaOk );
781
782 if ( redOk && greenOk && blueOk && alphaOk )
783 {
784 mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
785 }
786 }
787 refreshFrame( false );
788
789 //brush
790 const QDomNodeList bgColorList = element.elementsByTagName( u"BackgroundColor"_s );
791 if ( !bgColorList.isEmpty() )
792 {
793 const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
794 bool redOk, greenOk, blueOk, alphaOk;
795 int bgRed, bgGreen, bgBlue, bgAlpha;
796 bgRed = bgColorElem.attribute( u"red"_s ).toDouble( &redOk );
797 bgGreen = bgColorElem.attribute( u"green"_s ).toDouble( &greenOk );
798 bgBlue = bgColorElem.attribute( u"blue"_s ).toDouble( &blueOk );
799 bgAlpha = bgColorElem.attribute( u"alpha"_s ).toDouble( &alphaOk );
800 if ( redOk && greenOk && blueOk && alphaOk )
801 {
802 mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
803 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
804 }
805 //apply any data defined settings
806 refreshBackgroundColor( false );
807 }
808
809 //blend mode
810 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( element.attribute( u"blendMode"_s, u"0"_s ).toUInt() ) ) );
811
812 //opacity
813 if ( element.hasAttribute( u"opacity"_s ) )
814 {
815 setItemOpacity( element.attribute( u"opacity"_s, u"1"_s ).toDouble() );
816 }
817 else
818 {
819 setItemOpacity( 1.0 - element.attribute( u"transparency"_s, u"0"_s ).toInt() / 100.0 );
820 }
821
822 mExcludeFromExports = element.attribute( u"excludeFromExports"_s, u"0"_s ).toInt();
823 mEvaluatedExcludeFromExports = mExcludeFromExports;
824
825 const bool result = readPropertiesFromElement( element, doc, context );
826
827 mBlockUndoCommands = false;
828
829 emit changed();
830 update();
831 return result;
832}
833
836
837QgsAbstractLayoutUndoCommand *QgsLayoutItem::createCommand( const QString &text, int id, QUndoCommand *parent )
838{
839 return new QgsLayoutItemUndoCommand( this, text, id, parent );
840}
841
843{
844 if ( drawFrame == mFrame )
845 {
846 //no change
847 return;
848 }
849
850 mFrame = drawFrame;
851 refreshFrame( true );
852 emit frameChanged();
853}
854
855void QgsLayoutItem::setFrameStrokeColor( const QColor &color )
856{
857 if ( mFrameColor == color )
858 {
859 //no change
860 return;
861 }
862 mFrameColor = color;
863 // apply any datadefined overrides
864 refreshFrame( true );
865 emit frameChanged();
866}
867
869{
870 if ( mFrameWidth == width )
871 {
872 //no change
873 return;
874 }
875 mFrameWidth = width;
876 refreshFrame();
877 emit frameChanged();
878}
879
880void QgsLayoutItem::setFrameJoinStyle( const Qt::PenJoinStyle style )
881{
882 if ( mFrameJoinStyle == style )
883 {
884 //no change
885 return;
886 }
887 mFrameJoinStyle = style;
888
889 QPen itemPen = pen();
890 itemPen.setJoinStyle( mFrameJoinStyle );
891 setPen( itemPen );
892 emit frameChanged();
893}
894
896{
897 mBackground = drawBackground;
898 update();
899}
900
901void QgsLayoutItem::setBackgroundColor( const QColor &color )
902{
903 mBackgroundColor = color;
904 // apply any datadefined overrides
906}
907
908void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
909{
910 mBlendMode = mode;
911 // Update the item effect to use the new blend mode
913 update();
914}
915
916void QgsLayoutItem::setItemOpacity( double opacity )
917{
918 mOpacity = opacity;
919 refreshOpacity( mItemCachedImage.isNull() );
920 if ( !mItemCachedImage.isNull() )
922}
923
925{
926 return mExcludeFromExports;
927}
928
934
936{
937 return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
938}
939
941{
942 return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) || blendMode() != QPainter::CompositionMode_SourceOver;
943}
944
946{
947 if ( !frameEnabled() )
948 {
949 return 0;
950 }
951
952 return pen().widthF() / 2.0;
953}
954
956{
957 const double frameBleed = estimatedFrameBleed();
958 return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
959}
960
961void QgsLayoutItem::moveContent( double, double )
962{}
963
966
967void QgsLayoutItem::zoomContent( double, QPointF )
968{}
969
970void QgsLayoutItem::beginCommand( const QString &commandText, UndoCommand command )
971{
972 if ( !mLayout )
973 return;
974
975 mLayout->undoStack()->beginCommand( this, commandText, command );
976}
977
979{
980 if ( mLayout )
981 mLayout->undoStack()->endCommand();
982}
983
985{
986 if ( mLayout )
987 mLayout->undoStack()->cancelCommand();
988}
989
990QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
991{
992 if ( !mLayout )
993 {
994 return position;
995 }
996
997 const QgsExpressionContext context = createExpressionContext();
998 const double evaluatedX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::PositionX, context, position.x() );
999 const double evaluatedY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::PositionY, context, position.y() );
1000 return QgsLayoutPoint( evaluatedX, evaluatedY, position.units() );
1001}
1002
1003void QgsLayoutItem::applyDataDefinedOrientation( double &width, double &height, const QgsExpressionContext &context )
1004{
1005 bool ok = false;
1006 const QString orientationString = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::PaperOrientation, context, QString(), &ok );
1007 if ( ok && !orientationString.isEmpty() )
1008 {
1009 const QgsLayoutItemPage::Orientation orientation = QgsLayoutUtils::decodePaperOrientation( orientationString, ok );
1010 if ( ok )
1011 {
1012 double heightD = 0.0, widthD = 0.0;
1013 switch ( orientation )
1014 {
1016 {
1017 heightD = std::max( height, width );
1018 widthD = std::min( height, width );
1019 break;
1020 }
1022 {
1023 heightD = std::min( height, width );
1024 widthD = std::max( height, width );
1025 break;
1026 }
1027 }
1028 width = widthD;
1029 height = heightD;
1030 }
1031 }
1032}
1033
1035{
1036 if ( !mLayout )
1037 {
1038 return size;
1039 }
1040
1045 return size;
1046
1047
1049
1050 // lowest priority is page size
1051 const QString pageSize = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::PresetPaperSize, context );
1052 QgsPageSize matchedSize;
1053 double evaluatedWidth = size.width();
1054 double evaluatedHeight = size.height();
1055 if ( QgsApplication::pageSizeRegistry()->decodePageSize( pageSize, matchedSize ) )
1056 {
1057 const QgsLayoutSize convertedSize = mLayout->renderContext().measurementConverter().convert( matchedSize.size, size.units() );
1058 evaluatedWidth = convertedSize.width();
1059 evaluatedHeight = convertedSize.height();
1060 }
1061
1062 // highest priority is dd width/height
1063 evaluatedWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemWidth, context, evaluatedWidth );
1064 evaluatedHeight = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemHeight, context, evaluatedHeight );
1065
1066 //which is finally overwritten by data defined orientation
1067 applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1068
1069 return QgsLayoutSize( evaluatedWidth, evaluatedHeight, size.units() );
1070}
1071
1072QPainter::CompositionMode QgsLayoutItem::blendModeForRender() const
1073{
1074 QPainter::CompositionMode mode = mEvaluatedBlendMode;
1075 if ( mLayout->renderContext().rasterizedRenderingPolicy() == Qgis::RasterizedRenderingPolicy::ForceVector )
1076 {
1077 // this policy overrides everything, and absolutely DISABLES rasterisation
1078 // even when we need it to get correct rendering of opacity/blend modes/etc
1079 mode = QPainter::CompositionMode_SourceOver;
1080 }
1081 return mode;
1082}
1083
1084double QgsLayoutItem::applyDataDefinedRotation( const double rotation )
1085{
1086 if ( !mLayout )
1087 {
1088 return rotation;
1089 }
1090
1091 const QgsExpressionContext context = createExpressionContext();
1092 const double evaluatedRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::ItemRotation, context, rotation );
1093 return evaluatedRotation;
1094}
1095
1097{
1098 //update data defined properties and update item to match
1099
1100 //evaluate width and height first, since they may affect position if non-top-left reference point set
1102 {
1104 }
1106 {
1108 }
1110 {
1112 }
1114 {
1115 refreshOpacity( false );
1116 }
1118 {
1119 refreshFrame( false );
1120 }
1122 {
1123 refreshBackgroundColor( false );
1124 }
1126 {
1128 }
1130 {
1131 const bool exclude = mExcludeFromExports;
1132 //data defined exclude from exports set?
1134 }
1135
1136 update();
1137}
1138
1139void QgsLayoutItem::setItemRotation( double angle, const bool adjustPosition )
1140{
1141 if ( angle >= 360.0 || angle <= -360.0 )
1142 {
1143 angle = std::fmod( angle, 360.0 );
1144 }
1145
1146 const QPointF point = adjustPosition ? positionAtReferencePoint( QgsLayoutItem::Middle ) : pos();
1147 const double rotationRequired = angle - rotation();
1148 rotateItem( rotationRequired, point );
1149
1150 mItemRotation = angle;
1151}
1152
1153void QgsLayoutItem::updateStoredItemPosition()
1154{
1155 const QPointF layoutPosReferencePoint = positionAtReferencePoint( mReferencePoint );
1156 mItemPosition = mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.units() );
1157}
1158
1159void QgsLayoutItem::rotateItem( const double angle, const QPointF transformOrigin )
1160{
1161 double evaluatedAngle = angle + rotation();
1162 evaluatedAngle = QgsLayoutUtils::normalizedAngle( evaluatedAngle, true );
1163 mItemRotation = evaluatedAngle;
1164
1165 QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1166
1167 refreshItemRotation( &itemTransformOrigin );
1168}
1169
1171{
1172 return false;
1173}
1174
1181
1183{
1184 Q_UNUSED( visitor );
1185 return true;
1186}
1187
1189{
1190 return QgsGeometry();
1191}
1192
1200
1202{
1203 if ( !mItemCachedImage.isNull() )
1204 {
1205 mItemCachedImage = QImage();
1206 mItemCacheDpi = -1;
1207 update();
1208 }
1209}
1210
1212{
1213 update();
1214}
1215
1216void QgsLayoutItem::drawDebugRect( QPainter *painter )
1217{
1218 if ( !painter )
1219 {
1220 return;
1221 }
1222
1223 const QgsScopedQPainterState painterState( painter );
1224 painter->setRenderHint( QPainter::Antialiasing, false );
1225 painter->setPen( Qt::NoPen );
1226 painter->setBrush( QColor( 100, 255, 100, 200 ) );
1227 painter->drawRect( rect() );
1228}
1229
1230QPainterPath QgsLayoutItem::framePath() const
1231{
1232 QPainterPath path;
1233 path.addRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1234 return path;
1235}
1236
1238{
1239 if ( !mFrame || !context.painter() )
1240 return;
1241
1242 QPainter *p = context.painter();
1243
1244 const QgsScopedQPainterState painterState( p );
1245
1246 p->setPen( pen() );
1247 p->setBrush( Qt::NoBrush );
1248 context.setPainterFlagsUsingContext( p );
1249
1250 p->drawPath( framePath() );
1251}
1252
1254{
1255 if ( !mBackground || !context.painter() )
1256 return;
1257
1258 const QgsScopedQPainterState painterState( context.painter() );
1259
1260 QPainter *p = context.painter();
1261 p->setBrush( brush() );
1262 p->setPen( Qt::NoPen );
1263 context.setPainterFlagsUsingContext( p );
1264
1265 p->drawPath( framePath() );
1266}
1267
1268void QgsLayoutItem::drawRefreshingOverlay( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle )
1269{
1270 const QgsScopedQPainterState painterState( painter );
1271 bool fitsInCache = false;
1272 const int xSize = std::floor( static_cast<double>( QFontMetrics( QFont() ).horizontalAdvance( 'X' ) ) * painter->device()->devicePixelRatioF() );
1273 const QImage refreshingImage = QgsApplication::svgCache()->svgAsImage( u":/images/composer/refreshing_item.svg"_s, xSize * 3, QColor(), QColor(), 1, 1, fitsInCache );
1274
1275 const double previewScaleFactor = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
1276 painter->scale( 1.0 / previewScaleFactor / painter->device()->devicePixelRatioF(), 1.0 / previewScaleFactor / painter->device()->devicePixelRatioF() );
1277 painter->drawImage( xSize, xSize, refreshingImage );
1278}
1279
1281{
1282 mFixedSize = size;
1284}
1285
1287{
1288 mMinimumSize = size;
1290}
1291
1292QSizeF QgsLayoutItem::applyItemSizeConstraint( const QSizeF targetSize )
1293{
1294 return targetSize;
1295}
1296
1298{
1299 attemptResize( mItemSize );
1300}
1301
1303{
1304 attemptMove( mItemPosition );
1305}
1306
1307QPointF QgsLayoutItem::itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF size ) const
1308{
1309 switch ( reference )
1310 {
1311 case UpperMiddle:
1312 return QPointF( size.width() / 2.0, 0 );
1313 case UpperRight:
1314 return QPointF( size.width(), 0 );
1315 case MiddleLeft:
1316 return QPointF( 0, size.height() / 2.0 );
1317 case Middle:
1318 return QPointF( size.width() / 2.0, size.height() / 2.0 );
1319 case MiddleRight:
1320 return QPointF( size.width(), size.height() / 2.0 );
1321 case LowerLeft:
1322 return QPointF( 0, size.height() );
1323 case LowerMiddle:
1324 return QPointF( size.width() / 2.0, size.height() );
1325 case LowerRight:
1326 return QPointF( size.width(), size.height() );
1327 case UpperLeft:
1328 return QPointF( 0, 0 );
1329 }
1330 // no warnings
1331 return QPointF( 0, 0 );
1332}
1333
1334QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF position, const QSizeF size, const ReferencePoint reference ) const
1335{
1336 const QPointF itemPosition = mapFromScene( position ); //need to map from scene to handle item rotation
1337 const QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1338 return mapToScene( adjustedPointInsideItem );
1339}
1340
1342{
1343 const QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1344 return mapToScene( pointWithinItem );
1345}
1346
1348{
1349 const QPointF topLeft = mLayout->convertToLayoutUnits( point );
1350 const QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1351 return mLayout->convertFromLayoutUnits( refPoint, point.units() );
1352}
1353
1354bool QgsLayoutItem::writePropertiesToElement( QDomElement &, QDomDocument &, const QgsReadWriteContext & ) const
1355{
1356 return true;
1357}
1358
1359bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &, const QDomDocument &, const QgsReadWriteContext & )
1360{
1361 return true;
1362}
1363
1364void QgsLayoutItem::initConnectionsToLayout()
1365{
1366 if ( !mLayout )
1367 return;
1368}
1369
1370void QgsLayoutItem::preparePainter( QPainter *painter )
1371{
1372 if ( !painter || !painter->device() )
1373 {
1374 return;
1375 }
1376
1377 painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1378
1379 painter->setRenderHint( QPainter::LosslessImageRendering, mLayout && mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::LosslessImageRendering ) );
1380}
1381
1382bool QgsLayoutItem::shouldDrawAntialiased() const
1383{
1384 if ( !mLayout )
1385 {
1386 return true;
1387 }
1388 return mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Antialiasing ) && !mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Debug );
1389}
1390
1391bool QgsLayoutItem::shouldDrawDebugRect() const
1392{
1393 return mLayout && mLayout->renderContext().testFlag( Qgis::LayoutRenderFlag::Debug );
1394}
1395
1396QSizeF QgsLayoutItem::applyMinimumSize( const QSizeF targetSize )
1397{
1398 if ( !mLayout || minimumSize().isEmpty() )
1399 {
1400 return targetSize;
1401 }
1402 const QSizeF minimumSizeLayoutUnits = mLayout->convertToLayoutUnits( minimumSize() );
1403 return targetSize.expandedTo( minimumSizeLayoutUnits );
1404}
1405
1406QSizeF QgsLayoutItem::applyFixedSize( const QSizeF targetSize )
1407{
1408 if ( !mLayout || fixedSize().isEmpty() )
1409 {
1410 return targetSize;
1411 }
1412
1413 QSizeF size = targetSize;
1414 const QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
1415 if ( fixedSizeLayoutUnits.width() > 0 )
1416 size.setWidth( fixedSizeLayoutUnits.width() );
1417 if ( fixedSizeLayoutUnits.height() > 0 )
1418 size.setHeight( fixedSizeLayoutUnits.height() );
1419
1420 return size;
1421}
1422
1424{
1425 double r = mItemRotation;
1426
1427 //data defined rotation set?
1429
1430 if ( qgsDoubleNear( r, rotation() ) && !origin )
1431 {
1432 return;
1433 }
1434
1435 const QPointF transformPoint = origin ? *origin : mapFromScene( positionAtReferencePoint( QgsLayoutItem::Middle ) );
1436
1437 if ( !transformPoint.isNull() )
1438 {
1439 //adjustPosition set, so shift the position of the item so that rotation occurs around item center
1440 //create a line from the transform point to the item's origin, in scene coordinates
1441 QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1442 //rotate this line by the current rotation angle
1443 refLine.setAngle( refLine.angle() - r + rotation() );
1444 //get new end point of line - this is the new item position
1445 const QPointF rotatedReferencePoint = refLine.p2();
1446 setPos( rotatedReferencePoint );
1447 }
1448
1449 setTransformOriginPoint( 0, 0 );
1450 QGraphicsItem::setRotation( r );
1451
1452 //adjust stored position of item to match scene pos of reference point
1453 updateStoredItemPosition();
1454 emit sizePositionChanged();
1455
1456 emit rotationChanged( r );
1457
1458 //update bounds of scene, since rotation may affect this
1459 mLayout->updateBounds();
1460}
1461
1462void QgsLayoutItem::refreshOpacity( bool updateItem )
1463{
1464 //data defined opacity set?
1465 const double opacity = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::Opacity, createExpressionContext(), mOpacity * 100.0 );
1466
1467 // Set the QGraphicItem's opacity
1468 mEvaluatedOpacity = opacity / 100.0;
1469
1471 {
1472 // item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
1473 // we have to rely on QGraphicsItem opacity to handle this
1474 setOpacity( mEvaluatedOpacity );
1475 }
1476
1477 if ( updateItem )
1478 {
1479 update();
1480 }
1481}
1482
1483void QgsLayoutItem::refreshFrame( bool updateItem )
1484{
1485 if ( !mFrame )
1486 {
1487 setPen( Qt::NoPen );
1488 return;
1489 }
1490
1491 //data defined stroke color set?
1492 bool ok = false;
1493 const QColor frameColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::DataDefinedProperty::FrameColor, createExpressionContext(), mFrameColor, &ok );
1494 QPen itemPen;
1495 if ( ok )
1496 {
1497 itemPen = QPen( frameColor );
1498 }
1499 else
1500 {
1501 itemPen = QPen( mFrameColor );
1502 }
1503 itemPen.setJoinStyle( mFrameJoinStyle );
1504
1505 if ( mLayout )
1506 itemPen.setWidthF( mLayout->convertToLayoutUnits( mFrameWidth ) );
1507 else
1508 itemPen.setWidthF( mFrameWidth.length() );
1509
1510 setPen( itemPen );
1511
1512 if ( updateItem )
1513 {
1514 update();
1515 }
1516}
1517
1518QColor QgsLayoutItem::backgroundColor( bool useDataDefined ) const
1519{
1520 return useDataDefined ? brush().color() : mBackgroundColor;
1521}
1522
1523
1525{
1526 //data defined fill color set?
1527 bool ok = false;
1529 if ( ok )
1530 {
1531 setBrush( QBrush( backgroundColor, Qt::SolidPattern ) );
1532 }
1533 else
1534 {
1535 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1536 }
1537 if ( updateItem )
1538 {
1539 update();
1540 }
1541}
1542
1544{
1545 QPainter::CompositionMode blendMode = mBlendMode;
1546
1547 //data defined blend mode set?
1548 bool ok = false;
1549 const QString blendStr = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::BlendMode, createExpressionContext(), QString(), &ok );
1550 if ( ok && !blendStr.isEmpty() )
1551 {
1552 const QString blendstr = blendStr.trimmed();
1553 const QPainter::CompositionMode blendModeD = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1554 blendMode = blendModeD;
1555 }
1556
1557 mEvaluatedBlendMode = blendMode;
1558
1559 // we can only enable caching if no blend mode is set -- otherwise
1560 // we need to redraw the item every time it is painted
1561 if ( mEvaluatedBlendMode == QPainter::CompositionMode_Source && !( itemFlags() & QgsLayoutItem::FlagDisableSceneCaching ) )
1562 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
1563 else
1564 setCacheMode( QGraphicsItem::NoCache );
1565
1566 update();
1567}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2801
LayoutUnit
Layout measurement units.
Definition qgis.h:5360
@ Millimeters
Millimeters.
Definition qgis.h:5361
@ Pixels
Pixels.
Definition qgis.h:5368
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5087
@ Debug
Debug/testing mode, items are drawn as solid rectangles.
Definition qgis.h:5395
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
Definition qgis.h:5407
@ Antialiasing
Use antialiasing when drawing items.
Definition qgis.h:5397
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:58
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:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
#define CACHE_SIZE_LIMIT
Contains details of a particular export layer relating to a layout item.