2

I am building a C++ GUI application on QT Creator. I changed the location to Portuguese/Brazil, now only comma is the decimal separator.

I need the QDoubleSpinBox to get as decimal separator dot and comma. Officialy comma is the separator in Portuguese, but some keyboard only have points in the numeric part.

Please help,

5 Answers5

3

subClass QDoubleSpinBox and reimplement the virtual method validate

full solution here :

customSpinBox.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QRegExpValidator>
#include <QDoubleSpinBox>



class CustomSpinBox : public QDoubleSpinBox {
    Q_OBJECT

public:
    explicit CustomSpinBox(QWidget* parent =0);
    virtual QValidator::State validate(QString & text, int & pos) const;

private:
    QRegExpValidator* validator;

};
#endif // WIDGET_H

customSpinBox.cpp

CustomSpinBox::CustomSpinBox(QWidget *parent):QDoubleSpinBox(parent),
  validator(new QRegExpValidator(this))
{
    validator->setRegExp(QRegExp("\\d{1,}(?:[,.]{1})\\d*"));
}

QValidator::State CustomSpinBox::validate(QString &text, int &pos) const
{
    return validator->validate(text,pos);
}
Venom
  • 1,010
  • 10
  • 20
2

I tried to converted the solution from basslo to Qt 6.0 but it failed to accept integer numbers. The following solution works fine for me with Qt 6.0. Also it did not work, wehen a suffix is set for the spin with i.e spinBox->setSuffix("mm"); My current solution below:

class CRelaxedDoubleSpinBox : public QDoubleSpinBox {
    Q_OBJECT
    bool doConvert = false;
    QString decimalStr; 

public:
    explicit CRelaxedDoubleSpinBox(QWidget* parent =0, double minVal = -100, double maxVal = 100.) : QDoubleSpinBox(parent)
    {
        // setStepType(QAbstractSpinBox::AdaptiveDecimalStepType);
        setMinimum(minVal);
        setMaximum(maxVal);
        setAlignment(Qt::AlignRight);

        QLocale curLocale;
        decimalStr = curLocale.decimalPoint();
        doConvert = curLocale.decimalPoint() != ".";
    }

    virtual QValidator::State validate(QString & text, int & pos) const
    {
        QString s(text);
        if(doConvert)
            s = s.replace(".", decimalStr);
        return QDoubleSpinBox::validate(s,pos);
    }

    double valueFromText(const QString& text) const
    {
        QString s(text);
        if(doConvert)
            s = s.replace(".", decimalStr);
        return QDoubleSpinBox::valueFromText(s);
    }

};
RED SOFT ADAIR
  • 12,032
  • 10
  • 54
  • 92
0

You can subclass QDoubleSpinBox and reimplement the validate method to accept both the dot and the comma as the decimal separator. I think you can get by with just adding a special check when the input is a period and allow it to be accepted (provided there are no other periods or commas in the string), but otherwise call the base class implementation. I haven't compiled or tested this, but I think this is pretty close:

MyDoubleSpinBox::validate (QString &input, int &pos)
{
    if (input == ".")
        {
        return (text ().contains (".") || text ().contains (",")) ? QValidator::Invalid : QValidator::Acceptable;
        }

     return QDoubleSpinBox::validate (input, pos);
}
goug
  • 2,294
  • 1
  • 11
  • 15
  • Your solution doesn't work! First because your ternary is wrong (you should switch invalid and acceptable). Second your input is the whole text, so the only time you enter inside your if, is when you input a point at the beginning of the word inside the spinbox. I Was going to suggest the same solution differently but i waited because i prefer to test it first. – Venom Mar 01 '17 at 23:13
  • You're certainly welcome to provide a corrected answer. My apologies for not being perfectly accurate on an answer where I stated that I hadn't compiled or tested this, but that I thought it was pretty close. – goug Mar 02 '17 at 01:49
0

I'm using PySide6 and I adapted to the Python language the wonderful solution provided by @RED SOFT ADAIR to Python:

from PySide6.QtWidgets import QDoubleSpinBox,

class CustomDoubleSpinbox(QDoubleSpinBox):
    def validate(self, text: str, pos: int) -> object:
        text = text.replace(".", ",")
        return QDoubleSpinBox.validate(self, text, pos)

    def valueFromText(self, text: str) -> float:
        text = text.replace(",", ".")
        return float(text)

Here it is in case anyone ended up here in a similar situation.

Kastakin
  • 121
  • 1
  • 9
0

Solution given by basslo does not work for me. I also need to reimplement the valueFromText method. Otherwise, when I press the enter key in the DoubleSpinBox, the value is set to 0.

Here is the class

doublespinboxwithcommasandpoints.h

#ifndef DOUBLESPINBOWITHCOMMASANDPOINTS_H
#define DOUBLESPINBOWITHCOMMASANDPOINTS_H

#include <QDoubleSpinBox>
#include <QRegularExpressionValidator>

class DoubleSpinBoxWithCommasAndPoints : public QDoubleSpinBox
{
    Q_OBJECT

public:
    explicit DoubleSpinBoxWithCommasAndPoints(QWidget* parent = nullptr);
    virtual ~DoubleSpinBoxWithCommasAndPoints();

    QValidator::State validate(QString & text, int & pos) const override;

    qreal valueFromText(const QString &text) const override;

    void showEvent(QShowEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;

private:
    QRegularExpressionValidator* m_validator;

    QString current_suffix;

};

#endif // DOUBLESPINBOWITHCOMMASANDPOINTS_H

doublespinboxwithcommasandpoints.cpp

#include "doublespinboxwithcommasandpoints.h"

DoubleSpinBoxWithCommasAndPoints::DoubleSpinBoxWithCommasAndPoints(QWidget *parent)
    : QDoubleSpinBox(parent)
    , m_validator(nullptr)
    , m_current_suffix(QString())
{
    // validate positive or negative number written with "." or ","
    // and that may have a suffix at the end
    // also matches empty string "(?![\\s\\S]\r\n)" to allow user to clear the whole field
    const QString regex = QStringLiteral("((?![\\s\\S]\r\n)|-?\\d{1,}(?:[,.]{1})?\\d*\\s*)");
    m_validator = new QRegularExpressionValidator(QRegularExpression(regex), this);

    // check if a suffix is present
    // connect is triggered when setSuffix() is called
    connect(this, &DoubleSpinBoxWithCommasAndPoints::textChanged,
            this, [this](){
                if(suffix().isEmpty())
                    return;

                if(m_current_suffix.localeAwareCompare(suffix()) == 0)
                    return;

                m_current_suffix = suffix();


                QString previous_regex = m_validator->regularExpression().pattern();
                // remove the ending ")"
                previous_regex.chop(1);

                QString new_regex = previous_regex +
                                    QStringLiteral("(?:") + m_current_suffix + QStringLiteral(")?)");
                m_validator->setRegularExpression(QRegularExpression(new_regex));
            });
}

DoubleSpinBoxWithCommasAndPoints::~DoubleSpinBoxWithCommasAndPoints()
{
    delete m_validator;
}

QValidator::State DoubleSpinBoxWithCommasAndPoints::validate(QString &text, int &pos) const
{
    return m_validator->validate(text, pos);
}


qreal DoubleSpinBoxWithCommasAndPoints::valueFromText(const QString &text) const
{
    QString temp = text;
    temp.replace(QStringLiteral(","), QStringLiteral(".")); // replace comma with dot before toDouble()
    temp.remove(suffix()); // remove "°" at the end of the value

    return temp.toDouble();
}

void DoubleSpinBoxWithCommasAndPoints::showEvent(QShowEvent *event)
{
    // need to call manually textChanged(const QString &text)
    // otherwise the new regex may not be taken into account directly
    Q_EMIT textChanged(text());
    QDoubleSpinBox::showEvent(event);
}

void DoubleSpinBoxWithCommasAndPoints::wheelEvent(QWheelEvent *event)
{
    // Prevent value from changing when user scrolls
    event->ignore();
}

Few points:

  • this code works with Qt6
  • the regular expression accepts negative numbers as well
  • the regular expression accepts numbers without comma/point
  • the regular expression supports suffix (code can be adapted to also support prefix)
  • the regular expression matchs empty string which allows users to clear the whole field
  • the mouse wheel has been disabled to avoid user to change value when scrolling (comment wheelEvent(QWheelEvent *event) if you want to keep it).
Pamputt
  • 173
  • 1
  • 11