QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayoutitemgroup.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemgroup.cpp
3 -----------------------
4 begin : October 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 "qgslayoutitemgroup.h"
19#include "qgslayout.h"
20#include "qgslayoututils.h"
21#include "qgslayoutundostack.h"
23
25 : QgsLayoutItem( layout )
26{}
27
29{
31}
32
34{
35 //loop through group members and remove them from the scene
36 for ( QgsLayoutItem *item : std::as_const( mItems ) )
37 {
38 if ( !item )
39 continue;
40
41 //inform model that we are about to remove an item from the scene
42 if ( mLayout )
43 mLayout->removeLayoutItem( item );
44 else
45 {
46 item->cleanup();
47 item->deleteLater();
48 }
49 }
50 mItems.clear();
52}
53
55{
57}
58
60{
61 //return id, if it's not empty
62 if ( !id().isEmpty() )
63 {
64 return id();
65 }
66 return tr( "<Group>" );
67}
68
70{
71 return new QgsLayoutItemGroup( layout );
72}
73
75{
76 if ( !item )
77 {
78 return;
79 }
80
81 if ( mItems.contains( item ) )
82 {
83 return;
84 }
85
86 mItems << QPointer< QgsLayoutItem >( item );
87 item->setParentGroup( this );
88
89 updateBoundingRect();
90}
91
93{
94 for ( QgsLayoutItem *item : std::as_const( mItems ) )
95 {
96 if ( !item )
97 continue;
98
99 item->setParentGroup( nullptr );
100 }
101 mItems.clear();
102}
103
104QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
105{
106 QList<QgsLayoutItem *> val;
107 for ( QgsLayoutItem *item : std::as_const( mItems ) )
108 {
109 if ( !item )
110 continue;
111 val << item;
112 }
113 return val;
114}
115
116void QgsLayoutItemGroup::setVisibility( const bool visible )
117{
118 if ( !shouldBlockUndoCommands() )
119 mLayout->undoStack()->beginMacro( tr( "Set Group Visibility" ) );
120 //also set visibility for all items within the group
121 for ( QgsLayoutItem *item : std::as_const( mItems ) )
122 {
123 if ( !item )
124 continue;
125 bool prev = item->mBlockUndoCommands;
126 item->mBlockUndoCommands = mBlockUndoCommands;
127 item->setVisibility( visible );
128 item->mBlockUndoCommands = prev;
129 }
130 //lastly set visibility for group item itself
132
133 if ( !shouldBlockUndoCommands() )
134 mLayout->undoStack()->endMacro();
135}
136
137void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point, bool useReferencePoint, bool includesFrame, int page )
138{
139 Q_UNUSED( useReferencePoint ) //groups should always have reference point in top left
140 if ( !mLayout )
141 return;
142
143 if ( !shouldBlockUndoCommands() )
144 mLayout->undoStack()->beginMacro( tr( "Move group" ) );
145
146 QPointF scenePoint;
147 if ( page < 0 )
148 scenePoint = mLayout->convertToLayoutUnits( point );
149 else
150 scenePoint = mLayout->pageCollection()->pagePositionToLayoutPosition( page, point );
151
152 double deltaX = scenePoint.x() - pos().x();
153 double deltaY = scenePoint.y() - pos().y();
154
155 //also move all items within the group
156 for ( QgsLayoutItem *item : std::as_const( mItems ) )
157 {
158 if ( !item )
159 continue;
160
161 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
162 if ( !shouldBlockUndoCommands() )
163 {
164 command.reset( createCommand( QString(), 0 ) );
165 command->saveBeforeState();
166 }
167
168 item->attemptMoveBy( deltaX, deltaY );
169
170 if ( command )
171 {
172 command->saveAfterState();
173 mLayout->undoStack()->push( command.release() );
174 }
175 }
176 //lastly move group item itself
177 QgsLayoutItem::attemptMove( point, includesFrame );
178 if ( !shouldBlockUndoCommands() )
179 mLayout->undoStack()->endMacro();
180 updateBoundingRect();
181}
182
183void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includesFrame )
184{
185 if ( !mLayout )
186 return;
187
188 if ( !shouldBlockUndoCommands() )
189 mLayout->undoStack()->beginMacro( tr( "Resize Group" ) );
190
191 QRectF oldRect = rect();
192 QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
193 QRectF newRect;
194 newRect.setSize( newSizeLayoutUnits );
195
196 //also resize all items within the group
197 for ( QgsLayoutItem *item : std::as_const( mItems ) )
198 {
199 if ( !item )
200 continue;
201
202 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
203 if ( !shouldBlockUndoCommands() )
204 {
205 command.reset( createCommand( QString(), 0 ) );
206 command->saveBeforeState();
207 }
208
209 QRectF itemRect = mapRectFromItem( item, item->rect() );
210 QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
211
212 itemRect = itemRect.normalized();
213 QPointF newPos = mapToScene( itemRect.topLeft() );
214
215 QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
216 item->attemptResize( itemSize, includesFrame );
217
218 // translate new position to current item units
219 QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, item->positionWithUnits().units() );
220 item->attemptMove( itemPos, false );
221
222 if ( command )
223 {
224 command->saveAfterState();
225 mLayout->undoStack()->push( command.release() );
226 }
227 }
229 if ( !shouldBlockUndoCommands() )
230 mLayout->undoStack()->endMacro();
231
232 updateBoundingRect();
233}
234
235bool QgsLayoutItemGroup::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
236{
237 for ( QgsLayoutItem *item : mItems )
238 {
239 if ( !item )
240 continue;
241
242 QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
243 childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
244 element.appendChild( childItem );
245 }
246 return true;
247}
248
249bool QgsLayoutItemGroup::readPropertiesFromElement( const QDomElement &itemElement, const QDomDocument &, const QgsReadWriteContext & )
250{
251 mItemUuids.clear();
252
253 QDomNodeList elementNodes = itemElement.elementsByTagName( QStringLiteral( "ComposerItemGroupElement" ) );
254 for ( int i = 0; i < elementNodes.count(); ++i )
255 {
256 QDomNode elementNode = elementNodes.at( i );
257 if ( !elementNode.isElement() )
258 continue;
259
260 QString uuid = elementNode.toElement().attribute( QStringLiteral( "uuid" ) );
261 mItemUuids << uuid;
262 }
263 return true;
264}
265
267{
268 for ( const QString &uuid : std::as_const( mItemUuids ) )
269 {
270 QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
271 if ( item )
272 {
273 addItem( item );
274 }
275 }
276
277 updateBoundingRect();
278}
279
281{
282 return MustPlaceInOwnLayer;
283}
284
285void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
286{
287}
288
290{
291 // nothing to draw here!
292}
293
294
295void QgsLayoutItemGroup::updateBoundingRect()
296{
297
298 if ( mItems.isEmpty() )
299 {
300 setRect( QRectF() );
301 return;
302 }
303
304 //check if all child items have same rotation
305 auto itemIter = mItems.constBegin();
306
307 //start with rotation of first child
308 double rotation = ( *itemIter )->rotation();
309
310 //iterate through remaining children, checking if they have same rotation
311 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
312 {
313 if ( !qgsDoubleNear( ( *itemIter )->rotation(), rotation ) )
314 {
315 //item has a different rotation
316 rotation = 0.0;
317 break;
318 }
319 }
320 setScenePos( QPointF( 0, 0 ) );
321 setItemRotation( rotation );
322
323 itemIter = mItems.constBegin();
324
325 // start with handle bounds of first child
326 QRectF groupRect = mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
327 QRectF groupRectWithFrame = mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
328
329 //iterate through remaining children, expanding the bounds as required
330 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
331 {
332 groupRect |= mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
333 groupRectWithFrame |= mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
334 }
335
336 mItemSize = mLayout->convertFromLayoutUnits( groupRect.size(), sizeWithUnits().units() );
337 mItemPosition = mLayout->convertFromLayoutUnits( mapToScene( groupRect.topLeft() ), positionWithUnits().units() );
338 setRect( 0, 0, groupRect.width(), groupRect.height() );
339 setPos( mapToScene( groupRect.topLeft() ) );
340
341 QPointF bleedShift = groupRectWithFrame.topLeft() - groupRect.topLeft();
342 mRectWithFrame = QRectF( bleedShift, groupRectWithFrame.size() );
343}
344
346{
347 return mRectWithFrame;
348}
A container for grouping several QgsLayoutItems.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
QRectF rectWithFrame() const override
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
void removeItems()
Removes all items from the group (but does not delete them).
void addItem(QgsLayoutItem *item)
Adds an item to the group.
QList< QgsLayoutItem * > items() const
Returns a list of items contained by the group.
void setVisibility(bool visible) override
Sets whether the item is visible.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
bool writePropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
int type() const override
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
void cleanup() override
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
void attemptResize(const QgsLayoutSize &size, bool includesFrame=false) override
Attempts to resize the item to a specified target size.
static QgsLayoutItemGroup * create(QgsLayout *layout)
Returns a new group item for the specified layout.
void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1) override
Attempts to move the item to a specified point.
bool readPropertiesFromElement(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QString displayName() const override
Gets item display name.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:43
Base class for graphical items within a QgsLayout.
friend class QgsLayoutItemGroup
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item's rotation, in degrees clockwise.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
QgsLayoutPoint positionWithUnits() const
Returns the item's current position, including units.
int page() const
Returns the page the item is currently on, with the first page returning 0.
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target 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.
QString id() const
Returns the item's ID name.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
void setParentGroup(QgsLayoutItemGroup *group)
Sets the item's parent group.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
This class provides a method of storing points, consisting of an x and y coordinate,...
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
The class is used as a container of context for various read/write operations on other objects.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207