It appears that Qt by default sets the precision of the entered number to 2 decimals places. If decimals = 2 and a user inputs a number such as 4, it will display 4.00 to the user. Is it possible for it to JUST display 4 but with the option for the user to edit it up to 2 decimal places and display the updated value accordingly?
The first sample code I wrote was intended to "come in touch" with the problem:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QDoubleSpinBox qEdit;
qEdit.show();
// install signal handler
QObject::connect(&qEdit,
(void (QDoubleSpinBox::*)(double))&QDoubleSpinBox::valueChanged,
[&](double value){ qDebug() << "qEdit.value:" << qEdit.value(); });
// run application
return app.exec();
}
What I hadn't expected (though it's reasonable): You cannot input more decimal digits than configured.
The alternative behavior would've been: You can input as many decimal digits as you want. The input is parsed and considered completely (so that internally the value is stored with full precision). The output is limited to the configured digits but as long as you don't edit it again the hidden decimals are still active.
The behavior of the QDoubleSpinBox
may be changed by making a derived class DoubleSpinBox
and overloading the respective methods.
One of these methods isvirtual QValidator::State validate(QString &text, int &pos) const override
.
As the Qt. doc. didn't help much to accomplish this I had a look into the sourcecode of class QDoubleSpinBox
on woboq.org:
QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const
{
Q_D(const QDoubleSpinBox);
QValidator::State state;
d->validateAndInterpret(text, pos, state);
return state;
}
So, the actual work is done by QDoubleSpinBoxPrivate::validateAndInterpret()
which has (at the time of this writing) 148 lines of code - heavy stuff.
To provide a proof of concept, I decided to make this a little bit shorter and less perfect:
QValidator::State DoubleSpinBox::validate(QString &text, int&) const
{
bool ok = false;
locale().toDouble(text, &ok);
return ok ? QValidator::Acceptable : QValidator::Invalid;
}
Compiling and testing again showed that values are accepted now. Now, it is possible to input e.g. 4.1234
. While typing this, the output was:
4
4.1
4.12
0
Hence, the values are accepted but not yet stored internally. To achieve this, the methodvirtual double valueFromText(const QString &text) const
has to be overloaded, also:
double DoubleSpinBox::valueFromText(const QString &text) const
{
bool ok = false;
double value = locale().toDouble(text, &ok);
return ok ? value : QDoubleSpinBox::value();
}
Compiling and testing again showed that input of 4.1234
is now accepted and stored. The output was:
4
4.1
4.12
4.123
4.1234
When I pressed the up-arrow button, the display changed to 5.12
. However, the internal value still had full precision as visible in output:
5.1234
The complete sample code testQDoubleSpinBox.cc
:
#include <QtWidgets>
// a class for a customized QDoubleSpinBox
class DoubleSpinBox: public QDoubleSpinBox {
public:
// constructor.
explicit DoubleSpinBox(QWidget *pQParent = nullptr):
QDoubleSpinBox(pQParent)
{ }
// destructor.
virtual ~DoubleSpinBox() = default;
// disabled:
DoubleSpinBox(const DoubleSpinBox&) = delete;
DoubleSpinBox& operator=(const DoubleSpinBox&) = delete;
public:
/* converts value from text.
*
* Overloaded to process accepted input correctly.
*/
virtual double valueFromText(const QString &text) const override;
protected:
/* determines whether input is valid.
*
* Overloaded to change accepted input.
*/
virtual QValidator::State validate(QString &text, int &pos) const override;
};
QValidator::State DoubleSpinBox::validate(QString &text, int&) const
{
bool ok = false;
locale().toDouble(text, &ok);
return ok ? QValidator::Acceptable : QValidator::Invalid;
}
double DoubleSpinBox::valueFromText(const QString &text) const
{
bool ok = false;
double value = locale().toDouble(text, &ok);
return ok ? value : QDoubleSpinBox::value();
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
DoubleSpinBox qEdit;
qEdit.show();
// install signal handlers
QObject::connect(&qEdit,
(void (QDoubleSpinBox::*)(double))&QDoubleSpinBox::valueChanged,
[&](double){ qDebug() << "qEdit.value:" << qEdit.value(); });
// run application
return app.exec();
}
The Qt project file testQDoubleSpinBox.pro
:
SOURCES = testQDoubleSpinBox.cc
QT += widgets
Sample session in cygwin on Windows 10:
$ qmake-qt5 testQDoubleSpinBox.pro
$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQDoubleSpinBox.o testQDoubleSpinBox.cc
g++ -o testQDoubleSpinBox.exe testQDoubleSpinBox.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread
$ ./testQDoubleSpinBox
Qt Version: 5.9.2
qEdit.value: 4
qEdit.value: 4.1
qEdit.value: 4.12
qEdit.value: 4.123
qEdit.value: 4.1234
qEdit.value: 5.1234
Yes you can. You need to reimplement QDoubleSpinBox
though.
By creating a custom class that extends the spinbox, you can override QDoubleSpinBox::textFromValue
. This method as the name suggests is used to convert the double to a string representation.
Possible implementation:
QString MyDoubleSpinBox::textFromValue(double value) const
{
return QLocale().toString(value, 'g', QLocale::FloatingPointShortest);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With