QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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
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 <QLineEdit>
17#include <QMouseEvent>
18#include <QSettings>
19#include <QStyle>
20
21#include "qgsdoublespinbox.h"
22#include "moc_qgsdoublespinbox.cpp"
23#include "qgsexpression.h"
24#include "qgsapplication.h"
25#include "qgslogger.h"
26#include "qgsfilterlineedit.h"
27
28#define CLEAR_ICON_SIZE 16
29
30// This is required because private implementation of
31// QAbstractSpinBoxPrivate checks for specialText emptiness
32// and skips specialText handling if it's empty
33#ifdef _MSC_VER
34static QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
35#else
36static constexpr QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
37#endif
38
39
41 : QDoubleSpinBox( parent )
42{
43 mLineEdit = new QgsSpinBoxLineEdit();
44 connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsDoubleSpinBox::returnPressed );
45
46 // By default, group separator is off
47 setLineEdit( mLineEdit );
48
49 const QSize msz = minimumSizeHint();
50 setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2,
51 std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
52
53 connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsDoubleSpinBox::clear );
54 connect( this, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsDoubleSpinBox::changed );
55}
56
57void QgsDoubleSpinBox::setShowClearButton( const bool showClearButton )
58{
59 mShowClearButton = showClearButton;
60 mLineEdit->setShowClearButton( showClearButton );
61}
62
64{
65 mExpressionsEnabled = enabled;
66}
67
68void QgsDoubleSpinBox::changeEvent( QEvent *event )
69{
70 QDoubleSpinBox::changeEvent( event );
71
72 if ( event->type() == QEvent::FontChange )
73 {
74 lineEdit()->setFont( font() );
75 }
76
77 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
78}
79
80void QgsDoubleSpinBox::wheelEvent( QWheelEvent *event )
81{
82 const double step = singleStep();
83 if ( event->modifiers() & Qt::ControlModifier )
84 {
85 // ctrl modifier results in finer increments - 10% of usual step
86 double newStep = step / 10;
87 // but don't ever use an increment smaller than would be visible in the widget
88 // i.e. if showing 2 decimals, smallest increment will be 0.01
89 newStep = std::max( newStep, std::pow( 10.0, 0.0 - decimals() ) );
90
91 setSingleStep( newStep );
92
93 // clear control modifier before handing off event - Qt uses it for unwanted purposes
94 // (*increasing* step size, whereas QGIS UX convention is that control modifier
95 // results in finer changes!)
96 event->setModifiers( event->modifiers() & ~Qt::ControlModifier );
97 }
98 QDoubleSpinBox::wheelEvent( event );
99 setSingleStep( step );
100}
101
102void QgsDoubleSpinBox::timerEvent( QTimerEvent *event )
103{
104 // Process all events, which may include a mouse release event
105 // Only allow the timer to trigger additional value changes if the user
106 // has in fact held the mouse button, rather than the timer expiry
107 // simply appearing before the mouse release in the event queue
108 qApp->processEvents();
109 if ( QApplication::mouseButtons() & Qt::LeftButton )
110 QDoubleSpinBox::timerEvent( event );
111}
112
113void QgsDoubleSpinBox::paintEvent( QPaintEvent *event )
114{
115 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
116 QDoubleSpinBox::paintEvent( event );
117}
118
120{
121 const bool wasNull = mShowClearButton && value() == clearValue();
122 if ( wasNull && minimum() < 0 && maximum() > 0 && !( specialValueText().isEmpty() || specialValueText() == SPECIAL_TEXT_WHEN_EMPTY ) )
123 {
124 // value is currently null, and range allows both positive and negative numbers
125 // in this case we DON'T do the default step, as that would add one step to the NULL value,
126 // which is usually the minimum acceptable value... so the user will get a very large negative number!
127 // Instead, treat the initial value as 0 instead, and then perform the step.
128 whileBlocking( this )->setValue( 0 );
129 }
130 QDoubleSpinBox::stepBy( steps );
131}
132
133void QgsDoubleSpinBox::changed( double value )
134{
135 mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
136}
137
139{
140 setValue( clearValue() );
141 if ( mLineEdit->isNull() )
142 mLineEdit->clear();
143}
144
145void QgsDoubleSpinBox::setClearValue( double customValue, const QString &specialValueText )
146{
147 if ( mClearValueMode == CustomValue && mCustomClearValue == customValue && QAbstractSpinBox::specialValueText() == specialValueText )
148 {
149 return;
150 }
151
152 mClearValueMode = CustomValue;
153 mCustomClearValue = customValue;
154
155 if ( !specialValueText.isEmpty() )
156 {
157 const double v = value();
158 clear();
159 setSpecialValueText( specialValueText );
160 setValue( v );
161 }
162}
163
165{
166 if ( mClearValueMode == mode && mCustomClearValue == 0 && QAbstractSpinBox::specialValueText() == clearValueText )
167 {
168 return;
169 }
170
171 mClearValueMode = mode;
172 mCustomClearValue = 0;
173
174 if ( !clearValueText.isEmpty() )
175 {
176 const double v = value();
177 clear();
178 setSpecialValueText( clearValueText );
179 setValue( v );
180 }
181}
182
184{
185 if ( mClearValueMode == MinimumValue )
186 return minimum();
187 else if ( mClearValueMode == MaximumValue )
188 return maximum();
189 else
190 return mCustomClearValue;
191}
192
193void QgsDoubleSpinBox::setLineEditAlignment( Qt::Alignment alignment )
194{
195 mLineEdit->setAlignment( alignment );
196}
197
199{
200 if ( txt.isEmpty() )
201 {
202 QDoubleSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
203 mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
204 }
205 else
206 {
207 QDoubleSpinBox::setSpecialValueText( txt );
208 mLineEdit->setNullValue( txt );
209 }
210}
211
212QString QgsDoubleSpinBox::stripped( const QString &originalText ) const
213{
214 //adapted from QAbstractSpinBoxPrivate::stripped
215 //trims whitespace, prefix and suffix from spin box text
216 QString text = originalText;
217 if ( specialValueText().isEmpty() || text != specialValueText() )
218 {
219 // Strip SPECIAL_TEXT_WHEN_EMPTY
220 if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
221 text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
222 int from = 0;
223 int size = text.size();
224 bool changed = false;
225 if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
226 {
227 from += prefix().size();
228 size -= from;
229 changed = true;
230 }
231 if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
232 {
233 size -= suffix().size();
234 changed = true;
235 }
236 if ( changed )
237 text = text.mid( from, size );
238 }
239
240 text = text.trimmed();
241
242 return text;
243}
244
245double QgsDoubleSpinBox::valueFromText( const QString &text ) const
246{
247 if ( !mExpressionsEnabled )
248 {
249 return QDoubleSpinBox::valueFromText( text );
250 }
251
252 const QString trimmedText = stripped( text );
253 if ( trimmedText.isEmpty() )
254 {
255 return mShowClearButton ? clearValue() : value();
256 }
257
258 return QgsExpression::evaluateToDouble( trimmedText, value() );
259}
260
261QValidator::State QgsDoubleSpinBox::validate( QString &input, int &pos ) const
262{
263 if ( !mExpressionsEnabled )
264 {
265 const QValidator::State r = QDoubleSpinBox::validate( input, pos );
266 return r;
267 }
268
269 return QValidator::Acceptable;
270}
271
272int QgsDoubleSpinBox::frameWidth() const
273{
274 return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
275}
276
277bool QgsDoubleSpinBox::shouldShowClearForValue( const double value ) const
278{
279 if ( !mShowClearButton || !isEnabled() )
280 {
281 return false;
282 }
283 return value != clearValue();
284}
void paintEvent(QPaintEvent *e) override
void wheelEvent(QWheelEvent *event) override
void setLineEditAlignment(Qt::Alignment alignment)
Set alignment in the embedded line edit widget.
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.
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.
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 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.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5821
#define CLEAR_ICON_SIZE