mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-05 00:04:40 -05:00
Add a fixed range width option to QgsRangeSlider
Allows forcing the widget to have a specific fixed range width, so that interactions with the lower or upper slider automatically force the other slider to move to keep a constant width
This commit is contained in:
parent
a5b4d9743e
commit
a91f5bf7c2
@ -168,6 +168,32 @@ This corresponds to the larger increment or decrement applied when the user pres
|
||||
.. seealso:: :py:func:`setPageStep`
|
||||
|
||||
.. seealso:: :py:func:`singleStep`
|
||||
%End
|
||||
|
||||
int fixedRangeWidth() const;
|
||||
%Docstring
|
||||
Returns the slider's fixed range width, or -1 if not set.
|
||||
|
||||
If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
move the other slider accordingly, in order to keep the selected range at the specified
|
||||
fixed width.
|
||||
|
||||
.. seealso:: :py:func:`setFixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
void setFixedRangeWidth( int width );
|
||||
%Docstring
|
||||
Sets the slider's fixed range ``width``. Set to -1 if no fixed width is desired.
|
||||
|
||||
If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
move the other slider accordingly, in order to keep the selected range at the specified
|
||||
fixed width.
|
||||
|
||||
.. seealso:: :py:func:`fixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
public slots:
|
||||
@ -265,6 +291,17 @@ Emitted when the range selected in the widget is changed.
|
||||
void rangeLimitsChanged( int minimum, int maximum );
|
||||
%Docstring
|
||||
Emitted when the limits of values allowed in the widget is changed.
|
||||
%End
|
||||
|
||||
void fixedRangeWidthChanged( int width );
|
||||
%Docstring
|
||||
Emitted when the widget's fixed range width is changed.
|
||||
|
||||
.. seealso:: :py:func:`fixedRangeWidth`
|
||||
|
||||
.. seealso:: :py:func:`setFixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
@ -168,6 +168,32 @@ This corresponds to the larger increment or decrement applied when the user pres
|
||||
.. seealso:: :py:func:`setPageStep`
|
||||
|
||||
.. seealso:: :py:func:`singleStep`
|
||||
%End
|
||||
|
||||
int fixedRangeWidth() const;
|
||||
%Docstring
|
||||
Returns the slider's fixed range width, or -1 if not set.
|
||||
|
||||
If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
move the other slider accordingly, in order to keep the selected range at the specified
|
||||
fixed width.
|
||||
|
||||
.. seealso:: :py:func:`setFixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
void setFixedRangeWidth( int width );
|
||||
%Docstring
|
||||
Sets the slider's fixed range ``width``. Set to -1 if no fixed width is desired.
|
||||
|
||||
If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
move the other slider accordingly, in order to keep the selected range at the specified
|
||||
fixed width.
|
||||
|
||||
.. seealso:: :py:func:`fixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
public slots:
|
||||
@ -265,6 +291,17 @@ Emitted when the range selected in the widget is changed.
|
||||
void rangeLimitsChanged( int minimum, int maximum );
|
||||
%Docstring
|
||||
Emitted when the limits of values allowed in the widget is changed.
|
||||
%End
|
||||
|
||||
void fixedRangeWidthChanged( int width );
|
||||
%Docstring
|
||||
Emitted when the widget's fixed range width is changed.
|
||||
|
||||
.. seealso:: :py:func:`fixedRangeWidth`
|
||||
|
||||
.. seealso:: :py:func:`setFixedRangeWidth`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
@ -120,7 +120,15 @@ void QgsRangeSlider::setLowerValue( int lowerValue )
|
||||
return;
|
||||
|
||||
mLowerValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, lowerValue ) );
|
||||
mUpperValue = std::max( mLowerValue, mUpperValue );
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
else
|
||||
{
|
||||
mUpperValue = std::max( mLowerValue, mUpperValue );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -137,7 +145,16 @@ void QgsRangeSlider::setUpperValue( int upperValue )
|
||||
return;
|
||||
|
||||
mUpperValue = std::max( mStyleOption.minimum, std::min( mStyleOption.maximum, upperValue ) );
|
||||
mLowerValue = std::min( mLowerValue, mUpperValue );
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLowerValue = std::min( mLowerValue, mUpperValue );
|
||||
}
|
||||
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -152,6 +169,15 @@ void QgsRangeSlider::setRange( int lower, int upper )
|
||||
|
||||
mLowerValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, lower ) );
|
||||
mUpperValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, upper ) );
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
else
|
||||
{
|
||||
mUpperValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, upper ) );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -317,6 +343,24 @@ QRect QgsRangeSlider::selectedRangeRect()
|
||||
return selectionRect.adjusted( -1, 1, 1, -1 );
|
||||
}
|
||||
|
||||
int QgsRangeSlider::fixedRangeWidth() const
|
||||
{
|
||||
return mFixedRangeWidth;
|
||||
}
|
||||
|
||||
void QgsRangeSlider::setFixedRangeWidth( int width )
|
||||
{
|
||||
if ( width == mFixedRangeWidth )
|
||||
return;
|
||||
|
||||
mFixedRangeWidth = width;
|
||||
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
setUpperValue( mLowerValue + mFixedRangeWidth );
|
||||
|
||||
emit fixedRangeWidthChanged( mFixedRangeWidth );
|
||||
}
|
||||
|
||||
void QgsRangeSlider::applyStep( int step )
|
||||
{
|
||||
switch ( mFocusControl )
|
||||
@ -327,6 +371,11 @@ void QgsRangeSlider::applyStep( int step )
|
||||
if ( newLowerValue != mLowerValue )
|
||||
{
|
||||
mLowerValue = newLowerValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -339,6 +388,11 @@ void QgsRangeSlider::applyStep( int step )
|
||||
if ( newUpperValue != mUpperValue )
|
||||
{
|
||||
mUpperValue = newUpperValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -354,7 +408,15 @@ void QgsRangeSlider::applyStep( int step )
|
||||
if ( newLowerValue != mLowerValue )
|
||||
{
|
||||
mLowerValue = newLowerValue;
|
||||
mUpperValue = std::min( mStyleOption.maximum, mLowerValue + previousWidth );
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
else
|
||||
{
|
||||
mUpperValue = std::min( mStyleOption.maximum, mLowerValue + previousWidth );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -366,7 +428,15 @@ void QgsRangeSlider::applyStep( int step )
|
||||
if ( newUpperValue != mUpperValue )
|
||||
{
|
||||
mUpperValue = newUpperValue;
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - previousWidth );
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - previousWidth );
|
||||
}
|
||||
emit rangeChanged( mLowerValue, mUpperValue );
|
||||
update();
|
||||
}
|
||||
@ -604,6 +674,12 @@ void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
|
||||
{
|
||||
changed = true;
|
||||
mUpperValue = mPreDragUpperValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( newPosition > mStartDragPos )
|
||||
@ -614,6 +690,12 @@ void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
|
||||
{
|
||||
changed = true;
|
||||
mLowerValue = mPreDragLowerValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -623,11 +705,23 @@ void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
|
||||
{
|
||||
changed = true;
|
||||
mUpperValue = mPreDragUpperValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
}
|
||||
if ( mLowerValue != mPreDragLowerValue )
|
||||
{
|
||||
changed = true;
|
||||
mLowerValue = mPreDragLowerValue;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -645,6 +739,13 @@ void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
|
||||
if ( mLowerValue != newPosition )
|
||||
{
|
||||
mLowerValue = newPosition;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
@ -657,6 +758,13 @@ void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
|
||||
if ( mUpperValue != newPosition )
|
||||
{
|
||||
mUpperValue = newPosition;
|
||||
if ( mFixedRangeWidth >= 0 )
|
||||
{
|
||||
// don't permit fixed width drags if it pushes the other value out of range
|
||||
mLowerValue = std::max( mStyleOption.minimum, mUpperValue - mFixedRangeWidth );
|
||||
mUpperValue = std::min( mLowerValue + mFixedRangeWidth, mStyleOption.maximum );
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -169,6 +169,30 @@ class GUI_EXPORT QgsRangeSlider : public QWidget
|
||||
*/
|
||||
int pageStep() const;
|
||||
|
||||
/**
|
||||
* Returns the slider's fixed range width, or -1 if not set.
|
||||
*
|
||||
* If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
* move the other slider accordingly, in order to keep the selected range at the specified
|
||||
* fixed width.
|
||||
*
|
||||
* \see setFixedRangeWidth()
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
int fixedRangeWidth() const;
|
||||
|
||||
/**
|
||||
* Sets the slider's fixed range \a width. Set to -1 if no fixed width is desired.
|
||||
*
|
||||
* If a fixed range width is set then moving either the lower or upper slider will automatically
|
||||
* move the other slider accordingly, in order to keep the selected range at the specified
|
||||
* fixed width.
|
||||
*
|
||||
* \see fixedRangeWidth()
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setFixedRangeWidth( int width );
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
@ -255,6 +279,16 @@ class GUI_EXPORT QgsRangeSlider : public QWidget
|
||||
*/
|
||||
void rangeLimitsChanged( int minimum, int maximum );
|
||||
|
||||
/**
|
||||
* Emitted when the widget's fixed range width is changed.
|
||||
*
|
||||
* \see fixedRangeWidth()
|
||||
* \see setFixedRangeWidth()
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void fixedRangeWidthChanged( int width );
|
||||
|
||||
private:
|
||||
|
||||
int pick( const QPoint &pt ) const;
|
||||
@ -270,6 +304,8 @@ class GUI_EXPORT QgsRangeSlider : public QWidget
|
||||
int mSingleStep = 1;
|
||||
int mPageStep = 10;
|
||||
|
||||
int mFixedRangeWidth = -1;
|
||||
|
||||
QStyleOptionSlider mStyleOption;
|
||||
enum Control
|
||||
{
|
||||
|
||||
@ -41,6 +41,10 @@ class TestQgsRangeSlider(QgisTestCase):
|
||||
w.setPageStep(5)
|
||||
self.assertEqual(w.pageStep(), 5)
|
||||
|
||||
self.assertEqual(w.fixedRangeWidth(), -1)
|
||||
w.setFixedRangeWidth(5)
|
||||
self.assertEqual(w.fixedRangeWidth(), 5)
|
||||
|
||||
def testLimits(self):
|
||||
w = QgsRangeSlider()
|
||||
spy = QSignalSpy(w.rangeLimitsChanged)
|
||||
@ -268,6 +272,56 @@ class TestQgsRangeSlider(QgisTestCase):
|
||||
self.assertEqual(len(spy), 6)
|
||||
self.assertEqual(spy[-1], [3, 7])
|
||||
|
||||
def test_fixed_range_width(self):
|
||||
"""
|
||||
Test interactions with fixed range widths
|
||||
"""
|
||||
w = QgsRangeSlider()
|
||||
w.setRangeLimits(0, 100)
|
||||
w.setFixedRangeWidth(10)
|
||||
self.assertEqual(w.upperValue() - w.lowerValue(), 10)
|
||||
|
||||
w.setUpperValue(70)
|
||||
self.assertEqual(w.upperValue(), 70)
|
||||
self.assertEqual(w.lowerValue(), 60)
|
||||
|
||||
w.setLowerValue(5)
|
||||
self.assertEqual(w.upperValue(), 15)
|
||||
self.assertEqual(w.lowerValue(), 5)
|
||||
|
||||
# try to force value outside range
|
||||
w.setUpperValue(5)
|
||||
self.assertEqual(w.upperValue(), 10)
|
||||
self.assertEqual(w.lowerValue(), 0)
|
||||
|
||||
w.setLowerValue(95)
|
||||
self.assertEqual(w.upperValue(), 100)
|
||||
self.assertEqual(w.lowerValue(), 90)
|
||||
|
||||
w.setRange(0, 5)
|
||||
self.assertEqual(w.upperValue(), 10)
|
||||
self.assertEqual(w.lowerValue(), 0)
|
||||
|
||||
w.setRange(95, 100)
|
||||
self.assertEqual(w.upperValue(), 100)
|
||||
self.assertEqual(w.lowerValue(), 90)
|
||||
|
||||
# with zero width fixed range
|
||||
w.setFixedRangeWidth(0)
|
||||
self.assertEqual(w.upperValue() - w.lowerValue(), 0)
|
||||
|
||||
w.setUpperValue(70)
|
||||
self.assertEqual(w.upperValue(), 70)
|
||||
self.assertEqual(w.lowerValue(), 70)
|
||||
|
||||
w.setLowerValue(5)
|
||||
self.assertEqual(w.upperValue(), 5)
|
||||
self.assertEqual(w.lowerValue(), 5)
|
||||
|
||||
w.setRange(0, 5)
|
||||
self.assertEqual(w.upperValue(), 0)
|
||||
self.assertEqual(w.lowerValue(), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user