From 361dd312bc63d2fd984ae3e04bdd8c591bbe2cc1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 08:12:07 +1000 Subject: [PATCH] Start on layout snapper --- python/core/core_auto.sip | 1 + python/core/layout/qgslayout.sip | 8 ++ python/core/layout/qgslayoutcontext.sip | 67 ------------- python/core/layout/qgslayoutsnapper.sip | 100 +++++++++++++++++++ src/core/CMakeLists.txt | 2 + src/core/layout/qgslayout.h | 14 +++ src/core/layout/qgslayoutcontext.cpp | 6 +- src/core/layout/qgslayoutcontext.h | 68 ------------- src/core/layout/qgslayoutitempage.cpp | 21 ++-- src/core/layout/qgslayoutsnapper.cpp | 24 +++++ src/core/layout/qgslayoutsnapper.h | 111 ++++++++++++++++++++++ tests/src/python/CMakeLists.txt | 1 + tests/src/python/test_qgslayoutsnapper.py | 54 +++++++++++ 13 files changed, 327 insertions(+), 150 deletions(-) create mode 100644 python/core/layout/qgslayoutsnapper.sip create mode 100644 src/core/layout/qgslayoutsnapper.cpp create mode 100644 src/core/layout/qgslayoutsnapper.h create mode 100644 tests/src/python/test_qgslayoutsnapper.py diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index e91b847b576..d4630bf930e 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -161,6 +161,7 @@ %Include layout/qgspagesizeregistry.sip %Include layout/qgslayoutpoint.sip %Include layout/qgslayoutsize.sip +%Include layout/qgslayoutsnapper.sip %Include layout/qgslayoututils.sip %Include metadata/qgslayermetadata.sip %Include metadata/qgslayermetadatavalidator.sip diff --git a/python/core/layout/qgslayout.sip b/python/core/layout/qgslayout.sip index 3fe046e497a..f7db70211cf 100644 --- a/python/core/layout/qgslayout.sip +++ b/python/core/layout/qgslayout.sip @@ -141,6 +141,14 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator %End + QgsLayoutSnapper &snapper(); +%Docstring + Returns a reference to the layout's snapper, which stores handles layout snap grids and lines + and snapping points to the nearest matching point. + :rtype: QgsLayoutSnapper +%End + + virtual QgsExpressionContext createExpressionContext() const; %Docstring diff --git a/python/core/layout/qgslayoutcontext.sip b/python/core/layout/qgslayoutcontext.sip index 3c7f40fbcc8..9be6f4f75af 100644 --- a/python/core/layout/qgslayoutcontext.sip +++ b/python/core/layout/qgslayoutcontext.sip @@ -30,13 +30,6 @@ class QgsLayoutContext typedef QFlags Flags; - enum GridStyle - { - GridLines, - GridDots, - GridCrosses - }; - QgsLayoutContext(); void setFlags( const QgsLayoutContext::Flags flags ); @@ -138,66 +131,6 @@ class QgsLayoutContext :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 - }; diff --git a/python/core/layout/qgslayoutsnapper.sip b/python/core/layout/qgslayoutsnapper.sip new file mode 100644 index 00000000000..3d9d5c57de1 --- /dev/null +++ b/python/core/layout/qgslayoutsnapper.sip @@ -0,0 +1,100 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgslayoutsnapper.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + +class QgsLayoutSnapper +{ +%Docstring + Manages snapping grids and preset snap lines in a layout, and handles + snapping points to the nearest grid coordinate/snap line when possible. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoutsnapper.h" +%End + public: + + enum GridStyle + { + GridLines, + GridDots, + GridCrosses + }; + + QgsLayoutSnapper(); + + 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 + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgslayoutsnapper.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7f8db89fdce..d1104c9cc37 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -362,6 +362,7 @@ SET(QGIS_CORE_SRCS layout/qgslayoutmeasurementconverter.cpp layout/qgslayoutobject.cpp layout/qgslayoutpagecollection.cpp + layout/qgslayoutsnapper.cpp layout/qgslayoututils.cpp layout/qgspagesizeregistry.cpp layout/qgslayoutpoint.cpp @@ -940,6 +941,7 @@ SET(QGIS_CORE_HDRS layout/qgspagesizeregistry.h layout/qgslayoutpoint.h layout/qgslayoutsize.h + layout/qgslayoutsnapper.h layout/qgslayoututils.h metadata/qgslayermetadata.h diff --git a/src/core/layout/qgslayout.h b/src/core/layout/qgslayout.h index d7f397c8ac1..7ff640b9ec6 100644 --- a/src/core/layout/qgslayout.h +++ b/src/core/layout/qgslayout.h @@ -19,6 +19,7 @@ #include "qgis_core.h" #include #include "qgslayoutcontext.h" +#include "qgslayoutsnapper.h" #include "qgsexpressioncontextgenerator.h" #include "qgslayoutpagecollection.h" @@ -154,6 +155,18 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext */ SIP_SKIP const QgsLayoutContext &context() const { return mContext; } + /** + * Returns a reference to the layout's snapper, which stores handles layout snap grids and lines + * and snapping points to the nearest matching point. + */ + QgsLayoutSnapper &snapper() { return mSnapper; } + + /** + * Returns a reference to the layout's snapper, which stores handles layout snap grids and lines + * and snapping points to the nearest matching point. + */ + SIP_SKIP const QgsLayoutSnapper &snapper() const { return mSnapper; } + /** * Creates an expression context relating to the layout's current state. The context includes * scopes for global, project, layout and layout context properties. @@ -262,6 +275,7 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext QgsUnitTypes::LayoutUnit mUnits = QgsUnitTypes::LayoutMillimeters; QgsLayoutContext mContext; + QgsLayoutSnapper mSnapper; std::unique_ptr< QgsLayoutPageCollection > mPageCollection; diff --git a/src/core/layout/qgslayoutcontext.cpp b/src/core/layout/qgslayoutcontext.cpp index cc8bdb2d479..989164562bf 100644 --- a/src/core/layout/qgslayoutcontext.cpp +++ b/src/core/layout/qgslayoutcontext.cpp @@ -20,11 +20,7 @@ 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 ) { diff --git a/src/core/layout/qgslayoutcontext.h b/src/core/layout/qgslayoutcontext.h index b8adbc65469..00566165739 100644 --- a/src/core/layout/qgslayoutcontext.h +++ b/src/core/layout/qgslayoutcontext.h @@ -46,14 +46,6 @@ 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(); /** @@ -152,62 +144,6 @@ class CORE_EXPORT QgsLayoutContext */ 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; @@ -217,10 +153,6 @@ class CORE_EXPORT QgsLayoutContext QgsLayoutMeasurementConverter mMeasurementConverter; - QgsLayoutMeasurement mGridResolution; - QgsLayoutPoint mGridOffset; - QPen mGridPen; - GridStyle mGridStyle = GridLines; }; diff --git a/src/core/layout/qgslayoutitempage.cpp b/src/core/layout/qgslayoutitempage.cpp index 1b78ec050c9..242b1575d3c 100644 --- a/src/core/layout/qgslayoutitempage.cpp +++ b/src/core/layout/qgslayoutitempage.cpp @@ -206,12 +206,13 @@ void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphics return; const QgsLayoutContext &context = mLayout->context(); + const QgsLayoutSnapper &snapper = mLayout->snapper(); - if ( !context.gridVisible() || context.gridResolution().length() <= 0 ) + if ( !context.gridVisible() || snapper.gridResolution().length() <= 0 ) return; - QPointF gridOffset = mLayout->convertToLayoutUnits( context.gridOffset() ); - double gridResolution = mLayout->convertToLayoutUnits( context.gridResolution() ); + QPointF gridOffset = mLayout->convertToLayoutUnits( snapper.gridOffset() ); + double gridResolution = mLayout->convertToLayoutUnits( snapper.gridResolution() ); int gridMultiplyX = static_cast< int >( gridOffset.x() / gridResolution ); int gridMultiplyY = static_cast< int >( gridOffset.y() / gridResolution ); double currentXCoord = gridOffset.x() - gridMultiplyX * gridResolution; @@ -222,11 +223,11 @@ void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphics //turn of antialiasing so grid is nice and sharp painter->setRenderHint( QPainter::Antialiasing, false ); - switch ( context.gridStyle() ) + switch ( snapper.gridStyle() ) { - case QgsLayoutContext::GridLines: + case QgsLayoutSnapper::GridLines: { - painter->setPen( context.gridPen() ); + painter->setPen( snapper.gridPen() ); //draw vertical lines for ( ; currentXCoord <= rect().width(); currentXCoord += gridResolution ) @@ -243,14 +244,14 @@ void QgsLayoutItemPageGrid::paint( QPainter *painter, const QStyleOptionGraphics break; } - case QgsLayoutContext::GridDots: - case QgsLayoutContext::GridCrosses: + case QgsLayoutSnapper::GridDots: + case QgsLayoutSnapper::GridCrosses: { - QPen gridPen = context.gridPen(); + QPen gridPen = snapper.gridPen(); painter->setPen( gridPen ); painter->setBrush( QBrush( gridPen.color() ) ); double halfCrossLength = 1; - if ( context.gridStyle() == QgsLayoutContext::GridDots ) + if ( snapper.gridStyle() == QgsLayoutSnapper::GridDots ) { //dots are actually drawn as tiny crosses a few pixels across //set halfCrossLength to equivalent of 1 pixel diff --git a/src/core/layout/qgslayoutsnapper.cpp b/src/core/layout/qgslayoutsnapper.cpp new file mode 100644 index 00000000000..2a90a9a9dea --- /dev/null +++ b/src/core/layout/qgslayoutsnapper.cpp @@ -0,0 +1,24 @@ +/*************************************************************************** + qgslayoutsnapper.cpp + -------------------- + begin : July 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgslayoutsnapper.h" + +QgsLayoutSnapper::QgsLayoutSnapper() + : mGridResolution( QgsLayoutMeasurement( 10 ) ) +{ + mGridPen = QPen( QColor( 190, 190, 190, 100 ), 0 ); + mGridPen.setCosmetic( true ); +} diff --git a/src/core/layout/qgslayoutsnapper.h b/src/core/layout/qgslayoutsnapper.h new file mode 100644 index 00000000000..acb55594693 --- /dev/null +++ b/src/core/layout/qgslayoutsnapper.h @@ -0,0 +1,111 @@ +/*************************************************************************** + qgslayoutsnapper.h + ------------------- + begin : July 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSLAYOUTSNAPPER_H +#define QGSLAYOUTSNAPPER_H + +#include "qgis_core.h" +#include "qgslayoutmeasurement.h" +#include "qgslayoutpoint.h" +#include + +/** + * \ingroup core + * \class QgsLayoutSnapper + * \brief Manages snapping grids and preset snap lines in a layout, and handles + * snapping points to the nearest grid coordinate/snap line when possible. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsLayoutSnapper +{ + + public: + + //! Style for drawing the page/snapping grid + enum GridStyle + { + GridLines, //! Solid lines + GridDots, //! Dots + GridCrosses //! Crosses + }; + + QgsLayoutSnapper(); + + /** + * 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: + + QgsLayoutMeasurement mGridResolution; + QgsLayoutPoint mGridOffset; + QPen mGridPen; + GridStyle mGridStyle = GridLines; + +}; + +#endif //QGSLAYOUTSNAPPER_H diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 8ea9880c0b0..f24fd505440 100755 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -79,6 +79,7 @@ ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py) ADD_PYTHON_TEST(PyQgsLayoutPageCollection test_qgslayoutpagecollection.py) ADD_PYTHON_TEST(PyQgsLayoutView test_qgslayoutview.py) ADD_PYTHON_TEST(PyQgsLayoutItemPropertiesDialog test_qgslayoutitempropertiesdialog.py) +ADD_PYTHON_TEST(PyQgsLayoutSnapper test_qgslayoutsnapper.py) ADD_PYTHON_TEST(PyQgsLayoutUnitsComboBox test_qgslayoutunitscombobox.py) ADD_PYTHON_TEST(PyQgsLineSymbolLayers test_qgslinesymbollayers.py) ADD_PYTHON_TEST(PyQgsLocator test_qgslocator.py) diff --git a/tests/src/python/test_qgslayoutsnapper.py b/tests/src/python/test_qgslayoutsnapper.py new file mode 100644 index 00000000000..6de4ee23d7b --- /dev/null +++ b/tests/src/python/test_qgslayoutsnapper.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutSnapper. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '05/07/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsProject, + QgsLayout, + QgsLayoutSnapper, + QgsLayoutMeasurement, + QgsUnitTypes, + QgsLayoutPoint) +from qgis.PyQt.QtCore import QRectF +from qgis.PyQt.QtGui import (QTransform, + QPen, + QColor) + +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsLayoutSnapper(unittest.TestCase): + + def testGettersSetters(self): + s = QgsLayoutSnapper() + s.setGridResolution(QgsLayoutMeasurement(5, QgsUnitTypes.LayoutPoints)) + self.assertEqual(s.gridResolution().length(), 5.0) + self.assertEqual(s.gridResolution().units(), QgsUnitTypes.LayoutPoints) + + s.setGridOffset(QgsLayoutPoint(6, 7, QgsUnitTypes.LayoutPixels)) + self.assertEqual(s.gridOffset().x(), 6.0) + self.assertEqual(s.gridOffset().y(), 7.0) + self.assertEqual(s.gridOffset().units(), QgsUnitTypes.LayoutPixels) + + s.setGridPen(QPen(QColor(255, 0, 255))) + self.assertEqual(s.gridPen().color().name(), QColor(255, 0, 255).name()) + + s.setGridStyle(QgsLayoutSnapper.GridDots) + self.assertEqual(s.gridStyle(), QgsLayoutSnapper.GridDots) + + +if __name__ == '__main__': + unittest.main()