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