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