diff --git a/python/gui/layout/qgslayoutview.sip b/python/gui/layout/qgslayoutview.sip index c20d888c40e..b9b3629c095 100644 --- a/python/gui/layout/qgslayoutview.sip +++ b/python/gui/layout/qgslayoutview.sip @@ -67,6 +67,12 @@ class QgsLayoutView: QGraphicsView You don't have to call it manually, QgsLayoutViewTool takes care of it. %End + void scaleSafe( double scale ); +%Docstring + Scales the view in a safe way, by limiting the acceptable range + of the scale applied. The ``scale`` parameter specifies the zoom factor to scale the view by. +%End + signals: void layoutSet( QgsLayout *layout ); diff --git a/src/gui/layout/qgslayoutview.cpp b/src/gui/layout/qgslayoutview.cpp index 99b90117683..776a039bb8b 100644 --- a/src/gui/layout/qgslayoutview.cpp +++ b/src/gui/layout/qgslayoutview.cpp @@ -19,8 +19,13 @@ #include "qgslayout.h" #include "qgslayoutviewtool.h" #include "qgslayoutviewmouseevent.h" +#include "qgssettings.h" +#include "qgsrectangle.h" #include +#define MIN_VIEW_SCALE 0.05 +#define MAX_VIEW_SCALE 1000.0 + QgsLayoutView::QgsLayoutView( QWidget *parent ) : QGraphicsView( parent ) { @@ -74,6 +79,14 @@ void QgsLayoutView::unsetTool( QgsLayoutViewTool *tool ) } } +void QgsLayoutView::scaleSafe( double scale ) +{ + double currentScale = transform().m11(); + scale *= currentScale; + scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE ); + setTransform( QTransform::fromScale( scale, scale ) ); +} + void QgsLayoutView::mousePressEvent( QMouseEvent *event ) { if ( mTool ) @@ -134,7 +147,10 @@ void QgsLayoutView::wheelEvent( QWheelEvent *event ) } if ( !mTool || !event->isAccepted() ) - QGraphicsView::wheelEvent( event ); + { + event->accept(); + wheelZoom( event ); + } } void QgsLayoutView::keyPressEvent( QKeyEvent *event ) @@ -158,3 +174,53 @@ void QgsLayoutView::keyReleaseEvent( QKeyEvent *event ) if ( !mTool || !event->isAccepted() ) QGraphicsView::keyReleaseEvent( event ); } + +void QgsLayoutView::wheelZoom( QWheelEvent *event ) +{ + //get mouse wheel zoom behavior settings + QgsSettings settings; + double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble(); + + // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( event->angleDelta().y() ); + + if ( event->modifiers() & Qt::ControlModifier ) + { + //holding ctrl while wheel zooming results in a finer zoom + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0; + } + + //calculate zoom scale factor + bool zoomIn = event->angleDelta().y() > 0; + double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor ); + + //get current visible part of scene + QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() ); + QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() ); + + //transform the mouse pos to scene coordinates + QPointF scenePoint = mapToScene( event->pos() ); + + //adjust view center + QgsPointXY oldCenter( visibleRect.center() ); + QgsPointXY newCenter( scenePoint.x() + ( ( oldCenter.x() - scenePoint.x() ) * scaleFactor ), + scenePoint.y() + ( ( oldCenter.y() - scenePoint.y() ) * scaleFactor ) ); + centerOn( newCenter.x(), newCenter.y() ); + + //zoom layout + if ( zoomIn ) + { + scaleSafe( zoomFactor ); + } + else + { + scaleSafe( 1 / zoomFactor ); + } + + //update layout for new zoom +#if 0 // TODO + emit zoomLevelChanged(); + updateRulers(); +#endif + update(); +} diff --git a/src/gui/layout/qgslayoutview.h b/src/gui/layout/qgslayoutview.h index 79eb076959a..8415e01e1bf 100644 --- a/src/gui/layout/qgslayoutview.h +++ b/src/gui/layout/qgslayoutview.h @@ -85,6 +85,12 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView */ void unsetTool( QgsLayoutViewTool *tool ); + /** + * Scales the view in a safe way, by limiting the acceptable range + * of the scale applied. The \a scale parameter specifies the zoom factor to scale the view by. + */ + void scaleSafe( double scale ); + signals: /** @@ -111,6 +117,9 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView private: + //! Zoom layout from a mouse wheel event + void wheelZoom( QWheelEvent *event ); + QPointer< QgsLayoutViewTool > mTool; friend class TestQgsLayoutView; diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index f74b1a8486c..df4ab669b36 100755 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -73,6 +73,7 @@ ADD_PYTHON_TEST(PyQgsJsonUtils test_qgsjsonutils.py) ADD_PYTHON_TEST(PyQgsLayerTreeMapCanvasBridge test_qgslayertreemapcanvasbridge.py) ADD_PYTHON_TEST(PyQgsLayerTree test_qgslayertree.py) ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py) +ADD_PYTHON_TEST(PyQgsLayoutView test_qgslayoutview.py) ADD_PYTHON_TEST(PyQgsLineSymbolLayers test_qgslinesymbollayers.py) ADD_PYTHON_TEST(PyQgsLayerMetadata test_qgslayermetadata.py) ADD_PYTHON_TEST(PyQgsLocator test_qgslocator.py) diff --git a/tests/src/python/test_qgslayoutview.py b/tests/src/python/test_qgslayoutview.py new file mode 100644 index 00000000000..88551db2eaa --- /dev/null +++ b/tests/src/python/test_qgslayoutview.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutView. + +.. 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.gui import QgsLayoutView +from qgis.PyQt.QtCore import QRectF +from qgis.PyQt.QtGui import QTransform + +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsLayoutView(unittest.TestCase): + + def testScaleSafe(self): + """ test scaleSafe method """ + + view = QgsLayoutView() + view.fitInView(QRectF(0, 0, 10, 10)) + scale = view.transform().m11() + view.scaleSafe(2) + self.assertAlmostEqual(view.transform().m11(), 2) + view.scaleSafe(4) + self.assertAlmostEqual(view.transform().m11(), 8) + + # try to zoom in heaps + view.scaleSafe(99999999) + # assume we have hit the limit + scale = view.transform().m11() + view.scaleSafe(2) + self.assertAlmostEqual(view.transform().m11(), scale) + + view.setTransform(QTransform.fromScale(1, 1)) + self.assertAlmostEqual(view.transform().m11(), 1) + # test zooming out + view.scaleSafe(0.5) + self.assertAlmostEqual(view.transform().m11(), 0.5) + view.scaleSafe(0.1) + self.assertAlmostEqual(view.transform().m11(), 0.05) + + # try zooming out heaps + view.scaleSafe(0.000000001) + # assume we have hit the limit + scale = view.transform().m11() + view.scaleSafe(0.5) + self.assertAlmostEqual(view.transform().m11(), scale) + + +if __name__ == '__main__': + unittest.main()