Delete all children from QVBoxLayout - c++

I have a QVBoxLayout inside a scrollArea. I dynamically add QFormLayouts.
widgetTreeStruct* tree = new widgetTreeStruct(QString::number(numberOfGraphs));
QFormLayout* layout = tree->getTree(); // get QFormLayout
ui->verticalLayout_2->addLayout(layout); //add to the vertical layout
At one point I need to remove all the added QFormLayouts from the QVBoxLayout.
I tried several ways to do this.
Using qDeleteAll()
qDeleteAll(ui->verticalLayout_2->children());
2.delete item one by one
QLayoutItem* child;
while((child = ui->verticalLayout_2->takeAt(0)) != 0)
{
if(child->widget() != 0)
{
delete child->widget();
}
delete child;
}
But nothing happened. Only thing is when I try to add items to QVBoxLayout again new items are added on top of the previously added items.
I sense that I have to redraw, repaint, update, refresh or something. I tried ui->verticalLayout_2->update(); but didn't work for me.
So, What should I do?

I recursively deleted all the children and it worked for me.
This is my code.
void Widget::remove(QLayout* layout)
{
QLayoutItem* child;
while(layout->count()!=0)
{
child = layout->takeAt(0);
if(child->layout() != 0)
{
remove(child->layout());
}
else if(child->widget() != 0)
{
delete child->widget();
}
delete child;
}
}
remove(ui->verticalLayout_2);

Probably the widgets's parent is the containing widget, not their layout (what is passed to their constructors for the parent parameter?).
Maybe QObject::dumpObjectTree() can help you to understand the parent-child relationships.
What happens with your approach 2 (which does not rely on the widgets being children in the QObject-sense of the layout) is that it removes all items from the layout with the takeAt() method but deletes none of them: The children of your toplevel QVBoxLayout are the QFormLayouts, so calling widget() on their QLayoutItems returns 0. Just use delete child unconditionally to delete the child QLayouts. However, this still does not delete the child widgets. You could either recursively call takeAt() on the child layouts or delete all children of the parent widget (your QScrollArea) or keep a list of widgets and/or layouts yourself.

Related

Qt remove Layout from other Layout

How can I remove an layout_newInfo from layout_main in runtime (pressing button)?
code I tried:
QLayout *layout = this->layout();
QLayoutItem *item;
while ((item = layout->takeAt(0)) != 0)
layout->removeItem (item);
delete layout_newInfo;
layout_main->update();
What exactly do you want to achieve?
If you want to show/hide the widgets that are now in layout_newInfo, then
don't use a layout. Use a widget that you put in a layout_main (vertical layout), which itself has the newInfo items and layout, then just use setVisible(true/false) on the widget as you need.
How can I remove layout_newInfo from layout_main in runtime given layout_newInfo is nested to layout_main?
The semantically clearer method:
layout_main->removeItem(layout_newInfo); // make sure layout_newInfo object deleted
// after either by parent or somehow else
BTW, usually this should also do the same removing of nested layout:
delete layout_newInfo; // also removes it from upper layout
layout_main->update(); // triggers update on the screen
So, just 2 bottom lines of your code example should be sufficient where layout_main->update() call is needed only sometimes if no other update triggered.
The example from here shows that deleting QLayoutItem which is parent for QLayout does remove it from upper layout structure as well (its destructor does it).
Finally find an answer best way is making void method like void showNewInfo(QString action);
In class cpp file
void MainWind::showNewInfo(QString action)
{
if(action == "true")
{
bt_search->setEnabled(false);
bt_production->setEnabled(false);
bt_drying->setEnabled(false);
bt_storage->setEnabled(false);
ln_spent->show();
cb_thickness1->show();
cb_thickness2->show();
cb_thickness3->show();
cb_EFL1->show();
cb_EFL2->show();
bt_newItem->show();
}
else if(action == "false")
{
bt_search->setEnabled(true);
bt_production->setEnabled(true);
bt_drying->setEnabled(true);
bt_storage->setEnabled(true);
ln_spent->hide();
cb_thickness1->hide();
cb_thickness2->hide();
cb_thickness3->hide();
cb_EFL1->hide();
cb_EFL2->hide();
bt_newItem->hide();
}
}
Also there is possible to use setText(""), so next time showing fragment, it will be clear;

can't remove custom QWidget from QVBoxLayout

This is a reasonably well documented issue but after some reading and trying, I am still struggling to remove a custom widget from a QVBoxLayout.
in the MyClass.h file, I am declaring a vector to hold QPointer objects:
vector< QPointer<MyWidget> > myWidgets;
In the MyClass.cpp file, I am creating the widget and adding it to the layout with:
// create object
void MyClass::createMyWidget() {
QPointer<MyWidget> myWidget(new MyWidget());
myWidgets.push_back(myWidget);
}
//display in layout
void MyClass::displayWidgets() {
for(int i = 0; i < myWidgets.size(); i++) {
myWidgets[i]->setId(i);
ui->myVBoxLayout->addWidget(myWidgets[i].data());
}
}
I have tried the following to remove the widgets from the layout:
void MyClass::removeMyWidget(int id) { // id of the custom widget. id is also position in the layout
for(int i = 0; i < myWidgets.size(); i++) {
if(items[i]->getId() == id) { //correctly targets the widget
// method 1
ui->myVBoxLayout->removeItem(ui->myVBoxLayout->takeAt(i));
//method 2
//ui->myVBoxLayout->removeWidget(items[i]);
}
}
}
Neither of the above works and I have not been able to work out why.
In Qt, a layout is just a manager for sizing and positioning widgets (and other items, like nested layouts or spacers).
When you remove an item from a layout, it is still a child-widget of the parent widget that is layed out by the layout, only that its geometry is no longer managed. Still, the widget is present and displayed, and the user can interact with it.
You also have to remove it or hide it. Try
void MyClass::removeMyWidget(int id){ // id of the custom widget. id is also position in the layout
for(int i=0;i<myWidgets.size();i++){
if(items[i]->getId() == id){ //correctly targets the widget
ui->myVBoxLayout->removeWidget(myWidgets[i]);
delete myWidgets[i]; // <= Delete widget
}
}
}

Memoryleak with QListWidget addItem() + setItemWidget()

When I press a key there shall be a query to an engine. The results get put into a QListWidget by adding an item and setting the widget. Somehow this causes a massive memory overflow and even crashed my machine. But I dont get the error. Does clear() not delete the items passed to the QListWidget and the widgets set by setItemWidget(). I even tried to delete them on my own (comment), but still got a memoryleak. The error is in the if (!results.empty())-block, I guess, since commenting it out plugs the memoryleak.
void Widget::onTextEdited(const QString & text)
{
// QListWidgetItem * takenItem;
// while (takenItem = _results->takeItem(0)){
// delete _results->itemWidget(takenItem);
// delete takenItem;
// }
_results->clear(); _results->hide();
if (!text.isEmpty())
{
const std::vector<const Items::AbstractItem *> results = _engine.request(text);
if (!results.empty())
{
for (auto i : results){
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
ListItemWidget *w = new ListItemWidget;
w->setName(i->name());
w->setTooltip(i->path());
_results->setItemWidget(lwi, w);
}
_results->setFixedHeight(std::min(5,_results->count()) * 48); // TODO
_results->show();
}
}
this->adjustSize();
}
You should definitely use a memory leak detection tool instead of guessing around :)
UPDATE: clear() only deletes items but does not delete the widgets belonging to it. The widgets will be deleted if the QListWidget is deleted.
clear() does delete items and widgets belonging to it. And you mentioned that commenting out if(!results.empty()) solved the problem. I don't see any problem in the setItemWidget part. So I think the problem lies somewhere else, maybe ListItemWidget. How about you try replacing ListItemWidget with QLabel and see what happens. Eg:
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
//ListItemWidget *w = new ListItemWidget;
//w->setName(i->name());
//w->setTooltip(i->path());
QLabel *w = new QLabel;
w->setText("Hello");
_results->setItemWidget(lwi, w);

Cleaning a Qt layout and adding other widgets does not work. Ghost widgets stay. Qt bug?

I have a list of layouts with items inside. It forms a custom table.
To clean the table i loop through all the layouts and take out the items one by one. Then delete the layout.
// Delete all items
QHBoxLayout* row = NULL;
while( !rowLyts_.isEmpty() && (row = rowLyts_.takeAt(0)) != 0 )
{
QLayoutItem *item;
while ((item = row->takeAt(0)) != 0)
delete item;
delete row;
}
This seems to work. But when i start filling the table again i see "ghosts" of the items that were there before cleaning. Most of the times they are between lines, behind the new objets. And they still work.
This happens too when you use only a layout with widgets.
I just want to clean the whole layout of layouts without deleting the content widget. A safe way to clean a layout!.
You are deleting the layout items, but not the widgets that the items used to manage. You must delete the widgets. All of the non-layout items will be deleted automatically when you delete the layout itself.
QHBoxLayout* row;
while(!rowLyts_.isEmpty() && (row = rowLyts_.takeAt(0)))
{
QLayoutItem *item;
while ((item = row->takeAt(0))) {
// The item will be deleted when the layout itself is
// destructed. Items such as spacers will return a null
// widget, its deletion is a safe no-op.
delete item->widget();
// We don't handle recursion into sublayouts.
// We check for it so that we won't leak the layout.
Q_ASSERT(!item->layout());
}
delete row;
}
The assert is there to make sure the layout that the code works on matches the implied precondition: there must be no sub-layouts, since the code as written doesn't recurse into them. Any sub-layouts, with their widgets, would leak (not memory leak, but resource leak). The assert will abort the execution if the precondition is violated.
clearing the layout can be done by:
QLayoutItem* item;
while ( ( item = row->takeAt( 0 ) ) != NULL )
{
delete item->widget();
delete item;
}

Delete QGraphics Items from QGraphicsLinearLayout on QGraphicsScene

I'm having a really frustrating problem, trying to delete qgraphicsitems in my application. I have a menu controller which is responsible for adding buttons to a layout and adding them to the scene. These buttons are all connected with custom signals and slots. When I change states, I want to delete this controller and remove all of these qgraphicsitems.
Heres how I add them in my menu_controller.cpp:
QGraphicsWidget * temp;//this is used during iteration to add to the layout
this->layout = new QGraphicsLinearLayout(Qt::Vertical);//q graphics view layout
this->menu = new QGraphicsWidget;//holds the layout
// initialize the proper buttons
(this->game_state->is_logged_in()) ? (this->logged_in()) : (this->not_logged_in());//test whether or not the user is logged in to generate the correct menu
// now iterate through each button and add to the layout
for (int i = 0, z = this->buttons.size(); i < z; i++) {
temp = this->scene->addWidget(this->buttons[i]);//add widget to the scene
this->layout->addItem(temp);//add this widget to the layou
connect(this->buttons[i], SIGNAL(menu_selection(QString)), this, SLOT(set_menu_option(QString)));//connect the button to this
}
// set menu layout as the layout and then add the menu to the scene
this->menu->setLayout(this->layout);
this->position();
this->scene->addItem(this->menu);
Finally, my destructor looks like this:
QGraphicsScene * scene = this->game_state->get_scene();
QList<QGraphicsItem *> list = scene->items();
QList<QGraphicsItem *>::Iterator it = list.begin();
for (; it != list.end(); ++it)
if (*it)
scene->removeItem(*it);
for (int i = 0, z = this->buttons.size(); i < z; i++)
disconnect(this->buttons[i], 0, 0, 0);//button not connected to anything
// for each deletes each place in memory
for_each(this->buttons.begin(), this->buttons.end(), utilities::delete_ptr());
delete this->layout;//delete the layout container
delete this->menu;//delete the menu
I remove each of the buttons from the scene, disconnect the connected buttons and then try to call delete on them.
I get a segmentation fault each time. The scene items remove fine, and the disconnects work properly, but for some reason when I delete the items, it throws a segmentation fault and crashes the program.
My guess is there's something wrong in your utilities::delete_ptr().
But anyway. There's no need to disconnect the signal if you are deleting either the sender or receiver. That's automatically done when one of them is deleted.
There's also no need to go through the whole list of items in a scene and delete them. Calling QGraphicsScene::clear() will do. And even that is not necessary of you are deleting the scene anyway.
Thanks for the assistance.
What was causing the segmentation fault was the fact that the widgets were connected with signals and therefore needed to be deleted with the deleteLater() method.
It seems that deleting an element signals other widgets and when this occurred, it could not find the place an memory and thus called a seg fault ..