简体   繁体   English

QWidget失去了父母

[英]QWidget loses its parent

In my application I have a QDialog which itself contains a complex, QWidget -derived GUI element. 在我的应用程序中,我有一个QDialog ,它本身包含一个复杂的QWidget派生的GUI元素。 The QDialog is modal and opened with exec() and the embedded GUI element handles all user interactions. QDialog是模态的,并使用exec()打开,并且嵌入式GUI元素处理所有用户交互。

So only this child QWidget knows when the QDialog can be closed, which is done this way: 因此只有这个子QWidget知道何时可以关闭QDialog ,这是通过以下方式完成的:

QDialog* parent=qobject_cast<QDialog*>(parentWidget());
if (parent) parent->close();

This is necessary because the QDialog has to be closed and not only the QWidget . 这是必需的,因为不仅必须关闭QWidget而且必须关闭QDialog

Now a user reported a situation where QDialog::exec() has returned but where the dialog (or only the GUI element?) was still visible. 现在,用户报告了QDialog::exec()已返回但对话框(或仅GUI元素?)仍然可见的情况。 From the log files I can see QDialog::exec() really has returned and the code right after this call was executed. 从日志文件中,我可以看到QDialog::exec()确实已经返回,并且此调用执行后立即执行了代码。

So my current assumption: the GUI element has lost its parent so that the close() call shown above was not called because "parent" was null. 因此,我目前的假设是:GUI元素丢失了其父元素,因此未调用上面显示的close()调用,因为“ parent”为null。

Any idea how this can happen? 知道如何发生吗? Is there a regular way where the parent of a QWidget can disappear? QWidget的父级是否可以通过常规方式消失?

Generally speaking, using QDialog::exec to reenter the event loop will cause trouble, because suddenly all the code that runs in the main thread must be reentrant. 一般来说,使用QDialog::exec重新进入事件循环会造成麻烦,因为突然之间,在主线程中运行的所有代码都必须重新进入。 Most likely you're facing fallout from that. 很可能您会因此而面临后果。 Don't reenter the event loop, and you'll be fine or the problem will become reproducible. 不要重新进入事件循环,这样就可以了,否则问题将变得可重现。

If you need to react to the dialog being accepted or rejected, connect code to the relevant slots. 如果您需要对对话框被接受或拒绝做出反应,请将代码连接到相关插槽。 Ie change this: 即更改此:

void do() {
  MyDialog dialog{this};
  auto rc = dialog.exec();
  qDebug() << "dialog returned" << rc;
}

to that: 对此:

class Foo : public QWidget {
  MyDialog dialog{this};
  ...
  Foo() {
    connect(&dialog, &QDialog::done, this, &Foo::dialogDone);
  }
  void do() {
    dialog.show();
  }
  void dialogDone(int rc) {
    qDebug() << "dialog returned" << rc;
  }
};

or, if you want to lazily initialize the dialog: 或者,如果您想延迟初始化对话框:

class Foo : public QWidget {
  MyDialog * m_dialog = nullptr;
  MyDialog * dialog() {
    if (! m_dialog) {
      m_dialog = new MyDialog{this};
      connect(m_dialog, &QDialog::done, this, &Foo::dialogDone);
    }
    return m_dialog;
  }
  ...
  void do() {
    dialog()->show();
  }
  void dialogDone(int rc) {
    qDebug() << "dialog returned" << rc;
  }
};

It is a horrible antipattern for the child widget to attempt to meddle with the parent. 子控件试图与父控件进行干涉是一种可怕的反模式。 The knowledge that the widget has a parent should not leak into the widget, it should be localized to the parent. 小部件具有父级的知识不应泄漏到小部件中,而应将其本地化为父级。 Thus, the child widget should emit a signal that indicates that eg the data was accepted. 因此,子窗口小部件应发出指示例如数据已被接受的信号。 When you create the dialog, connect this signal to the dialog's accept() or close() slots: 创建对话框时,将此信号连接到对话框的accept()close()插槽:

class MyWidget : public QWidget {
  Q_OBJECT
public:
  Q_SIGNAL void isDone();
  ...
};

class MyDialog : public QDialog {
  QGridLayout layout{this};
  MyWidget widget;
public:
  MyDialog() {
    layout.addWidget(&widget, 0, 0);
    connect(&widget, &MyWidget::isDone, this, &QDialog::accepted);
  }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM