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.
This commit is contained in:
Nyall Dawson 2017-07-21 14:14:06 +10:00
parent eb5ac44b27
commit 38c8268400
5 changed files with 213 additions and 0 deletions

View File

@ -9,6 +9,7 @@
class QgsRatioLockButton : QToolButton
{
%Docstring
@ -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 );

View File

@ -20,6 +20,7 @@
#include <QPainter>
#include <QPushButton>
#include <QWidget>
#include <QDoubleSpinBox>
QgsRatioLockButton::QgsRatioLockButton( QWidget *parent )
: QToolButton( parent )
@ -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 )
@ -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 );
}

View File

@ -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.
@ -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.
@ -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

View File

@ -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)

View File

@ -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()