PDA

View Full Version : qdoublespinbox with scientific notation



pospiech
30th November 2008, 17:19
I have seen that there is a
QDoubleValidator::ScientificNotation
is it possible to set the validator of qdoublespinbox to such a Validator?

How would that work?

jpn
30th November 2008, 19:53
Perhaps you could construct your own QDoubleValidator and set it via QAbstractSpinBox::lineEdit() [protected].

pospiech
30th November 2008, 20:13
Yes, but in the end it means to reimplement qdoublespinbox from scratch. I also looked at the possibility to derive from qdoublespinbox, but that is not possible especially because the setvalue is not virtual (rounding is then hardcoded). I described that problem in detail on the qt-interest mailinglist. This in the end still seems like I have to reinvent the wheel.

jpn
1st December 2008, 08:48
Yes, but in the end it means to reimplement qdoublespinbox from scratch.
From scratch? All I meant was to change the validator of the QLineEdit that is inside the spin box.


I also looked at the possibility to derive from qdoublespinbox, but that is not possible especially because the setvalue is not virtual (rounding is then hardcoded). I described that problem in detail on the qt-interest mailinglist. This in the end still seems like I have to reinvent the wheel.
Did you read the detailed description? It clearly says that:


The spin box supports double values but can be extended to use different strings with validate(), textFromValue() and valueFromText().

pospiech
1st December 2008, 08:57
From scratch? All I meant was to change the validator of the QLineEdit that is inside the spin box.
Ok, I misread that. However it still wont work, see below



Did you read the detailed description? It clearly says that:
I know that these are virtual. But the round function inside setValue (both not virtual) are not changeable.


double QDoubleSpinBoxPrivate ::round(double value) const
{
Q_Q(const QDoubleSpinBox);
const QString strDbl = q->locale().toString(value, 'f', decimals);
return q->locale().toDouble(strDbl);
}

It is used in setValue:


void QDoubleSpinBox::setValue(double value)
{
Q_D(QDoubleSpinBox);
QVariant v(d->round(value)); // round according to decimals
d->setValue(v, EmitIfChanged);
}

this unfortunately would round up the digits, even if the value would be 1e-20, which would become 0 at decimals = 4. Unaccaptable for my intention. I instead want 1.02349e-20 to become 1.0235e-20 at the round function.

So that is where is my problem. If this could be solved, and I can set the Validator as you suggested, I could do it without reimplementing everything.

jpn
1st December 2008, 09:33
this unfortunately would round up the digits, even if the value would be 1e-20, which would become 0 at decimals = 4. Unaccaptable for my intention. I instead want 1.02349e-20 to become 1.0235e-20 at the round function.
Have you tried changing QDoubleSpinBox::decimals?

pospiech
1st December 2008, 11:20
Have you tried changing QDoubleSpinBox::decimals?
Changing decimals is not a solution. As long as the rounding is using 'f' (see http://doc.trolltech.com/4.4/qstring.html#arg-20) for QString, which means that exponentials are mapped to non exponentialst the decimal value becomes useless anyway.

wysota
1st December 2008, 12:00
Maybe you can avoid rounded() being called at all?

pospiech
1st December 2008, 12:44
Maybe you can avoid rounded() being called at all?

That becomes a principle question on C++ inheritance: If I have a function in the new class with the same Name and Properties as in the base class, will that work?
Also since round is not virtual, it means that I have to reimplement every function that uses round() and every function that calls a function using round etc.

That would be at least theses



double QDoubleSpinBoxPrivate::round(double value) const
{
Q_Q(const QDoubleSpinBox);
const QString strDbl = q->locale().toString(value, 'f', decimals);
return q->locale().toDouble(strDbl);
}

void QDoubleSpinBox::setValue(double value)
{
Q_D(QDoubleSpinBox);
QVariant v(d->round(value));
d->setValue(v, EmitIfChanged);
}

void QDoubleSpinBox::setDecimals(int decimals)
{
Q_D(QDoubleSpinBox);
d->decimals = qMax(0, decimals);

setRange(minimum(), maximum()); // make sure values are rounded
setValue(value());
}

void QDoubleSpinBox::setMinimum(double minimum)
{
Q_D(QDoubleSpinBox);
const QVariant m(d->round(minimum));
d->setRange(m, (d->variantCompare(d->maximum, m) > 0 ? d->maximum : m));
}

void QDoubleSpinBox::setMaximum(double maximum)
{
Q_D(QDoubleSpinBox);
const QVariant m(d->round(maximum));
d->setRange((d->variantCompare(d->minimum, m) < 0 ? d->minimum : m), m);
}

void QDoubleSpinBox::setRange(double minimum, double maximum)
{
Q_D(QDoubleSpinBox);
d->setRange(QVariant(d->round(minimum)), QVariant(d->round(maximum)));
}


So in the end it may work, but is a lot more than I initially wanted to change...
Or am I thinking the wrong way?

pospiech
1st December 2008, 23:02
I tried to implement setValue in the derived class, but that brings me to the next problem:

The original Code is this


void QDoubleSpinBox::setValue(double value)
{
Q_D(QDoubleSpinBox);
QVariant v(d->round(value));
d->setValue(v, EmitIfChanged);
}


I changed it to


class QScienceSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
QScienceSpinBox(QWidget* parent = 0);
virtual ~QScienceSpinBox();

double round(double value) const;

void setValue(double value);
...
};

void QScienceSpinBox::setValue(double value)
{
QVariant v(this->round(value));
QAbstractSpinBoxPrivate::setValue(v, EmitIfChanged);
}


the setValue calls a setValue from deep inside the derived class. However I cannot call QAbstractSpinBoxPrivate, since it is not known within "<QtGui/QDoubleSpinBox>". All the other functions also call their equivalents from deep inside the base class, which all work with QVariants.

If I start replacing all this calls, no data ever gets inside the members of QDoubleSpinBox and I actually rewrite QDoubleSpinBox.

So how should I proceed?

wysota
1st December 2008, 23:19
I think this is a wrong approach. Instead of fighting with the decimals, you should aim at converting the visual output of the spinbox to scientific notation and vice versa. Maybe you can leave the internal representation as it is now.

pospiech
2nd December 2008, 08:59
I think this is a wrong approach. Instead of fighting with the decimals, you should aim at converting the visual output of the spinbox to scientific notation and vice versa. Maybe you can leave the internal representation as it is now.

I have also thougth of that, but it is not possible. If I set the internal decimals to lets say 1000, then at the round function it will nevertheless be rounded to the maximum of decimals of a double. So at 1e-34 which is very likely more digits than a double without exponential can represent if will be rounded to 0. (Not tested, just my interpretation of the code)

Matthias

wysota
2nd December 2008, 10:57
Even if you use the scientific notation internally this is represented as a double so the precision will be limited nevertheless. So here it doesn't matter.

pospiech
3rd January 2009, 15:50
I have now a working solution. However the code amount is about 600 LOC.

This widget is derived from QDoubleSpinBox. It uses a decimal value of 1000 (that is more decimal points than a double can handle) and implements a new decimal value for the presentation in scientific notation. The Validator is realised by setting the LineEdit to a QDoubleValidator::ScientificNotation. However the most important part is the reimplementation of textFromValue and valueFromText. This unfortunately requires to copy the whole validation code of QDoubleSpinBox, which can not be borrowed and represents the major part of the code.

Since I can not paste the whole code I put it online at:
http://www.matthiaspospiech.de/blog/2009/01/03/qt-spinbox-widget-with-scientific-notation/

Can this code be shrinked by keeping the functionality?