I would like to use QRegExpValidator in order to force user to:
- type in values only from a certain range (double type),
- the double type should be typed in using dot not coma
- no other formats are allowed
So far I have:
QRegExpValidator* rxv = new QRegExpValidator(QRegExp("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"), this);
This validator forces user to type in only double values with dot. I Dont know however, how to prevent user from typing in values out of range (for example range would be from 0 to 100.0). I Would aprichiate all help.
You can write your own validator, derived from QValidator. All you need is to implement virtual State validate(QString &input, int &pos) const = 0.
UPDATE
Example:
MyValidator.h:
class MyValidator : public QValidator
{
Q_OBJECT
public:
MyValidator(double min, double max, QObject *parent = 0);
State validate(QString &input, int &pos) const;
private:
QRegExp mRexp;
double mMin;
double mMax;
};
MyValidator.cpp:
MyValidator::MyValidator(double min, double max, QObject *parent) :
QValidator(parent)
, mRexp("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?")
, mMin(min)
, mMax(max)
{}
QValidator::State MyValidator::validate(QString &input, int &pos) const
{
if (input.isEmpty())
return Acceptable;
if (!mRexp.exactMatch(input))
return Invalid;
const double val = input.toDouble();
if (mMin <= val && val <= mMax)
return Acceptable;
return Intermediate;
}
Related
Is there a property in Q(Double)SpinBox that allows the user to override digits that are right of the curser, just by typing?
For example: If the spinbox shows 12.52 I click between 1 and 2. Now I type 3.45 to get 13.45
Just checked it out - even activating Insert mode on keyboard doesn't help.
Apparently, you'll have to subclass QSPinBox and override some QAbstractSpinBox functions - at least, keyPressEvent.
QAbstractSpinBox has a pointer to underlying QLineEdit - which has a cursor position property.
There is no property, you can go with your own implementation like this:
Header:
#include <QObject>
#include <QDoubleSpinBox>
class CustomSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit CustomSpinBox(QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent *event) override;
private:
void updateTextField(const QString &text);
};
Source:
#include "CustomSpinBox.h"
#include <QKeyEvent>
#include <QLineEdit>
CustomSpinBox::CustomSpinBox(QWidget *parent)
: QDoubleSpinBox(parent)
{}
void CustomSpinBox::keyPressEvent(QKeyEvent *event)
{
auto isInt = false;
event->text().toInt(&isInt);
if (isInt)
{
updateTextField(event->text());
}
else
{
QDoubleSpinBox::keyPressEvent(event);
}
}
void CustomSpinBox::updateTextField(const QString &text)
{
auto lineEdit = this->lineEdit();
auto cursorPosition = lineEdit->cursorPosition();
auto lineEditText = lineEdit->text();
QChar currentChar;
if (cursorPosition < lineEditText.size())
{
currentChar = lineEditText.at(cursorPosition);
if (currentChar.isPunct())
{
lineEdit->cursorForward(false);
}
}
lineEdit->del();
lineEdit->insert(text);
}
But please note that the behaviour can be counter-intuitive. What if the value is 1,23? With that approach, you cannot enter any value bigger than 9,(9). Maybe it is what is expected, maybe it should not jump over the dot/comma unless the value before the punctuation mark has size length bigger than 2 digits (or 3 or any other number).
QDoubleSpinBox shows the number using decimal point by default
and I'm trying to format it to scientific notation
My solution was to subclass QDoubleSpinBox and redefine methods validate, valueFromText and textFromValue.
class SciNotDoubleSpinbox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SciNotDoubleSpinbox(QWidget *parent = 0) : QDoubleSpinBox(parent) {}
// Change the way we read the user input
double valueFromText(const QString & text) const
{
double numFromStr = text.toDouble();
return numFromStr;
}
// Change the way we show the internal number
QString textFromValue(double value) const
{
return QString::number(value, 'E', 6);
}
// Change the way we validate user input (if validate => valueFromText)
QValidator::State validate(QString &text, int&) const
{
// Try to convert the string to double
bool ok;
text.toDouble(&ok);
// See if it's a valid Double
QValidator::State validationState;
if(ok)
{
// If string conversion was valid, set as ascceptable
validationState = QValidator::Acceptable;
}
else
{
// If string conversion was invalid, set as invalid
validationState = QValidator::Invalid;
}
return validationState;
}
};
It works, but validate seems sloppy. It tries to convert the number to double using Qt's toDouble, and returns valid or invalid state depending if it was a successful conversion or not. It doesn't even use the position.
Is there a way to validate a string in scientific notation in a "cleaner" way?
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,
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);
}
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);
}
};
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);
}
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.
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;
private:
QRegularExpressionValidator* m_validator;
QString current_suffix;
};
#endif // DOUBLESPINBOWITHCOMMASANDPOINTS_H
doublespinboxwithcommasandpoints.cpp
#include "doublespinboxwithcommasandpoints.h"
DoubleSpinBoxWithCommasAndPoints::DoubleSpinBoxWithCommasAndPoints(QWidget *parent)
: QDoubleSpinBox(parent)
, m_validator(nullptr)
, current_suffix(QString())
{
// validate positive or negative number written with "." or ","
// and that may have a suffix at the end
// also match 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*)");
m_validator = new QRegularExpressionValidator(QRegularExpression(regex), this);
// check if a suffix has been added
connect(this, &DoubleSpinBoxWithCommasAndPoints::textChanged,
[this](){
if(!suffix().isEmpty()) {
if(current_suffix.localeAwareCompare(suffix()) == 0)
return;
else
current_suffix = suffix();
}
QString previous_regex = m_validator->regularExpression().pattern();
// remove the ending ")"
previous_regex.chop(1);
QString new_regex = previous_regex +
QStringLiteral("(?:") + suffix() + QStringLiteral(")?)");
m_validator->setRegularExpression(QRegularExpression(new_regex));
});
}
DoubleSpinBoxWithCommasAndPoints::~DoubleSpinBoxWithCommasAndPoints()
{
}
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 suffix at the end of the value
return temp.toDouble();
}
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
I need to validate that the text entered in a qlineedit has the form of the regexp, I tried to use this:
void MainWindow::checkReg( QLineEdit& mail, const QCheckBox& skip, string type )
{
if(type == "mail")
{
if( skip.checkState() != 2)
{
QRegExp mailREX("\\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
mailREX.setCaseSensitivity(Qt::CaseInsensitive);
mailREX.setPatternSyntax(QRegExp::Wildcard);
bool regMat = mailREX.exactMatch(mail.text());
if(regMat == false)
{
QMessageBox *message = new QMessageBox(this);
message->setWindowModality(Qt::NonModal);
message->setText("Inserte los datos en el formato correcto");
message->setStandardButtons(QMessageBox::Ok);
message->setWindowTitle("MainWindow");
message->setIcon(QMessageBox::Information);
message->exec();
this->ok = 0;
mail.clear();
}
else
this->ok = 1;
}
}
}
but every mail mail I entered like me#me.com, the error message appear. I also tried using
int regMat = mailREX.indexIn(mail.text());
and it didnt work.
Thanks in advance
Why did you set pattern syntax to wildcard? Your code works (assuming you understand, that your regexp itself is simplified) with RegExp pattern syntax:
QRegExp mailREX("\\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
mailREX.setCaseSensitivity(Qt::CaseInsensitive);
mailREX.setPatternSyntax(QRegExp::RegExp);
qDebug() << mailREX.exactMatch("me#me.com");
prints true
If you want to create a real QValidator for reusing in e.g. QLineEdits, you also have to check for Intermediate email adress strings, else nothing will be accepted unless you COPY&PASTE an email address into the edit.
Here is an example for an EmailValidator:
The emailvalidator.h file:
#ifndef EMAILVALIDATOR_H
#define EMAILVALIDATOR_H
#include <QValidator>
QT_BEGIN_NAMESPACE
class QRegExp;
QT_END_NAMESPACE
class EmailValidator : public QValidator
{
Q_OBJECT
public:
explicit EmailValidator(QObject *parent = 0);
State validate(QString &text, int &pos) const;
void fixup(QString &text) const;
private:
const QRegExp m_validMailRegExp;
const QRegExp m_intermediateMailRegExp;
};
#endif // EMAILVALIDATOR_H
And the emailvalidator.cpp file:
#include "emailvalidator.h"
EmailValidator::EmailValidator(QObject *parent) :
QValidator(parent),
m_validMailRegExp("[a-z0-9._%+-]+#[a-z0-9.-]+\\.[a-z]{2,4}"),
m_intermediateMailRegExp("[a-z0-9._%+-]*#?[a-z0-9.-]*\\.?[a-z]*")
{
}
QValidator::State EmailValidator::validate(QString &text, int &pos) const
{
Q_UNUSED(pos)
fixup(text);
if (m_validMailRegExp.exactMatch(text))
return Acceptable;
if (m_intermediateMailRegExp.exactMatch(text))
return Intermediate;
return Invalid;
}
void EmailValidator::fixup(QString &text) const
{
text = text.trimmed().toLower();
}
I'm building a windows program which shall have controls for 64bit numeric values. these controls shall be switchable to be signed or unsigned.
I found two controls:
"Spin Box"(int32) and "Double Spin Box"(double)
with double I'd be able to cover the range but it can't handle the precision.
Is there a way to change the data type of these controls?
Is it possible to create an own control which can handle signed and unsigned 64bit values?
Is it possible to create a 128bit Spin box?
The only work around I can see right now is in using a string control and manually convert to an INT64 or UINT64 but I'm not very happy with this solution
Any other Ideas?
I'm on QT 4.7.4 and VS2010 with C++
thx
You can derive QAbstractSpinBox and reimplement at least the virtual functions stepBy, stepEnabled and possibly validate() and fixup() for the input validation.
I don't use fixup function. See code of my Сustom QSpinBox.
class QLongLongSpinBox derived from QAbstractSpinBox
Don't forget call
setMaximum(std::numeric_limits<qlonglong>::max());
setMinimum(std::numeric_limits<qlonglong>::min());
after creating QLongLongSpinBox.
see qlonglongspinbox.h file:
#include <QtWidgets/QWidget>
#include <QtWidgets/QAbstractSpinBox>
#include <QtWidgets/QLineEdit>
class QLongLongSpinBoxPrivate;
class Q_WIDGETS_EXPORT QLongLongSpinBox : public QAbstractSpinBox
{
Q_OBJECT
Q_PROPERTY(qlonglong minimum READ minimum WRITE setMinimum)
Q_PROPERTY(qlonglong maximum READ maximum WRITE setMaximum)
Q_PROPERTY(qlonglong value READ value WRITE setValue NOTIFY valueChanged USER true)
qlonglong m_minimum;
qlonglong m_maximum;
qlonglong m_value;
public:
explicit QLongLongSpinBox(QWidget *parent = 0)
{
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
};
~QLongLongSpinBox() {};
qlonglong value() const
{
return m_value;
};
qlonglong minimum() const
{
return m_minimum;
};
void setMinimum(qlonglong min)
{
m_minimum = min;
}
qlonglong maximum() const
{
return m_maximum;
};
void setMaximum(qlonglong max)
{
m_maximum = max;
}
void setRange(qlonglong min, qlonglong max)
{
setMinimum(min);
setMaximum(max);
}
virtual void stepBy(int steps)
{
auto new_value = m_value;
if (steps < 0 && new_value + steps > new_value) {
new_value = std::numeric_limits<qlonglong>::min();
}
else if (steps > 0 && new_value + steps < new_value) {
new_value = std::numeric_limits<qlonglong>::max();
}
else {
new_value += steps;
}
lineEdit()->setText(textFromValue(new_value));
setValue(new_value);
}
protected:
//bool event(QEvent *event);
virtual QValidator::State validate(QString &input, int &pos) const
{
bool ok;
qlonglong val = input.toLongLong(&ok);
if (!ok)
return QValidator::Invalid;
if (val < m_minimum || val > m_maximum)
return QValidator::Invalid;
return QValidator::Acceptable;
}
virtual qlonglong valueFromText(const QString &text) const
{
return text.toLongLong();
}
virtual QString textFromValue(qlonglong val) const
{
return QString::number(val);
}
//virtual void fixup(QString &str) const;
virtual QAbstractSpinBox::StepEnabled stepEnabled() const
{
return StepUpEnabled | StepDownEnabled;
}
public Q_SLOTS:
void setValue(qlonglong val)
{
if (m_value != val) {
lineEdit()->setText(textFromValue(val));
m_value = val;
}
}
void onEditFinished()
{
QString input = lineEdit()->text();
int pos = 0;
if (QValidator::Acceptable == validate(input, pos))
setValue(valueFromText(input));
else
lineEdit()->setText(textFromValue(m_value));
}
Q_SIGNALS:
void valueChanged(qlonglong v);
private:
Q_DISABLE_COPY(QLongLongSpinBox)
Q_DECLARE_PRIVATE(QLongLongSpinBox)
};
To use you custom class (Widget) it in Qt Creator:
create QWidget widget
List item
select Promote to.. in widget menu
New Promoted Class:
QWigdet
Promoted class name QLongLongSpinBox
Header file: write qlonglongspinbox.h
Promote
select Promote to QLongLongSpinBox
save
You can see
<item>
<widget class="QLongLongSpinBox" name="value_integer" native="true"/>
</item>
and:
<customwidgets>
<customwidget>
<class>QLongLongSpinBox</class>
<extends>QWidget</extends>
<header>qlonglongspinbox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
in *.ui file
in ui_*.h generating file you see you class:
#include "qlonglongspinbox.h"
QLongLongSpinBox *object_name;
value_integer = new QLongLongSpinBox(YourWidgetName);
value_integer->setObjectName(QStringLiteral("value_integer"));
verticalLayout->addWidget(value_integer);
Just for the sake of completeness: every time you run into a problem like this, remember that (in case everything else fails) you can just download the Qt source code and make your own QSpinBox64 or QSpinBox128 class in a matter of minutes by modifying the needed parts.