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