QGIS API Documentation 4.3.0-Master (4ecdf0f3f59)
Loading...
Searching...
No Matches
qgsspinbox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsspinbox.cpp
3 --------------------------------------
4 Date : 09.2014
5 Copyright : (C) 2014 Denis Rouzaud
6 Email : denis.rouzaud@gmail.com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsspinbox.h"
17
18#include "qgsapplication.h"
19#include "qgsexpression.h"
21#include "qgsfilterlineedit.h"
22#include "qgslogger.h"
23
24#include <QLineEdit>
25#include <QMouseEvent>
26#include <QSettings>
27#include <QStyle>
28#include <QTimer>
29
30#include "moc_qgsspinbox.cpp"
31
32#define CLEAR_ICON_SIZE 16
33
34// This is required because private implementation of
35// QAbstractSpinBoxPrivate checks for specialText emptiness
36// and skips specialText handling if it's empty
37#ifdef _MSC_VER
38static QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
39#else
40static constexpr QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
41#endif
42
43QgsSpinBox::QgsSpinBox( QWidget *parent )
44 : QSpinBox( parent )
45{
46 mLineEdit = new QgsSpinBoxLineEdit();
47 connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsSpinBox::returnPressed );
48 connect( mLineEdit, &QLineEdit::textEdited, this, &QgsSpinBox::textEdited );
49 setLineEdit( mLineEdit );
50
51 const QSize msz = minimumSizeHint();
52 setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2, std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
53
54 connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsSpinBox::clear );
55 connect( this, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsSpinBox::changed );
56
57 mLastEditTimer = new QTimer( this );
58 mLastEditTimer->setSingleShot( true );
59 mLastEditTimer->setInterval( 1000 );
60 connect( mLastEditTimer, &QTimer::timeout, this, &QgsSpinBox::onLastEditTimeout );
61}
62
64{
65 mShowClearButton = showClearButton;
66 mLineEdit->setShowClearButton( showClearButton );
67}
68
69void QgsSpinBox::setExpressionsEnabled( const bool enabled )
70{
71 mExpressionsEnabled = enabled;
72}
73
74void QgsSpinBox::changeEvent( QEvent *event )
75{
76 QSpinBox::changeEvent( event );
77
78 if ( event->type() == QEvent::FontChange )
79 {
80 lineEdit()->setFont( font() );
81 }
82
83 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
84}
85
86void QgsSpinBox::paintEvent( QPaintEvent *event )
87{
88 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
89 QSpinBox::paintEvent( event );
90}
91
92void QgsSpinBox::wheelEvent( QWheelEvent *event )
93{
94 const int step = singleStep();
95 if ( event->modifiers() & Qt::ControlModifier )
96 {
97 // ctrl modifier results in finer increments - 10% of usual step
98 int newStep = step / 10;
99 // step should be at least 1
100 newStep = std::max( newStep, 1 );
101
102 setSingleStep( newStep );
103
104 // clear control modifier before handing off event - Qt uses it for unwanted purposes
105 // (*increasing* step size, whereas QGIS UX convention is that control modifier
106 // results in finer changes!)
107 event->setModifiers( event->modifiers() & ~Qt::ControlModifier );
108 }
109 QSpinBox::wheelEvent( event );
110 setSingleStep( step );
111}
112
113void QgsSpinBox::timerEvent( QTimerEvent *event )
114{
115 // Process all events, which may include a mouse release event
116 // Only allow the timer to trigger additional value changes if the user
117 // has in fact held the mouse button, rather than the timer expiry
118 // simply appearing before the mouse release in the event queue
119 qApp->processEvents();
120 if ( QApplication::mouseButtons() & Qt::LeftButton )
121 QSpinBox::timerEvent( event );
122}
123
124void QgsSpinBox::focusOutEvent( QFocusEvent *event )
125{
126 QSpinBox::focusOutEvent( event );
127 onLastEditTimeout();
128}
129
130void QgsSpinBox::changed( int value )
131{
132 mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
133 mLastEditTimer->start();
134}
135
136void QgsSpinBox::onLastEditTimeout()
137{
138 mLastEditTimer->stop();
139 const int currentValue = value();
140 if ( !mHasEmittedEditTimeout || mLastEditTimeoutValue != currentValue )
141 {
142 mHasEmittedEditTimeout = true;
143 mLastEditTimeoutValue = currentValue;
144 emit editingTimeout( mLastEditTimeoutValue );
145 }
146}
147
149{
150 setValue( clearValue() );
151 if ( mLineEdit->isNull() )
152 mLineEdit->clear();
153}
154
155void QgsSpinBox::setClearValue( int customValue, const QString &specialValueText )
156{
157 if ( mClearValueMode == CustomValue && mCustomClearValue == customValue && QAbstractSpinBox::specialValueText() == specialValueText )
158 {
159 return;
160 }
161
162 mClearValueMode = CustomValue;
163 mCustomClearValue = customValue;
164
165 if ( !specialValueText.isEmpty() )
166 {
167 const int v = value();
168 clear();
169 setSpecialValueText( specialValueText );
170 setValue( v );
171 }
172}
173
174void QgsSpinBox::setClearValueMode( QgsSpinBox::ClearValueMode mode, const QString &specialValueText )
175{
176 if ( mClearValueMode == mode && mCustomClearValue == 0 && QAbstractSpinBox::specialValueText() == specialValueText )
177 {
178 return;
179 }
180
181 mClearValueMode = mode;
182 mCustomClearValue = 0;
183
184 if ( !specialValueText.isEmpty() )
185 {
186 const int v = value();
187 clear();
188 setSpecialValueText( specialValueText );
189 setValue( v );
190 }
191}
192
194{
195 if ( mClearValueMode == MinimumValue )
196 return minimum();
197 else if ( mClearValueMode == MaximumValue )
198 return maximum();
199 else
200 return mCustomClearValue;
201}
202
203void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
204{
205 mLineEdit->setAlignment( alignment );
206}
207
208void QgsSpinBox::setSpecialValueText( const QString &txt )
209{
210 if ( txt.isEmpty() )
211 {
212 QSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
213 mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
214 }
215 else
216 {
217 QSpinBox::setSpecialValueText( txt );
218 mLineEdit->setNullValue( txt );
219 }
220}
221
222int QgsSpinBox::valueFromText( const QString &text ) const
223{
224 if ( !mExpressionsEnabled )
225 {
226 return QSpinBox::valueFromText( text );
227 }
228
229 const QString trimmedText = stripped( text );
230 if ( trimmedText.isEmpty() )
231 {
232 return mShowClearButton ? clearValue() : value();
233 }
234
235 QgsExpressionContext context;
237
238 return std::round( QgsExpression::evaluateToDouble( trimmedText, value(), &context ) );
239}
240
241QValidator::State QgsSpinBox::validate( QString &input, int &pos ) const
242{
243 if ( !mExpressionsEnabled )
244 {
245 const QValidator::State r = QSpinBox::validate( input, pos );
246 return r;
247 }
248
249 return QValidator::Acceptable;
250}
251
252void QgsSpinBox::stepBy( int steps )
253{
254 const bool wasNull = mShowClearButton && value() == clearValue();
255 if ( wasNull && minimum() < 0 && maximum() > 0 && !( specialValueText().isEmpty() || specialValueText() == SPECIAL_TEXT_WHEN_EMPTY ) )
256 {
257 // value is currently null, and range allows both positive and negative numbers
258 // in this case we DON'T do the default step, as that would add one step to the NULL value,
259 // which is usually the minimum acceptable value... so the user will get a very large negative number!
260 // Instead, treat the initial value as 0 instead, and then perform the step.
261 whileBlocking( this )->setValue( 0 );
262 }
263 QSpinBox::stepBy( steps );
264}
265
267{
268 return mLastEditTimer->interval();
269}
270
272{
273 mLastEditTimer->setInterval( timeout );
274}
275
276int QgsSpinBox::frameWidth() const
277{
278 return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
279}
280
281bool QgsSpinBox::shouldShowClearForValue( const int value ) const
282{
283 if ( !mShowClearButton || !isEnabled() )
284 {
285 return false;
286 }
287 return value != clearValue();
288}
289
290QString QgsSpinBox::stripped( const QString &originalText ) const
291{
292 //adapted from QAbstractSpinBoxPrivate::stripped
293 //trims whitespace, prefix and suffix from spin box text
294 QString text = originalText;
295 if ( specialValueText().isEmpty() || text != specialValueText() )
296 {
297 // Strip SPECIAL_TEXT_WHEN_EMPTY
298 if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
299 text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
300 int from = 0;
301 int size = text.size();
302 bool changed = false;
303 if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
304 {
305 from += prefix().size();
306 size -= from;
307 changed = true;
308 }
309 if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
310 {
311 size -= suffix().size();
312 changed = true;
313 }
314 if ( changed )
315 text = text.mid( from, size );
316 }
317
318 text = text.trimmed();
319
320 return text;
321}
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static Q_DECL_DEPRECATED double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
void cleared()
Emitted when the widget is cleared.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void setLineEditAlignment(Qt::Alignment alignment)
Set alignment in the embedded line edit widget.
void returnPressed()
Emitted when the Return or Enter key is used in the line edit.
ClearValueMode
Behavior when widget is cleared.
Definition qgsspinbox.h:63
@ MaximumValue
Reset value to maximum().
Definition qgsspinbox.h:65
@ MinimumValue
Reset value to minimum().
Definition qgsspinbox.h:64
@ CustomValue
Reset value to custom value (see setClearValue() ).
Definition qgsspinbox.h:66
bool showClearButton
Definition qgsspinbox.h:56
void wheelEvent(QWheelEvent *event) override
void editingTimeout(int value)
Emitted when either:
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
void focusOutEvent(QFocusEvent *event) override
QgsSpinBox(QWidget *parent=nullptr)
Constructor for QgsSpinBox.
QValidator::State validate(QString &input, int &pos) const override
void stepBy(int steps) override
void textEdited(const QString &text)
Emitted when the the value has been manually edited via line edit.
void paintEvent(QPaintEvent *event) override
bool clearValue
Definition qgsspinbox.h:57
void setClearValueMode(ClearValueMode mode, const QString &clearValueText=QString())
Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
int valueFromText(const QString &text) const override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
int editingTimeoutInterval() const
Returns the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after a...
void timerEvent(QTimerEvent *event) override
void setClearValue(int customValue, const QString &clearValueText=QString())
Defines the clear value as a custom value and will automatically set the clear value mode to CustomVa...
void setExpressionsEnabled(bool enabled)
Sets if the widget will allow entry of simple expressions, which are evaluated and then discarded.
void setSpecialValueText(const QString &txt)
Set the special-value text to be txt If set, the spin box will display this text instead of a numeric...
void changeEvent(QEvent *event) override
void clear() override
Sets the current value to the value defined by the clear value.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7665
#define CLEAR_ICON_SIZE