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