检查 ui 元素的值是不是已更改

Posted

技术标签:

【中文标题】检查 ui 元素的值是不是已更改【英文标题】:Checking that value of ui elements has been changed检查 ui 元素的值是否已更改 【发布时间】:2014-04-03 13:05:27 【问题描述】:

有什么方法可以检查对话框的ui元素(行编辑、组合框等)是否发生了变化。

我想要的是在用户更改任何单个 ui 元素的值时向用户显示一条消息,说明详细信息已部分填充。

我能做的是对每个 ui 元素使用 connect 并根据每个元素的值更改我设置一个布尔标志 & 在关闭事件时我正在检查该布尔标志。 但是为每个小部件检查它非常复杂。 有没有更简单的方法。 我用于单个 ui 元素的代码是,

connect(ui->leAge,SIGNAL(textChanged(QString)),this,SLOT(functChanged())); //In Constructor

void DemoDialog::functChanged()   //Will be called if value of line edit (ui->leAge) is changed

    flag=true;


void DemoDialog::closeEvent(QCloseEvent *event)

 if (flag) 
    if (QMessageBox::warning(this,"Close","Do you want to close?",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes) 
        this->close();
   

【问题讨论】:

【参考方案1】:

您不能重新实现closeEvent 来防止关闭窗口。您所做的close() 调用要么是多余的,要么是错误的(无限递归),因为closeEvent 方法调用只是通知即将关闭的一种方式。到那时再做任何事情都为时已晚。

请记住以下几点:

    关闭对话框通常等同于取消对话框。只有单击“确定”才能接受更改。

    当用户想要关闭对话框时,您不必询问他们。他们发起了行动。但是:

    如果有更改未被接受 - 在 OS X 以外的平台上,向用户询问对话框关闭是正确的。

所以,你必须做几件事:

    重新实现void event(QEvent*) 方法。这允许您拒绝关闭事件。

    提供Apply/Reset/Cancel按钮。


您的标志方法可以自动化。您可以找到对话框的所有控件并自动设置连接。对每种类型的控件重复以下语句 - 这很快就会变得乏味:

foreach(QTextEdit* w, findChildren<QTextEdit*>())
  connect(w, SIGNAL(textChanged(QString)), SLOT(functChanged()));

您可以利用元属性系统。大多数控件都有一个user 属性——该属性包含控件的主要值(如文本、选定项等)。您可以扫描所有小部件子级,并将用户属性的属性更改通知信号连接到您的标志:

QMetaMethod slot = metaObject().method(
                     metaObject().indexOfSlot("functChanged()"));
foreach (QWidget* w, findChildren<QWidget*>()) 
  QMetaObject mo = w->metaObject();
  if (!mo.userProperty().isValid() || !mo.userProperty().hasNotifySignal())
    continue;
  connect(w, mo.notifySignal(), this, slot);

每个小部件都是一个QObject。 QObjects 可以有属性,其中一个属性可以声明为用户属性。大多数可编辑的小部件控件都有这样的属性,它表示用户输入(文本、数值、项目的选定索引等)。通常,此类属性也具有更改通知信号。因此,您只需获取表示通知信号的QMetaMethod,并将其连接到设置标志的函数。


要确定更改的字段,您不一定需要一个标志。在许多对话框中,有一个表示对话框中数据的数据结构是有意义的。然后,您可以使用getset 方法从对话框中检索数据,或将其设置在对话框中。要检查更改的数据,只需将原始数据与当前数据进行比较:

struct UserData 
  QString name;
  int age;
  UserData(const QString & name_, int age_) :
    name(name_), age(age_) 
  UserData() 
;

class DialogBase : public QDialog 
  QDialogButtonBox m_box;
protected:
  QDialogButtonBox & buttonBox()  return m_box; 
  virtual void isAccepted() 
  virtual void isApplied() 
  virtual void isReset() 
  virtual void isRejected() 
public:
  DialogBase(QWidget * parent = 0) : QDialog(parent) 
    m_box.addButton(QDialogButtonBox::Apply);
    m_box.addButton(QDialogButtonBox::Reset);
    m_box.addButton(QDialogButtonBox::Cancel);
    m_box.addButton(QDialogButtonBox::Ok);
    connect(&m_box, SIGNAL(accepted()), SLOT(accept()));
    connect(&m_box, SIGNAL(rejected()), SLOT(reject()));
    connect(this, &QDialog::accepted, [] isAccepted(); );
    connect(this, &QDialog::rejected, [] isRejected(); );
    connect(&buttonBox(), &QDialogButtonBox::clicked, [this](QAbstractButton* btn)
      if (m_box.buttonRole(btn) == QDialogButtonBox::ApplyRole)
        isApplied();
      else if (m_box.buttonRole(btn) == QDialogButtonBox::ResetRole)
        isReset();
    );
  


class UserDialog : public DialogBase 
  QFormLayout m_layout;
  QLineEdit m_name;
  QSpinBox m_age;
  UserData m_initialData;
public:
  UserDialog(QWidget * parent = 0) : QDialog(parent), m_layout(this) 
    m_layout.addRow("Name", &m_name);
    m_layout.addRow("Age", &m_age);
    m_age.setRange(0, 200);
    m_layout.addRow(&buttonBox());    
  
  /// Used by external objects to be notified that the settings
  /// have changed and should be immediately put in effect.
  /// This signal is emitted when the data was changed.
  Q_SIGNAL void applied(UserData const &);
  UserData get() const 
    return UserData(
      m_name.text(), m_age.value());
  
  void set(const UserData & data) 
    m_name.setText(data.name);
    m_age.setValue(data.age);
   
  void setInitial(const UserData & data)  m_initialData = data; 
  bool isModified() const  return get() == m_initialData; 
protected:
  void isAccepted() Q_DECL_OVERRIDE  emit applied(get()); 
  void isApplied() Q_DECL_OVERRIDE  emit applied(get()); 
  void isReset() Q_DECL_OVERRIDE  set(m_initialData); 
;

【讨论】:

感谢您的回复它确实有帮助,现在我正在为每个小部件(即 QLineEdit、QTextEdit、QComboBox、QDateEdit)使用 foreach 循环。但是有没有其他方法可以直接检查小部件(QWidget 的 foreach 循环),这些方法是可修改的(即因为使用单个 foreach 循环很容易)。【参考方案2】:

如果您只是在对话框关闭时检查输入字段是否已填充,则不需要标志,您只能检查是否有任何输入。

如果您在某些时候以编程方式填充输入字段,但也只对对话框关闭时的更改感兴趣,您还可以在关闭函数中检查当前输入是否等于您之前设置的输入。

从您发布的代码中,我真的看不出您需要这些标志。

【讨论】:

如果假设我的对话框中有 20 到 30 个 ui 元素,那么通过检查每个输入,这将是一个巨大的数字。比较肯定不可靠。 那么每个人都有一个标志更好吗?

以上是关于检查 ui 元素的值是不是已更改的主要内容,如果未能解决你的问题,请参考以下文章

通过检查元素更改隐藏字段的值会更改服务器端的实际值

检查 UI 元素/RectTransform 是不是重叠

错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。以前的值:

ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。以前的值:“未定义”异常

如何检查程序集是不是已更改

如何检查 UI 是不是正在刷新?