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