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