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