Qt - Remove All Widgets from Layout

Qt - Clear all widgets from inside a QWidget's layout

The wonderful thing about layouts is that they handle a deletion of a widget automatically. So all you really need is to iterate over the widgets and you're done. Since you want to wipe out all children of a given widget, simply do:

for (auto widget: ui->myWidget::findChildren<QWidget*>
({}, Qt::FindDirectChildrenOnly))
delete widget;

No need to worry about layouts at all. This works whether the children were managed by a layout or not.

If you want to be really correct, you would need to ignore the widgets that are child widgets but are stand-alone windows. This would be the case if this was in general-purpose
library code:

for (auto widget: ui->myWidget::findChildren<QWidget*>
({}, Qt::FindDirectChildrenOnly))
if (! widget->windowFlags() & Qt::Window) delete widget;

Alternatively, if you only want to delete the children managed by a given layout and its sublayouts:

void clearWidgets(QLayout * layout) {
if (! layout)
return;
while (auto item = layout->takeAt(0)) {
delete item->widget();
clearWidgets(item->layout());
}
}

How to remove widgets from layout in Qt

It seems that you have some misconceptions about ownership. A QLayout takes ownership of any item that is added to it: http://doc.qt.io/qt-5/qlayout.html#addItem

That means the QLayout is responsible for deleting these items. If you delete them then the QLayout will also try to delete them and then you get the crash you're seeing now.

QLayout doesn't have good functionality for deleting contents and re-adding them (for example removeWidget probably doesn't work as you would hope.) But there's a reason for this.

QLayout is not intended to be used as a list view.

What you do want is a, wait for it, QListView. Which will even handle the scroll functionality for you, and make adding and removing elements a possibility.

About deleting, removing widgets and layouts in Qt 4

Ok. I got it working.
Let me explain how this Removing, keeping widgets works.

A widget is known by its parent layout. And you remove it through the layout. By doing:

layout()->removeAt(widget);
delete widget;

If you use takeAt(index) in a QLayout (or its children), it gives you a QLayoutItem. To access the widget inside, just use widget(). But there's no way to remove the widget without deleting it. So this approach is non valid.

In the Docs it tells a way to delete the elements:

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

A special thing to note in Qt is the following:
If you have a hierarchy tree of layouts, added with addLayout() inside layouts, no matter how deep your widget is inserted, you can remove it from the child layouts or any of the parent layouts, if the tree path from the layout and this item is built from child layouts.

The easiest thing is to keep a list of pointers to all the items, in a custom table. When clearing the table to reconstruct it, just do this inside your widget:

  CustomTableItem* item;
while ( !items_.isEmpty() && ( (item = items_.takeFirst()) != 0 ) ){
layout()->removeWidget(item);
delete item; // It works no matter where the item is
}

items_.clear(); // clear the list afterwards.

And it works perfectly, updates the layout too by itself.
If you want to keep the elements, just skip the "delete item;" and use them afterwards.

An important thing to note is that different "remove" functions work differently (as i understand on Qt Docs) in QList or similar widgets, and in a QLayout.

In the QList, removeAt actually removes the object.

(Qt 4.7 QList Docs)"Removes the item at index position i. i must be a valid index position in the list (i.e., 0 <= i < size())."

In a QLayout, removeWidget or removeItem don't remove the item/widget, you have the responsability to delete it, as I did before.

(Qt 4.7 QLayout Docs) "Removes the widget widget from the layout. After this call, it is the
caller's responsibility to give the widget a reasonable geometry or to
put the widget back into a layout."

Hope it helps. If you see any error, you could tell me and I will edit the answer!

More on deleting here:
Other stackoverflow post

Clear all widgets in a layout in pyqt

After a lot of research (and this one took quite time, so I add it here for future reference), this is the way I found to really clear and delete the widgets in a layout:

for i in reversed(range(layout.count())): 
layout.itemAt(i).widget().setParent(None)

What the documentation says about the QWidget is that:

The new widget is deleted when its parent is deleted.

Important note: You need to loop backwards because removing things from the beginning shifts items and changes the order of items in the layout.

To test and confirm that the layout is empty:

for i in range(layout.count()): print i

There seems to be another way to do it. Instead of using the setParent function, use the deleteLater() function like this:

for i in reversed(range(layout.count())): 
layout.itemAt(i).widget().deleteLater()

The documentation says that QObject.deleteLater (self)

Schedules this object for deletion.

However, if you run the test code specified above, it prints some values. This indicates that the layout still has items, as opposed to the code with setParent.

How to delete an already existing layout on a widget?

You just use

delete layout;

like you would with any other pointer you created using new.



Related Topics



Leave a reply



Submit