如何使用QDialogBu​​ttonBox处理程序中止退出对话框?

时间:2015-06-26 16:55:59

标签: c++ qt

我的对话框中有QDialogBu​​ttonBox按钮,它们是OkCancel按钮对。当按下“确定”按钮时,我已经实现了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

1 个答案:

答案 0 :(得分:3)

此问题来自与Qt Creator捆绑在一起的对话框模板。使用按钮创建空对话框时,.ui文件在按钮框和底层对话框之间存在连接。它们是在你背后创造的,可以这么说:

Dialog-with-buttons template, viewed in Qt Creator in signal edit mode, with default signals shown.

所以,确实没有问题,因为按钮框实际上并不接受对话框。 必须接受该对话框,否则对话框将保持打开状态。

简单的解决方法是删除默认连接。

其他Nitpicks

您不应该使用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"