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