PDA

View Full Version : QStackedWidget and memory management



martinn
29th March 2010, 20:45
I'm trying to implement a stack solution in my application so I can push and pop widgets into the stack. I'm using a QStackedWidget. When a widget is removed from the stack I want to make sure that it is also deallocated. In my code below will the pageWidget get deleted and deallocated when I run removeWidget()? Or will it be deleted when stackedWidget is deleted? How can I make it get deleted when it is removed from the stackedWidget?



QStackedWidget *stackedWidget = new QStackedWidget(this);
QWidget *pageWidget = new QWidget(stackedWidget);
stackedWidget->addWidget(pageWidget);

//Will this delete this pageWidget and deallocate memory?
stackedWidget->removeWidget(pageWidget);

Lykurg
29th March 2010, 20:51
Well the documentation is clear on that:
Note: The ownership of widget remains the same. The widget is not deleted, but simply removed from the widget's stacked layout, causing it to be hidden.

So you have to do it yourself:
stackedWidget->removeWidget(pageWidget);
pageWidget->deleteLater(); // or: delete pageWidget;

martinn
30th March 2010, 09:33
Aha, must have missed that! :-) So using deleteLater will delete the widget as soon it is removed from stackedWidget and not is visible? Or will it still need stackedWidget to get deleted before widget gets deleted (as stackedWidget is the parent)?

Lykurg
30th March 2010, 09:51
deleteLater has nothing to do with your stacked widget. By calling stackedWidget->removeWidget(widget), widget is removed from the stacked widget and is not visible any more. Then you have free the memory by calling delete. That's the normal way. deleteLater ist just a little bit saver, but behaves like delete, only that "delete" is called on the widget after control returns to the event loop. Compare:

void Class::function()
{
//...
delete widget;
int i = widget->getSomeInt(); // this will crash your app!
delete widget; // invalide
}

void Class::function()
{
//...
widget->deleteLater();
int i = widget->getSomeInt(); // this will NOT crash your app!
widget->deleteLater(); // possible
}

martinn
30th March 2010, 10:20
Yes, I think I understand. But when testing my application I can't see the memory get released it keeps growing. To be totally sure that I understand right I'll try and explain my solution:

My application is built on a stack solution looking like this:

I have one QStackedWidget. The first widget in the QStackedWidget is a kind of home screen. It has a list of categories. When the user clicks a category a new widget is added to the QStackedWidget with a list of sub categories. If the user clicks a sub category a new widget is added with a new list. There are some steps of sub categories until a widget shows where the user can edit items. When the user have saved the changes of an item he get sent back to the home screen and all the other widgets should get deleted from the stack.

It should look something like this:

Home screen->Categories->Categories->Items->Edit Item->Save---->
<-----------------back to home screen, delete all widgets------------



//To create the QStackedWidget and add the "home screen"
QStackedWidget *stackedWidget = new QStackedWidget(this);
QWidget *homescreenWidget = new QWidget(this);
stackedWidget->addWidget(homescreenWidget);
stackedWidget->setCurrentWidget(homescreenWidget);

//When the user clicks a category in the list (this is made each time a category in the list is clicked)
QWidget *categoryListWidget = new QWidget(this);
stackedWidget->addWidget(categoryListWidget);
stackedWidget->setCurrentWidget(categoryListWidget);

//When the user has saved the changes to an item he should be sent back to home screen
//All items except home screen should be removed from stackedWidget and deleted from memory
stackedWidget->setCurrentWidget(stackedWidget->widget(0));
for(int i = stackedWidget->count(); i>0; i--){
QWidget* widget = stackedWidget->widget(i);
stackedWidget->removeWidget(widget);
widget->deleteLater();
}

Lykurg
30th March 2010, 10:27
First you should reconsider your approach. Why do you do not reuse the widgets? I think it is better than permanent creating and removing. But anyway.

try to use delete and see what the memory is telling you. I guess the same. What tool do you use of measuring the memory usage? And then, the application (or better the system) frees the memory but kept it allocated with the application. So the memory is freed but in the "memory watching tool" it seems that is doesn't.

wysota
30th March 2010, 10:54
If you are using a decent OS, it will not deallocate memory immediately because you might want it back a moment later so it's keeping it for you. When something else requests a large block of memory, the allocator will deallocate the chunk from your process. If you want to make sure the object is deleted, you can place some debug statement in its constructor or use tools such as valgrind to monitor the memory.

martinn
30th March 2010, 13:12
I'm running the Symbian Emulator with Carbide C++, and in Diagnostics I can see the memory increasing and after maybe 4-5 times of clicking down in my solution and editing items, my app craches. The same thing happens if I run on the Nokia 5800 device, after some time I get a memory warning. I can understand this, it makes totally sense as the widgets parent never gets deleted and therefore neither the widgets, so for each time I click down in the solution more widgets are created.

What would be a better solution then using this way, how could I reuse the widgets? My approach is taken from http://www.forum.nokia.com/info/sw.nokia.com/id/d6287a35-5494-44af-abce-f65637c6b0b9/Qt_for_S60_QStackedWidget_Example.html In their code they don't use delete anywhere, maybe I shouldn't need that either but am doing something wrong. In the code in the nokia example I tried adding widget->deleteLater() in QBaseWidget after removing the widget from the stackedwiget but that crashes the application.

I'm not really sure how to solve this as adding widget->deleteLater() when deleting widget from stack widget craches the application and if I don't delete it I will get problem with memory. Any ideas?

martinn
6th April 2010, 18:43
Anyone have an idea on how to solve this?

wysota
6th April 2010, 21:35
You still didn't say what exactly is the problem. Of course we can try to guess how your code looks like but it would probably be easier if you just shew it to us.

martinn
6th April 2010, 23:35
Okey, its quite a large mess of code. But I'll make a smaller project that shows the problem and post it here asap. Thanks!

martinn
7th April 2010, 22:50
Ok this is going to be quite a long post:

I'm building an application for Qt for Symbian. Like I said before the idea of my application is built on a stack solution looking like this: I have one stacked widget. The first widget in the stacked widget is a kind of home screen. It has a list of categories. When the user clicks a category a new widget is added to the stacked widget with a list of sub categories. If the user clicks a sub category a new widget is added with a new list. The user clicks through the lists, and these steps can differ depending on user choices on the way. So the same widgets maybe wont be added in the same order each time. At last a widget shows where the user can edit items. When the user have saved the changes of an item he get sent back to the home screen and all the other widgets should get deleted from the stack.

It should look something like this:

Home screen->Categories->SOME STEPS->Items->Edit Item->Save---->
<-----------------back to home screen, delete all widgets------------

To be able to solve this I have subclassed QStackedWidget with my own CustomStackedWidget. The CustomStackedWidget can insert new widgets into the stack, remove them, and pop back to the home screen. This is my CustomStackedWidget:

CustomStackedWidget.h:


class CustomStackedWidget : public QStackedWidget
{
Q_OBJECT

public:
CustomStackedWidget(QWidget *parent);
virtual ~CustomStackedWidget();

//Pops the stack
virtual void popTo(int index);

// Activates existing view
virtual void activateWidget(QWidget* widgetToFind);

// Removes view
virtual void removeWidgetWithWidget(QWidget* widget);

// Activates new view.
virtual void activateNewWidget(QWidget* newWidget);

public slots:

// Activates previous view and deletes current
virtual void activatePreviousView();

};


CustomStackedWidget.cpp:


#include "CustomStackedWidget.h"

CustomStackedWidget::CustomStackedWidget(QWidget *parent)
: QStackedWidget(parent)
{

}

CustomStackedWidget::~CustomStackedWidget()
{
// TODO Auto-generated destructor stub
}

void CustomStackedWidget::popTo(int index){
this->setCurrentWidget(this->widget(index));
for(int i = this->count(); i > index; i--){
QWidget* widget = this->widget(i);
this->removeWidgetWithWidget(widget);
}
}

void CustomStackedWidget::activateWidget(QWidget* widgetToFind){
for (int i=0; i < this->count(); i++)
{
QWidget* widget = this->widget(i);
if (widget == widgetToFind)
{
this->setCurrentWidget(widget);
break;
}
}
}

void CustomStackedWidget::activateNewWidget(QWidget* newWidget){
this->addWidget(newWidget);
activateWidget(newWidget);
}

void CustomStackedWidget::removeWidgetWithWidget(QWidge t* widget)
{
this->removeWidget(widget);
widget->deleteLater();
}

void CustomStackedWidget::activatePreviousView()
{
QWidget *currentWidget = this->currentWidget();
QWidget *previous;

for (int i=0; i < this->count(); i++)
{
QWidget* widget = this->widget(i);
if (widget==currentWidget)
{
if (i>0)
{
i--;
previous = this->widget(i);

// Remove current widget
this->removeWidgetWithWidget(currentWidget);

// Activate previous widget
activateWidget(previous);
}
break;
}
}
}


Every widget that I insert into the stack is a sub class of my own widget MyWidget. To make it simple, in this example the UI of MyWidget just consist of one label showing which index in the stack the widget has. The UI also has one button to insert another widget, one button to remove the current widget and another one to pop to the first widget in the stack. This is just to show you the idea, in the real application the widget can have a lot of more stuff in it. This is MyWidget:

MyWidget.h:


class MyWidget : public QWidget
{
Q_OBJECT

public:
MyWidget(CustomStackedWidget *stackedWidget, QWidget *parent = 0);
MyWidget(int index, CustomStackedWidget *stackedWidget, QWidget *parent = 0);
~MyWidget();
CustomStackedWidget *stackedWidget;
int index;

private:
Ui::MyWidgetClass ui;

private slots:
void on_pushButtonRemove_clicked();
void on_pushButtonInsert_clicked();
void on_pushButtonPop_clicked();

};


MyWidget.cpp:



MyWidget::MyWidget(CustomStackedWidget *stackedWidget, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}

MyWidget::MyWidget(int index, CustomStackedWidget *stackedWidget, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this->stackedWidget = stackedWidget;
this->index = index;
ui.label->setText(QString::number(index,10));
}

MyWidget::~MyWidget()
{

}


void MyWidget::on_pushButtonRemove_clicked() {
this->stackedWidget->activatePreviousView();
}

void MyWidget::on_pushButtonInsert_clicked() {
MyWidget *newMyWidget = new MyWidget(this->index+1,this->stackedWidget);
this->stackedWidget->activateNewWidget(newMyWidget);
}

void MyWidget::on_pushButtonPop_clicked() {
this->stackedWidget->popTo(0);
}



You can see my test project in full here:
http://www.easy-share.com/1909777246/StackedWidgetTest.rar

The problem that I'm having is that if I insert a lot of widgets into the stack and then pop back to the home screen and then do this like 5-10 times I got a memory warning (this have been tested on both Nokia N97 and Nokia 5800). This memory warning doesn't show on this small test project, as it is very light-weighted widgets that I use in the test. In the real application these widgets is more heavy. But I'm just curious if the solution I'm using seems right. If you wan't I could give you the full (giant) project but I really can't post it here.

1. The widgets in the stacked widget should they all have the stacked widget as parent? It maybe doesn't matter as I always delete the widgets by my own when they are removed from the stacked widget?
2. As you can see I'm using deleteLater to delete my widgets. It wouldn't really matter if I used delete instead as I always do deleteLater as the last thing and then the control returns to the event loop and the widget will be deleted?
3. Is this a good approach at all? Should I think of another way instead of adding and deleting my widgets like this? The different steps that the user can take when using the application is quite complex so it seemed easy to just add and deleting like this without reuse the widgets. How would a solution where I reuse the widgets look like?

I have been working with solving this for over a week and I would be very very glad for all help I can get. Thanks!

wysota
7th April 2010, 22:59
Have you seen this article by Johan?

http://doc.trolltech.com/qq/qq27-panelstack.html

As a general remark, removing a widget from the stacked widget doesn't cause it to be deleted. You have to explicitely delete it yourself.

martinn
7th April 2010, 23:49
Thanks! I will take a look at the article.

Yes, I know I need to delete them myself. I do:

stackedWidget->removeWidget(widget);
widget->deleteLater();

Do you have any other comments of my code?

wysota
8th April 2010, 00:14
It's too much code for such a late hour :) Next time try to limit yourself to 10 lines of code if you want someone to read it :rolleyes: