Estou escrevendo uma ferramenta simples em Qt 6.8 C++23 para aprender sobre Qt. Ubuntu 22.04 VS Code.
Tenho 2 QLineEdit para diretório de origem e diretório de destino. Quando o usuário foca na edição de linha, a ferramenta deve abrir o QFileDialog para que ele não precise digitar o caminho completo. A edição de linha em si deve ser somente leitura.
A fonte para o diálogo contido pode ser encontrada no GitHub .
O problema é que quando a caixa de diálogo do arquivo fecha, ela reabre imediatamente porque o foco ainda está na linha de edição. Como faço para perder o foco depois que a caixa de diálogo do arquivo fecha?
#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_
É importante ter muito cuidado ao criar/destruir widgets em eventos de foco, especialmente se forem janelas, pois isso provavelmente criaria problemas com algum nÃvel de recursão, que é exatamente o seu caso: embora isso não seja uma "recursão instantânea", conceitualmente é, porque o foco é automaticamente readquirido assim que a caixa de diálogo é fechada.
O QFocusEvent fornece uma
reason()
função que nos permite entender o que causou o evento de foco.Verificar o motivo é de extrema importância para um caso como esse (criação de uma nova caixa de diálogo), porque há muitos motivos pelos quais a substituição não deve prosseguir: mesmo supondo que você não tenha o problema de recursão parcial explicado acima, a caixa de diálogo seria exibida se o usuário abrisse o menu de contexto da edição de linha (especificamente, logo após o menu ser fechado) ou se o usuário alternasse para outro programa e depois retornasse para o seu.
Nessa situação, você provavelmente deve mostrar o diálogo somente se o motivo for um dos seguintes:
MouseFocusReason
TabFocusReason
BacktabFocusReason
ShortcutFocusReason
(se você usou um QLabel como "amigo" )A função seria algo como isto:
Você também pode querer verificar se a edição de linha não tem conteúdo antes de abrir a caixa de diálogo quando o motivo for a navegação por abas, caso contrário seria muito chato alternar entre os widgets: você ainda pode substituir as edições de linha e
keyPressEvent
abrir a caixa de diálogo apenas com as teclas comuns de "edição" (como F2, Spaceou ).EnterReturnLembre-se também de que o usuário pode cancelar o diálogo (o que retorna uma string vazia), então você provavelmente deve chamar
setText()
apenas enquantotextToChange
não estiver vazio.Então, se você quiser permitir o menu de contexto (e normalmente deveria), você também deve consultar o
QGuiApplication::mouseButtons()
in case ofMouseFocusReason
e mostrar a caixa de diálogo somente se a edição de linha não tiver conteúdo ou se o botão não forRightButton
.Por fim, considere que o acima obviamente impede a exibição do diálogo enquanto a edição de linha já tiver o foco, portanto, se o usuário selecionar o diretório errado por engano, ele precisará clicar em outro widget e clicar novamente na edição de linha para alterá-la, o que é bastante pouco intuitivo. Isso pode ser resolvido substituindo
mousePressEvent()
também ou adicionando uma ação à edição de linha (vejaQLineEdit::addAction()
), o que provavelmente é muito mais intuitivo.