How to Clear all the Widgets in Parent Widgets?
Asked Answered
H

5

23

I am using the constructor QWidget(QWidget *parent). This parent widget contains a lot of child widgets. I need to clear all the child widgets from the parent at runtime. How can I do this?

Hamitosemitic answered 15/10, 2010 at 7:52 Comment(1)
Answers to the question #4272696 may be relevant.Escallop
V
-6

You can use the following in your parent widget class:

QList<QWidget *> widgets = findChildren<QWidget *>();
foreach(QWidget * widget, widgets)
{
    delete widget;
}
Vandenberg answered 15/10, 2010 at 8:2 Comment(3)
There is also a function provided by Qt called qDeleteAll, which takes a container and deletes every item in the container. So you could simplify this to qDeleteAll(findChildren<QWidget*>());Portuna
if there are LOTS of children, you might also consider to setUpdatesEnabled(false); qDeleteAll(findChildren<QWidget*>()); setUpdatesEnabled(true);Farika
This answer is wrong, it works only if child widgets have no children themselves. Otherwise, grandchildren will be deleted twice. Because findChildren recursively lists all children, grandchildren, etc...Comet
C
28

Previous answer is wrong!! You cannot use findChildren to delete a widget's children, because Qt4's findChildren recursively lists children. Therefore, you will delete children of children, which then may be deleted twice, potentially crashing your app.

More generally, in Qt, taking a list of QObject pointers and deleting them one by one is dangerous, as destroying an object may chain-destroy other objects, due to the parent ownership mechanism, or by connecting a destroyed() signal to a deleteLater() slot. Therefore, destroying the first objects in the list may invalidate the next ones.

You need to list children widgets either by:

  • Passing the Qt::FindDirectChildrenOnly flag to findChild if you are using Qt5 (which did not exist when the question was asked...)
  • Using QLayout functions for listing items,
  • Using QObject::children, and for each test if it is a widget using isWidgetType() or a cast
  • Using findChild() in a loop and delete the result until it returns a null pointer
Comet answered 5/2, 2013 at 8:58 Comment(7)
You can also use Qt::FindDirectChildrenOnly on findChildren to avoid the double-deletionConnoisseur
@DrewMcGowen : true, but only since Qt5Comet
@DrewMcGowen you should post an answer with an exampleNatalia
This answer is missing out the simple fact that findChildren can be called with Qt::FindDirectChildrenOnly. It is very much misleading.Richly
@KubaOber : except that the question was asked three years before the release of Qt5, and that Qt::FindDirectChildrenOnly is Qt5 only. If you dig a lot of graves you will probably find lot of stuff like this...Comet
Hah, didn't know that. Today I Learned :) Thank you.Richly
qDeleteAll uses delete on the container's items. Would it be more appropriate/safer to manually loop and call deleteLater instead?Cinereous
T
24

To take care of the recursivity problem pointed out by @galinette you can just remove the widgets in a while loop

while ( QWidget* w = findChild<QWidget*>() )
    delete w;
Toastmaster answered 13/5, 2014 at 12:17 Comment(3)
This is the most suitable answer in my opinionMercurialism
Have a look at https://mcmap.net/q/556022/-how-to-clear-all-the-widgets-in-parent-widgets if you are using Qt5 (which you should nowadays), the answer is much more complete. But this code still works.Toastmaster
This still seems the best answer for Qt version 4. According to the documentation for QObject::findChild() (doc.qt.io/archives/qt-4.8/qobject.html#findChild), if more than one child [sic, but what they really mean is descendant] matches the search criteria, the most direct [emphasis mine] ancestor [sic, but what they really mean is descendant] is returned. Thus, a direct child widget is certain to be destroyed first, which in turn will recursively destroy more indirect descendant widgets under that.Ask
E
11

Summarizing and supplementing:

For Qt5 in one line:

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

For Qt5 for a lot of children, using setUpdatesEnabled():

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

Note that this is not exception safe! While Qt does not at this time appear to throw exceptions here, the signal destroyed() could be connected to code that does throw, or an overridden Object::childEvent(QChildEvent*) could throw.

Better would be to use a helper class:

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

For Qt4:

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

Removing from the QLayout works in both Qt4 and Qt5:

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

QObjects (and therefore QWidgets) remove themselves (automagically) from their parent in their (QObject) destructor.

Easygoing answered 4/3, 2016 at 17:17 Comment(2)
For me (Qt 5.12.6), findChildren<QWidget*>("", Qt::FindDirectChildrenOnly) finds nothing, but findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly) works.Schizomycete
@Schizomycete but for Qt 5.15 worksLyse
N
6

From Qt docs

The following code fragment shows a safe way to remove all items from a layout:

QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
    ...
    delete child;
}
Narco answered 8/10, 2015 at 11:8 Comment(0)
V
-6

You can use the following in your parent widget class:

QList<QWidget *> widgets = findChildren<QWidget *>();
foreach(QWidget * widget, widgets)
{
    delete widget;
}
Vandenberg answered 15/10, 2010 at 8:2 Comment(3)
There is also a function provided by Qt called qDeleteAll, which takes a container and deletes every item in the container. So you could simplify this to qDeleteAll(findChildren<QWidget*>());Portuna
if there are LOTS of children, you might also consider to setUpdatesEnabled(false); qDeleteAll(findChildren<QWidget*>()); setUpdatesEnabled(true);Farika
This answer is wrong, it works only if child widgets have no children themselves. Otherwise, grandchildren will be deleted twice. Because findChildren recursively lists all children, grandchildren, etc...Comet

© 2022 - 2024 — McMap. All rights reserved.