About deleting, removing widgets and layouts in Qt 4 - qt

(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?

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

A widget in Qt is a regular C++ object and can be deleted with the C++ delete operator as any other object:
delete myWidget;
In Qt there is always a parent-child relation between widgets. When the parent widget is destroyed, it will delete all its children. Usually, you do not need to think about explicitly deleting any widgets but the top level widgets, i.e., windows and dialogs. Qt will take care of deleting any child widgets.
QList::removeAt(int) does not delete the object that is removed, it only removes the object from the list. If you also want to delete the object you would have to do something like:
delete myList.takeAt(0);
This applies to all functions such as removeAt(int), takeAt(int), takeFirst(), etc. They never delete objects, they only remove them from the container (list, layout, scrollarea, etc). In most cases the ownership of the widget is then transferred to the caller, (the caller becomes responsible for deleting the widget as the parent-child relation breaks), but do not assume this is always the case, always read the documentation of the function.

Related

Issue regarding setting parent of widget in Qt

From this post here, in general:
All QObjects will delete their own child objects automatically. (See
docs here.) QWidgets are QObjects. So as long as you establish a
parent/child relationship, you do not need to manually delete your
objects. To do that, simply pass a pointer to the parent object to the
constructor:
QLabel *label1 = new QLabel; // <<- NEED TO DELETE
QLabel *label2 = new QLabel(some_parent_obj); // Will be deleted when some_parent_obj is deleted
So some questions arises:
Does every widget necessary needed/required a parent? If no, what are the exceptions? If yes, what happens to widgets without parent?
I asked this because from examples in Qt Docs, some example widgets have parents (QLabel example) but some doesn't (QBarChart example, and also QFont, QColor, etc...).
So I'm wondering if there's an exception, or those widgets just don't need any parents, or if I declare them with new for some reason, I have to delete afterward.
And vice versa...
Does a widget without parent guarantee to cause a memory leak (or something similar) when the widget which it stays in (not necessary its parent) is deleted? Or if it's removed from a layout without any deletion happening?
Because from my experience with my code, I've created probably quite a lot (~100) of widgets and other stuffs without neither setting any parent (nor using delete afterward), and the project appears to run fine without any stalls even after a while (the effect might be underlying though, as I haven't run Memcheck), hence this question is here.
Does every widget necessary needed/required a parent?
If you want them to be deleted automatically - yes. But...
If no, what are the exceptions? If yes, what happens to widgets without parent?
You do not need to provide a parent to widget if you attach it to layout using QLayout::addWidget. If you look into source code, you'll see that when you do so, it automatically attaches layout's parent as widget's parent(unless you didn't attach layout to any widget).
But if you leave the widget created with new without parent and do not attach to anything - it is leaking memory. You must delete it either using delete or QObject::deleteLater. The last option is recommended when object has any connections.
Does a widget without parent guarantee to cause a memory leak (or something similar) when the widget which it stays in (not necessary its parent) is deleted? Or if it's removed from a layout without any deletion happening?
As I already mention QLayout::addWidget sets parent for added widget, so the answer is no. Also note, that when you call QLayout::removeWidget, it removes only QLayoutItem from layout, but widget's parent stays the same as it was after calling QLayout::addWidget.

How to enable both internal reordering and external dropping in a Qt widget?

I have created a widget which inherits QListWidget.
My goal is for it to accept files dropped into it from an external file manager, and for the user to be able to reorder the elements in the widget. I can achieve both, but not at the same time.
If I just set
myWidget->setDragDropMode(QListView::InternalMove);
myWidget->setDragEnabled(true);
I can reorder the items within the widget, but I can't drop external items into it.
If I reimplement the dragMoveEvent, dragEnterEvent and dropEvent events, all of them just having acceptProposedAction(); and some debug messages inside them, I can drop external files into my widget, but I can no longer rearrange the items.
Is there a way to have the above two at the same time, or do I have to manage the items myself in the reimplemented functions? If so, how can I know if a dropped item is internal or external, and how can I know from which position it was taken and into which position in the list it was dropped into?
If I parse the mimeData which I got from the event, I can see whether it as a file or a text, and I get "qabstractitemmodeldatalist" if it was an internal item, but it still doesn't give me its position.
I can check event->pos() to know in pixels where the drop has been made, and event->source() to learn about what was dropped there, but is this really best practice, to start calculating pixel values and adding objects "manually"?
The solution was very simple: I just had to call the functions of the parent class at the end of each function I've overridden.
void myWidget::dropEvent(QDropEvent *event)
{
do_stuff_with_received_data(event);
QListWidget::dropEvent(event);
}

Qt hide QLayout (switch between two layouts)

I didn't find solution for my problem with two QLayouts. I need app with QHBoxLayout with possible expandind when I will add new widgets, push buttons, ....
So what I have: One QDialog and two layouts. Now I know that I can't hide the layout.
So I tray just :
layout()->removeItem(firstlayout);
layout()->addLayout(secondLayout);
But when I did this, I saw all items in first layout on possition [0,0].
So next step I try:
for (all items in first layout) if (widget) widget->hide();
But this is working only with QWidget and I have many different items in layouts.
Simply way is use the widget, because there is possible to use hide/show, but I need auto expanding window when I add new items.
Just rebuild the layout, there is no need to keep the two layouts in existence at the same time. Probably clearest is to have two (or more) methods, which first delete current layout, then create new layout, add widgets to it, hide all widgets you want hidden, and set it as current layout. Note that you don't even need to keep a member variable for the layout, since QWidget has that anyway and provides you with setter and getter.
Or, if you have different widgets in different layouts, and actually want to be able "switch pages" so to say, simply use QStackedWidget. Or if you have a fixed part (buttons etc), and then part with "pages", then put the "pages" into QStackedWidget, and keep fixed part out of it.
'addLayout(secondLayout/firstLayout) ' will remove the other layout automatically, you do not have to remove it. If you keep a pointer to the layout(which has addWidget() before), you can simply use the layout and widgets in it later. :)

How to remove widget from another Qwidget?

i created one QWidget(Parent). in the inside of the parent widget i created another one QWidget(Child). In the run time i need to remove the child widget. how to do that?
i am not using any layout. i am directly putting in the Parent Widget.
Please Help me to fix this.
If you add the widget with e.g.:
QWidget *w = new QWidget(parent);
...then you can remove it with:
delete w;
Another approach would be to just hide it:
w->hide();
This answer is for those arriving from search engines and want an answer to the question as stated in the title.
If you want to remove a child from a parent without deleting it or hiding it (which does NOT remove it from its parent), set the child's parent to NULL.
QWidget::setParent(NULL)
Note that explicitly reparenting a widget like this carries several implications (e.g visibility automatically set to hidden). See QWidgets documentation for more information.

How to remove QWidgets from QSplitter

In my app have a window splitted by a QSplitter, and I need to remove an widget.
How can I do that? I can't find useful methods
It's not clear to me if you want to preserve the widget and put it somewhere else, or if you want to destroy the widget.
Destroying the widget: If you can
get a pointer to the widget, you can
simply delete it. The splitter will
safely be notified that its child is
being deleted and will remove it
from itself.
Preserving the widget: If you grab
the pointer to the widget, you can
simply set its parent to some other
widget and add it to another
widget's layout and it will show up
there. This is safe because the
QSplitter will be notified that one
of its children is being reparented.
If you want to set the parent to NULL (cjhuitt's answer) be aware that you are now responsible for cleaning up that memory because the widget no longer has a parent.
Many things in Qt cannot be "traditionally" removed. Instead call hide() on it and destruct it. From QSplitter documentation:
When you hide() a child its space will
be distributed among the other
children. It will be reinstated when
you show() it again.
I like Tuminoid's answer. But if you absolutely need it removed, try getting the widget you want to remove, and calling setParent( NULL ) on that widget. That's my best guess.
If you hold a pointer to the widget, then just delete it, or use deleteLater() if you want to be on the safe side.
If you do not have the widget pointer, use QSplitter::widget(int index) function. Then, you can use invoke its deleteLater() slot.
If you do not have the widget index, but you still know the widget objectName(), then QObject::findChild() is your only way to get the widget pointer.
I ran into the same problem. In Qt 4.8 to temporally hide one of the widget of a QSplitter I simply hide it. However it is not enough, as the splitter handle is still available to move. But the handle can be accessed and hidden as well:
frameA->setVisible(conditionA);
frameB->setVisible(conditionB);
if ( !(conditionA && conditionB) ) // if only 1 frame is visible
{
splitter->handle(0)->setVisible(false);
}
Another easy way to prevent the child widget from getting deleted is to use QSplitter.takeWidget(child). This is also the recommended way of removing the widget from a splitter. (Qt Documentation)

Resources