Để triển khai tính năng tự động chọn toàn bộ văn bản khi QLineEdit nhận focus, có một số cách tiếp cận khác nhau. Dưới đây là phân tích chi tiết các phương pháp và giải pháp hiệu quả.
Phương pháp 1: Sử dụng event filter
Cài đặt bộ lọc sự kiện cho các QLineEdit để phát hiện khi chúng nhận focus. Tuy nhiên, phương pháp này không hoàn toàn đáp ứng yêu cầu vì nó chỉ hoạt động khi cửa sổ chuyển từ trạng thái mất focus sang có focus.
Bước 1: Trong constructor của cửa sổ chính, đăng ký bộ lọc:
ui->ledSendStr->installEventFilter(this);
ui->ledSendFile->installEventFilter(this);
ui->ledSendPreTime->installEventFilter(this);
Bước 2: Khai báo phương thức trong file header:
public slots:
bool eventFilter(QObject *, QEvent *);
Bước 3: Triển khai logic xử lý:
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
{
if (obj == ui->ledSendStr) {
if (ev->type() == QEvent::FocusIn) {
qDebug() << "Focus acquired";
ui->ledSendStr->selectAll();
ui->ledSendStr->setFocus(Qt::OtherFocusReason);
}
else if (ev->type() == QEvent::FocusOut) {
qDebug() << "Focus lost";
}
}
return QWidget::eventFilter(obj, ev);
}
Hạn chế: Phương pháp này không kích hoạt selectAll() khi nhấp chuột trực tiếp vào QLineEdit.
Phương pháp 2: Kế thừa QLineEdit
Tạo lớp con kế thừa QLineEdit và ghi đè các phương thức xử lý sự kiện focus:
File header:
#ifndef CUSTOMLINEEDIT_H
#define CUSTOMLINEEDIT_H
#include <QLineEdit>
#include <QDebug>
#include <QPalette>
class CustomLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit CustomLineEdit(QWidget *parent = nullptr);
~CustomLineEdit();
signals:
void focusAcquired(const QString &objectName);
protected:
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
};
#endif // CUSTOMLINEEDIT_H
File triển khai:
#include "customlineedit.h"
CustomLineEdit::CustomLineEdit(QWidget *parent) : QLineEdit(parent) {}
CustomLineEdit::~CustomLineEdit() {}
void CustomLineEdit::focusInEvent(QFocusEvent *event)
{
QPalette pal;
pal.setColor(QPalette::Base, Qt::lightGray);
setPalette(pal);
// Phát tín hiệu để xử lý selectAll bên ngoài
emit focusAcquired(objectName());
QLineEdit::focusInEvent(event);
}
void CustomLineEdit::focusOutEvent(QFocusEvent *event)
{
QPalette pal;
pal.setColor(QPalette::Base, Qt::white);
setPalette(pal);
QLineEdit::focusOutEvent(event);
}
Vấn đề phát hiện: Việc gọi trực tiếp selectAll() trong focusInEvent không hoạt động vì tại thời điểm này, widget chưa thực sự sẵn sàng để nhận lệnh chọn văn bản.
Giải pháp thành công: Sử dụng timer trì hoãn
Phương pháp duy nhất hoạt động hiệu quả là sử dụng QTimer để trì hoãn hành động selectAll cho đến khi sự kiện focus kết thúc:
Khai báo trong header:
public slots:
void handleFocusAcquired(const QString &name);
void delayedSelectAll();
private:
QTimer *m_delayedTimer;
QString m_currentCtrlName;
Triển khai:
void MainWindow::handleFocusAcquired(const QString &name)
{
if (m_delayedTimer) {
m_delayedTimer->stop();
delete m_delayedTimer;
}
m_currentCtrlName = name;
m_delayedTimer = new QTimer(this);
connect(m_delayedTimer, &QTimer::timeout, this, &MainWindow::delayedSelectAll);
m_delayedTimer->start(150); // Trì hoãn 150ms
}
void MainWindow::delayedSelectAll()
{
QLineEdit *target = nullptr;
if (m_currentCtrlName == "ledSendStr")
target = ui->ledSendStr;
else if (m_currentCtrlName == "ledSendFile")
target = ui->ledSendFile;
else if (m_currentCtrlName == "ledSendPreTime")
target = ui->ledSendPreTime;
if (target) {
target->selectAll();
target->setFocus(Qt::OtherFocusReason);
}
if (m_delayedTimer) {
m_delayedTimer->stop();
delete m_delayedTimer;
m_delayedTimer = nullptr;
}
}
Lưu ý về layout
Khi sử dụng lớp CustomLineEdit thay vì QLineEdit tiêu chuẩn, không thể kéo thả trực tiếp trong Qt Designer. Cần thay thế bằng code:
CustomLineEdit *customLed = new CustomLineEdit(this);
customLed->setObjectName("ledSendStr");
ui->horizontalLayout->addWidget(customLed);
Sự kết hợp giữa layout được tạo trong Designer và các widget được thêm bằng code cho phép đạt được bố cục mong muốn.
Kết luận
Cơ chế sự kiện focus trong Qt yêu cầu một độ trễ nhỏ trước khi thực hiện các thao tác như selectAll. Sử dụng QTimer với thời gian trì hoãn 100-200ms là giải pháp đáng tin cậy nhất để đảm bảo chức năng hoạt động chính xác.