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