Skip to content

Commit

Permalink
Add editingTimeout signal to Qgs(Double)SpinBox
Browse files Browse the repository at this point in the history
Emitted when either:
1. 2 seconds has elapsed since the last value change in the widget
  (eg last key press or scroll wheel event)
2. or, immediately after the widget has lost focus after its value
  was changed.

This signal can be used to respond semi-instantly to changes in
the spin box, without responding too quickly
while the user in the middle of setting the value.
  • Loading branch information
nyalldawson committed Dec 9, 2024
1 parent ece4058 commit d3af853
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 8 deletions.
4 changes: 2 additions & 2 deletions python/PyQt6/gui/auto_additions/qgsdoublespinbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
QgsDoubleSpinBox.MaximumValue = QgsDoubleSpinBox.ClearValueMode.MaximumValue
QgsDoubleSpinBox.CustomValue = QgsDoubleSpinBox.ClearValueMode.CustomValue
try:
QgsDoubleSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit.\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n'}
QgsDoubleSpinBox.__signal_arguments__ = {'textEdited': ['text: str']}
QgsDoubleSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit.\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n', 'editingTimeout': 'Emitted when either:\n\n1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)\n2. or, immediately after the widget has lost focus after its value was changed.\n\nThis signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly\nwhile the user in the middle of setting the value.\n\n.. versionadded:: 3.42\n'}
QgsDoubleSpinBox.__signal_arguments__ = {'textEdited': ['text: str'], 'editingTimeout': ['value: float']}
QgsDoubleSpinBox.__group__ = ['editorwidgets']
except (NameError, AttributeError):
pass
4 changes: 2 additions & 2 deletions python/PyQt6/gui/auto_additions/qgsspinbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
QgsSpinBox.MaximumValue = QgsSpinBox.ClearValueMode.MaximumValue
QgsSpinBox.CustomValue = QgsSpinBox.ClearValueMode.CustomValue
try:
QgsSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n'}
QgsSpinBox.__signal_arguments__ = {'textEdited': ['text: str']}
QgsSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n', 'editingTimeout': 'Emitted when either:\n\n1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)\n2. or, immediately after the widget has lost focus after its value was changed.\n\nThis signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly\nwhile the user in the middle of setting the value.\n\n.. versionadded:: 3.42\n'}
QgsSpinBox.__signal_arguments__ = {'textEdited': ['text: str'], 'editingTimeout': ['value: int']}
QgsSpinBox.__group__ = ['editorwidgets']
except (NameError, AttributeError):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,28 @@ Emitted when the Return or Enter key is used in the line edit.
Emitted when the the value has been manually edited via line edit.

.. versionadded:: 3.40
%End

void editingTimeout( double value );
%Docstring
Emitted when either:

1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
2. or, immediately after the widget has lost focus after its value was changed.

This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
while the user in the middle of setting the value.

.. versionadded:: 3.42
%End

protected:
virtual void changeEvent( QEvent *event );

virtual void wheelEvent( QWheelEvent *event );

virtual void focusOutEvent( QFocusEvent *event );

virtual void timerEvent( QTimerEvent *event );


Expand Down
15 changes: 15 additions & 0 deletions python/PyQt6/gui/auto_generated/editorwidgets/qgsspinbox.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ Emitted when the Return or Enter key is used in the line edit
Emitted when the the value has been manually edited via line edit.

.. versionadded:: 3.40
%End

void editingTimeout( int value );
%Docstring
Emitted when either:

1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
2. or, immediately after the widget has lost focus after its value was changed.

This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
while the user in the middle of setting the value.

.. versionadded:: 3.42
%End

protected:
Expand All @@ -160,6 +173,8 @@ Emitted when the the value has been manually edited via line edit.

virtual void timerEvent( QTimerEvent *event );

virtual void focusOutEvent( QFocusEvent *event );


};

Expand Down
4 changes: 2 additions & 2 deletions python/gui/auto_additions/qgsdoublespinbox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# The following has been generated automatically from src/gui/editorwidgets/qgsdoublespinbox.h
try:
QgsDoubleSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit.\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n'}
QgsDoubleSpinBox.__signal_arguments__ = {'textEdited': ['text: str']}
QgsDoubleSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit.\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n', 'editingTimeout': 'Emitted when either:\n\n1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)\n2. or, immediately after the widget has lost focus after its value was changed.\n\nThis signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly\nwhile the user in the middle of setting the value.\n\n.. versionadded:: 3.42\n'}
QgsDoubleSpinBox.__signal_arguments__ = {'textEdited': ['text: str'], 'editingTimeout': ['value: float']}
QgsDoubleSpinBox.__group__ = ['editorwidgets']
except (NameError, AttributeError):
pass
4 changes: 2 additions & 2 deletions python/gui/auto_additions/qgsspinbox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# The following has been generated automatically from src/gui/editorwidgets/qgsspinbox.h
try:
QgsSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n'}
QgsSpinBox.__signal_arguments__ = {'textEdited': ['text: str']}
QgsSpinBox.__attribute_docs__ = {'returnPressed': 'Emitted when the Return or Enter key is used in the line edit\n\n.. versionadded:: 3.40\n', 'textEdited': 'Emitted when the the value has been manually edited via line edit.\n\n.. versionadded:: 3.40\n', 'editingTimeout': 'Emitted when either:\n\n1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)\n2. or, immediately after the widget has lost focus after its value was changed.\n\nThis signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly\nwhile the user in the middle of setting the value.\n\n.. versionadded:: 3.42\n'}
QgsSpinBox.__signal_arguments__ = {'textEdited': ['text: str'], 'editingTimeout': ['value: int']}
QgsSpinBox.__group__ = ['editorwidgets']
except (NameError, AttributeError):
pass
15 changes: 15 additions & 0 deletions python/gui/auto_generated/editorwidgets/qgsdoublespinbox.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,28 @@ Emitted when the Return or Enter key is used in the line edit.
Emitted when the the value has been manually edited via line edit.

.. versionadded:: 3.40
%End

void editingTimeout( double value );
%Docstring
Emitted when either:

1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
2. or, immediately after the widget has lost focus after its value was changed.

This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
while the user in the middle of setting the value.

.. versionadded:: 3.42
%End

protected:
virtual void changeEvent( QEvent *event );

virtual void wheelEvent( QWheelEvent *event );

virtual void focusOutEvent( QFocusEvent *event );

virtual void timerEvent( QTimerEvent *event );


Expand Down
15 changes: 15 additions & 0 deletions python/gui/auto_generated/editorwidgets/qgsspinbox.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ Emitted when the Return or Enter key is used in the line edit
Emitted when the the value has been manually edited via line edit.

.. versionadded:: 3.40
%End

void editingTimeout( int value );
%Docstring
Emitted when either:

1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
2. or, immediately after the widget has lost focus after its value was changed.

This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
while the user in the middle of setting the value.

.. versionadded:: 3.42
%End

protected:
Expand All @@ -160,6 +173,8 @@ Emitted when the the value has been manually edited via line edit.

virtual void timerEvent( QTimerEvent *event );

virtual void focusOutEvent( QFocusEvent *event );


};

Expand Down
24 changes: 24 additions & 0 deletions src/gui/editorwidgets/qgsdoublespinbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <QMouseEvent>
#include <QSettings>
#include <QStyle>
#include <QTimer>

#include "qgsdoublespinbox.h"
#include "moc_qgsdoublespinbox.cpp"
Expand Down Expand Up @@ -51,6 +52,11 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent )

connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsDoubleSpinBox::clear );
connect( this, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsDoubleSpinBox::changed );

mLastEditTimer = new QTimer( this );
mLastEditTimer->setSingleShot( true );
mLastEditTimer->setInterval( 2000 );
connect( mLastEditTimer, &QTimer::timeout, this, &QgsDoubleSpinBox::onLastEditTimeout );
}

void QgsDoubleSpinBox::setShowClearButton( const bool showClearButton )
Expand Down Expand Up @@ -98,6 +104,12 @@ void QgsDoubleSpinBox::wheelEvent( QWheelEvent *event )
setSingleStep( step );
}

void QgsDoubleSpinBox::focusOutEvent( QFocusEvent *event )
{
QDoubleSpinBox::focusOutEvent( event );
onLastEditTimeout();
}

void QgsDoubleSpinBox::timerEvent( QTimerEvent *event )
{
// Process all events, which may include a mouse release event
Expand Down Expand Up @@ -132,6 +144,18 @@ void QgsDoubleSpinBox::stepBy( int steps )
void QgsDoubleSpinBox::changed( double value )
{
mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
mLastEditTimer->start();
}

void QgsDoubleSpinBox::onLastEditTimeout()
{
mLastEditTimer->stop();
const double currentValue = value();
if ( std::isnan( mLastEditTimeoutValue ) || mLastEditTimeoutValue != currentValue )
{
mLastEditTimeoutValue = currentValue;
emit editingTimeout( mLastEditTimeoutValue );
}
}

void QgsDoubleSpinBox::clear()
Expand Down
18 changes: 18 additions & 0 deletions src/gui/editorwidgets/qgsdoublespinbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,31 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
*/
void textEdited( const QString &text );

/**
* Emitted when either:
*
* 1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
* 2. or, immediately after the widget has lost focus after its value was changed.
*
* This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
* while the user in the middle of setting the value.
*
* \since QGIS 3.42
*/
void editingTimeout( double value );

protected:
void changeEvent( QEvent *event ) override;
void wheelEvent( QWheelEvent *event ) override;
void focusOutEvent( QFocusEvent *event ) override;
// This is required because private implementation of
// QAbstractSpinBoxPrivate may trigger a second
// undesired event from the auto-repeat mouse timer
void timerEvent( QTimerEvent *event ) override;

private slots:
void changed( double value );
void onLastEditTimeout();

private:
int frameWidth() const;
Expand All @@ -185,6 +200,9 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox

bool mExpressionsEnabled = true;

QTimer *mLastEditTimer = nullptr;
double mLastEditTimeoutValue = std::numeric_limits<double>::quiet_NaN();

QString stripped( const QString &originalText ) const;

friend class TestQgsRangeWidgetWrapper;
Expand Down
25 changes: 25 additions & 0 deletions src/gui/editorwidgets/qgsspinbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <QMouseEvent>
#include <QSettings>
#include <QStyle>
#include <QTimer>

#include "qgsspinbox.h"
#include "moc_qgsspinbox.cpp"
Expand Down Expand Up @@ -49,6 +50,11 @@ QgsSpinBox::QgsSpinBox( QWidget *parent )

connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsSpinBox::clear );
connect( this, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsSpinBox::changed );

mLastEditTimer = new QTimer( this );
mLastEditTimer->setSingleShot( true );
mLastEditTimer->setInterval( 2000 );
connect( mLastEditTimer, &QTimer::timeout, this, &QgsSpinBox::onLastEditTimeout );
}

void QgsSpinBox::setShowClearButton( const bool showClearButton )
Expand Down Expand Up @@ -112,9 +118,28 @@ void QgsSpinBox::timerEvent( QTimerEvent *event )
QSpinBox::timerEvent( event );
}

void QgsSpinBox::focusOutEvent( QFocusEvent *event )
{
QSpinBox::focusOutEvent( event );
onLastEditTimeout();
}

void QgsSpinBox::changed( int value )
{
mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
mLastEditTimer->start();
}

void QgsSpinBox::onLastEditTimeout()
{
mLastEditTimer->stop();
const int currentValue = value();
if ( !mHasEmittedEditTimeout || mLastEditTimeoutValue != currentValue )
{
mHasEmittedEditTimeout = true;
mLastEditTimeoutValue = currentValue;
emit editingTimeout( mLastEditTimeoutValue );
}
}

void QgsSpinBox::clear()
Expand Down
19 changes: 19 additions & 0 deletions src/gui/editorwidgets/qgsspinbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
*/
void textEdited( const QString &text );

/**
* Emitted when either:
*
* 1. 2 seconds has elapsed since the last value change in the widget (eg last key press or scroll wheel event)
* 2. or, immediately after the widget has lost focus after its value was changed.
*
* This signal can be used to respond semi-instantly to changes in the spin box, without responding too quickly
* while the user in the middle of setting the value.
*
* \since QGIS 3.42
*/
void editingTimeout( int value );

protected:
void changeEvent( QEvent *event ) override;
void paintEvent( QPaintEvent *event ) override;
Expand All @@ -161,9 +174,11 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
// QAbstractSpinBoxPrivate may trigger a second
// undesired event from the auto-repeat mouse timer
void timerEvent( QTimerEvent *event ) override;
void focusOutEvent( QFocusEvent *event ) override;

private slots:
void changed( int value );
void onLastEditTimeout();

private:
int frameWidth() const;
Expand All @@ -177,6 +192,10 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox

bool mExpressionsEnabled = true;

QTimer *mLastEditTimer = nullptr;
bool mHasEmittedEditTimeout = false;
int mLastEditTimeoutValue = 0;

QString stripped( const QString &originalText ) const;

friend class TestQgsRangeWidgetWrapper;
Expand Down
Loading

0 comments on commit d3af853

Please sign in to comment.