1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: ModuleBase_DoubleSpinBox.cxx
4 // Author: Sergey TELKOV
6 #include "ModuleBase_DoubleSpinBox.h"
9 #include <QDoubleValidator>
15 const double PSEUDO_ZERO = 1.e-20;
18 \class ModuleBase_DoubleSpinBox
19 \brief Enhanced version of the Qt's double spin box.
21 The ModuleBase_DoubleSpinBox class represents the widget for entering the
22 floating point values. In addition to the functionality provided by
23 QDoubleSpinBox, this class supports "cleared" state - this is the
24 state corresponding to "None" (or empty) entered value.
26 To set "cleared" state use setCleared() method. To check if the spin
27 box stores "cleared" state, use isCleared() method.
30 if (myDblSpinBox->isCleared()) {
31 ... // process "None" state
34 double value = myDblSpinBox->value();
35 ... // process entered value
39 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
40 for the widget text. To enable this, negative precision should be specified either
41 through a constructor or using setPrecision() method.
43 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
44 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
45 for proper size hint calculation and for rounding minimum and maximum bounds of
52 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
53 a step value of 1.0 and a precision of 2 decimal places.
54 The value is initially set to 0.00.
56 \param theParent parent object
57 \param thePrecision precision of values input
59 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
60 : QDoubleSpinBox(theParent),
62 myIsEmitKeyPressEvent(false)
64 // VSR 01/07/2010: Disable thousands separator for spin box
65 // (to avoid inconsistency of double-2-string and string-2-double conversion)
67 loc.setNumberOptions(loc.numberOptions() |
68 QLocale::OmitGroupSeparator |
69 QLocale::RejectGroupSeparator);
72 // MPV 15/09/2014: this must be set before setDecimals;
73 // otherwise in release mode setDecimals may crash
74 myPrecision = thePrecision;
76 // Use precision equal to default Qt decimals
77 // it's necessary to set decimals before the range setting,
78 // by default Qt rounds boundaries to 2 decimals at setRange
79 setDecimals(qAbs(myPrecision));
81 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
82 SLOT(onTextChanged( const QString& )));
84 myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
90 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
95 \brief Check if spin box is in the "cleared" state.
96 \return \c true if spin box is cleared
99 bool ModuleBase_DoubleSpinBox::isCleared() const
105 \brief Change "cleared" status of the spin box.
106 \param on new "cleared" status
109 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
115 setSpecialValueText(specialValueText());
119 \brief Set precision of the spin box
121 If precision value is less than 0, the 'g' format is used for value output,
122 otherwise 'f' format is used.
124 \param prec new precision value.
127 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
129 int newPrec = qAbs(prec);
130 int oldPrec = qAbs(myPrecision);
132 if (newPrec != oldPrec)
137 \brief Get precision value of the spin box
138 \return current precision value
141 int ModuleBase_DoubleSpinBox::getPrecision() const
147 \brief Interpret text entered by the user as a value.
148 \param text text entered by the user
152 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
155 return text.toDouble();
157 return QDoubleSpinBox::valueFromText(text);
161 \brief This function is used by the spin box whenever it needs to display
164 \param val spin box value
165 \return text representation of the value
168 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
170 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
171 return removeTrailingZeroes(s);
175 \brief Return source string with removed leading and trailing zeros.
176 \param src source string
177 \return resulting string
179 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
181 QString delim(locale().decimalPoint());
183 int idx = src.lastIndexOf(delim);
187 QString iPart = src.left(idx);
188 QString fPart = src.mid(idx + 1);
190 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
192 ePart = fPart.mid(idx1);
193 fPart = fPart.left(idx1);
196 fPart.remove(QRegExp("0+$"));
199 if (!fPart.isEmpty())
200 res += delim + fPart;
206 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent* theEvent)
208 switch (theEvent->key()) {
210 case Qt::Key_Return: {
211 // do not react to the Enter key, the property panel processes it
212 if (!myIsEmitKeyPressEvent)
219 QDoubleSpinBox::keyPressEvent(theEvent);
222 void ModuleBase_DoubleSpinBox::keyReleaseEvent(QKeyEvent* theEvent)
224 switch (theEvent->key()) {
226 case Qt::Key_Return: {
227 // the enter has already been processed when key is pressed,
228 // key release should not be processed in operation manager
229 if (myIsEmitKeyPressEvent) {
231 emit enterReleased();
239 QDoubleSpinBox::keyReleaseEvent(theEvent);
243 \brief Perform \a steps increment/decrement steps.
245 The \a steps value can be any integer number. If it is > 0,
246 the value incrementing is done, otherwise value is decremented
249 \param steps number of increment/decrement steps
251 void ModuleBase_DoubleSpinBox::stepBy(int steps)
255 QDoubleSpinBox::stepBy(steps);
256 double tmpval = value();
257 if (qAbs(tmpval) < PSEUDO_ZERO)
259 if (tmpval < minimum())
261 else if (tmpval > maximum())
267 \brief This function is used to determine whether input is valid.
268 \param str currently entered value
269 \param pos cursor position in the string
270 \return validating operation result
272 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
274 QString pref = this->prefix();
275 QString suff = this->suffix();
276 uint overhead = pref.length() + suff.length();
277 QValidator::State state = QValidator::Invalid;
279 QDoubleValidator v(NULL);
281 // If 'g' format is used (myPrecision < 0), then
282 // myPrecision - 1 digits are allowed after the decimal point.
283 // Otherwise, expect myPrecision digits after the decimal point.
284 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
286 v.setLocale(this->locale());
288 v.setBottom(minimum());
291 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
294 state = v.validate(str, pos);
296 if ((uint)(str.length()) >= overhead &&
297 str.startsWith(pref) &&
298 str.right(suff.length()) == suff) {
299 QString core = str.mid(pref.length(), str.length() - overhead);
300 int corePos = pos - pref.length();
301 state = v.validate(core, corePos);
302 pos = corePos + pref.length();
303 str.replace(pref.length(), str.length() - overhead, core);
305 state = v.validate(str, pos);
306 if (state == QValidator::Invalid) {
307 QString special = this->specialValueText().trimmed();
308 QString candidate = str.trimmed();
309 if (special.startsWith(candidate)) {
310 if (candidate.length() == special.length())
311 state = QValidator::Acceptable;
313 state = QValidator::Intermediate;
319 // Treat values outside (min; max) range as Invalid
320 // This check is enabled by assigning "strict_validity_check" dynamic property
321 // with value "true" to the spin box instance.
322 if (state == QValidator::Intermediate) {
324 double val = str.toDouble(&isOk);
326 QVariant propVal = property("strict_validity_check");
327 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
328 if (val < minimum() || val > maximum())
329 state = QValidator::Invalid;
331 } else if (myPrecision < 0) {
332 // Consider too large negative exponent as Invalid
333 QChar e(locale().exponential());
334 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
336 epos++; // Skip exponential symbol itself
337 QString exponent = str.right(str.length() - epos);
338 int expValue = exponent.toInt(&isOk);
339 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
340 state = QValidator::Invalid;
349 \brief Called when user enters the text in the spin box.
351 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
356 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
358 bool aPreviousValue = myIsEmitKeyPressEvent;
359 myIsEmitKeyPressEvent = theEnable;
361 return aPreviousValue;
364 void ModuleBase_DoubleSpinBox::setValueEnabled(const bool& theEnable)
366 setReadOnly(!theEnable);
368 QPalette aPal = palette();
369 aPal.setColor(QPalette::All, QPalette::Base,
370 theEnable? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));