In one of my projects I have a series of QLineEdit
Widgets, that should accept double numbers, that are lying in a certain range. For certain reasons I can not use QDoubleSpinBox
.
Now I'm using QDoubleValidator
to check, if my number lies in the given range. Unfortunately, the signal editingFinished
is only emitted if QValidator
gives QValidator::Acceptable
.
Now assume, that I might have a series of such QLineEdit
widgets. In one I enter a bad number and then I switch the focus to another widget. The user is left with a bad value inside the QLineEdit
.
My desired behavior instead would be to set the focus to the widget containing the bad input and giving a warning.
For some reasons, I'm failing to implement this feature. Even after I consulted the Qt docs.
Here is my full code.
ValidatedDoubleEditorWidget.h
#pragma once
#include <QWidget>
namespace Ui {
class validatedDoubleEditorWidget;
}
class QDoubleValidator;
class ValidatedDoubleEditorWidget : public QWidget {
Q_OBJECT
public:
ValidatedDoubleEditorWidget(double min, double max, double value);
double getValue() const;
void setValue(const double value);
private slots:
void on_lineEdit_editingFinished();
protected:
virtual void focusOutEvent(QFocusEvent *event) override;
virtual void focusInEvent(QFocusEvent *event) override;
private:
Ui::validatedDoubleEditorWidget* mWidget = nullptr;
double mValue = 0.;
double mMin = 0.;
double mMax = 0.;
QDoubleValidator* mValidator = nullptr;
};
ValidatedDoubleEditorWidget.cpp
#include "ValidatedDoubleEditorWidget.h"
#include <QDoubleValidator>
#include <QMessageBox>
#include <QDebug>
#include "ui_ValidatedDoubleEditorWidget.h"
ValidatedDoubleEditorWidget::ValidatedDoubleEditorWidget(double min, double max, double value)
{
mWidget = new Ui::validatedDoubleEditorWidget;
mWidget->setupUi(this);
mValue = value;
mWidget->lineEdit->setText(QString("%1").arg(value));
mValidator = new QDoubleValidator(min, max, 20, this);
mWidget->lineEdit->setValidator(mValidator);
setFocusProxy(mWidget->lineEdit);
setFocusPolicy(Qt::StrongFocus);
}
double ValidatedDoubleEditorWidget::getValue() const
{
return mValue;
}
void ValidatedDoubleEditorWidget::setValue(const double value)
{
mValue = value;
mWidget->lineEdit->setText(QString("%1").arg(value));
}
void ValidatedDoubleEditorWidget::on_lineEdit_editingFinished()
{
QString text = mWidget->lineEdit->text();
qDebug() << "Editing finished";
bool ok;
double value = text.toDouble(&ok);
if (!ok) {
//
}
else {
mValue = value;
}
}
void ValidatedDoubleEditorWidget::focusOutEvent(QFocusEvent *event)
{
qDebug() << "OutFocus";
QString text = mWidget->lineEdit->text();
int i;
auto state=mValidator->validate(text, i);
if (state != QValidator::Acceptable) {
QMessageBox::warning(this, tr("Invalid Input!"), tr("Please check your input."), QMessageBox::Ok);
mWidget->lineEdit->setText(QString("%1").arg(mValue));
mWidget->lineEdit->setFocus();
}
}
void ValidatedDoubleEditorWidget::focusInEvent(QFocusEvent *event)
{
qDebug() << "InFocus";
}
TestRunner.cpp
#include <QApplication>
#include <QMap>
#include <QFrame>
#include <QHBoxLayout>
#include "ValidatedDoubleEditorWidget.h"
int main(int argc, char** args) {
QApplication app(argc, args);
QFrame frame;
frame.setLayout(new QHBoxLayout);
frame.layout()->addWidget(new ValidatedDoubleEditorWidget(-1., 4., 1.));
frame.layout()->addWidget(new ValidatedDoubleEditorWidget(-2., 4., 5.));
frame.show();
app.exec();
return 0;
}
You can subclass QLineEdit
and reimplement focusOutEvent
method. Call QLineEdit::hasAcceptableInput
to check if the input is valid. If it is not, you can call setFocus
to regain input focus. You can also show a warning dialog or emit some signal. Here is an example:
#include <QtWidgets>
class CustomLineEdit : public QLineEdit
{
Q_OBJECT
public:
CustomLineEdit(QWidget *parent = nullptr) : QLineEdit(parent){}
protected:
void focusOutEvent(QFocusEvent *event)
{
QLineEdit::focusOutEvent(event);
if(!hasAcceptableInput())
{
setFocus();
emit validationError();
}
}
signals:
void validationError();
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow m;
m.setCentralWidget(new QWidget);
m.centralWidget()->setLayout(new QVBoxLayout);
QDoubleValidator d_validator(0, 10, 2);
CustomLineEdit l1;
CustomLineEdit l2;
l1.setValidator(&d_validator);
l2.setValidator(&d_validator);
QObject::connect(&l1, &CustomLineEdit::validationError, [=]{qDebug() << "Validation error!";});
QObject::connect(&l2, &CustomLineEdit::validationError, [=]{qDebug() << "Validation error!";});
m.centralWidget()->layout()->addWidget(&l1);
m.centralWidget()->layout()->addWidget(&l2);
m.show();
return a.exec();
}
#include "main.moc"