简体   繁体   中英

QWidget loses its parent

In my application I have a QDialog which itself contains a complex, QWidget -derived GUI element. The QDialog is modal and opened with exec() and the embedded GUI element handles all user interactions.

So only this child QWidget knows when the QDialog can be closed, which is done this way:

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 .

Now a user reported a situation where QDialog::exec() has returned but where the dialog (or only the GUI element?) was still visible. From the log files I can see QDialog::exec() really has returned and the code right after this call was executed.

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.

Any idea how this can happen? Is there a regular way where the parent of a QWidget can disappear?

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. 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:

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);
  }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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