QWidget теряет своего родителя

В моем приложении у меня есть QDialog, который сам содержит сложный, производный от QWidget GUI-элемент. QDialog является модальным и открывается с помощью exec(), а встроенный GUI-элемент обрабатывает все взаимодействия с пользователем.

Так что только этот child-QWidget знает, когда QDialog может быть закрыт, что делается следующим образом:

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

Это необходимо, потому что должен быть закрыт QDialog, а не только QWidget.

Теперь пользователь сообщил о ситуации, когда QDialog->exec() вернулся, но где диалог (или только GUI-элемент?) Все еще был виден. Из файлов журнала я вижу, что QDialog->exec() действительно вернул код и код сразу после того, как этот вызов был выполнен.

Итак, мое текущее предположение: GUI-элемент потерял своего родителя, так что показанный выше вызов close() не был вызван, потому что "parent" был NULL.

Есть идеи, как это может произойти? Есть ли обычный способ, где родитель QWidget может исчезнуть?

Спасибо!

1 ответ

Вообще говоря, используя QDialog::exec повторный вход в цикл обработки событий вызовет проблемы, потому что внезапно весь код, который выполняется в основном потоке, должен быть повторно введен. Скорее всего, вы столкнулись с последствиями. Не повторяйте цикл обработки событий, и все будет в порядке, иначе проблема станет воспроизводимой.

Если вам нужно отреагировать на то, что диалоговое окно принято или отклонено, подключите код к соответствующим слотам. Т.е. изменить это:

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

к этому:

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

или, если вы хотите лениво инициализировать диалог:

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

Это ужасный антипаттерн для дочернего виджета - пытаться вмешиваться с родителем. Знание, что у виджета есть родитель, не должно попадать в виджет, оно должно быть локализовано для родителя. Таким образом, дочерний виджет должен излучать сигнал, который указывает, что, например, данные были приняты. Когда вы создаете диалог, подключите этот сигнал к диалогу 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);
  }
};
Другие вопросы по тегам