Set display precision different from internal precision in QDoubleSpinBox
How can I set the display precision separate from the internal precision of the value in a QDoubleSpinBox? If I use setDecimals, it changes the internal precision such that setDecimals(4) changes value from 1.12345 to 1.1235. This ruins the numerical stability of some code I have.
I also don’t want to have 1.12345 displayed in the spinbox, but rather something like 1.123.
So, the client for whom I’m doing this work wants the display to real 10 when the value is 10.00001. What happens now is the SpinBox is displaying 10.00001. If I call setDecimals with some value, say 3, then the underlying value is rounded to three significant digits. So, setDecimals changes the precision of the underlying value, ruining the numerical stability of the algorithm, and does not just affect the display precision.
What I need is a way to change only the display precision and keep the underlying value unchanged.
I as a user would drive nuts, if the displayed value of “10” is actually “translated” to a value of “10.0000001” or “10” for the application, depending on the source of the “10” (rounded set-value in the first case, user typed-in in the latter).
I consider this bad user experience and programming style…
An no, it is not just a case of printf. The spinbox is an input element, not just a fancy display. Naturally, the entered value is exactly represented internally.
Actually, I would not be too sure that when using a Double, 10 isn’t just 10.000000001 anyway. I would display the precision you are going to use the number with. That is, if the 10th decimal is relevant for your algorithm, make sure you display it.
What I would do (and should do for my own purposes as well, thanks for reminding me), is make a QDoubleSpinbox that has an extra button or dropdown or something that you use to control the precision. That makes it easy to use the spinbox with the set precision, but also possible to change to a higher precision if needed.
You can set the decimals property to a large value to prevent it from rounding and then use textFromValue(double) to display the number in your own way. I use it to create a spin box which displays using the ‘g’ format. Since the sizeHint is based upon the decimals property, it must also be adjusted.
Here is the idea. (The code below is incomplete.)
- DoubleSpinBox::DoubleSpinBox(QWidget *parent)
- // Save default sizeHint before changing decimal property
- cachedSizeHint = QDoubleSpinBox::sizeHint();
- // Set decimals to large value since QDoubleSpinBox rounds
- // with QString::number(value, 'f', decimals).toDouble()
- QString DoubleSpinBox::textFromValue(double value) const
- return QString::number(value, 'g', std::numeric_limits<double>::digits10);
- QSize DoubleSpinBox::sizeHint() const
- return cachedSizeHint;
Volker, I understand where you are coming from. The application has three spinboxes, say a, b , and c. The user can input parameters into any one of the three. If the user inputs a parameter into spinbox a, some calculations are done and the values in spinboxes b and c are are updated. What is happening is that the calculations are off if I use setDecimal and set its integer value to something reasonable for display. This is why I’m asking if there is something like printf.
From what I think I understand is going on, it sounds like the private member value is an unadulturated double. The member function value() returns value rounded to the precision set with setDecimal. This may be where the problem is. If this is the case, without subclassing QDoubleSpinBox, is there a way to access value in its unadulturated form?
Bradley, I saw your reply just after I sent mine in. Thanks for the code. I’m thinking that subclassing might be the only way to go.
So, just to be sure, setValue() is called when setDecimals() has been called and value() is called to display the value in the SpinBox? I.e. setDecimal(4) leads to something like setValue( round( value() , 4 ) ) in order to display the value in the spinbox?
Below is the source for setDecimals() and setValue(). Yes, calling setDecimals() calls setValue(value()) and in setValue(), round(value) is called which uses the decimals property to round the number.
- void QDoubleSpinBox::setDecimals(int decimals)
- d->decimals = qBound(0, decimals, DBL_MAX_10_EXP + DBL_DIG);
- setRange(d->actualMin, d->actualMax); // make sure values are rounded
- void QDoubleSpinBox::setValue(double value)
- QVariant v(d->round(value));
- d->setValue(v, EmitIfChanged);
Wow! That’s crazy! Why would you do that?
To me, I was expecting setDecimals() to behave much like the iostream modifiers, and not to change the underlying value. This should be documented somewhere as I doubt it is the expected behavior from reading the documentation.
Looks like I’ll have to subclass QDoubleSpinBox…
Note: QDoubleSpinBox will round numbers so they can be displayed with the current precision. In a QDoubleSpinBox with decimals set to 2, calling setValue(2.555) will cause value() to return 2.56.