PDA

View Full Version : Removing widget from layout



lotek
17th July 2011, 12:19
Hello,

I'd like to remove a widget from the layout and delete it.

for this I'd like to use the following source code

...
int cnt;
QLayoutItem *it;
MyWidget *w;

cnt = aVLayout->count();

for( int i = 0; i < cnt; i++ )
{
it = aVLayout->itemAt(0);
w = ((MyWidget *)(it->widget()));
aVLayout->removeWidget( w );
delete w;
aVLayout->removeItem( it );
delete it; // SEGMENTATION VIOLATION CRASH!!!
}

My questions:
- Is it necessary to remove and delete the QLayoutItem item?
- how to do it to avoid the crash?

In my understanding, the QLayoutItem has been created and added, when the addWidget() method has been called, so when I am removing a widget from the layout and deleting it, also the QLayoutItem shall be removed and deleted. Is it correct?

I am using Qt 4.6.3

Thx for your help

DanH
17th July 2011, 13:07
I suspect you should remove the item first, then delete it. Since it owns the widget, there would be no need to separately delete the widget. In your code you've deleted the widget while the item still "owns" it, so the item destructor attempts to delete the widget again.

SixDegrees
17th July 2011, 13:07
This approach can never work; you're guaranteed to walk off the end of the layout vector once you remove something from it, because you keep looping over the entire initial count before removal.

Zlatomir
17th July 2011, 13:21
A simple approach for this kind of "dynamic" layout is to keep a QList (or other container) of pointers to Widgets and use those pointers to add/remove from the layout.

//you can code add and remove helper functions that add or remove widget pointer both to layout and to QList.
LE:
the remove function also delete the widget pointer.

lotek
17th July 2011, 13:29
???
I am always removing the first item cnt times. What's wrong about it?

I've corrected the attached source code, there was the copy & paste bug, the name of the layout was the real one instead of the "aVLayout" used for this example.

Added after 4 minutes:

Hello Zlatomir,

This is the possible workaround. But why my code does not work?
And what about empty (without QWidget) QLayout items? Shall they stay ?
Is it a bug or feature?

Zlatomir
17th July 2011, 13:34
I don't have time right now to check the QLayoutItem (http://doc.qt.nokia.com/latest/qlayoutitem.html) documentation, but you can check it, my guess you shouldn't delete it (or anyway what you want is to delete the contained widget <if any>)

lotek
17th July 2011, 13:49
This approach can never work; you're guaranteed to walk off the end of the layout vector once you remove something from it, because you keep looping over the entire initial count before removal.

???
I am always removing the first item cnt times. What's wrong about it?

Added after 8 minutes:


I don't have time right now to check the QLayoutItem (http://doc.qt.nokia.com/latest/qlayoutitem.html) documentation, but you can check it, my guess you shouldn't delete it (or anyway what you want is to delete the contained widget <if any>)

Unfortunately, there is no word about it.
In the void QLayout::removeItem ( QLayoutItem * item )
it is stated"
"Removes the layout item item from the layout. It is the caller's responsibility to delete the item."
( it is what I attempt do do)

in the description of void QLayout::removeWidget ( QWidget * widget )
there is:
"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.
Note: The ownership of widget remains the same as when it was added."

DanH
17th July 2011, 14:42
Zlatomir, there is no interface (that I can find) to remove a widget from a QLayoutItem -- once it's in the layout item you can only delete the layout item.

[To the OP:]
Might we ask why you want to do all this in the first place? Why don't you just delete the entire layout and be done with it?

At the very least, change your loop to work from the end backwards, or just delete item(0) cnt times.

And remember, you need to check the item type. An item may be a spacer, or another layout.

[And thanks to this strange forum software for glomming all my posts together, even though the later ones are directed at the OP.]

lotek
17th July 2011, 16:12
Zlatomir, there is no interface (that I can find) to remove a widget from a QLayoutItem -- once it's in the layout item you can only delete the layout item.

[To the OP:]
Might we ask why you want to do all this in the first place? Why don't you just delete the entire layout and be done with it?

At the very least, change your loop to work from the end backwards,
or just delete item(0) cnt times.


And remember, you need to check the item type. An item may be a spacer, or another layout.
Yes, BUT there IS NO spacers or other layouts, Besides, there is no special treatment for such
QLayoutItems

[And thanks to this strange forum software for glomming all my posts together, even though the later ones are directed at the OP.]

"QLayoutItem -- once it's in the layout item you can only delete the layout item."
Deleting the QLayoutItem does not delete a widget - checked!
It is also stated in the description of the layout d-tor - in opposite to QLayoutItems the widgets are not deleted.

"Might we ask why you want to do all this in the first place? Why don't you just delete the entire layout and be done with it?"
Well it's a possible workaround. What when I need to exchange only some widgets? :)

"At the very least, change your loop to work from the end backwards,"
It does no matter, I've checked both variants, always the same.
"or just delete item(0) cnt times."
??? that's exactly what I am doing!

layout and be done with it?

At the very least, change your loop to work from the end backwards,
or just delete item(0) cnt times.


"And remember, you need to check the item type. An item may be a spacer, or another layout."
Yes, BUT there IS NO spacers or other layouts, Besides, why they should be treated in a special way? They would only return NULL on widget() call.


Fine, then let's change the Question:
Is it necessary to remove and delete the QLayoutItem, after removing and deleting of QWidget?

DanH
17th July 2011, 23:25
If you delete the widget, you haven't told the layout item it's deleted. The layout item still will attempt to address it. And there's no way to tell the layout item that its widget has been deleted.

lotek
18th July 2011, 11:04
If you delete the widget, you haven't told the layout item it's deleted. The layout item still will attempt to address it. And there's no way to tell the layout item that its widget has been deleted.

Please see the QLayout::removeWidget() method and note that I am using it before deleting the widget

DanH
18th July 2011, 13:00
Does removeWidget say you can destroy the widget?

lotek
20th July 2011, 11:56
Resolved:
After looking in the source code:
removeWidget method also remowes according QLayoutItem.

It's a pity that it is not well documented.
Also the API is very misleading - to get a widget at the position one have to get the QLayoutItem first.

alexandersv
21st July 2011, 04:40
Yup it does remove the item and you should destroy both.

I often use this function when I need to clear a layout:


void Class::clearLayout(QLayout *layout) {
if (layout) {
QLayoutItem *item;

//the key point here is that the layout items are stored inside the layout in a stack
while((item = layout->takeAt(0)) != 0) {
if (item->widget()) {
layout->removeWidget(item->widget());
delete item->widget();
}

delete item;
}
}
}

The code is easy to read and good looking ;)