识别 QLineEdit 是否失去焦点
Recognize if a QLineEdit loses a focus
在我的一个项目中,我有一系列 QLineEdit
小部件,它们应该接受位于特定范围内的双数。由于某些原因,我无法使用 QDoubleSpinBox
.
现在我正在使用 QDoubleValidator
检查我的号码是否在给定范围内。不幸的是,信号 editingFinished
仅在 QValidator
给出 QValidator::Acceptable
时才会发出。
现在假设,我可能有一系列这样的 QLineEdit
小部件。在其中一个中,我输入了一个错误的数字,然后将焦点切换到另一个小部件。用户在 QLineEdit
中留下了错误的值。
我想要的行为是将焦点设置到包含错误输入的小部件并发出警告。
由于某些原因,我未能实现此功能。即使在我查阅了 Qt 文档之后。
这是我的完整代码。
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;
}
SetFocusProxy() 并不意味着父控件将获得子控件的焦点事件。这意味着当父窗口小部件获得焦点时,子窗口小部件将获得焦点。
因此,在您的情况下,您的 focusIn() 和 focusOut() 事件处理程序用于父窗口小部件,但父窗口小部件已获得行编辑作为其焦点代理,这意味着行编辑将是您想要的窗口小部件检查焦点进出事件。
检测行编辑的相关焦点进出事件的一种方法是使用事件过滤器。基本上 WidgetA 可以在 WidgetB 上安装一个事件过滤器,然后在 WidgetB 即将接收事件(例如聚焦或聚焦事件)时得到通知。
因此,在您的情况下,您可以在 ValidatedDoubleEditorWidget class 的构造函数中的编辑行上安装事件过滤器,如下所示:
mWidget->lineEdit->installEventFilter( this );
然后您可以添加事件过滤器实现(再次添加到您的 ValidatedDoubleEditorWidget class):
// Declaration
virtual bool eventFilter(QObject *watched, QEvent *event) override;
// Implementation
bool ValidatedDoubleEditorWidget::eventFilter(QObject *watched, QEvent *event)
{
if( event->type() == QEvent::FocusIn ) {
qDebug() << "Line edit focus in event";
}
else if( event->type() == QEvent::FocusOut ) {
qDebug() << "Line edit focus out event";
}
return false; // We return false to ignore the event and allow the child to recieve the event normally
}
这应该可以让您检测到 ValidatedDoubleEditorWidget 何时失去焦点并且不包含有效输入。
您可以继承 QLineEdit
并重新实现 focusOutEvent
method. Call QLineEdit::hasAcceptableInput
to check if the input is valid. If it is not, you can call setFocus
以重新获得输入焦点。您还可以显示警告对话框或发出一些信号。这是一个例子:
#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"
在我的一个项目中,我有一系列 QLineEdit
小部件,它们应该接受位于特定范围内的双数。由于某些原因,我无法使用 QDoubleSpinBox
.
现在我正在使用 QDoubleValidator
检查我的号码是否在给定范围内。不幸的是,信号 editingFinished
仅在 QValidator
给出 QValidator::Acceptable
时才会发出。
现在假设,我可能有一系列这样的 QLineEdit
小部件。在其中一个中,我输入了一个错误的数字,然后将焦点切换到另一个小部件。用户在 QLineEdit
中留下了错误的值。
我想要的行为是将焦点设置到包含错误输入的小部件并发出警告。
由于某些原因,我未能实现此功能。即使在我查阅了 Qt 文档之后。
这是我的完整代码。
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;
}
SetFocusProxy() 并不意味着父控件将获得子控件的焦点事件。这意味着当父窗口小部件获得焦点时,子窗口小部件将获得焦点。
因此,在您的情况下,您的 focusIn() 和 focusOut() 事件处理程序用于父窗口小部件,但父窗口小部件已获得行编辑作为其焦点代理,这意味着行编辑将是您想要的窗口小部件检查焦点进出事件。
检测行编辑的相关焦点进出事件的一种方法是使用事件过滤器。基本上 WidgetA 可以在 WidgetB 上安装一个事件过滤器,然后在 WidgetB 即将接收事件(例如聚焦或聚焦事件)时得到通知。
因此,在您的情况下,您可以在 ValidatedDoubleEditorWidget class 的构造函数中的编辑行上安装事件过滤器,如下所示:
mWidget->lineEdit->installEventFilter( this );
然后您可以添加事件过滤器实现(再次添加到您的 ValidatedDoubleEditorWidget class):
// Declaration
virtual bool eventFilter(QObject *watched, QEvent *event) override;
// Implementation
bool ValidatedDoubleEditorWidget::eventFilter(QObject *watched, QEvent *event)
{
if( event->type() == QEvent::FocusIn ) {
qDebug() << "Line edit focus in event";
}
else if( event->type() == QEvent::FocusOut ) {
qDebug() << "Line edit focus out event";
}
return false; // We return false to ignore the event and allow the child to recieve the event normally
}
这应该可以让您检测到 ValidatedDoubleEditorWidget 何时失去焦点并且不包含有效输入。
您可以继承 QLineEdit
并重新实现 focusOutEvent
method. Call QLineEdit::hasAcceptableInput
to check if the input is valid. If it is not, you can call setFocus
以重新获得输入焦点。您还可以显示警告对话框或发出一些信号。这是一个例子:
#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"