我正在用 Qt 6.8 C++23 编写一个简单的工具来了解 Qt。Ubuntu 22.04 VS Code。
我有 2 个 QLineEdit 用于源目录和目标目录。当用户关注行编辑时,该工具应该打开 QFileDialog,这样他们就不必输入完整路径。行编辑本身应该是只读的。
包含对话框的源代码可以在 GitHub 上找到。
问题是,当文件对话框关闭时,它会立即重新打开,因为焦点仍然在行编辑上。如何在文件对话框关闭后失去焦点?
#ifndef DIRECTORYLINEEDIT_H_
#define DIRECTORYLINEEDIT_H_
#include <QLineEdit>
#include <QFileDialog>
class DirectoryLineEdit : public QLineEdit {
Q_OBJECT
public:
explicit DirectoryLineEdit(const char* dleName, const char* title, int leWidth, QWidget *parent = nullptr)
: QLineEdit{parent}, fileDialogTitle{title}
{
setObjectName(QString::fromUtf8(dleName));
setStyleSheet("width: " + QString::number(leWidth) + "px;");
setReadOnly(true);
}
void focusInEvent(QFocusEvent *event)
{
QString textToChange = text();
textToChange = QFileDialog::getExistingDirectory(nullptr, fileDialogTitle,
textToChange, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
setText(textToChange);
}
private:
QString fileDialogTitle;
};
#endif // DIRECTORYLINEEDIT_H_
在焦点事件上创建/销毁小部件时务必非常小心,特别是如果它们是窗口,因为它可能会产生某种程度的递归问题,这正是您的情况:即使这不是“即时递归”,但从概念上讲它是,因为一旦对话框关闭,焦点就会自动重新获得。
QFocusEvent提供了一个函数
reason()
让我们了解导致焦点事件的原因。对于这种情况(创建新对话框),检查原因至关重要,因为有许多原因导致覆盖不应继续:即使假设您没有上面解释的部分递归问题,如果用户打开行编辑的上下文菜单(具体来说,在菜单关闭后),或者用户切换到另一个程序然后切换回您的程序,则会显示对话框。
对于这种情况,您可能仅当出于以下原因之一时才显示对话框:
MouseFocusReason
TabFocusReason
BacktabFocusReason
ShortcutFocusReason
(如果您使用 QLabel 作为“伙伴”)该功能将是这样的:
当原因是选项卡导航时,您可能还需要在打开对话框之前检查行编辑是否没有内容,否则循环浏览小部件会非常烦人:您仍然可以覆盖行编辑并仅使用常用“编辑”键(例如、或)
keyPressEvent
打开对话框。F2SpaceEnterReturn还要记住,用户可以取消对话框(返回一个空字符串),因此您可能只应
setText()
在textToChange
不为空时调用。然后,如果您想允许上下文菜单(通常您应该这样做),您还应该
QGuiApplication::mouseButtons()
在的情况下查询MouseFocusReason
,并且仅在行编辑没有内容或按钮不是时显示对话框RightButton
。最后,请考虑一下,只要行编辑已经获得焦点,上述操作显然会阻止显示对话框,因此如果用户错误地选择了错误的目录,他们就需要单击另一个小部件并再次单击行编辑才能进行更改,这非常不直观。这可以通过覆盖或向行编辑添加操作来解决
mousePressEvent()
(请参阅QLineEdit::addAction()
),这可能更直观。