如何防止QDialog类关闭 [英] How can I prevent QDialog class from closing

查看:94
本文介绍了如何防止QDialog类关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何防止在按下确定"按钮后关闭QDialog类?只有在此对话框中正确执行了某些操作后,我才需要关闭窗口.在其他情况下,则不需要关闭该窗口.

解决方案

通常来说,对用户撒谎是一种坏习惯.如果未禁用按钮,则在用户单击按钮时会更好地工作.

因此,显而易见的解决方案是禁用按钮,直到满足必要的前提条件为止.对于完成对话框的按钮,应该使用 QDialogBu​​ttonBox 而不是离散按钮,因为在不同的平台上,这些按钮在框内的排列方式会有所不同-基于按钮的角色/类型.

下面是如何完成此操作的示例.适用于Qt 4和5.

代码与现有样式表互操作时要特别注意.

 <代码>//https://github.com/KubaO/stackoverflown/tree/master/questions/buttonbox-22404318#include< QtGui>#if QT_VERSION> = QT_VERSION_CHECK(5,0,0)#include< QtWidgets>#万一#include< type_traits> 

首先,让我们使用一些样式表操作助手:

  void styleSheetSet(QWidget * w,const QString&what){auto const token = QStringLiteral("/*> */%1/*< */").arg(what);如果(what.isEmpty()|| w-> styleSheet().contains(token))返回;w-> setStyleSheet(w-> styleSheet().append(token));}void styleSheetClear(QWidget * w,const QString&what){const auto token = QStringLiteral("/*> */%1/*< */").arg(what);如果(what.isEmpty()||!w-> styleSheet().contains(token))返回;w-> setStyleSheet(w-> styleSheet().remove(token));}void styleSheetSelect(QWidget * w,bool选择器,常量QString&onTrue,常量QString&onFalse = {}){styleSheetSet(w,选择器?onTrue:onFalse);styleSheetClear(w,选择器?onFalse:onTrue);}模板< typename T,typename U>void setSelect(QSet< T& set,bool b,const U& val){如果(b)set.insert(val);否则set.remove(val);} 

和递归父搜索:

  bool hasParent(QObject * obj,QObject * const parent){Q_ASSERT(obj);while(obj = obj-> parent())如果(obj == parent)返回true;返回obj == parent;} 

DialogValidator 管理单个对话框的验证器.首先,当 QLineEdit 的内容和通用小部件更改时调用的插槽:

  class DialogValidator:公共QObject {Q_OBJECTQSet< QWidget *>m_validWidgets;int m_needsValid = 0;Q_SLOT void checkWidget(){如果(sender()-> isWidgetType())checkValidity(static_cast< QWidget *>(sender()));}Q_SLOT void checkLineEdit(){如果(自动* l = qobject_cast< QLineEdit *>(sender()))checkValidity(l);}void checkValidity(QLineEdit * l){indicatorValidity(l,l-> hasAcceptableInput());}void checkValidity(QWidget * w){自动验证程序= w-> findChild< QValidator *>();如果(!validator)返回;自动道具= w-> metaObject()-> userProperty();QVariant值= prop.read(w);int pos;QString文本= value.toString();bool isValid =验证器->验证(文本,pos)== QValidator :: Acceptable;indicatorValidity(w,isValid);}void notifyValidity(QWidget * w,bool isValid){自动* combo = qobject_cast< QComboBox *>(w-> parentWidget());setSelect(m_validWidgets,isValid,组合?combo:w);styleSheetSelect(w,!isValid,QStringLiteral(%1 {背景:黄色}").arg(QLatin1String(w-> metaObject()-> className())));发出newValidity(m_validWidgets.count()== m_needsValid);} 

使用 add 方法将验证器添加到对话框验证器中.如果我们希望对动态类型的窗口小部件进行特殊处理,则应使用 addPoly 方法-该方法将分派到类型特定的重载(如果有):

 模板<类型名W>类型名称std :: enable_if<!std :: is_same< QWidget,W> :: value,bool> :: typeaddPoly(W * w,QValidator * v){如果(!w)返回false;返回(add(w,v),true);}上市:DialogValidator(QObject * parent = {}):QObject(parent){}Q_SIGNAL void newValidity(bool);void addPoly(QWidget * w,QValidator * v){addPoly(qobject_cast< QLineEdit *>(w),v)||addPoly(qobject_cast< QComboBox *>(w),v)||(add(w,v),true);} 

然后是静态类型的 add 方法:

  void add(QComboBox * b,QValidator * v){如果(自动* l = b-> lineEdit())加(l,v);}void add(QLineEdit * l,QValidator * v){l-> setValidator(v);connect(l,SIGNAL(textChanged(QString)),SLOT(checkLineEdit()));m_needsValid ++;checkValidity(l);}void add(QWidget * w,QValidator * v){Q_ASSERT(hasParent(v,w));自动道具= w-> metaObject()-> userProperty();自动propChanged = prop.notifySignal();静态自动检查= metaObject()-> method(metaObject()-> indexOfSlot("checkWidget()"));Q_ASSERT(check.isValid());如果(!prop.isValid()||!propChanged.isValid())return qWarning("DialogValidator :: add:目标窗口小部件没有带有通知程序的用户属性.");if(connect(w,propChanged,this,check)){m_needsValid ++;checkValidity(w);}} 

最后,构造验证器的便捷方法:

 模板< typename V,typename W,typename ... Args>类型名称std :: enable_if<std :: is_base_of< QWidget,W> :: value&&std :: is_base_of< QValidator,V> :: value,V *> :: typeadd(W * w,Args ... args){V * validator = new V(std :: forward< Args>(args)...,w);返回add(w,validator),validator;}}; 

我们的验证对话框:

  class MyDialog:公共QDialog {Q_OBJECTQFormLayout m_layout {this};QLineEdit m_cFactor;QLineEdit m_dFactor;QDialogBu​​ttonBox m_buttons {QDialogBu​​ttonBox :: Ok |QDialogBu​​ttonBox :: Cancel};DialogValidator m_validator;上市:MyDialog(QWidget * parent = {},Qt :: WindowFlags f = {}):QDialog(parent,f){m_layout.addRow("Combobulation Factor",& m_cFactor);m_layout.addRow("Decombobulation Factor",& m_dFactor);m_layout.addRow(& m_buttons);connect(&m_buttons,SIGNAL(accepted()),SLOT(accept()));connect(&m_buttons,SIGNAL(rejected()),SLOT(reject()));connect(&m_validator,SIGNAL(newValidity(bool)),m_buttons.button(QDialogBu​​ttonBox :: Ok),SLOT(setEnabled(bool)));//特定于QLineEdit的验证器m_validator.add< QIntValidator>(& m_cFactor,0,50);//通用的基于用户属性的验证器m_validator.add< QIntValidator,QWidget>(& m_dFactor,-50,0);}};int main(int argc,char * argv []){QApplication a(argc,argv);MyDialog d;d.show();返回a.exec();}#include"main.moc" 

How can I prevent QDialog class from closing after "Ok" button was pressed? I need to close the window only if the some actions were performed correctly on this dialog, in other cases I don't need to close this window.

解决方案

Generally speaking, it's a bad habit to lie to the user. If a button is not disabled, then it better work when the user clicks on it.

So, the obvious solution is to disable the button until the necessary preconditions are met. For buttons that finish the dialog, you should be using QDialogButtonBox instead of discrete buttons, since on different platforms those buttons will be arranged differently within the box - based on the roles/types of the buttons.

Below is an example of how it might be done. Works with Qt 4 and 5.

Care has been taken for the code to interoperate with existing stylesheets.

// https://github.com/KubaO/stackoverflown/tree/master/questions/buttonbox-22404318
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <type_traits>

First, let's have some stylesheet manipulation helpers:

void styleSheetSet(QWidget *w, const QString &what) {
   auto const token = QStringLiteral("/*>*/%1/*<*/").arg(what);
   if (what.isEmpty() || w->styleSheet().contains(token)) return;
   w->setStyleSheet(w->styleSheet().append(token));
}

void styleSheetClear(QWidget *w, const QString &what) {
   const auto token = QStringLiteral("/*>*/%1/*<*/").arg(what);
   if (what.isEmpty() || ! w->styleSheet().contains(token)) return;
   w->setStyleSheet(w->styleSheet().remove(token));
}

void styleSheetSelect(QWidget *w, bool selector,
                      const QString & onTrue,
                      const QString & onFalse = {})
{
   styleSheetSet(w, selector ? onTrue : onFalse);
   styleSheetClear(w, selector ? onFalse : onTrue);
}

template <typename T, typename U>
void setSelect(QSet<T> &set, bool b, const U &val) {
   if (b) set.insert(val); else set.remove(val);
}

And a recursive parent search:

    bool hasParent(QObject *obj, QObject *const parent) {
       Q_ASSERT(obj);
       while (obj = obj->parent())
          if (obj == parent) return true;
       return obj == parent;
    }

The DialogValidator manages validators for a single dialog box. First, the slots invoked when the contents of a QLineEdit and a generic widget change:

class DialogValidator : public QObject {
   Q_OBJECT
   QSet<QWidget*> m_validWidgets;
   int m_needsValid = 0;
   Q_SLOT void checkWidget() {
      if (sender()->isWidgetType())
         checkValidity(static_cast<QWidget*>(sender()));
   }
   Q_SLOT void checkLineEdit() {
      if (auto *l = qobject_cast<QLineEdit*>(sender()))
         checkValidity(l);
   }
   void checkValidity(QLineEdit *l) {
      indicateValidity(l, l->hasAcceptableInput());
   }
   void checkValidity(QWidget *w) {
      auto validator = w->findChild<QValidator*>();
      if (!validator) return;
      auto prop = w->metaObject()->userProperty();
      QVariant value = prop.read(w);
      int pos;
      QString text = value.toString();
      bool isValid =
            validator->validate(text, pos) == QValidator::Acceptable;
      indicateValidity(w, isValid);
   }
   void indicateValidity(QWidget *w, bool isValid) {
      auto *combo = qobject_cast<QComboBox*>(w->parentWidget());
      setSelect(m_validWidgets, isValid, combo ? combo : w);
      styleSheetSelect(w, !isValid,
                       QStringLiteral("%1 { background: yellow }")
                       .arg(QLatin1String(w->metaObject()->className())));
      emit newValidity(m_validWidgets.count() == m_needsValid);
   }

The validators are added to the dialog validator using the add methods. If we desire special handling for a dynamically typed widget, the addPoly method should be used - it'll dispatch to type-specific overloads, if any:

   template<typename W>
   typename std::enable_if<!std::is_same<QWidget,W>::value, bool>::type
   addPoly(W* w, QValidator *v) {
      if (!w) return false;
      return (add(w,v), true);
   }
public:
   DialogValidator(QObject *parent = {}) : QObject(parent) {}
   Q_SIGNAL void newValidity(bool);
   void addPoly(QWidget *w, QValidator *v) {
      addPoly(qobject_cast<QLineEdit*>(w), v) ||
            addPoly(qobject_cast<QComboBox*>(w), v) ||
            (add(w, v), true);
   }

Then, the static-typed add methods:

   void add(QComboBox *b, QValidator *v) {
      if (auto *l = b->lineEdit())
         add(l, v);
   }
   void add(QLineEdit *l, QValidator *v) {
      l->setValidator(v);
      connect(l, SIGNAL(textChanged(QString)), SLOT(checkLineEdit()));
      m_needsValid++;
      checkValidity(l);
   }
   void add(QWidget *w, QValidator *v) {
      Q_ASSERT(hasParent(v, w));
      auto prop = w->metaObject()->userProperty();
      auto propChanged = prop.notifySignal();
      static auto check = metaObject()->method(metaObject()->indexOfSlot("checkWidget()"));
      Q_ASSERT(check.isValid());
      if (!prop.isValid() || !propChanged.isValid())
         return qWarning("DialogValidator::add: The target widget has no user property with a notifier.");
      if (connect(w, propChanged, this, check)) {
         m_needsValid++;
         checkValidity(w);
      }
   }

And finally, the convenience method that constructs the validator:

   template <typename V, typename W, typename...Args>
   typename std::enable_if<
   std::is_base_of<QWidget, W>::value && std::is_base_of<QValidator, V>::value, V*>::type
   add(W *w, Args...args) {
      V *validator = new V(std::forward<Args>(args)..., w);
      return add(w, validator), validator;
   }
};

Our dialog with validation:

class MyDialog : public QDialog {
   Q_OBJECT
   QFormLayout m_layout{this};
   QLineEdit m_cFactor;
   QLineEdit m_dFactor;
   QDialogButtonBox m_buttons{QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
   DialogValidator m_validator;
public:
   MyDialog(QWidget *parent = {}, Qt::WindowFlags f = {}) : QDialog(parent, f) {
      m_layout.addRow("Combobulation Factor", &m_cFactor);
      m_layout.addRow("Decombobulation Factor", &m_dFactor);
      m_layout.addRow(&m_buttons);
      connect(&m_buttons, SIGNAL(accepted()), SLOT(accept()));
      connect(&m_buttons, SIGNAL(rejected()), SLOT(reject()));
      connect(&m_validator, SIGNAL(newValidity(bool)),
              m_buttons.button(QDialogButtonBox::Ok), SLOT(setEnabled(bool)));
      // QLineEdit-specific validator
      m_validator.add<QIntValidator>(&m_cFactor, 0, 50);
      // Generic user property-based validator
      m_validator.add<QIntValidator, QWidget>(&m_dFactor, -50, 0);
   }
};

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   MyDialog d;
   d.show();
   return a.exec();
}
#include "main.moc"

这篇关于如何防止QDialog类关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆