About deleting, removing widgets and layouts in Qt 4

darkgaze picture darkgaze · Jul 17, 2013 · Viewed 55.1k times · Source

(I use Qt 4.7, Windows 7, 64bit).

I created a custom table. Each row is a horizontal layout with widgets. The rows are kept in a QList for easy access, and the children too. The rows are also added inside the parent widget.

If I resize the parent widget, I calculate the new sizes, delete everything, and recreate it again.

My problem is that I don't want to delete any widget. Only when I clear the table, I do it.

Since I have the widgets inside a QList and inside the parent layouts, How can I remove all widgets in each row, delete all layouts, and then add those to new layouts?

If I do: takeAt(0) for every element inside each layout I have a QLayoutItem with a widget inside... How can I delete the layoutItem without deleting the widget?.... How do I remove the widget without killing it, no matter if it's in the parent or the child? Because there are many methods for deleting: removeItem, removeWidget... in a layout, but not takeWidget... just takeAt() and it gives a Qlayoutitem.

I tried several ways, but I still see the widgets no matter what happened to them.

Questions about this:

  • When does a widget get deleted? If I takeWidget(index) from a layout, is it deleted some time by itself? does it happen if I have a pointer to it in another list?

  • removeAt(index) does execute the delete method of a widget?

Answer

darkgaze picture darkgaze · Jul 18, 2013

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