Restore drawing of page grids

This commit is contained in:
Nyall Dawson 2017-07-24 07:57:25 +10:00
parent ec5698393b
commit b42c055e97
9 changed files with 339 additions and 1 deletions

View File

@ -23,7 +23,9 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
enum ZValues
{
ZPage,
ZGrid,
ZMapTool,
};
QgsLayout( QgsProject *project );

View File

@ -30,6 +30,13 @@ class QgsLayoutContext
typedef QFlags<QgsLayoutContext::Flag> Flags;
enum GridStyle
{
GridLines,
GridDots,
GridCrosses
};
QgsLayoutContext();
void setFlags( const QgsLayoutContext::Flags flags );
@ -125,6 +132,72 @@ class QgsLayoutContext
:rtype: QgsLayoutMeasurementConverter
%End
bool gridVisible() const;
%Docstring
Returns true if the page grid should be drawn.
:rtype: bool
%End
void setGridResolution( const QgsLayoutMeasurement &resolution );
%Docstring
Sets the page/snap grid ``resolution``.
.. seealso:: gridResolution()
.. seealso:: setGridOffset()
%End
QgsLayoutMeasurement gridResolution() const;
%Docstring
Returns the page/snap grid resolution.
.. seealso:: setGridResolution()
.. seealso:: gridOffset()
:rtype: QgsLayoutMeasurement
%End
void setGridOffset( const QgsLayoutPoint offset );
%Docstring
Sets the ``offset`` of the page/snap grid.
.. seealso:: gridOffset()
.. seealso:: setGridResolution()
%End
QgsLayoutPoint gridOffset() const;
%Docstring
Returns the offset of the page/snap grid.
.. seealso:: setGridOffset()
.. seealso:: gridResolution()
:rtype: QgsLayoutPoint
%End
void setGridPen( const QPen &pen );
%Docstring
Sets the ``pen`` used for drawing page/snap grids.
.. seealso:: gridPen()
.. seealso:: setGridStyle()
%End
QPen gridPen() const;
%Docstring
Returns the pen used for drawing page/snap grids.
.. seealso:: setGridPen()
.. seealso:: gridStyle()
:rtype: QPen
%End
void setGridStyle( const GridStyle style );
%Docstring
Sets the ``style`` used for drawing the page/snap grids.
.. seealso:: gridStyle()
.. seealso:: setGridPen()
%End
GridStyle gridStyle() const;
%Docstring
Returns the style used for drawing the page/snap grids.
.. seealso:: setGridStyle()
.. seealso:: gridPen()
:rtype: GridStyle
%End
};

View File

@ -8,6 +8,8 @@
class QgsLayoutItemPage : QgsLayoutItem
{
%Docstring
@ -75,6 +77,9 @@ class QgsLayoutItemPage : QgsLayoutItem
:rtype: QgsLayoutItemPage.Orientation
%End
virtual void attemptResize( const QgsLayoutSize &size );
protected:
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );

View File

@ -40,7 +40,9 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
enum ZValues
{
ZPage = 0, //!< Z-value for page (paper) items
ZGrid = 9999, //!< Z-value for page grids
ZMapTool = 10000, //!< Z-value for temporary map tool items
};
/**

View File

@ -20,7 +20,11 @@
QgsLayoutContext::QgsLayoutContext()
: mFlags( FlagAntialiasing | FlagUseAdvancedEffects )
{}
, mGridResolution( QgsLayoutMeasurement( 10 ) )
{
mGridPen = QPen( QColor( 190, 190, 190, 100 ), 0 );
mGridPen.setCosmetic( true );
}
void QgsLayoutContext::setFlags( const QgsLayoutContext::Flags flags )
{
@ -77,3 +81,8 @@ double QgsLayoutContext::dpi() const
{
return mMeasurementConverter.dpi();
}
bool QgsLayoutContext::gridVisible() const
{
return true;
}

View File

@ -46,6 +46,14 @@ class CORE_EXPORT QgsLayoutContext
};
Q_DECLARE_FLAGS( Flags, Flag )
//! Style for drawing the page/snapping grid
enum GridStyle
{
GridLines, //! Solid lines
GridDots, //! Dots
GridCrosses //! Crosses
};
QgsLayoutContext();
/**
@ -139,6 +147,67 @@ class CORE_EXPORT QgsLayoutContext
*/
QgsLayoutMeasurementConverter &measurementConverter() { return mMeasurementConverter; }
/**
* Returns true if the page grid should be drawn.
*/
bool gridVisible() const;
/**
* Sets the page/snap grid \a resolution.
* \see gridResolution()
* \see setGridOffset()
*/
void setGridResolution( const QgsLayoutMeasurement &resolution ) { mGridResolution = resolution; }
/**
* Returns the page/snap grid resolution.
* \see setGridResolution()
* \see gridOffset()
*/
QgsLayoutMeasurement gridResolution() const { return mGridResolution;}
/**
* Sets the \a offset of the page/snap grid.
* \see gridOffset()
* \see setGridResolution()
*/
void setGridOffset( const QgsLayoutPoint offset ) { mGridOffset = offset; }
/**
* Returns the offset of the page/snap grid.
* \see setGridOffset()
* \see gridResolution()
*/
QgsLayoutPoint gridOffset() const { return mGridOffset; }
/**
* Sets the \a pen used for drawing page/snap grids.
* \see gridPen()
* \see setGridStyle()
*/
void setGridPen( const QPen &pen ) { mGridPen = pen; }
/**
* Returns the pen used for drawing page/snap grids.
* \see setGridPen()
* \see gridStyle()
*/
QPen gridPen() const { return mGridPen; }
/**
* Sets the \a style used for drawing the page/snap grids.
* \see gridStyle()
* \see setGridPen()
*/
void setGridStyle( const GridStyle style ) { mGridStyle = style; }
/**
* Returns the style used for drawing the page/snap grids.
* \see setGridStyle()
* \see gridPen()
*/
GridStyle gridStyle() const { return mGridStyle; }
private:
Flags mFlags = 0;
@ -148,6 +217,11 @@ class CORE_EXPORT QgsLayoutContext
QgsLayoutMeasurementConverter mMeasurementConverter;
QgsLayoutMeasurement mGridResolution;
QgsLayoutPoint mGridOffset;
QPen mGridPen;
GridStyle mGridStyle = GridLines;
};
#endif //QGSLAYOUTCONTEXT_H

View File

@ -20,6 +20,7 @@
#include "qgspagesizeregistry.h"
#include "qgssymbollayerutils.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>
QgsLayoutItemPage::QgsLayoutItemPage( QgsLayout *layout )
: QgsLayoutItem( layout )
@ -36,6 +37,9 @@ QgsLayoutItemPage::QgsLayoutItemPage( QgsLayout *layout )
QFont font;
QFontMetrics fm( font );
mMaximumShadowWidth = fm.width( "X" );
mGrid.reset( new QgsLayoutItemPageGrid( pos().x(), pos().y(), rect().width(), rect().height(), mLayout ) );
mGrid->setParentItem( this );
}
void QgsLayoutItemPage::setPageSize( const QgsLayoutSize &size )
@ -106,6 +110,13 @@ QgsLayoutItemPage::Orientation QgsLayoutItemPage::decodePageOrientation( const Q
return Landscape;
}
void QgsLayoutItemPage::attemptResize( const QgsLayoutSize &size )
{
QgsLayoutItem::attemptResize( size );
//update size of attached grid to reflect new page size and position
mGrid->setRect( 0, 0, rect().width(), rect().height() );
}
void QgsLayoutItemPage::draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * )
{
if ( !context.painter() || !mLayout /*|| !mLayout->pagesVisible() */ )
@ -166,3 +177,103 @@ void QgsLayoutItemPage::draw( QgsRenderContext &context, const QStyleOptionGraph
painter->restore();
}
//
// QgsLayoutItemPageGrid
//
///@cond PRIVATE
QgsLayoutItemPageGrid::QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout )
: QGraphicsRectItem( 0, 0, width, height )
, mLayout( layout )
{
// needed to access current view transform during paint operations
setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption );
setCacheMode( QGraphicsItem::DeviceCoordinateCache );
setFlag( QGraphicsItem::ItemIsSelectable, false );
setFlag( QGraphicsItem::ItemIsMovable, false );
setZValue( QgsLayout::ZGrid );
setPos( x, y );
}
void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
{
Q_UNUSED( pWidget );
//draw grid
if ( !mLayout )
return;
const QgsLayoutContext &context = mLayout->context();
if ( !context.gridVisible() || context.gridResolution().length() <= 0 )
return;
QPointF gridOffset = mLayout->convertToLayoutUnits( context.gridOffset() );
double gridResolution = mLayout->convertToLayoutUnits( context.gridResolution() );
int gridMultiplyX = static_cast< int >( gridOffset.x() / gridResolution );
int gridMultiplyY = static_cast< int >( gridOffset.y() / gridResolution );
double currentXCoord = gridOffset.x() - gridMultiplyX * gridResolution;
double currentYCoord;
double minYCoord = gridOffset.y() - gridMultiplyY * gridResolution;
painter->save();
//turn of antialiasing so grid is nice and sharp
painter->setRenderHint( QPainter::Antialiasing, false );
switch ( context.gridStyle() )
{
case QgsLayoutContext::GridLines:
{
painter->setPen( context.gridPen() );
//draw vertical lines
for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
{
painter->drawLine( QPointF( currentXCoord, 0 ), QPointF( currentXCoord, rect().height() ) );
}
//draw horizontal lines
currentYCoord = minYCoord;
for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
{
painter->drawLine( QPointF( 0, currentYCoord ), QPointF( rect().width(), currentYCoord ) );
}
break;
}
case QgsLayoutContext::GridDots:
case QgsLayoutContext::GridCrosses:
{
QPen gridPen = context.gridPen();
painter->setPen( gridPen );
painter->setBrush( QBrush( gridPen.color() ) );
double halfCrossLength = 1;
if ( context.gridStyle() == QgsLayoutContext::GridDots )
{
//dots are actually drawn as tiny crosses a few pixels across
//set halfCrossLength to equivalent of 1 pixel
halfCrossLength = 1 / itemStyle->matrix.m11();
}
else
{
halfCrossLength = gridResolution / 6;
}
for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution )
{
currentYCoord = minYCoord;
for ( ; currentYCoord <= rect().height(); currentYCoord += gridResolution )
{
painter->drawLine( QPointF( currentXCoord - halfCrossLength, currentYCoord ), QPointF( currentXCoord + halfCrossLength, currentYCoord ) );
painter->drawLine( QPointF( currentXCoord, currentYCoord - halfCrossLength ), QPointF( currentXCoord, currentYCoord + halfCrossLength ) );
}
}
break;
}
}
painter->restore();
}
///@endcond

View File

@ -22,6 +22,29 @@
#include "qgslayoutitemregistry.h"
#include "qgis_sip.h"
///@cond PRIVATE
#ifndef SIP_RUN
/**
* \ingroup core
* Item representing a grid. This is drawn separately to the underlying page item since the grid needs to be
* drawn above all other layout items, while the paper item is drawn below all others.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLayoutItemPageGrid: public QGraphicsRectItem
{
public:
QgsLayoutItemPageGrid( double x, double y, double width, double height, QgsLayout *layout );
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
private:
QgsLayout *mLayout = nullptr;
};
#endif
///@endcond
/**
* \ingroup core
* \class QgsLayoutItemPage
@ -85,6 +108,8 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
*/
static QgsLayoutItemPage::Orientation decodePageOrientation( const QString &string, bool *ok SIP_OUT = nullptr );
void attemptResize( const QgsLayoutSize &size ) override;
protected:
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
@ -93,6 +118,9 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
double mMaximumShadowWidth = -1;
std::unique_ptr< QgsLayoutItemPageGrid > mGrid;
friend class TestQgsLayoutPage;
};
#endif //QGSLAYOUTITEMPAGE_H

View File

@ -35,6 +35,7 @@ class TestQgsLayoutPage : public QObject
void itemType();
void pageSize();
void decodePageOrientation();
void grid();
private:
QString mReport;
@ -125,5 +126,38 @@ void TestQgsLayoutPage::decodePageOrientation()
QVERIFY( !ok );
}
void TestQgsLayoutPage::grid()
{
// test that grid follows page around
QgsProject p;
QgsLayout l( &p );
QgsLayoutItemPage *page = new QgsLayoutItemPage( &l );
// should have a grid
QVERIFY( page->mGrid.get() );
// grid is parented to page, so while the grid should resize to match
// page size, it should always report pos() of 0,0 (origin of page)
page->attemptMove( QgsLayoutPoint( 5, 15 ) );
page->attemptResize( QgsLayoutSize( 100, 200 ) );
QCOMPARE( page->mGrid->rect().width(), 100.0 );
QCOMPARE( page->mGrid->rect().height(), 200.0 );
QCOMPARE( page->mGrid->pos().x(), 0.0 );
QCOMPARE( page->mGrid->pos().y(), 0.0 );
page->attemptMove( QgsLayoutPoint( 25, 35 ) );
QCOMPARE( page->mGrid->rect().width(), 100.0 );
QCOMPARE( page->mGrid->rect().height(), 200.0 );
QCOMPARE( page->mGrid->pos().x(), 0.0 );
QCOMPARE( page->mGrid->pos().y(), 0.0 );
page->attemptResize( QgsLayoutSize( 150, 250 ) );
QCOMPARE( page->mGrid->rect().width(), 150.0 );
QCOMPARE( page->mGrid->rect().height(), 250.0 );
QCOMPARE( page->mGrid->pos().x(), 0.0 );
QCOMPARE( page->mGrid->pos().y(), 0.0 );
}
QGSTEST_MAIN( TestQgsLayoutPage )
#include "testqgslayoutpage.moc"