我的对话框中有QDialogButtonBox按钮,它们是Ok
和Cancel
按钮对。当按下“确定”按钮时,我已经实现了accept()信号进行处理,但是如果目录路径无效,我想中止退出对话框。
void SettingsDialog::on_buttonBox_accepted()
{
QDir path( ui->lineEditRootPath->text() );
if ( path.exists( ui->lineEditRootPath->text() ) )
{
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text() );
}
else
{
// abort cancelling the dialog here
}
}
退出对话框可以从此处理程序中止吗?我是否必须在其他信号中实现上述代码?我是否必须使用简单按钮来完成此操作而不是QDialogButtonBox
?
答案 0 :(得分:3)
此问题来自与Qt Creator捆绑在一起的对话框模板。使用按钮创建空对话框时,.ui
文件在按钮框和底层对话框之间存在连接。它们是在你背后创造的,可以这么说:
所以,确实没有问题,因为按钮框实际上并不接受对话框。 您必须接受该对话框,否则对话框将保持打开状态。
简单的解决方法是删除默认连接。
您不应该使用QDir::exists(const QString &)
重载 - 它不起作用。您已经提供了dir构造函数的路径。只需使用exists()
。
因此:
void SettingsDialog::on_buttonBox_accepted()
{
QDir path(ui->lineEditRootPath->text());
if (!path.exists()) return;
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text());
accept(); // accepts the dialog, closing it
}
您还可以使用静态QFileInfo::exists
:
void SettingsDialog::on_buttonBox_accepted()
{
if (! QFileInfo.exists(ui->lineEditRootPath->text()) return;
...
}
最后,当输入无效时,向用户提供某种反馈可能是个不错的主意。在C ++ 11中,这很容易做到:
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!QFileInfo::exists(edit.text())) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, [&](const QString&){
if (QFileInfo::exists(edit.text()))
edit.setStyleSheet("");
else
edit.setStyleSheet("* { background: red; }");
});
dialog.show();
return a.exec();
}
经过一些测试后,您意识到用户很难输入可能在断开连接的网络卷上的路径。当您尝试检查它们是否存在时,它会阻止GUI,以便操作系统可以礼貌地告诉您“嗯,不,”。
解决方案是在工作线程中执行检查,这样如果它阻塞,UI将不会直接受到影响。如果工作线程阻塞,则路径编辑器背景将变为黄色。如果路径不存在,背景将变为红色,“确定”按钮将被禁用。
一些代码需要一些解释:QObject::connect(&checker, &Checker::exists, &app, [&](...){...})
将检查器的信号连接到应用程序对象的线程上下文中的lambda 。由于checker
的信号是在检查器的线程中发出的,没有上下文(&app
),代码将在检查器的线程中执行。我们绝对不希望这样,GUI更改必须在主线程中执行。最简单的方法是在主线程中传递一个我们确实知道生命的对象:应用程序实例。如果您没有通过适当的上下文,例如QObject::connect(&checker, &Checker::exists, [&](...){...})
),你会得到未定义的行为和崩溃。
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QThread>
#include <QTimer>
class Thread : public QThread {
using QThread::run; // final
public:
~Thread() { quit(); wait(); }
};
class Checker : public QObject {
Q_OBJECT
public:
Q_SIGNAL void exists(bool, const QString & path);
Q_SLOT void check(const QString & path) { emit exists(QFileInfo::exists(path), path); }
};
int main(int argc, char *argv[])
{
bool pathExists = true;
QApplication app(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QTimer checkTimer;
Checker checker;
Thread checkerThread;
checker.moveToThread(&checkerThread);
checkerThread.start();
checkTimer.setInterval(500);
checkTimer.setSingleShot(true);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!pathExists) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, &checker, &Checker::check);
QObject::connect(&edit, &QLineEdit::textChanged, &checkTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(&checkTimer, &QTimer::timeout, [&]{ edit.setStyleSheet("background: yellow"); });
QObject::connect(&checker, &Checker::exists, &app, [&](bool ok, const QString & path){
if (path != edit.text()) return; // stale result
checkTimer.stop();
edit.setStyleSheet(ok ? "" : "background: red");
buttons.button(QDialogButtonBox::Ok)->setEnabled(ok);
pathExists = ok;
});
dialog.show();
return app.exec();
}
#include "main.moc"