Skip to content

Commit b42c055

Browse files
committedAug 7, 2017
Restore drawing of page grids
1 parent ec56983 commit b42c055

File tree

9 files changed

+339
-1
lines changed

9 files changed

+339
-1
lines changed
 

‎python/core/layout/qgslayout.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
2323
enum ZValues
2424
{
2525
ZPage,
26+
ZGrid,
2627
ZMapTool,
28+
2729
};
2830

2931
QgsLayout( QgsProject *project );

‎python/core/layout/qgslayoutcontext.sip

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class QgsLayoutContext
3030
typedef QFlags<QgsLayoutContext::Flag> Flags;
3131

3232

33+
enum GridStyle
34+
{
35+
GridLines,
36+
GridDots,
37+
GridCrosses
38+
};
39+
3340
QgsLayoutContext();
3441

3542
void setFlags( const QgsLayoutContext::Flags flags );
@@ -125,6 +132,72 @@ class QgsLayoutContext
125132
:rtype: QgsLayoutMeasurementConverter
126133
%End
127134

135+
bool gridVisible() const;
136+
%Docstring
137+
Returns true if the page grid should be drawn.
138+
:rtype: bool
139+
%End
140+
141+
void setGridResolution( const QgsLayoutMeasurement &resolution );
142+
%Docstring
143+
Sets the page/snap grid ``resolution``.
144+
.. seealso:: gridResolution()
145+
.. seealso:: setGridOffset()
146+
%End
147+
148+
QgsLayoutMeasurement gridResolution() const;
149+
%Docstring
150+
Returns the page/snap grid resolution.
151+
.. seealso:: setGridResolution()
152+
.. seealso:: gridOffset()
153+
:rtype: QgsLayoutMeasurement
154+
%End
155+
156+
void setGridOffset( const QgsLayoutPoint offset );
157+
%Docstring
158+
Sets the ``offset`` of the page/snap grid.
159+
.. seealso:: gridOffset()
160+
.. seealso:: setGridResolution()
161+
%End
162+
163+
QgsLayoutPoint gridOffset() const;
164+
%Docstring
165+
Returns the offset of the page/snap grid.
166+
.. seealso:: setGridOffset()
167+
.. seealso:: gridResolution()
168+
:rtype: QgsLayoutPoint
169+
%End
170+
171+
void setGridPen( const QPen &pen );
172+
%Docstring
173+
Sets the ``pen`` used for drawing page/snap grids.
174+
.. seealso:: gridPen()
175+
.. seealso:: setGridStyle()
176+
%End
177+
178+
QPen gridPen() const;
179+
%Docstring
180+
Returns the pen used for drawing page/snap grids.
181+
.. seealso:: setGridPen()
182+
.. seealso:: gridStyle()
183+
:rtype: QPen
184+
%End
185+
186+
void setGridStyle( const GridStyle style );
187+
%Docstring
188+
Sets the ``style`` used for drawing the page/snap grids.
189+
.. seealso:: gridStyle()
190+
.. seealso:: setGridPen()
191+
%End
192+
193+
GridStyle gridStyle() const;
194+
%Docstring
195+
Returns the style used for drawing the page/snap grids.
196+
.. seealso:: setGridStyle()
197+
.. seealso:: gridPen()
198+
:rtype: GridStyle
199+
%End
200+
128201
};
129202

130203

‎python/core/layout/qgslayoutitempage.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99

1010

11+
12+
1113
class QgsLayoutItemPage : QgsLayoutItem
1214
{
1315
%Docstring
@@ -75,6 +77,9 @@ class QgsLayoutItemPage : QgsLayoutItem
7577
:rtype: QgsLayoutItemPage.Orientation
7678
%End
7779

80+
virtual void attemptResize( const QgsLayoutSize &size );
81+
82+
7883
protected:
7984

8085
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );

‎src/core/layout/qgslayout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
4040
enum ZValues
4141
{
4242
ZPage = 0, //!< Z-value for page (paper) items
43+
ZGrid = 9999, //!< Z-value for page grids
4344
ZMapTool = 10000, //!< Z-value for temporary map tool items
45+
4446
};
4547

4648
/**

‎src/core/layout/qgslayoutcontext.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020

2121
QgsLayoutContext::QgsLayoutContext()
2222
: mFlags( FlagAntialiasing | FlagUseAdvancedEffects )
23-
{}
23+
, mGridResolution( QgsLayoutMeasurement( 10 ) )
24+
{
25+
mGridPen = QPen( QColor( 190, 190, 190, 100 ), 0 );
26+
mGridPen.setCosmetic( true );
27+
}
2428

2529
void QgsLayoutContext::setFlags( const QgsLayoutContext::Flags flags )
2630
{
@@ -77,3 +81,8 @@ double QgsLayoutContext::dpi() const
7781
{
7882
return mMeasurementConverter.dpi();
7983
}
84+
85+
bool QgsLayoutContext::gridVisible() const
86+
{
87+
return true;
88+
}

‎src/core/layout/qgslayoutcontext.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ class CORE_EXPORT QgsLayoutContext
4646
};
4747
Q_DECLARE_FLAGS( Flags, Flag )
4848

49+
//! Style for drawing the page/snapping grid
50+
enum GridStyle
51+
{
52+
GridLines, //! Solid lines
53+
GridDots, //! Dots
54+
GridCrosses //! Crosses
55+
};
56+
4957
QgsLayoutContext();
5058

5159
/**
@@ -139,6 +147,67 @@ class CORE_EXPORT QgsLayoutContext
139147
*/
140148
QgsLayoutMeasurementConverter &measurementConverter() { return mMeasurementConverter; }
141149

150+
/**
151+
* Returns true if the page grid should be drawn.
152+
*/
153+
bool gridVisible() const;
154+
155+
/**
156+
* Sets the page/snap grid \a resolution.
157+
* \see gridResolution()
158+
* \see setGridOffset()
159+
*/
160+
void setGridResolution( const QgsLayoutMeasurement &resolution ) { mGridResolution = resolution; }
161+
162+
/**
163+
* Returns the page/snap grid resolution.
164+
* \see setGridResolution()
165+
* \see gridOffset()
166+
*/
167+
QgsLayoutMeasurement gridResolution() const { return mGridResolution;}
168+
169+
/**
170+
* Sets the \a offset of the page/snap grid.
171+
* \see gridOffset()
172+
* \see setGridResolution()
173+
*/
174+
void setGridOffset( const QgsLayoutPoint offset ) { mGridOffset = offset; }
175+
176+
/**
177+
* Returns the offset of the page/snap grid.
178+
* \see setGridOffset()
179+
* \see gridResolution()
180+
*/
181+
QgsLayoutPoint gridOffset() const { return mGridOffset; }
182+
183+
/**
184+
* Sets the \a pen used for drawing page/snap grids.
185+
* \see gridPen()
186+
* \see setGridStyle()
187+
*/
188+
void setGridPen( const QPen &pen ) { mGridPen = pen; }
189+
190+
/**
191+
* Returns the pen used for drawing page/snap grids.
192+
* \see setGridPen()
193+
* \see gridStyle()
194+
*/
195+
QPen gridPen() const { return mGridPen; }
196+
197+
/**
198+
* Sets the \a style used for drawing the page/snap grids.
199+
* \see gridStyle()
200+
* \see setGridPen()
201+
*/
202+
void setGridStyle( const GridStyle style ) { mGridStyle = style; }
203+
204+
/**
205+
* Returns the style used for drawing the page/snap grids.
206+
* \see setGridStyle()
207+
* \see gridPen()
208+
*/
209+
GridStyle gridStyle() const { return mGridStyle; }
210+
142211
private:
143212

144213
Flags mFlags = 0;
@@ -148,6 +217,11 @@ class CORE_EXPORT QgsLayoutContext
148217

149218
QgsLayoutMeasurementConverter mMeasurementConverter;
150219

220+
QgsLayoutMeasurement mGridResolution;
221+
QgsLayoutPoint mGridOffset;
222+
QPen mGridPen;
223+
GridStyle mGridStyle = GridLines;
224+
151225
};
152226

153227
#endif //QGSLAYOUTCONTEXT_H

‎src/core/layout/qgslayoutitempage.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgspagesizeregistry.h"
2121
#include "qgssymbollayerutils.h"
2222
#include <QPainter>
23+
#include <QStyleOptionGraphicsItem>
2324

2425
QgsLayoutItemPage::QgsLayoutItemPage( QgsLayout *layout )
2526
: QgsLayoutItem( layout )
@@ -36,6 +37,9 @@ QgsLayoutItemPage::QgsLayoutItemPage( QgsLayout *layout )
3637
QFont font;
3738
QFontMetrics fm( font );
3839
mMaximumShadowWidth = fm.width( "X" );
40+
41+
mGrid.reset( new QgsLayoutItemPageGrid( pos().x(), pos().y(), rect().width(), rect().height(), mLayout ) );
42+
mGrid->setParentItem( this );
3943
}
4044

4145
void QgsLayoutItemPage::setPageSize( const QgsLayoutSize &size )
@@ -106,6 +110,13 @@ QgsLayoutItemPage::Orientation QgsLayoutItemPage::decodePageOrientation( const Q
106110
return Landscape;
107111
}
108112

113+
void QgsLayoutItemPage::attemptResize( const QgsLayoutSize &size )
114+
{
115+
QgsLayoutItem::attemptResize( size );
116+
//update size of attached grid to reflect new page size and position
117+
mGrid->setRect( 0, 0, rect().width(), rect().height() );
118+
}
119+
109120
void QgsLayoutItemPage::draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * )
110121
{
111122
if ( !context.painter() || !mLayout /*|| !mLayout->pagesVisible() */ )
@@ -166,3 +177,103 @@ void QgsLayoutItemPage::draw( QgsRenderContext &context, const QStyleOptionGraph
166177

167178
painter->restore();
168179
}
180+
181+
182+
//
183+
// QgsLayoutItemPageGrid
184+
//
185+
///@cond PRIVATE
186+
187+
QgsLayoutItemPageGrid::QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout )
188+
: QGraphicsRectItem( 0, 0, width, height )
189+
, mLayout( layout )
190+
{
191+
// needed to access current view transform during paint operations
192+
setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption );
193+
setCacheMode( QGraphicsItem::DeviceCoordinateCache );
194+
setFlag( QGraphicsItem::ItemIsSelectable, false );
195+
setFlag( QGraphicsItem::ItemIsMovable, false );
196+
setZValue( QgsLayout::ZGrid );
197+
setPos( x, y );
198+
}
199+
200+
void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
201+
{
202+
Q_UNUSED( pWidget );
203+
204+
//draw grid
205+
if ( !mLayout )
206+
return;
207+
208+
const QgsLayoutContext &context = mLayout->context();
209+
210+
if ( !context.gridVisible() || context.gridResolution().length() <= 0 )
211+
return;
212+
213+
QPointF gridOffset = mLayout->convertToLayoutUnits( context.gridOffset() );
214+
double gridResolution = mLayout->convertToLayoutUnits( context.gridResolution() );
215+
int gridMultiplyX = static_cast< int >( gridOffset.x() / gridResolution );
216+
int gridMultiplyY = static_cast< int >( gridOffset.y() / gridResolution );
217+
double currentXCoord = gridOffset.x() - gridMultiplyX * gridResolution;
218+
double currentYCoord;
219+
double minYCoord = gridOffset.y() - gridMultiplyY * gridResolution;
220+
221+
painter->save();
222+
//turn of antialiasing so grid is nice and sharp
223+
painter->setRenderHint( QPainter::Antialiasing, false );
224+
225+
switch ( context.gridStyle() )
226+
{
227+
case QgsLayoutContext::GridLines:
228+
{
229+
painter->setPen( context.gridPen() );
230+
231+
//draw vertical lines
232+
for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
233+
{
234+
painter->drawLine( QPointF( currentXCoord, 0 ), QPointF( currentXCoord, rect().height() ) );
235+
}
236+
237+
//draw horizontal lines
238+
currentYCoord = minYCoord;
239+
for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
240+
{
241+
painter->drawLine( QPointF( 0, currentYCoord ), QPointF( rect().width(), currentYCoord ) );
242+
}
243+
break;
244+
}
245+
246+
case QgsLayoutContext::GridDots:
247+
case QgsLayoutContext::GridCrosses:
248+
{
249+
QPen gridPen = context.gridPen();
250+
painter->setPen( gridPen );
251+
painter->setBrush( QBrush( gridPen.color() ) );
252+
double halfCrossLength = 1;
253+
if ( context.gridStyle() == QgsLayoutContext::GridDots )
254+
{
255+
//dots are actually drawn as tiny crosses a few pixels across
256+
//set halfCrossLength to equivalent of 1 pixel
257+
halfCrossLength = 1 / itemStyle->matrix.m11();
258+
}
259+
else
260+
{
261+
halfCrossLength = gridResolution / 6;
262+
}
263+
264+
for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
265+
{
266+
currentYCoord = minYCoord;
267+
for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
268+
{
269+
painter->drawLine( QPointF( currentXCoord - halfCrossLength, currentYCoord ), QPointF( currentXCoord + halfCrossLength, currentYCoord ) );
270+
painter->drawLine( QPointF( currentXCoord, currentYCoord - halfCrossLength ), QPointF( currentXCoord, currentYCoord + halfCrossLength ) );
271+
}
272+
}
273+
break;
274+
}
275+
}
276+
painter->restore();
277+
}
278+
279+
///@endcond

‎src/core/layout/qgslayoutitempage.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,29 @@
2222
#include "qgslayoutitemregistry.h"
2323
#include "qgis_sip.h"
2424

25+
26+
///@cond PRIVATE
27+
#ifndef SIP_RUN
28+
29+
/**
30+
* \ingroup core
31+
* Item representing a grid. This is drawn separately to the underlying page item since the grid needs to be
32+
* drawn above all other layout items, while the paper item is drawn below all others.
33+
* \since QGIS 3.0
34+
*/
35+
class CORE_EXPORT QgsLayoutItemPageGrid: public QGraphicsRectItem
36+
{
37+
public:
38+
QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout );
39+
40+
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
41+
42+
private:
43+
QgsLayout *mLayout = nullptr;
44+
};
45+
#endif
46+
///@endcond
47+
2548
/**
2649
* \ingroup core
2750
* \class QgsLayoutItemPage
@@ -85,6 +108,8 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
85108
*/
86109
static QgsLayoutItemPage::Orientation decodePageOrientation( const QString &string, bool *ok SIP_OUT = nullptr );
87110

111+
void attemptResize( const QgsLayoutSize &size ) override;
112+
88113
protected:
89114

90115
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
@@ -93,6 +118,9 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
93118

94119
double mMaximumShadowWidth = -1;
95120

121+
std::unique_ptr< QgsLayoutItemPageGrid > mGrid;
122+
123+
friend class TestQgsLayoutPage;
96124
};
97125

98126
#endif //QGSLAYOUTITEMPAGE_H

‎tests/src/core/testqgslayoutpage.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class TestQgsLayoutPage : public QObject
3535
void itemType();
3636
void pageSize();
3737
void decodePageOrientation();
38+
void grid();
3839

3940
private:
4041
QString mReport;
@@ -125,5 +126,38 @@ void TestQgsLayoutPage::decodePageOrientation()
125126
QVERIFY( !ok );
126127
}
127128

129+
void TestQgsLayoutPage::grid()
130+
{
131+
// test that grid follows page around
132+
QgsProject p;
133+
QgsLayout l( &p );
134+
QgsLayoutItemPage *page = new QgsLayoutItemPage( &l );
135+
136+
// should have a grid
137+
QVERIFY( page->mGrid.get() );
138+
139+
// grid is parented to page, so while the grid should resize to match
140+
// page size, it should always report pos() of 0,0 (origin of page)
141+
page->attemptMove( QgsLayoutPoint( 5, 15 ) );
142+
page->attemptResize( QgsLayoutSize( 100, 200 ) );
143+
QCOMPARE( page->mGrid->rect().width(), 100.0 );
144+
QCOMPARE( page->mGrid->rect().height(), 200.0 );
145+
QCOMPARE( page->mGrid->pos().x(), 0.0 );
146+
QCOMPARE( page->mGrid->pos().y(), 0.0 );
147+
148+
page->attemptMove( QgsLayoutPoint( 25, 35 ) );
149+
QCOMPARE( page->mGrid->rect().width(), 100.0 );
150+
QCOMPARE( page->mGrid->rect().height(), 200.0 );
151+
QCOMPARE( page->mGrid->pos().x(), 0.0 );
152+
QCOMPARE( page->mGrid->pos().y(), 0.0 );
153+
154+
page->attemptResize( QgsLayoutSize( 150, 250 ) );
155+
QCOMPARE( page->mGrid->rect().width(), 150.0 );
156+
QCOMPARE( page->mGrid->rect().height(), 250.0 );
157+
QCOMPARE( page->mGrid->pos().x(), 0.0 );
158+
QCOMPARE( page->mGrid->pos().y(), 0.0 );
159+
160+
}
161+
128162
QGSTEST_MAIN( TestQgsLayoutPage )
129163
#include "testqgslayoutpage.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.