QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
19#include <memory>
20
21#include "qgsfillsymbol.h"
22#include "qgslayout.h"
26#include "qgslayoututils.h"
27#include "qgspagesizeregistry.h"
28#include "qgsstyle.h"
30#include "qgssymbollayerutils.h"
31
32#include <QPainter>
33#include <QString>
34#include <QStyleOptionGraphicsItem>
35
36#include "moc_qgslayoutitempage.cpp"
37
38using namespace Qt::StringLiterals;
39
41 : QgsLayoutItem( layout, false )
42{
43 setFlag( QGraphicsItem::ItemIsSelectable, false );
44 setFlag( QGraphicsItem::ItemIsMovable, false );
45 setZValue( QgsLayout::ZPage );
46
47 connect( this, &QgsLayoutItem::sizePositionChanged, this, [this]
48 {
49 prepareGeometryChange();
50 mBoundingRect = QRectF();
51 } );
52
53 const QFont font;
54 const QFontMetrics fm( font );
55 mMaximumShadowWidth = fm.boundingRect( u"X"_s ).width();
56
57 mGrid = std::make_unique<QgsLayoutItemPageGrid>( pos().x(), pos().y(), rect().width(), rect().height(), mLayout );
58 mGrid->setParentItem( this );
59
60 createDefaultPageStyleSymbol();
61}
62
64
69
74
76{
77 return QObject::tr( "Page" );
78}
79
81{
82 attemptResize( size );
83}
84
86{
87 QgsPageSize newSize;
88 if ( QgsApplication::pageSizeRegistry()->decodePageSize( size, newSize ) )
89 {
90 switch ( orientation )
91 {
92 case Portrait:
93 break; // nothing to do
94
95 case Landscape:
96 {
97 // flip height and width
98 const double x = newSize.size.width();
99 newSize.size.setWidth( newSize.size.height() );
100 newSize.size.setHeight( x );
101 break;
102 }
103 }
104
105 setPageSize( newSize.size );
106 return true;
107 }
108 else
109 {
110 return false;
111 }
112}
113
115{
116 QPageLayout pageLayout;
117 pageLayout.setMargins( {0, 0, 0, 0} );
118 pageLayout.setMode( QPageLayout::FullPageMode );
120
121 if ( pageSize().width() > pageSize().height() )
122 {
123 pageLayout.setOrientation( QPageLayout::Landscape );
124 pageLayout.setPageSize( QPageSize( QSizeF( size.height(), size.width() ), QPageSize::Millimeter ) );
125 }
126 else
127 {
128 pageLayout.setOrientation( QPageLayout::Portrait );
129 pageLayout.setPageSize( QPageSize( size, QPageSize::Millimeter ) );
130 }
131 pageLayout.setUnits( QPageLayout::Millimeter );
132 return pageLayout;
133}
134
139
141{
142 if ( sizeWithUnits().width() >= sizeWithUnits().height() )
143 return Landscape;
144 else
145 return Portrait;
146}
147
149{
150 mPageStyleSymbol.reset( symbol );
151 update();
152}
153
155{
156 if ( ok )
157 *ok = false;
158
159 const QString trimmedString = string.trimmed();
160 if ( trimmedString.compare( "portrait"_L1, Qt::CaseInsensitive ) == 0 )
161 {
162 if ( ok )
163 *ok = true;
164 return Portrait;
165 }
166 else if ( trimmedString.compare( "landscape"_L1, Qt::CaseInsensitive ) == 0 )
167 {
168 if ( ok )
169 *ok = true;
170 return Landscape;
171 }
172 return Landscape;
173}
174
176{
177 if ( mBoundingRect.isNull() )
178 {
179 const double shadowWidth = mLayout->pageCollection()->pageShadowWidth();
180 mBoundingRect = rect();
181 mBoundingRect.adjust( 0, 0, shadowWidth, shadowWidth );
182 }
183 return mBoundingRect;
184}
185
186void QgsLayoutItemPage::attemptResize( const QgsLayoutSize &size, bool includesFrame )
187{
188 QgsLayoutItem::attemptResize( size, includesFrame );
189 //update size of attached grid to reflect new page size and position
190 mGrid->setRect( 0, 0, rect().width(), rect().height() );
191
192 mLayout->guides().update();
193}
194
195void QgsLayoutItemPage::createDefaultPageStyleSymbol()
196{
197 QVariantMap properties;
198 properties.insert( u"color"_s, u"white"_s );
199 properties.insert( u"style"_s, u"solid"_s );
200 properties.insert( u"style_border"_s, u"no"_s );
201 properties.insert( u"joinstyle"_s, u"miter"_s );
202 mPageStyleSymbol = QgsFillSymbol::createSimple( properties );
203}
204
205
206
208class QgsLayoutItemPageUndoCommand: public QgsLayoutItemUndoCommand
209{
210 public:
211
212 QgsLayoutItemPageUndoCommand( QgsLayoutItemPage *page, const QString &text, int id = 0, QUndoCommand *parent SIP_TRANSFERTHIS = nullptr )
213 : QgsLayoutItemUndoCommand( page, text, id, parent )
214 {}
215
216 void restoreState( QDomDocument &stateDoc ) override
217 {
218 QgsLayoutItemUndoCommand::restoreState( stateDoc );
219 layout()->pageCollection()->reflow();
220 }
221
222 protected:
223
224 QgsLayoutItem *recreateItem( int, QgsLayout *layout ) override
225 {
226 QgsLayoutItemPage *page = new QgsLayoutItemPage( layout );
227 layout->pageCollection()->addPage( page );
228 return page;
229 }
230};
232
233QgsAbstractLayoutUndoCommand *QgsLayoutItemPage::createCommand( const QString &text, int id, QUndoCommand *parent )
234{
235 return new QgsLayoutItemPageUndoCommand( this, text, id, parent );
236}
237
242
244{
245 QgsStyleSymbolEntity entity( mPageStyleSymbol.get() );
246 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, u"page"_s, QObject::tr( "Page" ) ) ) )
247 return false;
248 return true;
249}
250
252{
254 mGrid->update();
255}
256
258{
259 if ( !context.renderContext().painter() || !mLayout || !mLayout->renderContext().pagesVisible() )
260 {
261 return;
262 }
263
264 const double scale = context.renderContext().convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
265
266 const QgsExpressionContext expressionContext = createExpressionContext();
267 context.renderContext().setExpressionContext( expressionContext );
268
269 QPainter *painter = context.renderContext().painter();
270 const QgsScopedQPainterState painterState( painter );
271
272 if ( mLayout->renderContext().isPreviewRender() )
273 {
274 //if in preview mode, draw page border and shadow so that it's
275 //still possible to tell where pages with a transparent style begin and end
276 painter->setRenderHint( QPainter::Antialiasing, false );
277
278 const QRectF pageRect = QRectF( 0, 0, scale * rect().width(), scale * rect().height() );
279
280 //shadow
281 painter->setBrush( QBrush( QColor( 150, 150, 150 ) ) );
282 painter->setPen( Qt::NoPen );
283 painter->drawRect( pageRect.translated( std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ),
284 std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ) ) );
285
286 //page area
287 painter->setBrush( QColor( 215, 215, 215 ) );
288 QPen pagePen = QPen( QColor( 100, 100, 100 ), 0 );
289 pagePen.setJoinStyle( Qt::MiterJoin );
290 pagePen.setCosmetic( true );
291 painter->setPen( pagePen );
292 painter->drawRect( pageRect );
293 }
294
295 if ( mPageStyleSymbol )
296 {
297 std::unique_ptr< QgsFillSymbol > symbol( mPageStyleSymbol->clone() );
298 symbol->startRender( context.renderContext() );
299
300 //get max bleed from symbol
301 double maxBleedPixels = QgsSymbolLayerUtils::estimateMaxSymbolBleed( symbol.get(), context.renderContext() );
302
303 //Now subtract 1 pixel to prevent semi-transparent borders at edge of solid page caused by
304 //anti-aliased painting. This may cause a pixel to be cropped from certain edge lines/symbols,
305 //but that can be counteracted by adding a dummy transparent line symbol layer with a wider line width
306 if ( !mLayout->renderContext().isPreviewRender() || !qgsDoubleNear( maxBleedPixels, 0.0 ) )
307 {
308 maxBleedPixels = std::floor( maxBleedPixels - 2 );
309 }
310
311 // round up
312 const QPolygonF pagePolygon = QPolygonF( QRectF( maxBleedPixels, maxBleedPixels,
313 std::ceil( rect().width() * scale ) - 2 * maxBleedPixels, std::ceil( rect().height() * scale ) - 2 * maxBleedPixels ) );
314 const QVector<QPolygonF> rings; //empty list
315
316 symbol->renderPolygon( pagePolygon, &rings, nullptr, context.renderContext() );
317 symbol->stopRender( context.renderContext() );
318 }
319}
320
323
326
327bool QgsLayoutItemPage::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
328{
329 const QDomElement styleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mPageStyleSymbol.get(), document, context );
330 element.appendChild( styleElem );
331 return true;
332}
333
334bool QgsLayoutItemPage::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
335{
336 const QDomElement symbolElem = element.firstChildElement( u"symbol"_s );
337 if ( !symbolElem.isNull() )
338 {
339 mPageStyleSymbol = QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElem, context );
340 }
341 else
342 {
343 createDefaultPageStyleSymbol();
344 }
345
346 return true;
347}
348
349//
350// QgsLayoutItemPageGrid
351//
353
354QgsLayoutItemPageGrid::QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout )
355 : QGraphicsRectItem( 0, 0, width, height )
356 , mLayout( layout )
357{
358 // needed to access current view transform during paint operations
359 setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption );
360 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
361 setFlag( QGraphicsItem::ItemIsSelectable, false );
362 setFlag( QGraphicsItem::ItemIsMovable, false );
363 setZValue( QgsLayout::ZGrid );
364 setPos( x, y );
365}
366
367void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
368{
369 Q_UNUSED( pWidget )
370
371 //draw grid
372 if ( !mLayout )
373 return;
374
375 if ( !mLayout->renderContext().isPreviewRender() )
376 return;
377
378 const QgsLayoutRenderContext &context = mLayout->renderContext();
379 const QgsLayoutGridSettings &grid = mLayout->gridSettings();
380
381 if ( !context.gridVisible() || grid.resolution().length() <= 0 )
382 return;
383
384 const QPointF gridOffset = mLayout->convertToLayoutUnits( grid.offset() );
385 const double gridResolution = mLayout->convertToLayoutUnits( grid.resolution() );
386 const int gridMultiplyX = static_cast< int >( gridOffset.x() / gridResolution );
387 const int gridMultiplyY = static_cast< int >( gridOffset.y() / gridResolution );
388 double currentXCoord = gridOffset.x() - gridMultiplyX * gridResolution;
389 double currentYCoord;
390 const double minYCoord = gridOffset.y() - gridMultiplyY * gridResolution;
391
392 const QgsScopedQPainterState painterState( painter );
393 //turn of antialiasing so grid is nice and sharp
394 painter->setRenderHint( QPainter::Antialiasing, false );
395
396 switch ( grid.style() )
397 {
399 {
400 painter->setPen( grid.pen() );
401
402 //draw vertical lines
403 for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
404 {
405 painter->drawLine( QPointF( currentXCoord, 0 ), QPointF( currentXCoord, rect().height() ) );
406 }
407
408 //draw horizontal lines
409 currentYCoord = minYCoord;
410 for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
411 {
412 painter->drawLine( QPointF( 0, currentYCoord ), QPointF( rect().width(), currentYCoord ) );
413 }
414 break;
415 }
416
419 {
420 const QPen gridPen = grid.pen();
421 painter->setPen( gridPen );
422 painter->setBrush( QBrush( gridPen.color() ) );
423 double halfCrossLength = 1;
425 {
426 //dots are actually drawn as tiny crosses a few pixels across
427 //set halfCrossLength to equivalent of 1 pixel
428 halfCrossLength = 1 / QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
429 }
430 else
431 {
432 halfCrossLength = gridResolution / 6;
433 }
434
435 for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
436 {
437 currentYCoord = minYCoord;
438 for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
439 {
440 painter->drawLine( QPointF( currentXCoord - halfCrossLength, currentYCoord ), QPointF( currentXCoord + halfCrossLength, currentYCoord ) );
441 painter->drawLine( QPointF( currentXCoord, currentYCoord - halfCrossLength ), QPointF( currentXCoord, currentYCoord + halfCrossLength ) );
442 }
443 }
444 break;
445 }
446 }
447}
448
@ Millimeters
Millimeters.
Definition qgis.h:5276
@ Millimeters
Millimeters.
Definition qgis.h:5256
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.
static std::unique_ptr< 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.
~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.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
friend class QgsLayout
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
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.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
double height() const
Returns the height of the size.
void setWidth(const double width)
Sets the width for the size.
double width() const
Returns the width of the size.
void setHeight(const double height)
Sets the height for the size.
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 ...
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
@ 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.
A container for the context for various read/write operations on 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:1398
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
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:6900
#define SIP_TRANSFERTHIS
Definition qgis_sip.h:53
Contains information relating to the style entity currently being visited.