New class QgsMargins for storing margins (left/right/top/bottom)

Basically a direct port of QMarginF, but forced to always use
double values, and with added toString()/fromString() methods.
This commit is contained in:
Nyall Dawson 2017-01-30 10:50:48 +10:00
parent 7a2be42259
commit a7be9968b8
7 changed files with 534 additions and 0 deletions

View File

@ -93,6 +93,7 @@
%Include qgsmapsettings.sip
%Include qgsmaptopixel.sip
%Include qgsmapunitscale.sip
%Include qgsmargins.sip
%Include qgsmessagelog.sip
%Include qgsmessageoutput.sip
%Include qgsmimedatautils.sip

View File

@ -0,0 +1,47 @@
class QgsMargins
{
%TypeHeaderCode
#include <qgsmargins.h>
%End
public:
QgsMargins();
QgsMargins( double left, double top, double right, double bottom );
bool isNull() const;
double left() const;
double top() const;
double right() const;
double bottom() const;
void setLeft( double left );
void setTop( double top );
void setRight( double right );
void setBottom( double bottom );
QgsMargins &operator+=( const QgsMargins &margins );
QgsMargins &operator-=( const QgsMargins &margins );
QgsMargins &operator+=( double addend );
QgsMargins &operator-=( double subtrahend );
QgsMargins &operator*=( double factor );
QgsMargins &operator/=( double divisor );
QString toString() const;
static QgsMargins fromString( const QString& string );
};
bool operator==( const QgsMargins &lhs, const QgsMargins &rhs );
bool operator!=( const QgsMargins &lhs, const QgsMargins &rhs );
QgsMargins operator+( const QgsMargins &m1, const QgsMargins &m2 );
QgsMargins operator-( const QgsMargins &m1, const QgsMargins &m2 );
QgsMargins operator+( const QgsMargins &lhs, double rhs );
QgsMargins operator+( double lhs, const QgsMargins &rhs );
QgsMargins operator-( const QgsMargins &lhs, double rhs );
QgsMargins operator*( const QgsMargins &margins, double factor );
QgsMargins operator*( double factor, const QgsMargins &margins );
QgsMargins operator/( const QgsMargins &margins, double divisor );
QgsMargins operator+( const QgsMargins &margins );
QgsMargins operator-( const QgsMargins &margins );

View File

@ -170,6 +170,7 @@ SET(QGIS_CORE_SRCS
qgsmaptopixel.cpp
qgsmaptopixelgeometrysimplifier.cpp
qgsmapunitscale.cpp
qgsmargins.cpp
qgsmessagelog.cpp
qgsmessageoutput.cpp
qgsmimedatautils.cpp
@ -714,6 +715,7 @@ SET(QGIS_CORE_HDRS
qgsmaptopixel.h
qgsmaptopixelgeometrysimplifier.h
qgsmapunitscale.h
qgsmargins.h
qgsmimedatautils.h
qgsmultirenderchecker.h
qgsobjectcustomproperties.h

37
src/core/qgsmargins.cpp Normal file
View File

@ -0,0 +1,37 @@
/***************************************************************************
qgsmargins.cpp
--------------
Date : January 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 "qgsmargins.h"
QString QgsMargins::toString() const
{
if ( isNull() )
return QString();
else
return QStringLiteral( "%1,%2,%3,%4" ).arg( qgsDoubleToString( mLeft ), qgsDoubleToString( mTop ),
qgsDoubleToString( mRight ), qgsDoubleToString( mBottom ) );
}
QgsMargins QgsMargins::fromString( const QString& string )
{
QStringList margins = string.split( ',' );
if ( margins.count() != 4 )
return QgsMargins();
return QgsMargins( margins.at( 0 ).toDouble(),
margins.at( 1 ).toDouble(),
margins.at( 2 ).toDouble(),
margins.at( 3 ).toDouble() );
}

323
src/core/qgsmargins.h Normal file
View File

@ -0,0 +1,323 @@
/***************************************************************************
qgsmargins.h
------------
Date : January 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 QGSMARGINS_H
#define QGSMARGINS_H
#include "qgis_core.h"
#include "qgis.h"
#include <QString>
/**
* \ingroup core
* \class QgsMargins
* \brief The QgsMargins class defines the four margins of a rectangle.
*
* QgsMargins defines a set of four margins; left, top, right and bottom, that describe the size of the borders surrounding a rectangle.
*
* The isNull() function returns true only if all margins are set to zero.
* \note Added in QGIS 3.0
*/
//This class was originally based off Qt's QgsMarginsF class
//It was forked in order to always use double values, rather than qreal values.
class CORE_EXPORT QgsMargins
{
public:
/**
* Constructs a margins object with all margins set to 0.
*/
QgsMargins() = default;
/**
* Constructs margins with the given \a left, \a top, \a right, \a bottom
* @see setLeft()
* @see setRight()
* @see setTop()
* @see setBottom()
*/
QgsMargins( double left, double top, double right, double bottom )
: mLeft( left )
, mTop( top )
, mRight( right )
, mBottom( bottom )
{}
/**
* Returns \c true if all margins are is 0; otherwise returns false.
*/
bool isNull() const
{
return qgsDoubleNear( mLeft, 0.0 ) && qgsDoubleNear( mTop, 0.0 ) && qgsDoubleNear( mRight, 0.0 ) && qgsDoubleNear( mBottom, 0.0 );
}
/**
* Returns the left margin.
* @see setLeft()
*/
double left() const { return mLeft; }
/**
* Returns the top margin.
* @see setTop()
*/
double top() const { return mTop; }
/**
* Returns the right margin.
* @see setRight()
*/
double right() const { return mRight; }
/**
* Returns the bottom margin.
* @see setBottom()
*/
double bottom() const { return mBottom; }
/**
* Sets the left margin to \a left.
* @see left()
*/
void setLeft( double left ) { mLeft = left; }
/**
* Sets the top margin to \a top.
* @see top()
*/
void setTop( double top ) { mTop = top; }
/**
* Sets the right margin to \a right.
* @see right()
*/
void setRight( double right ) { mRight = right; }
/**
* Sets the bottom margin to \a bottom.
* @see bottom()
*/
void setBottom( double bottom ) { mBottom = bottom; }
/**
* Add each component of \a margins to the respective component of this object
* and returns a reference to it.
*/
inline QgsMargins &operator+=( const QgsMargins &margins );
/**
* Subtract each component of \a margins from the respective component of this object
* and returns a reference to it.
*/
inline QgsMargins &operator-=( const QgsMargins &margins );
/**
* Adds the \a addend to each component of this object and returns a reference to it.
*/
inline QgsMargins &operator+=( double addend );
/**
* Subtracts the \a subtrahend from each component of this object
* and returns a reference to it.
*/
inline QgsMargins &operator-=( double subtrahend );
/**
* Multiplies each component of this object by \a factor
* and returns a reference to it.
*/
inline QgsMargins &operator*=( double factor );
/**
* Multiplies each component of this object by \a factor
* and returns a reference to it.
*/
inline QgsMargins &operator/=( double divisor );
/**
* Returns the margins encoded to a string.
* @see fromString()
*/
QString toString() const;
/**
* Returns a QgsMargins object decoded from a string, or a null QgsMargins
* if the string could not be interpreted as margins.
* @see toString()
*/
static QgsMargins fromString( const QString& string );
private:
double mLeft = 0.0;
double mTop = 0.0;
double mRight = 0.0;
double mBottom = 0.0;
};
/**
* Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
*/
inline bool operator==( const QgsMargins &lhs, const QgsMargins &rhs )
{
return qgsDoubleNear( lhs.left(), rhs.left() )
&& qgsDoubleNear( lhs.top(), rhs.top() )
&& qgsDoubleNear( lhs.right(), rhs.right() )
&& qgsDoubleNear( lhs.bottom(), rhs.bottom() );
}
/**
* Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
*/
inline bool operator!=( const QgsMargins &lhs, const QgsMargins &rhs )
{
return !operator==( lhs, rhs );
}
/**
* Returns a QgsMargins object that is the sum of the given margins, \a m1
* and \a m2; each component is added separately.
*/
inline QgsMargins operator+( const QgsMargins &m1, const QgsMargins &m2 )
{
return QgsMargins( m1.left() + m2.left(), m1.top() + m2.top(),
m1.right() + m2.right(), m1.bottom() + m2.bottom() );
}
/**
* Returns a QgsMargins object that is formed by subtracting \a m2 from
* \a m1; each component is subtracted separately.
*/
inline QgsMargins operator-( const QgsMargins &m1, const QgsMargins &m2 )
{
return QgsMargins( m1.left() - m2.left(), m1.top() - m2.top(),
m1.right() - m2.right(), m1.bottom() - m2.bottom() );
}
/**
* Returns a QgsMargins object that is formed by adding \a rhs to \a lhs.
*/
inline QgsMargins operator+( const QgsMargins &lhs, double rhs )
{
return QgsMargins( lhs.left() + rhs, lhs.top() + rhs,
lhs.right() + rhs, lhs.bottom() + rhs );
}
/**
* Returns a QgsMargins object that is formed by adding \a lhs to \a rhs.
*/
inline QgsMargins operator+( double lhs, const QgsMargins &rhs )
{
return QgsMargins( rhs.left() + lhs, rhs.top() + lhs,
rhs.right() + lhs, rhs.bottom() + lhs );
}
/**
* Returns a QgsMargins object that is formed by subtracting \a rhs from \a lhs.
*/
inline QgsMargins operator-( const QgsMargins &lhs, double rhs )
{
return QgsMargins( lhs.left() - rhs, lhs.top() - rhs,
lhs.right() - rhs, lhs.bottom() - rhs );
}
/**
* Returns a QgsMargins object that is formed by multiplying each component
* of the given \a margins by \a factor.
*/
inline QgsMargins operator*( const QgsMargins &margins, double factor )
{
return QgsMargins( margins.left() * factor, margins.top() * factor,
margins.right() * factor, margins.bottom() * factor );
}
/**
* Returns a QgsMargins object that is formed by multiplying each component
* of the given \a margins by \a factor.
*/
inline QgsMargins operator*( double factor, const QgsMargins &margins )
{
return QgsMargins( margins.left() * factor, margins.top() * factor,
margins.right() * factor, margins.bottom() * factor );
}
/**
* Returns a QgsMargins object that is formed by dividing the components of
* the given \a margins by the given \a divisor.
*/
inline QgsMargins operator/( const QgsMargins &margins, double divisor )
{
return QgsMargins( margins.left() / divisor, margins.top() / divisor,
margins.right() / divisor, margins.bottom() / divisor );
}
inline QgsMargins& QgsMargins::operator+=( const QgsMargins & margins )
{
return *this = *this + margins;
}
inline QgsMargins& QgsMargins::operator-=( const QgsMargins & margins )
{
return *this = *this - margins;
}
inline QgsMargins& QgsMargins::operator+=( double addend )
{
mLeft += addend;
mTop += addend;
mRight += addend;
mBottom += addend;
return *this;
}
inline QgsMargins& QgsMargins::operator-=( double subtrahend )
{
mLeft -= subtrahend;
mTop -= subtrahend;
mRight -= subtrahend;
mBottom -= subtrahend;
return *this;
}
inline QgsMargins& QgsMargins::operator*=( double factor )
{
return *this = *this * factor;
}
inline QgsMargins& QgsMargins::operator/=( double divisor )
{
return *this = *this / divisor;
}
/**
* Returns a QgsMargins object that is formed from all components of \a margins.
*/
inline QgsMargins operator+( const QgsMargins &margins )
{
return margins;
}
/**
* Returns a QgsMargins object that is formed by negating all components of \a margins.
*/
inline QgsMargins operator-( const QgsMargins &margins )
{
return QgsMargins( -margins.left(), -margins.top(), -margins.right(), -margins.bottom() );
}
Q_DECLARE_TYPEINFO( QgsMargins, Q_MOVABLE_TYPE );
#endif // QGSMARGINS_H

View File

@ -64,6 +64,7 @@ ADD_PYTHON_TEST(PyQgsJSONUtils test_qgsjsonutils.py)
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
ADD_PYTHON_TEST(PyQgsMapUnitScale test_qgsmapunitscale.py)
ADD_PYTHON_TEST(PyQgsMargins test_qgsmargins.py)
ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)
ADD_PYTHON_TEST(PyQgsMultiEditToolButton test_qgsmultiedittoolbutton.py)
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)

View File

@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsMargins.
.. 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__ = '2017-01'
__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.testing import unittest
from qgis.core import QgsMargins
class TestQgsOptional(unittest.TestCase):
def testGetSet(self):
margins = QgsMargins()
margins.setLeft(1.1)
self.assertEqual(margins.left(), 1.1)
margins.setTop(2.2)
self.assertEqual(margins.top(), 2.2)
margins.setBottom(3.3)
self.assertEqual(margins.bottom(), 3.3)
margins.setRight(4.4)
self.assertEqual(margins.right(), 4.4)
margins = QgsMargins()
self.assertTrue(margins.isNull())
margins.setLeft(5.5)
margins.setRight(5.5)
self.assertFalse(margins.isNull())
self.assertEqual(margins, QgsMargins(5.5, 0.0, 5.5, 0.0))
def testOperators(self):
m1 = QgsMargins(12.1, 14.1, 16.1, 18.1)
m2 = QgsMargins(2.1, 3.1, 4.1, 5.1)
added = m1 + m2
self.assertAlmostEqual(added.left(), 14.2)
self.assertAlmostEqual(added.top(), 17.2)
self.assertAlmostEqual(added.right(), 20.2)
self.assertAlmostEqual(added.bottom(), 23.2)
a = QgsMargins(m1)
a += m2
self.assertEqual(a, added)
subtracted = m1 - m2
self.assertAlmostEqual(subtracted.left(), 10.0)
self.assertAlmostEqual(subtracted.top(), 11.0)
self.assertAlmostEqual(subtracted.right(), 12.0)
self.assertAlmostEqual(subtracted.bottom(), 13.0)
a = QgsMargins(m1)
a -= m2
self.assertEqual(a, subtracted)
h = QgsMargins(m1)
h += 2.1
self.assertAlmostEqual(h.left(), 14.2)
self.assertAlmostEqual(h.top(), 16.2)
self.assertAlmostEqual(h.right(), 18.2)
self.assertAlmostEqual(h.bottom(), 20.2)
h -= 2.1
self.assertEqual(h, m1)
doubled = m1 * 2.0
self.assertEqual(doubled, QgsMargins(24.2, 28.2, 32.2, 36.2))
self.assertEqual(2.0 * m1, doubled)
self.assertEqual(m1 * 2.0, doubled)
a = QgsMargins(m1)
a *= 2.0
self.assertEqual(a, doubled)
halved = m1 / 2.0
self.assertAlmostEqual(halved.left(), 6.05)
self.assertAlmostEqual(halved.top(), 7.05)
self.assertAlmostEqual(halved.right(), 8.05)
self.assertAlmostEqual(halved.bottom(), 9.05)
a = QgsMargins(m1)
a /= 2.0
self.assertEqual(a, halved)
self.assertEqual(m1 + (-m1), QgsMargins())
m3 = QgsMargins(10.3, 11.4, 12.5, 13.6)
self.assertEqual(m3 + 1.1, QgsMargins(11.4, 12.5, 13.6, 14.7))
self.assertEqual(1.1 + m3, QgsMargins(11.4, 12.5, 13.6, 14.7))
m4 = m3 - 1.1
self.assertAlmostEqual(m4.left(), 9.2)
self.assertAlmostEqual(m4.top(), 10.3)
self.assertAlmostEqual(m4.right(), 11.4)
self.assertAlmostEqual(m4.bottom(), 12.5)
self.assertEqual(+m3, QgsMargins(10.3, 11.4, 12.5, 13.6))
self.assertEqual(-m3, QgsMargins(-10.3, -11.4, -12.5, -13.6))
def testToString(self):
# null margin
self.assertFalse(QgsMargins().toString())
self.assertEqual(QgsMargins(1, 2, 3, 4).toString(), '1,2,3,4')
self.assertEqual(QgsMargins(1, -2, 3, -4).toString(), '1,-2,3,-4')
def testFromString(self):
self.assertTrue(QgsMargins.fromString('').isNull())
self.assertTrue(QgsMargins.fromString('not good').isNull())
self.assertTrue(QgsMargins.fromString('1,2,3').isNull())
self.assertTrue(QgsMargins.fromString('1,2,3,4,5').isNull())
self.assertEqual(QgsMargins.fromString('1,2,3,4'), QgsMargins(1, 2, 3, 4))
self.assertEqual(QgsMargins.fromString('1,-2,3,-4'), QgsMargins(1, -2, 3, -4))
if __name__ == '__main__':
unittest.main()