QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
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"
18#include "moc_qgslayoutitemgroup.cpp"
20#include "qgslayout.h"
21#include "qgslayoututils.h"
22#include "qgslayoutundostack.h"
24
28
33
35{
36 //loop through group members and remove them from the scene
37 for ( QgsLayoutItem *item : std::as_const( mItems ) )
38 {
39 if ( !item )
40 continue;
41
42 //inform model that we are about to remove an item from the scene
43 if ( mLayout )
44 mLayout->removeLayoutItem( item );
45 else
46 {
47 item->cleanup();
48 item->deleteLater();
49 }
50 }
51 mItems.clear();
53}
54
59
61{
62 //return id, if it's not empty
63 if ( !id().isEmpty() )
64 {
65 return id();
66 }
67 return tr( "<Group>" );
68}
69
74
76{
77 if ( !item )
78 {
79 return;
80 }
81
82 if ( mItems.contains( item ) )
83 {
84 return;
85 }
86
87 mItems << QPointer< QgsLayoutItem >( item );
88 item->setParentGroup( this );
89
90 updateBoundingRect();
91}
92
94{
95 for ( QgsLayoutItem *item : std::as_const( mItems ) )
96 {
97 if ( !item )
98 continue;
99
100 item->setParentGroup( nullptr );
101 }
102 mItems.clear();
103}
104
105QList<QgsLayoutItem *> QgsLayoutItemGroup::items() const
106{
107 QList<QgsLayoutItem *> val;
108 for ( QgsLayoutItem *item : std::as_const( mItems ) )
109 {
110 if ( !item )
111 continue;
112 val << item;
113 }
114 return val;
115}
116
117void QgsLayoutItemGroup::setVisibility( const bool visible )
118{
119 if ( !shouldBlockUndoCommands() )
120 mLayout->undoStack()->beginMacro( tr( "Set Group Visibility" ) );
121 //also set visibility for all items within the group
122 for ( QgsLayoutItem *item : std::as_const( mItems ) )
123 {
124 if ( !item )
125 continue;
126 bool prev = item->mBlockUndoCommands;
127 item->mBlockUndoCommands = mBlockUndoCommands;
128 item->setVisibility( visible );
129 item->mBlockUndoCommands = prev;
130 }
131 //lastly set visibility for group item itself
133
134 if ( !shouldBlockUndoCommands() )
135 mLayout->undoStack()->endMacro();
136}
137
138void QgsLayoutItemGroup::attemptMove( const QgsLayoutPoint &point, bool useReferencePoint, bool includesFrame, int page )
139{
140 Q_UNUSED( useReferencePoint ) //groups should always have reference point in top left
141 if ( !mLayout )
142 return;
143
144 if ( !shouldBlockUndoCommands() )
145 mLayout->undoStack()->beginMacro( tr( "Move group" ) );
146
147 QPointF scenePoint;
148 if ( page < 0 )
149 scenePoint = mLayout->convertToLayoutUnits( point );
150 else
151 scenePoint = mLayout->pageCollection()->pagePositionToLayoutPosition( page, point );
152
153 double deltaX = scenePoint.x() - pos().x();
154 double deltaY = scenePoint.y() - pos().y();
155
156 //also move all items within the group
157 for ( QgsLayoutItem *item : std::as_const( mItems ) )
158 {
159 if ( !item )
160 continue;
161
162 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
163 if ( !shouldBlockUndoCommands() )
164 {
165 command.reset( createCommand( QString(), 0 ) );
166 command->saveBeforeState();
167 }
168
169 item->attemptMoveBy( deltaX, deltaY );
170
171 if ( command )
172 {
173 command->saveAfterState();
174 mLayout->undoStack()->push( command.release() );
175 }
176 }
177 //lastly move group item itself
178 QgsLayoutItem::attemptMove( point, includesFrame );
179 if ( !shouldBlockUndoCommands() )
180 mLayout->undoStack()->endMacro();
181 updateBoundingRect();
182}
183
184void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includesFrame )
185{
186 if ( !mLayout )
187 return;
188
189 if ( !shouldBlockUndoCommands() )
190 mLayout->undoStack()->beginMacro( tr( "Resize Group" ) );
191
192 QRectF oldRect = rect();
193 QSizeF newSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
194 QRectF newRect;
195 newRect.setSize( newSizeLayoutUnits );
196
197 //also resize all items within the group
198 for ( QgsLayoutItem *item : std::as_const( mItems ) )
199 {
200 if ( !item )
201 continue;
202
203 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
204 if ( !shouldBlockUndoCommands() )
205 {
206 command.reset( createCommand( QString(), 0 ) );
207 command->saveBeforeState();
208 }
209
210 const QRectF originalItemRect = item->rect();
211 QRectF itemRect = mapRectFromItem( item, originalItemRect );
212 QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );
213
214 itemRect = itemRect.normalized();
215 QPointF newPos = mapToScene( itemRect.topLeft() );
216
217 QgsLayoutSize itemSize = mLayout->convertFromLayoutUnits( itemRect.size(), item->sizeWithUnits().units() );
218 item->attemptResize( itemSize, includesFrame );
219
220 // translate new position to current item units
221 QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, item->positionWithUnits().units() );
222 item->attemptMove( itemPos, false );
223
224 if ( command )
225 {
226 command->saveAfterState();
227 mLayout->undoStack()->push( command.release() );
228 }
229 }
231 if ( !shouldBlockUndoCommands() )
232 mLayout->undoStack()->endMacro();
233
234 updateBoundingRect();
235}
236
237bool QgsLayoutItemGroup::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
238{
239 for ( QgsLayoutItem *item : mItems )
240 {
241 if ( !item )
242 continue;
243
244 QDomElement childItem = document.createElement( QStringLiteral( "ComposerItemGroupElement" ) );
245 childItem.setAttribute( QStringLiteral( "uuid" ), item->uuid() );
246 element.appendChild( childItem );
247 }
248 return true;
249}
250
251bool QgsLayoutItemGroup::readPropertiesFromElement( const QDomElement &itemElement, const QDomDocument &, const QgsReadWriteContext & )
252{
253 mItemUuids.clear();
254
255 QDomNodeList elementNodes = itemElement.elementsByTagName( QStringLiteral( "ComposerItemGroupElement" ) );
256 for ( int i = 0; i < elementNodes.count(); ++i )
257 {
258 QDomNode elementNode = elementNodes.at( i );
259 if ( !elementNode.isElement() )
260 continue;
261
262 QString uuid = elementNode.toElement().attribute( QStringLiteral( "uuid" ) );
263 mItemUuids << uuid;
264 }
265 return true;
266}
267
269{
270 for ( const QString &uuid : std::as_const( mItemUuids ) )
271 {
272 QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
273 if ( item )
274 {
275 addItem( item );
276 }
277 }
278
279 updateBoundingRect();
280}
281
286
287void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
288{
289}
290
292{
293 // nothing to draw here!
294}
295
296
297void QgsLayoutItemGroup::updateBoundingRect()
298{
299
300 if ( mItems.isEmpty() )
301 {
302 setRect( QRectF() );
303 return;
304 }
305
306 //check if all child items have same rotation
307 auto itemIter = mItems.constBegin();
308
309 //start with rotation of first child
310 double rotation = ( *itemIter )->rotation();
311
312 //iterate through remaining children, checking if they have same rotation
313 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
314 {
315 if ( !qgsDoubleNear( ( *itemIter )->rotation(), rotation ) )
316 {
317 //item has a different rotation
318 rotation = 0.0;
319 break;
320 }
321 }
322 setScenePos( QPointF( 0, 0 ) );
323 setItemRotation( rotation );
324
325 itemIter = mItems.constBegin();
326
327 // start with handle bounds of first child
328 QRectF groupRect = mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
329 QRectF groupRectWithFrame = mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
330
331 //iterate through remaining children, expanding the bounds as required
332 for ( ++itemIter; itemIter != mItems.constEnd(); ++itemIter )
333 {
334 groupRect |= mapFromItem( ( *itemIter ), ( *itemIter )->rect() ).boundingRect();
335 groupRectWithFrame |= mapFromItem( ( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
336 }
337
338 mItemSize = mLayout->convertFromLayoutUnits( groupRect.size(), sizeWithUnits().units() );
339 mItemPosition = mLayout->convertFromLayoutUnits( mapToScene( groupRect.topLeft() ), positionWithUnits().units() );
340 setRect( 0, 0, groupRect.width(), groupRect.height() );
341 setPos( mapToScene( groupRect.topLeft() ) );
342
343 QPointF bleedShift = groupRectWithFrame.topLeft() - groupRect.topLeft();
344 mRectWithFrame = QRectF( bleedShift, groupRectWithFrame.size() );
345}
346
348{
349 return mRectWithFrame;
350}
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.
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...
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:5958