Skip to content

Commit 38c8268

Browse files
committed
Allow setting width/height spin boxes to link to QgsRatioLockButton
When set, these spin boxes will automatically be updated when their accompanying spin box changes value so that the ratio is maintained.
1 parent eb5ac44 commit 38c8268

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

python/gui/qgsratiolockbutton.sip

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010

1111

12+
1213
class QgsRatioLockButton : QToolButton
1314
{
1415
%Docstring
@@ -42,6 +43,30 @@ class QgsRatioLockButton : QToolButton
4243
:rtype: bool
4344
%End
4445

46+
void setWidthSpinBox( QDoubleSpinBox *widget );
47+
%Docstring
48+
Registers a spin box ``widget`` as the linked "width" spin box.
49+
50+
If both a width and height spin box are linked to the button, they will automatically
51+
have their values updates when if the other spin box value is changed. I.e. changing the
52+
width spin box will automatically update the height spin box to a value which keeps the
53+
same locked ratio.
54+
55+
.. seealso:: setHeightSpinBox()
56+
%End
57+
58+
void setHeightSpinBox( QDoubleSpinBox *widget );
59+
%Docstring
60+
Registers a spin box ``widget`` as the linked "height" spin box.
61+
62+
If both a width and height spin box are linked to the button, they will automatically
63+
have their values updates when if the other spin box value is changed. I.e. changing the
64+
width spin box will automatically update the height spin box to a value which keeps the
65+
same locked ratio.
66+
67+
.. seealso:: setWidthSpinBox()
68+
%End
69+
4570
signals:
4671

4772
void lockChanged( const bool locked );

src/gui/qgsratiolockbutton.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <QPainter>
2121
#include <QPushButton>
2222
#include <QWidget>
23+
#include <QDoubleSpinBox>
2324

2425
QgsRatioLockButton::QgsRatioLockButton( QWidget *parent )
2526
: QToolButton( parent )
@@ -48,6 +49,38 @@ void QgsRatioLockButton::buttonClicked()
4849
drawButton();
4950
}
5051

52+
void QgsRatioLockButton::widthSpinBoxChanged( double value )
53+
{
54+
if ( mUpdatingRatio || qgsDoubleNear( value, 0.0 ) || qgsDoubleNear( mPrevWidth, 0.0 )
55+
|| qgsDoubleNear( mPrevHeight, 0.0 ) || !mHeightSpinBox || !mLocked )
56+
{
57+
mPrevWidth = value;
58+
return;
59+
}
60+
61+
double oldRatio = mPrevHeight / mPrevWidth;
62+
mUpdatingRatio = true;
63+
mHeightSpinBox->setValue( oldRatio * value );
64+
mUpdatingRatio = false;
65+
mPrevWidth = value;
66+
}
67+
68+
void QgsRatioLockButton::heightSpinBoxChanged( double value )
69+
{
70+
if ( mUpdatingRatio || qgsDoubleNear( value, 0.0 ) || qgsDoubleNear( mPrevWidth, 0.0 )
71+
|| qgsDoubleNear( mPrevHeight, 0.0 ) || !mWidthSpinBox || !mLocked )
72+
{
73+
mPrevHeight = value;
74+
return;
75+
}
76+
77+
double oldRatio = mPrevWidth / mPrevHeight;
78+
mUpdatingRatio = true;
79+
mWidthSpinBox->setValue( oldRatio * value );
80+
mUpdatingRatio = false;
81+
mPrevHeight = value;
82+
}
83+
5184
void QgsRatioLockButton::changeEvent( QEvent *e )
5285
{
5386
if ( e->type() == QEvent::EnabledChange )
@@ -108,3 +141,17 @@ void QgsRatioLockButton::drawButton()
108141
setIconSize( currentIconSize );
109142
setIcon( pm );
110143
}
144+
145+
void QgsRatioLockButton::setWidthSpinBox( QDoubleSpinBox *widget )
146+
{
147+
mWidthSpinBox = widget;
148+
mPrevWidth = widget->value();
149+
connect( mWidthSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsRatioLockButton::widthSpinBoxChanged );
150+
}
151+
152+
void QgsRatioLockButton::setHeightSpinBox( QDoubleSpinBox *widget )
153+
{
154+
mHeightSpinBox = widget;
155+
mPrevHeight = widget->value();
156+
connect( mHeightSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsRatioLockButton::heightSpinBoxChanged );
157+
}

src/gui/qgsratiolockbutton.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "qgis_gui.h"
2323
#include "qgis.h"
2424

25+
#include <QPointer>
26+
class QDoubleSpinBox;
27+
2528
/** \ingroup gui
2629
* \class QgsRatioLockButton
2730
* A cross platform button subclass used to represent a locked / unlocked ratio state.
@@ -51,6 +54,30 @@ class GUI_EXPORT QgsRatioLockButton : public QToolButton
5154
*/
5255
bool locked() const { return mLocked; }
5356

57+
/**
58+
* Registers a spin box \a widget as the linked "width" spin box.
59+
*
60+
* If both a width and height spin box are linked to the button, they will automatically
61+
* have their values updates when if the other spin box value is changed. I.e. changing the
62+
* width spin box will automatically update the height spin box to a value which keeps the
63+
* same locked ratio.
64+
*
65+
* \see setHeightSpinBox()
66+
*/
67+
void setWidthSpinBox( QDoubleSpinBox *widget );
68+
69+
/**
70+
* Registers a spin box \a widget as the linked "height" spin box.
71+
*
72+
* If both a width and height spin box are linked to the button, they will automatically
73+
* have their values updates when if the other spin box value is changed. I.e. changing the
74+
* width spin box will automatically update the height spin box to a value which keeps the
75+
* same locked ratio.
76+
*
77+
* \see setWidthSpinBox()
78+
*/
79+
void setHeightSpinBox( QDoubleSpinBox *widget );
80+
5481
signals:
5582

5683
/** Emitted whenever the lock state changes.
@@ -69,10 +96,19 @@ class GUI_EXPORT QgsRatioLockButton : public QToolButton
6996

7097
bool mLocked = false;
7198

99+
QPointer< QDoubleSpinBox > mWidthSpinBox;
100+
double mPrevWidth = 0;
101+
QPointer< QDoubleSpinBox > mHeightSpinBox;
102+
double mPrevHeight = 0;
103+
bool mUpdatingRatio = false;
104+
72105
private slots:
73106

74107
void buttonClicked();
75108

109+
void widthSpinBoxChanged( double value );
110+
void heightSpinBoxChanged( double value );
111+
76112
};
77113

78114
#endif

tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ ADD_PYTHON_TEST(PyQgsRasterFileWriter test_qgsrasterfilewriter.py)
115115
ADD_PYTHON_TEST(PyQgsRasterFileWriterTask test_qgsrasterfilewritertask.py)
116116
ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)
117117
ADD_PYTHON_TEST(PyQgsRasterColorRampShader test_qgsrastercolorrampshader.py)
118+
ADD_PYTHON_TEST(PyQgsRatioLockButton test_qgsratiolockbutton.py)
118119
ADD_PYTHON_TEST(PyQgsRectangle test_qgsrectangle.py)
119120
ADD_PYTHON_TEST(PyQgsRelation test_qgsrelation.py)
120121
ADD_PYTHON_TEST(PyQgsRelationManager test_qgsrelationmanager.py)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsRatioLockButton
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__ = '18/07/2017'
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+
import qgis # NOQA
16+
17+
from qgis.gui import QgsRatioLockButton
18+
19+
from qgis.PyQt.QtWidgets import QDoubleSpinBox
20+
21+
from qgis.testing import start_app, unittest
22+
23+
start_app()
24+
25+
26+
class TestQgsRatioLockButton(unittest.TestCase):
27+
28+
def testLinkedWidgets(self):
29+
""" test linking spin boxes to combobox"""
30+
w = qgis.gui.QgsRatioLockButton()
31+
32+
spin_width = QDoubleSpinBox()
33+
spin_width.setMaximum(100000)
34+
spin_height = QDoubleSpinBox()
35+
spin_height.setMaximum(100000)
36+
37+
w.setWidthSpinBox(spin_width)
38+
spin_width.setValue(1000)
39+
self.assertEqual(spin_width.value(), 1000)
40+
41+
w.setLocked(True)
42+
spin_width.setValue(2000)
43+
self.assertEqual(spin_width.value(), 2000)
44+
w.setLocked(False)
45+
46+
w.setHeightSpinBox(spin_height)
47+
spin_width.setValue(1000)
48+
self.assertEqual(spin_width.value(), 1000)
49+
self.assertEqual(spin_height.value(), 0)
50+
51+
w.setLocked(True)
52+
spin_width.setValue(2000)
53+
self.assertEqual(spin_width.value(), 2000)
54+
self.assertEqual(spin_height.value(), 0)
55+
56+
spin_height.setValue(1000)
57+
self.assertEqual(spin_width.value(), 2000)
58+
self.assertEqual(spin_height.value(), 1000)
59+
60+
# ok, that was all setup tests... let's check the real thing now
61+
spin_width.setValue(1000)
62+
self.assertEqual(spin_width.value(), 1000)
63+
self.assertEqual(spin_height.value(), 500)
64+
spin_height.setValue(1000)
65+
self.assertEqual(spin_width.value(), 2000)
66+
self.assertEqual(spin_height.value(), 1000)
67+
68+
w.setLocked(False)
69+
spin_width.setValue(1000)
70+
self.assertEqual(spin_width.value(), 1000)
71+
self.assertEqual(spin_height.value(), 1000)
72+
spin_height.setValue(2000)
73+
self.assertEqual(spin_width.value(), 1000)
74+
self.assertEqual(spin_height.value(), 2000)
75+
76+
w.setLocked(True)
77+
spin_height.setValue(1000)
78+
self.assertEqual(spin_width.value(), 500)
79+
self.assertEqual(spin_height.value(), 1000)
80+
81+
# setting to 0 should "break" lock
82+
spin_height.setValue(0)
83+
self.assertEqual(spin_width.value(), 500)
84+
self.assertEqual(spin_height.value(), 0)
85+
spin_width.setValue(1000)
86+
self.assertEqual(spin_width.value(), 1000)
87+
self.assertEqual(spin_height.value(), 0)
88+
spin_height.setValue(100)
89+
self.assertEqual(spin_width.value(), 1000)
90+
self.assertEqual(spin_height.value(), 100)
91+
92+
spin_width.setValue(0)
93+
self.assertEqual(spin_width.value(), 0)
94+
self.assertEqual(spin_height.value(), 100)
95+
spin_height.setValue(1000)
96+
self.assertEqual(spin_width.value(), 0)
97+
self.assertEqual(spin_height.value(), 1000)
98+
spin_width.setValue(200)
99+
self.assertEqual(spin_width.value(), 200)
100+
self.assertEqual(spin_height.value(), 1000)
101+
102+
103+
if __name__ == '__main__':
104+
unittest.main()

0 commit comments

Comments
 (0)