Skip to content

Commit a7be996

Browse files
committedJan 30, 2017
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.
1 parent 7a2be42 commit a7be996

File tree

7 files changed

+534
-0
lines changed

7 files changed

+534
-0
lines changed
 

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
%Include qgsmapsettings.sip
9494
%Include qgsmaptopixel.sip
9595
%Include qgsmapunitscale.sip
96+
%Include qgsmargins.sip
9697
%Include qgsmessagelog.sip
9798
%Include qgsmessageoutput.sip
9899
%Include qgsmimedatautils.sip

‎python/core/qgsmargins.sip

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
class QgsMargins
2+
{
3+
%TypeHeaderCode
4+
#include <qgsmargins.h>
5+
%End
6+
7+
public:
8+
9+
QgsMargins();
10+
QgsMargins( double left, double top, double right, double bottom );
11+
12+
bool isNull() const;
13+
14+
double left() const;
15+
double top() const;
16+
double right() const;
17+
double bottom() const;
18+
19+
void setLeft( double left );
20+
void setTop( double top );
21+
void setRight( double right );
22+
void setBottom( double bottom );
23+
24+
QgsMargins &operator+=( const QgsMargins &margins );
25+
QgsMargins &operator-=( const QgsMargins &margins );
26+
QgsMargins &operator+=( double addend );
27+
QgsMargins &operator-=( double subtrahend );
28+
QgsMargins &operator*=( double factor );
29+
QgsMargins &operator/=( double divisor );
30+
31+
QString toString() const;
32+
static QgsMargins fromString( const QString& string );
33+
};
34+
35+
bool operator==( const QgsMargins &lhs, const QgsMargins &rhs );
36+
bool operator!=( const QgsMargins &lhs, const QgsMargins &rhs );
37+
QgsMargins operator+( const QgsMargins &m1, const QgsMargins &m2 );
38+
QgsMargins operator-( const QgsMargins &m1, const QgsMargins &m2 );
39+
QgsMargins operator+( const QgsMargins &lhs, double rhs );
40+
QgsMargins operator+( double lhs, const QgsMargins &rhs );
41+
QgsMargins operator-( const QgsMargins &lhs, double rhs );
42+
QgsMargins operator*( const QgsMargins &margins, double factor );
43+
QgsMargins operator*( double factor, const QgsMargins &margins );
44+
QgsMargins operator/( const QgsMargins &margins, double divisor );
45+
QgsMargins operator+( const QgsMargins &margins );
46+
QgsMargins operator-( const QgsMargins &margins );
47+

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ SET(QGIS_CORE_SRCS
170170
qgsmaptopixel.cpp
171171
qgsmaptopixelgeometrysimplifier.cpp
172172
qgsmapunitscale.cpp
173+
qgsmargins.cpp
173174
qgsmessagelog.cpp
174175
qgsmessageoutput.cpp
175176
qgsmimedatautils.cpp
@@ -714,6 +715,7 @@ SET(QGIS_CORE_HDRS
714715
qgsmaptopixel.h
715716
qgsmaptopixelgeometrysimplifier.h
716717
qgsmapunitscale.h
718+
qgsmargins.h
717719
qgsmimedatautils.h
718720
qgsmultirenderchecker.h
719721
qgsobjectcustomproperties.h

‎src/core/qgsmargins.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/***************************************************************************
2+
qgsmargins.cpp
3+
--------------
4+
Date : January 2017
5+
Copyright : (C) 2017 by Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmargins.h"
17+
18+
QString QgsMargins::toString() const
19+
{
20+
if ( isNull() )
21+
return QString();
22+
else
23+
return QStringLiteral( "%1,%2,%3,%4" ).arg( qgsDoubleToString( mLeft ), qgsDoubleToString( mTop ),
24+
qgsDoubleToString( mRight ), qgsDoubleToString( mBottom ) );
25+
}
26+
27+
QgsMargins QgsMargins::fromString( const QString& string )
28+
{
29+
QStringList margins = string.split( ',' );
30+
if ( margins.count() != 4 )
31+
return QgsMargins();
32+
33+
return QgsMargins( margins.at( 0 ).toDouble(),
34+
margins.at( 1 ).toDouble(),
35+
margins.at( 2 ).toDouble(),
36+
margins.at( 3 ).toDouble() );
37+
}

‎src/core/qgsmargins.h

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/***************************************************************************
2+
qgsmargins.h
3+
------------
4+
Date : January 2017
5+
Copyright : (C) 2017 by Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMARGINS_H
17+
#define QGSMARGINS_H
18+
19+
#include "qgis_core.h"
20+
#include "qgis.h"
21+
#include <QString>
22+
23+
/**
24+
* \ingroup core
25+
* \class QgsMargins
26+
* \brief The QgsMargins class defines the four margins of a rectangle.
27+
*
28+
* QgsMargins defines a set of four margins; left, top, right and bottom, that describe the size of the borders surrounding a rectangle.
29+
*
30+
* The isNull() function returns true only if all margins are set to zero.
31+
* \note Added in QGIS 3.0
32+
*/
33+
34+
//This class was originally based off Qt's QgsMarginsF class
35+
//It was forked in order to always use double values, rather than qreal values.
36+
37+
class CORE_EXPORT QgsMargins
38+
{
39+
public:
40+
41+
/**
42+
* Constructs a margins object with all margins set to 0.
43+
*/
44+
QgsMargins() = default;
45+
46+
/**
47+
* Constructs margins with the given \a left, \a top, \a right, \a bottom
48+
* @see setLeft()
49+
* @see setRight()
50+
* @see setTop()
51+
* @see setBottom()
52+
*/
53+
QgsMargins( double left, double top, double right, double bottom )
54+
: mLeft( left )
55+
, mTop( top )
56+
, mRight( right )
57+
, mBottom( bottom )
58+
{}
59+
60+
/**
61+
* Returns \c true if all margins are is 0; otherwise returns false.
62+
*/
63+
bool isNull() const
64+
{
65+
return qgsDoubleNear( mLeft, 0.0 ) && qgsDoubleNear( mTop, 0.0 ) && qgsDoubleNear( mRight, 0.0 ) && qgsDoubleNear( mBottom, 0.0 );
66+
}
67+
68+
/**
69+
* Returns the left margin.
70+
* @see setLeft()
71+
*/
72+
double left() const { return mLeft; }
73+
74+
/**
75+
* Returns the top margin.
76+
* @see setTop()
77+
*/
78+
double top() const { return mTop; }
79+
80+
/**
81+
* Returns the right margin.
82+
* @see setRight()
83+
*/
84+
double right() const { return mRight; }
85+
86+
/**
87+
* Returns the bottom margin.
88+
* @see setBottom()
89+
*/
90+
double bottom() const { return mBottom; }
91+
92+
/**
93+
* Sets the left margin to \a left.
94+
* @see left()
95+
*/
96+
void setLeft( double left ) { mLeft = left; }
97+
98+
/**
99+
* Sets the top margin to \a top.
100+
* @see top()
101+
*/
102+
void setTop( double top ) { mTop = top; }
103+
104+
/**
105+
* Sets the right margin to \a right.
106+
* @see right()
107+
*/
108+
void setRight( double right ) { mRight = right; }
109+
110+
/**
111+
* Sets the bottom margin to \a bottom.
112+
* @see bottom()
113+
*/
114+
void setBottom( double bottom ) { mBottom = bottom; }
115+
116+
/**
117+
* Add each component of \a margins to the respective component of this object
118+
* and returns a reference to it.
119+
*/
120+
inline QgsMargins &operator+=( const QgsMargins &margins );
121+
122+
/**
123+
* Subtract each component of \a margins from the respective component of this object
124+
* and returns a reference to it.
125+
*/
126+
inline QgsMargins &operator-=( const QgsMargins &margins );
127+
128+
/**
129+
* Adds the \a addend to each component of this object and returns a reference to it.
130+
*/
131+
inline QgsMargins &operator+=( double addend );
132+
133+
/**
134+
* Subtracts the \a subtrahend from each component of this object
135+
* and returns a reference to it.
136+
*/
137+
inline QgsMargins &operator-=( double subtrahend );
138+
139+
/**
140+
* Multiplies each component of this object by \a factor
141+
* and returns a reference to it.
142+
*/
143+
inline QgsMargins &operator*=( double factor );
144+
145+
/**
146+
* Multiplies each component of this object by \a factor
147+
* and returns a reference to it.
148+
*/
149+
inline QgsMargins &operator/=( double divisor );
150+
151+
/**
152+
* Returns the margins encoded to a string.
153+
* @see fromString()
154+
*/
155+
QString toString() const;
156+
157+
/**
158+
* Returns a QgsMargins object decoded from a string, or a null QgsMargins
159+
* if the string could not be interpreted as margins.
160+
* @see toString()
161+
*/
162+
static QgsMargins fromString( const QString& string );
163+
164+
private:
165+
double mLeft = 0.0;
166+
double mTop = 0.0;
167+
double mRight = 0.0;
168+
double mBottom = 0.0;
169+
};
170+
171+
/**
172+
* Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
173+
*/
174+
inline bool operator==( const QgsMargins &lhs, const QgsMargins &rhs )
175+
{
176+
return qgsDoubleNear( lhs.left(), rhs.left() )
177+
&& qgsDoubleNear( lhs.top(), rhs.top() )
178+
&& qgsDoubleNear( lhs.right(), rhs.right() )
179+
&& qgsDoubleNear( lhs.bottom(), rhs.bottom() );
180+
}
181+
182+
/**
183+
* Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
184+
*/
185+
inline bool operator!=( const QgsMargins &lhs, const QgsMargins &rhs )
186+
{
187+
return !operator==( lhs, rhs );
188+
}
189+
190+
/**
191+
* Returns a QgsMargins object that is the sum of the given margins, \a m1
192+
* and \a m2; each component is added separately.
193+
*/
194+
inline QgsMargins operator+( const QgsMargins &m1, const QgsMargins &m2 )
195+
{
196+
return QgsMargins( m1.left() + m2.left(), m1.top() + m2.top(),
197+
m1.right() + m2.right(), m1.bottom() + m2.bottom() );
198+
}
199+
200+
/**
201+
* Returns a QgsMargins object that is formed by subtracting \a m2 from
202+
* \a m1; each component is subtracted separately.
203+
*/
204+
inline QgsMargins operator-( const QgsMargins &m1, const QgsMargins &m2 )
205+
{
206+
return QgsMargins( m1.left() - m2.left(), m1.top() - m2.top(),
207+
m1.right() - m2.right(), m1.bottom() - m2.bottom() );
208+
}
209+
210+
/**
211+
* Returns a QgsMargins object that is formed by adding \a rhs to \a lhs.
212+
*/
213+
inline QgsMargins operator+( const QgsMargins &lhs, double rhs )
214+
{
215+
return QgsMargins( lhs.left() + rhs, lhs.top() + rhs,
216+
lhs.right() + rhs, lhs.bottom() + rhs );
217+
}
218+
219+
/**
220+
* Returns a QgsMargins object that is formed by adding \a lhs to \a rhs.
221+
*/
222+
inline QgsMargins operator+( double lhs, const QgsMargins &rhs )
223+
{
224+
return QgsMargins( rhs.left() + lhs, rhs.top() + lhs,
225+
rhs.right() + lhs, rhs.bottom() + lhs );
226+
}
227+
228+
/**
229+
* Returns a QgsMargins object that is formed by subtracting \a rhs from \a lhs.
230+
*/
231+
inline QgsMargins operator-( const QgsMargins &lhs, double rhs )
232+
{
233+
return QgsMargins( lhs.left() - rhs, lhs.top() - rhs,
234+
lhs.right() - rhs, lhs.bottom() - rhs );
235+
}
236+
237+
/**
238+
* Returns a QgsMargins object that is formed by multiplying each component
239+
* of the given \a margins by \a factor.
240+
*/
241+
inline QgsMargins operator*( const QgsMargins &margins, double factor )
242+
{
243+
return QgsMargins( margins.left() * factor, margins.top() * factor,
244+
margins.right() * factor, margins.bottom() * factor );
245+
}
246+
247+
/**
248+
* Returns a QgsMargins object that is formed by multiplying each component
249+
* of the given \a margins by \a factor.
250+
*/
251+
inline QgsMargins operator*( double factor, const QgsMargins &margins )
252+
{
253+
return QgsMargins( margins.left() * factor, margins.top() * factor,
254+
margins.right() * factor, margins.bottom() * factor );
255+
}
256+
257+
/**
258+
* Returns a QgsMargins object that is formed by dividing the components of
259+
* the given \a margins by the given \a divisor.
260+
*/
261+
inline QgsMargins operator/( const QgsMargins &margins, double divisor )
262+
{
263+
return QgsMargins( margins.left() / divisor, margins.top() / divisor,
264+
margins.right() / divisor, margins.bottom() / divisor );
265+
}
266+
267+
inline QgsMargins& QgsMargins::operator+=( const QgsMargins & margins )
268+
{
269+
return *this = *this + margins;
270+
}
271+
272+
inline QgsMargins& QgsMargins::operator-=( const QgsMargins & margins )
273+
{
274+
return *this = *this - margins;
275+
}
276+
277+
inline QgsMargins& QgsMargins::operator+=( double addend )
278+
{
279+
mLeft += addend;
280+
mTop += addend;
281+
mRight += addend;
282+
mBottom += addend;
283+
return *this;
284+
}
285+
286+
inline QgsMargins& QgsMargins::operator-=( double subtrahend )
287+
{
288+
mLeft -= subtrahend;
289+
mTop -= subtrahend;
290+
mRight -= subtrahend;
291+
mBottom -= subtrahend;
292+
return *this;
293+
}
294+
295+
inline QgsMargins& QgsMargins::operator*=( double factor )
296+
{
297+
return *this = *this * factor;
298+
}
299+
300+
inline QgsMargins& QgsMargins::operator/=( double divisor )
301+
{
302+
return *this = *this / divisor;
303+
}
304+
305+
/**
306+
* Returns a QgsMargins object that is formed from all components of \a margins.
307+
*/
308+
inline QgsMargins operator+( const QgsMargins &margins )
309+
{
310+
return margins;
311+
}
312+
313+
/**
314+
* Returns a QgsMargins object that is formed by negating all components of \a margins.
315+
*/
316+
inline QgsMargins operator-( const QgsMargins &margins )
317+
{
318+
return QgsMargins( -margins.left(), -margins.top(), -margins.right(), -margins.bottom() );
319+
}
320+
321+
Q_DECLARE_TYPEINFO( QgsMargins, Q_MOVABLE_TYPE );
322+
323+
#endif // QGSMARGINS_H

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ ADD_PYTHON_TEST(PyQgsJSONUtils test_qgsjsonutils.py)
6464
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
6565
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
6666
ADD_PYTHON_TEST(PyQgsMapUnitScale test_qgsmapunitscale.py)
67+
ADD_PYTHON_TEST(PyQgsMargins test_qgsmargins.py)
6768
ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)
6869
ADD_PYTHON_TEST(PyQgsMultiEditToolButton test_qgsmultiedittoolbutton.py)
6970
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)

‎tests/src/python/test_qgsmargins.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsMargins.
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '2017-01'
11+
__copyright__ = 'Copyright 2017, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
16+
import qgis # NOQA
17+
18+
from qgis.testing import unittest
19+
from qgis.core import QgsMargins
20+
21+
22+
class TestQgsOptional(unittest.TestCase):
23+
24+
def testGetSet(self):
25+
margins = QgsMargins()
26+
margins.setLeft(1.1)
27+
self.assertEqual(margins.left(), 1.1)
28+
margins.setTop(2.2)
29+
self.assertEqual(margins.top(), 2.2)
30+
margins.setBottom(3.3)
31+
self.assertEqual(margins.bottom(), 3.3)
32+
margins.setRight(4.4)
33+
self.assertEqual(margins.right(), 4.4)
34+
35+
margins = QgsMargins()
36+
self.assertTrue(margins.isNull())
37+
margins.setLeft(5.5)
38+
margins.setRight(5.5)
39+
self.assertFalse(margins.isNull())
40+
self.assertEqual(margins, QgsMargins(5.5, 0.0, 5.5, 0.0))
41+
42+
def testOperators(self):
43+
m1 = QgsMargins(12.1, 14.1, 16.1, 18.1)
44+
m2 = QgsMargins(2.1, 3.1, 4.1, 5.1)
45+
46+
added = m1 + m2
47+
self.assertAlmostEqual(added.left(), 14.2)
48+
self.assertAlmostEqual(added.top(), 17.2)
49+
self.assertAlmostEqual(added.right(), 20.2)
50+
self.assertAlmostEqual(added.bottom(), 23.2)
51+
a = QgsMargins(m1)
52+
a += m2
53+
self.assertEqual(a, added)
54+
55+
subtracted = m1 - m2
56+
self.assertAlmostEqual(subtracted.left(), 10.0)
57+
self.assertAlmostEqual(subtracted.top(), 11.0)
58+
self.assertAlmostEqual(subtracted.right(), 12.0)
59+
self.assertAlmostEqual(subtracted.bottom(), 13.0)
60+
a = QgsMargins(m1)
61+
a -= m2
62+
self.assertEqual(a, subtracted)
63+
64+
h = QgsMargins(m1)
65+
h += 2.1
66+
self.assertAlmostEqual(h.left(), 14.2)
67+
self.assertAlmostEqual(h.top(), 16.2)
68+
self.assertAlmostEqual(h.right(), 18.2)
69+
self.assertAlmostEqual(h.bottom(), 20.2)
70+
h -= 2.1
71+
self.assertEqual(h, m1)
72+
73+
doubled = m1 * 2.0
74+
self.assertEqual(doubled, QgsMargins(24.2, 28.2, 32.2, 36.2))
75+
self.assertEqual(2.0 * m1, doubled)
76+
self.assertEqual(m1 * 2.0, doubled)
77+
78+
a = QgsMargins(m1)
79+
a *= 2.0
80+
self.assertEqual(a, doubled)
81+
82+
halved = m1 / 2.0
83+
self.assertAlmostEqual(halved.left(), 6.05)
84+
self.assertAlmostEqual(halved.top(), 7.05)
85+
self.assertAlmostEqual(halved.right(), 8.05)
86+
self.assertAlmostEqual(halved.bottom(), 9.05)
87+
88+
a = QgsMargins(m1)
89+
a /= 2.0
90+
self.assertEqual(a, halved)
91+
92+
self.assertEqual(m1 + (-m1), QgsMargins())
93+
94+
m3 = QgsMargins(10.3, 11.4, 12.5, 13.6)
95+
self.assertEqual(m3 + 1.1, QgsMargins(11.4, 12.5, 13.6, 14.7))
96+
self.assertEqual(1.1 + m3, QgsMargins(11.4, 12.5, 13.6, 14.7))
97+
m4 = m3 - 1.1
98+
self.assertAlmostEqual(m4.left(), 9.2)
99+
self.assertAlmostEqual(m4.top(), 10.3)
100+
self.assertAlmostEqual(m4.right(), 11.4)
101+
self.assertAlmostEqual(m4.bottom(), 12.5)
102+
self.assertEqual(+m3, QgsMargins(10.3, 11.4, 12.5, 13.6))
103+
self.assertEqual(-m3, QgsMargins(-10.3, -11.4, -12.5, -13.6))
104+
105+
def testToString(self):
106+
# null margin
107+
self.assertFalse(QgsMargins().toString())
108+
109+
self.assertEqual(QgsMargins(1, 2, 3, 4).toString(), '1,2,3,4')
110+
self.assertEqual(QgsMargins(1, -2, 3, -4).toString(), '1,-2,3,-4')
111+
112+
def testFromString(self):
113+
114+
self.assertTrue(QgsMargins.fromString('').isNull())
115+
self.assertTrue(QgsMargins.fromString('not good').isNull())
116+
self.assertTrue(QgsMargins.fromString('1,2,3').isNull())
117+
self.assertTrue(QgsMargins.fromString('1,2,3,4,5').isNull())
118+
119+
self.assertEqual(QgsMargins.fromString('1,2,3,4'), QgsMargins(1, 2, 3, 4))
120+
self.assertEqual(QgsMargins.fromString('1,-2,3,-4'), QgsMargins(1, -2, 3, -4))
121+
122+
if __name__ == '__main__':
123+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.