Как очистить все виджеты в родительских виджетах?

Я использую конструктор QWidget(QWidget *parent). Этот родительский виджет содержит много дочерних виджетов. Мне нужно очистить все дочерние виджеты от родителя во время выполнения. Как я могу это сделать?

Ответы

Ответ 1

В классе родительского виджета вы можете использовать следующее:

QList<QWidget *> widgets = findChildren<QWidget *>();
foreach(QWidget * widget, widgets)
{
    delete widget;
}

Ответ 2

Предыдущий ответ неверен! Вы не можете использовать findChildren для удаления дочерних элементов виджетов, потому что Qt4 findChildren рекурсивно отображает список. Таким образом, вы удалите дочерние элементы детей, которые затем могут быть удалены дважды, что может привести к сбою вашего приложения.

В более общем плане, в Qt, перечисление указателей QObject и их удаление один за другим опасно, так как уничтожение объекта может привести к разрушению других объектов из-за механизма родительского права или путем подключения destroyed() в слот deleteLater(). Следовательно, уничтожение первых объектов в списке может привести к недействительности следующих.

Вам нужно перечислить детские виджеты:

  • Передача флага Qt:: FindDirectChildrenOnly для findChild, если вы используете Qt5 (которого не было, когда задавался вопрос...)
  • Использование функций QLayout для перечисления элементов,
  • Использование QObject:: children и для каждого теста, если это виджет с использованием isWidgetType() или литой
  • Использование findChild() в цикле и удаление результата до тех пор, пока он не вернет нулевой указатель

Ответ 3

Чтобы решить проблему рекурсивности, отмеченную @galinette, вы можете просто удалить виджеты в цикле while

while ( QWidget* w = findChild<QWidget*>() )
    delete w;

Ответ 4

Из Qt docs

Следующий фрагмент кода показывает безопасный способ удаления всех элементов из макета:

QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
    ...
    delete child;
}

Ответ 5

Подведение итогов и дополнение:

Для Qt5 в одной строке:

qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));

Для Qt5 для большого количества детей, используя setUpdatesEnabled():

parentWidget->setUpdatesEnabled(false);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
parentWidget->setUpdatesEnabled(true);

Обратите внимание, что это не исключение! В то время как Qt в это время не появляется, чтобы генерировать исключения здесь, сигнал destroy() может быть связан с кодом, который выполняет бросок, или может быть брошен переопределенный Object:: childEvent (QChildEvent *).

Лучше было бы использовать вспомогательный класс:

class UpdatesEnabledHelper
{
    QWidget* m_parentWidget;
public:
    UpdatesEnabledHelper(QWidget* parentWidget) : m_parentWidget(parentWidget) { parentWidget->setUpdatesEnabled(false); }
    ~UpdatesEnabledHelper() { m_parentWidget->setUpdatesEnabled(true); }
};

...

UpdatesEnabledHelper helper(parentWidget);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));

Для Qt4:

QList<QWidget*> childWidgets = parentWidget->findChildren<QWidget*>();
foreach(QWidget* widget, childWidgets)
    if (widget->parentWidget() == parentWidget)
        delete widget;

Удаление из QLayout работает как в Qt4, так и в Qt5:

QLayoutItem* child;
while (NULL != (child = layout->takeAt(0))) // or nullptr instead of NULL
    delete child;

QObjects (и, следовательно, QWidgets) удаляют себя (автоматически) из своего родителя в их (QObject) деструкторе.