QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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{
30 //loop through group members and remove them from the scene
31 for ( QgsLayoutItem *item : std::as_const( mItems ) )
32 {
33 if ( !item )
34 continue;
35
36 //inform model that we are about to remove an item from the scene
37 if ( mLayout )
38 mLayout->removeLayoutItem( item );
39 else
40 {
41 item->cleanup();
42 item->deleteLater();
43 }
44 }
45 mItems.clear();
47}
48
50{
52}
53
55{
56 //return id, if it's not empty
57 if ( !id().isEmpty() )
58 {
59 return id();
60 }
61 return tr( "<Group>" );
62}
63
65{
66 return new QgsLayoutItemGroup( layout );
67}
68
70{
71 if ( !item )
72 {
73 return;
74 }
75
76 if ( mItems.contains( item ) )
77 {
78 return;
79 }
80
81 mItems << QPointer< QgsLayoutItem >( item );
82 item->setParentGroup( this );
83
84 updateBoundingRect( item );
85}
86
88{
89 for ( QgsLayoutItem *item : std::as_const( mItems ) )
90 {
91 if ( !item )
92 continue;
93
94 item->setParentGroup( nullptr );
95 }
96 mItems.clear();
97}
98
99QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
100{
101 QList<QgsLayoutItem *> val;
102 for ( QgsLayoutItem *item : std::as_const( mItems ) )
103 {
104 if ( !item )
105 continue;
106 val << item;
107 }
108 return val;
109}
110
111void QgsLayoutItemGroup::setVisibility( const bool visible )
112{
113 if ( !shouldBlockUndoCommands() )
114 mLayout->undoStack()->beginMacro( tr( "Set Group Visibility" ) );
115 //also set visibility for all items within the group
116 for ( QgsLayoutItem *item : std::as_const( mItems ) )
117 {
118 if ( !item )
119 continue;
120 bool prev = item->mBlockUndoCommands;
121 item->mBlockUndoCommands = mBlockUndoCommands;
122 item->setVisibility( visible );
123 item->mBlockUndoCommands = prev;
124 }
125 //lastly set visibility for group item itself
127
128 if ( !shouldBlockUndoCommands() )
129 mLayout->undoStack()->endMacro();
130}
131
132void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point, bool useReferencePoint, bool includesFrame, int page )
133{
134 Q_UNUSED( useReferencePoint ) //groups should always have reference point in top left
135 if ( !mLayout )
136 return;
137
138 if ( !shouldBlockUndoCommands() )
139 mLayout->undoStack()->beginMacro( tr( "Move group" ) );
140
141 QPointF scenePoint;
142 if ( page < 0 )
143 scenePoint = mLayout->convertToLayoutUnits( point );
144 else
145 scenePoint = mLayout->pageCollection()->pagePositionToLayoutPosition( page, point );
146
147 double deltaX = scenePoint.x() - pos().x();
148 double deltaY = scenePoint.y() - pos().y();
149
150 //also move all items within the group
151 for ( QgsLayoutItem *item : std::as_const( mItems ) )
152 {
153 if ( !item )
154 continue;
155
156 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
157 if ( !shouldBlockUndoCommands() )
158 {
159 command.reset( createCommand( QString(), 0 ) );
160 command->saveBeforeState();
161 }
162
163 item->attemptMoveBy( deltaX, deltaY );
164
165 if ( command )
166 {
167 command->saveAfterState();
168 mLayout->undoStack()->push( command.release() );
169 }
170 }
171 //lastly move group item itself
172 QgsLayoutItem::attemptMove( point, includesFrame );
173 if ( !shouldBlockUndoCommands() )
174 mLayout->undoStack()->endMacro();
175 resetBoundingRect();
176}
177
178void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includesFrame )
179{
180 if ( !mLayout )
181 return;
182
183 if ( !shouldBlockUndoCommands() )
184 mLayout->undoStack()->beginMacro( tr( "Resize Group" ) );
185
186 QRectF oldRect = rect();
187 QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
188 QRectF newRect;
189 newRect.setSize( newSizeLayoutUnits );
190
191 //also resize all items within the group
192 for ( QgsLayoutItem *item : std::as_const( mItems ) )
193 {
194 if ( !item )
195 continue;
196
197 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
198 if ( !shouldBlockUndoCommands() )
199 {
200 command.reset( createCommand( QString(), 0 ) );
201 command->saveBeforeState();
202 }
203
204 QRectF itemRect = mapRectFromItem( item, item->rect() );
205 QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
206
207 itemRect = itemRect.normalized();
208 QPointF newPos = mapToScene( itemRect.topLeft() );
209
210 QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
211 item->attemptResize( itemSize, includesFrame );
212
213 // translate new position to current item units
214 QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, item->positionWithUnits().units() );
215 item->attemptMove( itemPos, false );
216
217 if ( command )
218 {
219 command->saveAfterState();
220 mLayout->undoStack()->push( command.release() );
221 }
222 }
224 if ( !shouldBlockUndoCommands() )
225 mLayout->undoStack()->endMacro();
226
227 resetBoundingRect();
228}
229
230bool QgsLayoutItemGroup::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
231{
232 for ( QgsLayoutItem *item : mItems )
233 {
234 if ( !item )
235 continue;
236
237 QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
238 childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
239 element.appendChild( childItem );
240 }
241 return true;
242}
243
244bool QgsLayoutItemGroup::readPropertiesFromElement( const QDomElement &itemElement, const QDomDocument &, const QgsReadWriteContext & )
245{
246 mItemUuids.clear();
247
248 QDomNodeList elementNodes = itemElement.elementsByTagName( QStringLiteral( "ComposerItemGroupElement" ) );
249 for ( int i = 0; i < elementNodes.count(); ++i )
250 {
251 QDomNode elementNode = elementNodes.at( i );
252 if ( !elementNode.isElement() )
253 continue;
254
255 QString uuid = elementNode.toElement().attribute( QStringLiteral( "uuid" ) );
256 mItemUuids << uuid;
257 }
258 return true;
259}
260
262{
263 for ( const QString &uuid : std::as_const( mItemUuids ) )
264 {
265 QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
266 if ( item )
267 {
268 addItem( item );
269 }
270 }
271
272 resetBoundingRect();
273}
274
276{
277 return MustPlaceInOwnLayer;
278}
279
280void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
281{
282}
283
285{
286 // nothing to draw here!
287}
288
289void QgsLayoutItemGroup::resetBoundingRect()
290{
291 mBoundingRectangle = QRectF();
292 for ( QgsLayoutItem *item : std::as_const( mItems ) )
293 {
294 updateBoundingRect( item );
295 }
296}
297
298void QgsLayoutItemGroup::updateBoundingRect( QgsLayoutItem *item )
299{
300 //update extent
301 if ( mBoundingRectangle.isEmpty() ) //we add the first item
302 {
303 mBoundingRectangle = QRectF( 0, 0, item->rect().width(), item->rect().height() );
304 setSceneRect( QRectF( item->pos().x(), item->pos().y(), item->rect().width(), item->rect().height() ) );
305
306 if ( !qgsDoubleNear( item->rotation(), 0.0 ) )
307 {
308 setItemRotation( item->rotation() );
309 }
310 }
311 else
312 {
313 if ( !qgsDoubleNear( item->rotation(), rotation() ) )
314 {
315 //items have mixed rotation, so reset rotation of group
316 mBoundingRectangle = mapRectToScene( mBoundingRectangle );
317 setItemRotation( 0 );
318 mBoundingRectangle = mBoundingRectangle.united( item->mapRectToScene( item->rect() ) );
319 setSceneRect( mBoundingRectangle );
320 }
321 else
322 {
323 //items have same rotation, so keep rotation of group
324 mBoundingRectangle = mBoundingRectangle.united( mapRectFromItem( item, item->rect() ) );
325 QPointF newPos = mapToScene( mBoundingRectangle.topLeft().x(), mBoundingRectangle.topLeft().y() );
326 mBoundingRectangle = QRectF( 0, 0, mBoundingRectangle.width(), mBoundingRectangle.height() );
327 setSceneRect( QRectF( newPos.x(), newPos.y(), mBoundingRectangle.width(), mBoundingRectangle.height() ) );
328 }
329 }
330}
331
332void QgsLayoutItemGroup::setSceneRect( const QRectF &rectangle )
333{
334 mItemPosition = mLayout->convertFromLayoutUnits( rectangle.topLeft(), positionWithUnits().units() );
335 mItemSize = mLayout->convertFromLayoutUnits( rectangle.size(), sizeWithUnits().units() );
336 setScenePos( rectangle.topLeft() );
337 setRect( 0, 0, rectangle.width(), rectangle.height() );
338}
A container for grouping several QgsLayoutItems.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
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:45
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:41
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:51
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:2527