Skip to content

Commit

Permalink
Allow setting width/height spin boxes to link to QgsRatioLockButton
Browse files Browse the repository at this point in the history
When set, these spin boxes will automatically be updated when their
accompanying spin box changes value so that the ratio is maintained.
  • Loading branch information
nyalldawson committed Jul 21, 2017
1 parent eb5ac44 commit 38c8268
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 0 deletions.
25 changes: 25 additions & 0 deletions python/gui/qgsratiolockbutton.sip
Expand Up @@ -9,6 +9,7 @@




class QgsRatioLockButton : QToolButton
{
%Docstring
Expand Down Expand Up @@ -42,6 +43,30 @@ class QgsRatioLockButton : QToolButton
:rtype: bool
%End

void setWidthSpinBox( QDoubleSpinBox *widget );
%Docstring
Registers a spin box ``widget`` as the linked "width" spin box.

If both a width and height spin box are linked to the button, they will automatically
have their values updates when if the other spin box value is changed. I.e. changing the
width spin box will automatically update the height spin box to a value which keeps the
same locked ratio.

.. seealso:: setHeightSpinBox()
%End

void setHeightSpinBox( QDoubleSpinBox *widget );
%Docstring
Registers a spin box ``widget`` as the linked "height" spin box.

If both a width and height spin box are linked to the button, they will automatically
have their values updates when if the other spin box value is changed. I.e. changing the
width spin box will automatically update the height spin box to a value which keeps the
same locked ratio.

.. seealso:: setWidthSpinBox()
%End

signals:

void lockChanged( const bool locked );
Expand Down
47 changes: 47 additions & 0 deletions src/gui/qgsratiolockbutton.cpp
Expand Up @@ -20,6 +20,7 @@
#include <QPainter>
#include <QPushButton>
#include <QWidget>
#include <QDoubleSpinBox>

QgsRatioLockButton::QgsRatioLockButton( QWidget *parent )
: QToolButton( parent )
Expand Down Expand Up @@ -48,6 +49,38 @@ void QgsRatioLockButton::buttonClicked()
drawButton();
}

void QgsRatioLockButton::widthSpinBoxChanged( double value )
{
if ( mUpdatingRatio || qgsDoubleNear( value, 0.0 ) || qgsDoubleNear( mPrevWidth, 0.0 )
|| qgsDoubleNear( mPrevHeight, 0.0 ) || !mHeightSpinBox || !mLocked )
{
mPrevWidth = value;
return;
}

double oldRatio = mPrevHeight / mPrevWidth;
mUpdatingRatio = true;
mHeightSpinBox->setValue( oldRatio * value );
mUpdatingRatio = false;
mPrevWidth = value;
}

void QgsRatioLockButton::heightSpinBoxChanged( double value )
{
if ( mUpdatingRatio || qgsDoubleNear( value, 0.0 ) || qgsDoubleNear( mPrevWidth, 0.0 )
|| qgsDoubleNear( mPrevHeight, 0.0 ) || !mWidthSpinBox || !mLocked )
{
mPrevHeight = value;
return;
}

double oldRatio = mPrevWidth / mPrevHeight;
mUpdatingRatio = true;
mWidthSpinBox->setValue( oldRatio * value );
mUpdatingRatio = false;
mPrevHeight = value;
}

void QgsRatioLockButton::changeEvent( QEvent *e )
{
if ( e->type() == QEvent::EnabledChange )
Expand Down Expand Up @@ -108,3 +141,17 @@ void QgsRatioLockButton::drawButton()
setIconSize( currentIconSize );
setIcon( pm );
}

void QgsRatioLockButton::setWidthSpinBox( QDoubleSpinBox *widget )
{
mWidthSpinBox = widget;
mPrevWidth = widget->value();
connect( mWidthSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsRatioLockButton::widthSpinBoxChanged );
}

void QgsRatioLockButton::setHeightSpinBox( QDoubleSpinBox *widget )
{
mHeightSpinBox = widget;
mPrevHeight = widget->value();
connect( mHeightSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsRatioLockButton::heightSpinBoxChanged );
}
36 changes: 36 additions & 0 deletions src/gui/qgsratiolockbutton.h
Expand Up @@ -22,6 +22,9 @@
#include "qgis_gui.h"
#include "qgis.h"

#include <QPointer>
class QDoubleSpinBox;

/** \ingroup gui
* \class QgsRatioLockButton
* A cross platform button subclass used to represent a locked / unlocked ratio state.
Expand Down Expand Up @@ -51,6 +54,30 @@ class GUI_EXPORT QgsRatioLockButton : public QToolButton
*/
bool locked() const { return mLocked; }

/**
* Registers a spin box \a widget as the linked "width" spin box.
*
* If both a width and height spin box are linked to the button, they will automatically
* have their values updates when if the other spin box value is changed. I.e. changing the
* width spin box will automatically update the height spin box to a value which keeps the
* same locked ratio.
*
* \see setHeightSpinBox()
*/
void setWidthSpinBox( QDoubleSpinBox *widget );

/**
* Registers a spin box \a widget as the linked "height" spin box.
*
* If both a width and height spin box are linked to the button, they will automatically
* have their values updates when if the other spin box value is changed. I.e. changing the
* width spin box will automatically update the height spin box to a value which keeps the
* same locked ratio.
*
* \see setWidthSpinBox()
*/
void setHeightSpinBox( QDoubleSpinBox *widget );

signals:

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

bool mLocked = false;

QPointer< QDoubleSpinBox > mWidthSpinBox;
double mPrevWidth = 0;
QPointer< QDoubleSpinBox > mHeightSpinBox;
double mPrevHeight = 0;
bool mUpdatingRatio = false;

private slots:

void buttonClicked();

void widthSpinBoxChanged( double value );
void heightSpinBoxChanged( double value );

};

#endif
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -115,6 +115,7 @@ ADD_PYTHON_TEST(PyQgsRasterFileWriter test_qgsrasterfilewriter.py)
ADD_PYTHON_TEST(PyQgsRasterFileWriterTask test_qgsrasterfilewritertask.py)
ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)
ADD_PYTHON_TEST(PyQgsRasterColorRampShader test_qgsrastercolorrampshader.py)
ADD_PYTHON_TEST(PyQgsRatioLockButton test_qgsratiolockbutton.py)
ADD_PYTHON_TEST(PyQgsRectangle test_qgsrectangle.py)
ADD_PYTHON_TEST(PyQgsRelation test_qgsrelation.py)
ADD_PYTHON_TEST(PyQgsRelationManager test_qgsrelationmanager.py)
Expand Down
104 changes: 104 additions & 0 deletions tests/src/python/test_qgsratiolockbutton.py
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsRatioLockButton
.. 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__ = '18/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 QgsRatioLockButton

from qgis.PyQt.QtWidgets import QDoubleSpinBox

from qgis.testing import start_app, unittest

start_app()


class TestQgsRatioLockButton(unittest.TestCase):

def testLinkedWidgets(self):
""" test linking spin boxes to combobox"""
w = qgis.gui.QgsRatioLockButton()

spin_width = QDoubleSpinBox()
spin_width.setMaximum(100000)
spin_height = QDoubleSpinBox()
spin_height.setMaximum(100000)

w.setWidthSpinBox(spin_width)
spin_width.setValue(1000)
self.assertEqual(spin_width.value(), 1000)

w.setLocked(True)
spin_width.setValue(2000)
self.assertEqual(spin_width.value(), 2000)
w.setLocked(False)

w.setHeightSpinBox(spin_height)
spin_width.setValue(1000)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 0)

w.setLocked(True)
spin_width.setValue(2000)
self.assertEqual(spin_width.value(), 2000)
self.assertEqual(spin_height.value(), 0)

spin_height.setValue(1000)
self.assertEqual(spin_width.value(), 2000)
self.assertEqual(spin_height.value(), 1000)

# ok, that was all setup tests... let's check the real thing now
spin_width.setValue(1000)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 500)
spin_height.setValue(1000)
self.assertEqual(spin_width.value(), 2000)
self.assertEqual(spin_height.value(), 1000)

w.setLocked(False)
spin_width.setValue(1000)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 1000)
spin_height.setValue(2000)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 2000)

w.setLocked(True)
spin_height.setValue(1000)
self.assertEqual(spin_width.value(), 500)
self.assertEqual(spin_height.value(), 1000)

# setting to 0 should "break" lock
spin_height.setValue(0)
self.assertEqual(spin_width.value(), 500)
self.assertEqual(spin_height.value(), 0)
spin_width.setValue(1000)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 0)
spin_height.setValue(100)
self.assertEqual(spin_width.value(), 1000)
self.assertEqual(spin_height.value(), 100)

spin_width.setValue(0)
self.assertEqual(spin_width.value(), 0)
self.assertEqual(spin_height.value(), 100)
spin_height.setValue(1000)
self.assertEqual(spin_width.value(), 0)
self.assertEqual(spin_height.value(), 1000)
spin_width.setValue(200)
self.assertEqual(spin_width.value(), 200)
self.assertEqual(spin_height.value(), 1000)


if __name__ == '__main__':
unittest.main()

0 comments on commit 38c8268

Please sign in to comment.