QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgslayoutitempage.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitempage.cpp
3 ---------------------
4 begin : July 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 "qgslayoutitempage.h"
18#include "qgslayout.h"
19#include "qgslayoututils.h"
20#include "qgspagesizeregistry.h"
21#include "qgssymbollayerutils.h"
25#include "qgsstyle.h"
27#include "qgsfillsymbol.h"
28
29#include <QPainter>
30#include <QStyleOptionGraphicsItem>
31
33 : QgsLayoutItem( layout, false )
34{
35 setFlag( QGraphicsItem::ItemIsSelectable, false );
36 setFlag( QGraphicsItem::ItemIsMovable, false );
37 setZValue( QgsLayout::ZPage );
38
39 connect( this, &QgsLayoutItem::sizePositionChanged, this, [ = ]
40 {
41 mBoundingRect = QRectF();
42 prepareGeometryChange();
43 } );
44
45 const QFont font;
46 const QFontMetrics fm( font );
47 mMaximumShadowWidth = fm.boundingRect( QStringLiteral( "X" ) ).width();
48
49 mGrid.reset( new QgsLayoutItemPageGrid( pos().x(), pos().y(), rect().width(), rect().height(), mLayout ) );
50 mGrid->setParentItem( this );
51
52 createDefaultPageStyleSymbol();
53}
54
56
58{
59 return new QgsLayoutItemPage( layout );
60}
61
63{
65}
66
68{
69 return QObject::tr( "Page" );
70}
71
73{
74 attemptResize( size );
75}
76
77bool QgsLayoutItemPage::setPageSize( const QString &size, Orientation orientation )
78{
79 QgsPageSize newSize;
80 if ( QgsApplication::pageSizeRegistry()->decodePageSize( size, newSize ) )
81 {
82 switch ( orientation )
83 {
84 case Portrait:
85 break; // nothing to do
86
87 case Landscape:
88 {
89 // flip height and width
90 const double x = newSize.size.width();
91 newSize.size.setWidth( newSize.size.height() );
92 newSize.size.setHeight( x );
93 break;
94 }
95 }
96
97 setPageSize( newSize.size );
98 return true;
99 }
100 else
101 {
102 return false;
103 }
104}
105
107{
108 QPageLayout pageLayout;
109 pageLayout.setMargins( {0, 0, 0, 0} );
110 pageLayout.setMode( QPageLayout::FullPageMode );
111 const QSizeF size = layout()->renderContext().measurementConverter().convert( pageSize(), Qgis::LayoutUnit::Millimeters ).toQSizeF();
112
113 if ( pageSize().width() > pageSize().height() )
114 {
115 pageLayout.setOrientation( QPageLayout::Landscape );
116 pageLayout.setPageSize( QPageSize( QSizeF( size.height(), size.width() ), QPageSize::Millimeter ) );
117 }
118 else
119 {
120 pageLayout.setOrientation( QPageLayout::Portrait );
121 pageLayout.setPageSize( QPageSize( size, QPageSize::Millimeter ) );
122 }
123 pageLayout.setUnits( QPageLayout::Millimeter );
124 return pageLayout;
125}
126
128{
129 return sizeWithUnits();
130}
131
133{
134 if ( sizeWithUnits().width() >= sizeWithUnits().height() )
135 return Landscape;
136 else
137 return Portrait;
138}
139
141{
142 mPageStyleSymbol.reset( symbol );
143 update();
144}
145
147{
148 if ( ok )
149 *ok = false;
150
151 const QString trimmedString = string.trimmed();
152 if ( trimmedString.compare( QLatin1String( "portrait" ), Qt::CaseInsensitive ) == 0 )
153 {
154 if ( ok )
155 *ok = true;
156 return Portrait;
157 }
158 else if ( trimmedString.compare( QLatin1String( "landscape" ), Qt::CaseInsensitive ) == 0 )
159 {
160 if ( ok )
161 *ok = true;
162 return Landscape;
163 }
164 return Landscape;
165}
166
168{
169 if ( mBoundingRect.isNull() )
170 {
171 const double shadowWidth = mLayout->pageCollection()->pageShadowWidth();
172 mBoundingRect = rect();
173 mBoundingRect.adjust( 0, 0, shadowWidth, shadowWidth );
174 }
175 return mBoundingRect;
176}
177
178void QgsLayoutItemPage::attemptResize( const QgsLayoutSize &size, bool includesFrame )
179{
180 QgsLayoutItem::attemptResize( size, includesFrame );
181 //update size of attached grid to reflect new page size and position
182 mGrid->setRect( 0, 0, rect().width(), rect().height() );
183
184 mLayout->guides().update();
185}
186
187void QgsLayoutItemPage::createDefaultPageStyleSymbol()
188{
189 QVariantMap properties;
190 properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
191 properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
192 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
193 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
194 mPageStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
195}
196
197
198
200class QgsLayoutItemPageUndoCommand: public QgsLayoutItemUndoCommand
201{
202 public:
203
204 QgsLayoutItemPageUndoCommand( QgsLayoutItemPage *page, const QString &text, int id = 0, QUndoCommand *parent SIP_TRANSFERTHIS = nullptr )
205 : QgsLayoutItemUndoCommand( page, text, id, parent )
206 {}
207
208 void restoreState( QDomDocument &stateDoc ) override
209 {
210 QgsLayoutItemUndoCommand::restoreState( stateDoc );
211 layout()->pageCollection()->reflow();
212 }
213
214 protected:
215
216 QgsLayoutItem *recreateItem( int, QgsLayout *layout ) override
217 {
218 QgsLayoutItemPage *page = new QgsLayoutItemPage( layout );
219 layout->pageCollection()->addPage( page );
220 return page;
221 }
222};
224
225QgsAbstractLayoutUndoCommand *QgsLayoutItemPage::createCommand( const QString &text, int id, QUndoCommand *parent )
226{
227 return new QgsLayoutItemPageUndoCommand( this, text, id, parent );
228}
229
231{
233}
234
236{
237 QgsStyleSymbolEntity entity( mPageStyleSymbol.get() );
238 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, QStringLiteral( "page" ), QObject::tr( "Page" ) ) ) )
239 return false;
240 return true;
241}
242
244{
246 mGrid->update();
247}
248
250{
251 if ( !context.renderContext().painter() || !mLayout || !mLayout->renderContext().pagesVisible() )
252 {
253 return;
254 }
255
256 const double scale = context.renderContext().convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
257
258 const QgsExpressionContext expressionContext = createExpressionContext();
259 context.renderContext().setExpressionContext( expressionContext );
260
261 QPainter *painter = context.renderContext().painter();
262 const QgsScopedQPainterState painterState( painter );
263
264 if ( mLayout->renderContext().isPreviewRender() )
265 {
266 //if in preview mode, draw page border and shadow so that it's
267 //still possible to tell where pages with a transparent style begin and end
268 painter->setRenderHint( QPainter::Antialiasing, false );
269
270 const QRectF pageRect = QRectF( 0, 0, scale * rect().width(), scale * rect().height() );
271
272 //shadow
273 painter->setBrush( QBrush( QColor( 150, 150, 150 ) ) );
274 painter->setPen( Qt::NoPen );
275 painter->drawRect( pageRect.translated( std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ),
276 std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ) ) );
277
278 //page area
279 painter->setBrush( QColor( 215, 215, 215 ) );
280 QPen pagePen = QPen( QColor( 100, 100, 100 ), 0 );
281 pagePen.setJoinStyle( Qt::MiterJoin );
282 pagePen.setCosmetic( true );
283 painter->setPen( pagePen );
284 painter->drawRect( pageRect );
285 }
286
287 if ( mPageStyleSymbol )
288 {
289 std::unique_ptr< QgsFillSymbol > symbol( mPageStyleSymbol->clone() );
290 symbol->startRender( context.renderContext() );
291
292 //get max bleed from symbol
293 double maxBleedPixels = QgsSymbolLayerUtils::estimateMaxSymbolBleed( symbol.get(), context.renderContext() );
294
295 //Now subtract 1 pixel to prevent semi-transparent borders at edge of solid page caused by
296 //anti-aliased painting. This may cause a pixel to be cropped from certain edge lines/symbols,
297 //but that can be counteracted by adding a dummy transparent line symbol layer with a wider line width
298 if ( !mLayout->renderContext().isPreviewRender() || !qgsDoubleNear( maxBleedPixels, 0.0 ) )
299 {
300 maxBleedPixels = std::floor( maxBleedPixels - 2 );
301 }
302
303 // round up
304 const QPolygonF pagePolygon = QPolygonF( QRectF( maxBleedPixels, maxBleedPixels,
305 std::ceil( rect().width() * scale ) - 2 * maxBleedPixels, std::ceil( rect().height() * scale ) - 2 * maxBleedPixels ) );
306 const QVector<QPolygonF> rings; //empty list
307
308 symbol->renderPolygon( pagePolygon, &rings, nullptr, context.renderContext() );
309 symbol->stopRender( context.renderContext() );
310 }
311}
312
314{}
315
317{}
318
319bool QgsLayoutItemPage::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
320{
321 const QDomElement styleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mPageStyleSymbol.get(), document, context );
322 element.appendChild( styleElem );
323 return true;
324}
325
326bool QgsLayoutItemPage::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
327{
328 const QDomElement symbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
329 if ( !symbolElem.isNull() )
330 {
331 mPageStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElem, context ) );
332 }
333 else
334 {
335 createDefaultPageStyleSymbol();
336 }
337
338 return true;
339}
340
341//
342// QgsLayoutItemPageGrid
343//
345
346QgsLayoutItemPageGrid::QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout )
347 : QGraphicsRectItem( 0, 0, width, height )
348 , mLayout( layout )
349{
350 // needed to access current view transform during paint operations
351 setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption );
352 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
353 setFlag( QGraphicsItem::ItemIsSelectable, false );
354 setFlag( QGraphicsItem::ItemIsMovable, false );
355 setZValue( QgsLayout::ZGrid );
356 setPos( x, y );
357}
358
359void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
360{
361 Q_UNUSED( pWidget )
362
363 //draw grid
364 if ( !mLayout )
365 return;
366
367 if ( !mLayout->renderContext().isPreviewRender() )
368 return;
369
370 const QgsLayoutRenderContext &context = mLayout->renderContext();
371 const QgsLayoutGridSettings &grid = mLayout->gridSettings();
372
373 if ( !context.gridVisible() || grid.resolution().length() <= 0 )
374 return;
375
376 const QPointF gridOffset = mLayout->convertToLayoutUnits( grid.offset() );
377 const double gridResolution = mLayout->convertToLayoutUnits( grid.resolution() );
378 const int gridMultiplyX = static_cast< int >( gridOffset.x() / gridResolution );
379 const int gridMultiplyY = static_cast< int >( gridOffset.y() / gridResolution );
380 double currentXCoord = gridOffset.x() - gridMultiplyX * gridResolution;
381 double currentYCoord;
382 const double minYCoord = gridOffset.y() - gridMultiplyY * gridResolution;
383
384 const QgsScopedQPainterState painterState( painter );
385 //turn of antialiasing so grid is nice and sharp
386 painter->setRenderHint( QPainter::Antialiasing, false );
387
388 switch ( grid.style() )
389 {
391 {
392 painter->setPen( grid.pen() );
393
394 //draw vertical lines
395 for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
396 {
397 painter->drawLine( QPointF( currentXCoord, 0 ), QPointF( currentXCoord, rect().height() ) );
398 }
399
400 //draw horizontal lines
401 currentYCoord = minYCoord;
402 for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
403 {
404 painter->drawLine( QPointF( 0, currentYCoord ), QPointF( rect().width(), currentYCoord ) );
405 }
406 break;
407 }
408
411 {
412 const QPen gridPen = grid.pen();
413 painter->setPen( gridPen );
414 painter->setBrush( QBrush( gridPen.color() ) );
415 double halfCrossLength = 1;
417 {
418 //dots are actually drawn as tiny crosses a few pixels across
419 //set halfCrossLength to equivalent of 1 pixel
420 halfCrossLength = 1 / QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
421 }
422 else
423 {
424 halfCrossLength = gridResolution / 6;
425 }
426
427 for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
428 {
429 currentYCoord = minYCoord;
430 for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
431 {
432 painter->drawLine( QPointF( currentXCoord - halfCrossLength, currentYCoord ), QPointF( currentXCoord + halfCrossLength, currentYCoord ) );
433 painter->drawLine( QPointF( currentXCoord, currentYCoord - halfCrossLength ), QPointF( currentXCoord, currentYCoord + halfCrossLength ) );
434 }
435 }
436 break;
437 }
438 }
439}
440
Base class for commands to undo/redo layout and layout object changes.
static QgsPageSizeRegistry * pageSizeRegistry()
Returns the application's page size registry, used for managing layout page sizes.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Contains settings relating to the appearance, spacing and offset for layout grids.
QgsLayoutMeasurement resolution() const
Returns the page/snap grid resolution.
QgsLayoutPoint offset() const
Returns the offset of the page/snap grid.
Style style() const
Returns the style used for drawing the page/snap grids.
QPen pen() const
Returns the pen used for drawing page/snap grids.
Item representing the paper in a layout.
~QgsLayoutItemPage() override
bool readPropertiesFromElement(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QRectF boundingRect() const override
void setPageSize(const QgsLayoutSize &size)
Sets the size of the page.
int type() const override
QgsLayoutSize pageSize() const
Returns the size of the page.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
void attemptResize(const QgsLayoutSize &size, bool includesFrame=false) override
Attempts to resize the item to a specified target size.
Orientation orientation() const
Returns the page orientation.
QString displayName() const override
Gets item display name.
void drawBackground(QgsRenderContext &context) override
Draws the background for 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.
static QgsLayoutItemPage * create(QgsLayout *layout)
Returns a new page item for the specified layout.
QgsLayoutItemPage(QgsLayout *layout)
Constructor for QgsLayoutItemPage, with the specified parent layout.
void setPageStyleSymbol(QgsFillSymbol *symbol)
Sets the symbol to use for drawing the page background.
Orientation
Page orientation.
@ Landscape
Landscape orientation.
@ Portrait
Portrait orientation.
void drawFrame(QgsRenderContext &context) override
Draws the frame around the item.
ExportLayerBehavior exportLayerBehavior() const override
Returns the behavior of this item during exporting to layered exports (e.g.
static QgsLayoutItemPage::Orientation decodePageOrientation(const QString &string, bool *ok=nullptr)
Decodes a string representing a page orientation.
bool writePropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
QPageLayout pageLayout() const
Returns the page layout for the page, suitable to pass to QPrinter::setPageLayout.
void redraw() override
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:71
Base class for graphical items within a QgsLayout.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
virtual void redraw()
Triggers a redraw (update) of the item.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
void sizePositionChanged()
Emitted when the item's size or position changes.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ CanGroupWithItemsOfSameType
Item can only be placed on layers with other items of the same type, but multiple items of this type ...
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, Qgis::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
double length() const
Returns the length of the measurement.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
void addPage(QgsLayoutItemPage *page)
Adds a page to the collection.
Stores information relating to the current rendering settings for a layout.
bool gridVisible() const
Returns true if the page grid should be drawn.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:41
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
void setHeight(const double height)
Sets the height for the size.
Definition: qgslayoutsize.h:97
static Q_DECL_DEPRECATED double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:367
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
Definition: qgslayout.cpp:467
@ ZPage
Z-value for page (paper) items.
Definition: qgslayout.h:58
@ ZGrid
Z-value for page grids.
Definition: qgslayout.h:60
A named page size for layouts.
QgsLayoutSize size
Page size.
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 convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
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.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1341
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3988
#define SIP_TRANSFERTHIS
Definition: qgis_sip.h:53
Contains information relating to the style entity currently being visited.