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() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
70 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
71 myPrecision = thePrecision;
73 // Use precision equal to default Qt decimals
74 // it's necessary to set decimals before the range setting,
75 // by default Qt rounds boundaries to 2 decimals at setRange
76 setDecimals(qAbs(myPrecision));
78 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
79 SLOT(onTextChanged( const QString& )));
81 myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
87 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
92 \brief Check if spin box is in the "cleared" state.
93 \return \c true if spin box is cleared
96 bool ModuleBase_DoubleSpinBox::isCleared() const
102 \brief Change "cleared" status of the spin box.
103 \param on new "cleared" status
106 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
112 setSpecialValueText(specialValueText());
116 \brief Set precision of the spin box
118 If precision value is less than 0, the 'g' format is used for value output,
119 otherwise 'f' format is used.
121 \param prec new precision value.
124 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
126 int newPrec = qAbs(prec);
127 int oldPrec = qAbs(myPrecision);
129 if (newPrec != oldPrec)
134 \brief Get precision value of the spin box
135 \return current precision value
138 int ModuleBase_DoubleSpinBox::getPrecision() const
144 \brief Interpret text entered by the user as a value.
145 \param text text entered by the user
149 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
152 return text.toDouble();
154 return QDoubleSpinBox::valueFromText(text);
158 \brief This function is used by the spin box whenever it needs to display
161 \param val spin box value
162 \return text representation of the value
165 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
167 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
168 return removeTrailingZeroes(s);
172 \brief Return source string with removed leading and trailing zeros.
173 \param src source string
174 \return resulting string
176 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
178 QString delim(locale().decimalPoint());
180 int idx = src.lastIndexOf(delim);
184 QString iPart = src.left(idx);
185 QString fPart = src.mid(idx + 1);
187 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
189 ePart = fPart.mid(idx1);
190 fPart = fPart.left(idx1);
193 fPart.remove(QRegExp("0+$"));
196 if (!fPart.isEmpty())
197 res += delim + fPart;
203 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent* theEvent)
205 switch (theEvent->key()) {
207 case Qt::Key_Return: {
208 // do not react to the Enter key, the property panel processes it
209 if (!myIsEmitKeyPressEvent)
216 QDoubleSpinBox::keyPressEvent(theEvent);
219 void ModuleBase_DoubleSpinBox::keyReleaseEvent(QKeyEvent* theEvent)
221 switch (theEvent->key()) {
223 case Qt::Key_Return: {
224 // the enter has already been processed when key is pressed,
225 // key release should not be processed in operation manager
226 if (myIsEmitKeyPressEvent) {
228 emit enterReleased();
236 QDoubleSpinBox::keyReleaseEvent(theEvent);
240 \brief Perform \a steps increment/decrement steps.
242 The \a steps value can be any integer number. If it is > 0,
243 the value incrementing is done, otherwise value is decremented
246 \param steps number of increment/decrement steps
248 void ModuleBase_DoubleSpinBox::stepBy(int steps)
252 QDoubleSpinBox::stepBy(steps);
253 double tmpval = value();
254 if (qAbs(tmpval) < PSEUDO_ZERO)
256 if (tmpval < minimum())
258 else if (tmpval > maximum())
264 \brief This function is used to determine whether input is valid.
265 \param str currently entered value
266 \param pos cursor position in the string
267 \return validating operation result
269 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
271 QString pref = this->prefix();
272 QString suff = this->suffix();
273 uint overhead = pref.length() + suff.length();
274 QValidator::State state = QValidator::Invalid;
276 QDoubleValidator v(NULL);
278 // If 'g' format is used (myPrecision < 0), then
279 // myPrecision - 1 digits are allowed after the decimal point.
280 // Otherwise, expect myPrecision digits after the decimal point.
281 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
283 v.setLocale(this->locale());
285 v.setBottom(minimum());
288 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
291 state = v.validate(str, pos);
293 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
294 QString core = str.mid(pref.length(), str.length() - overhead);
295 int corePos = pos - pref.length();
296 state = v.validate(core, corePos);
297 pos = corePos + pref.length();
298 str.replace(pref.length(), str.length() - overhead, core);
300 state = v.validate(str, pos);
301 if (state == QValidator::Invalid) {
302 QString special = this->specialValueText().trimmed();
303 QString candidate = str.trimmed();
304 if (special.startsWith(candidate)) {
305 if (candidate.length() == special.length())
306 state = QValidator::Acceptable;
308 state = QValidator::Intermediate;
314 // Treat values outside (min; max) range as Invalid
315 // This check is enabled by assigning "strict_validity_check" dynamic property
316 // with value "true" to the spin box instance.
317 if (state == QValidator::Intermediate) {
319 double val = str.toDouble(&isOk);
321 QVariant propVal = property("strict_validity_check");
322 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
323 if (val < minimum() || val > maximum())
324 state = QValidator::Invalid;
326 } else if (myPrecision < 0) {
327 // Consider too large negative exponent as Invalid
328 QChar e(locale().exponential());
329 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
331 epos++; // Skip exponential symbol itself
332 QString exponent = str.right(str.length() - epos);
333 int expValue = exponent.toInt(&isOk);
334 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
335 state = QValidator::Invalid;
344 \brief Called when user enters the text in the spin box.
346 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
351 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
353 bool aPreviousValue = myIsEmitKeyPressEvent;
354 myIsEmitKeyPressEvent = theEnable;
356 return aPreviousValue;
359 void ModuleBase_DoubleSpinBox::setValueEnabled(const bool& theEnable)
361 setReadOnly(!theEnable);
363 QPalette aPal = palette();
364 aPal.setColor(QPalette::All, QPalette::Base,
365 theEnable? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));