PDA

View Full Version : pixmap or qimage second time usage in widgets ( platform S60 )



ada10
8th September 2010, 07:15
I have come across a scenario where I have to load the same pixmap / same QImage onto two different widgets ( belonging to the same mainwindow ).

First time I load a pixmap 1 on widget 1 , it works fine.
Second time if I try to load the same pixmap ( or QImage ) on another widget ( say widget 2), the code just throws some error ( saying memory full ) or does not load the pixmap on widget 2.

Ultimately I end up duplicating the png files i use to load the pixmaps, giving them distinct file names. Is there a solution to avoid this loading problem so that the I can use the same png file to be loaded any number of times ?

wysota
8th September 2010, 09:10
Why not reuse the same image instead of loading it again?

ada10
8th September 2010, 11:14
I faced the same issue with the usage of the same pixmap more than once when adding it to a model class ( without loading ). Is there a solution to this ?

wysota
8th September 2010, 14:19
Please provide a minimal compilable example reproducing the problem.

ada10
9th September 2010, 09:33
The code segment for this is as shown below -



gridLayout = new QGridLayout(mainwindow);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
gridLayout->setContentsMargins(0, 0, 0, 0);
label = new QLabel(gridLayout);
label->setObjectName(QString::fromUtf8("label"));
label->setPixmap(QPixmap(QString::fromUtf8("../folder/user_group_icon.bmp")));

gridLayout->addWidget(label, 0, 0, 1, 1);

label_2 = new QLabel(gridLayout);
label_2->setObjectName(QString::fromUtf8("label_2"));
label_2->setPixmap(QPixmap(QString::fromUtf8("../folder/user_group_icon.bmp")));

gridLayout->addWidget(label_2, 0, 1, 1, 1);


Here the gridLayout is added to a mainwindow & contains 2 labels. Both set the same bmp file as pixmap. The first label displays it but the second label does not. This problem occurs ONLY ON S60 ( not on windows ).

Has anyone faced this issue before?

wysota
9th September 2010, 10:39
What if you do this:


QPixmap pixmap(QString::fromUtf8("../folder/user_group_icon.bmp"));
gridLayout = new QGridLayout(mainwindow);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
gridLayout->setContentsMargins(0, 0, 0, 0);
label = new QLabel(gridLayout);
label->setObjectName(QString::fromUtf8("label"));
label->setPixmap(pixmap);

gridLayout->addWidget(label, 0, 0, 1, 1);

label_2 = new QLabel(gridLayout);
label_2->setObjectName(QString::fromUtf8("label_2"));
label_2->setPixmap(pixmap);

gridLayout->addWidget(label_2, 0, 1, 1, 1);

Mah.Adly
13th January 2011, 12:35
Well, I have the same problem, and didn't find a solution till now. I move a lot between Widgets that need Pixmaps to paint in the background, view in labels, and others for some downloaded news. If I go back and forth between widgets, after about 10 times the applications freezes and sometimes the message of "Memory Full" appears. I traced this problem for days and notices the console output. It goes fine when going back and forth, until the time that crashes:



[Qt Message] My Headlines Widget Message: Loading pixmaps in the constructor
[Qt Message] QPainter::begin: Cannot paint on a null pixmap
[Qt Message] QPainter::begin: Cannot paint on a null pixmap
[Qt Message] QPainter::begin: Cannot paint on a null pixmap
[Qt Message] My Headlines Widget Message: drawing bg in the paintEvent
[Qt Message] My Headlines Widget Message: drawing bannar in the paintEvent
[Qt Message] My Headlines Widget Message: drawing title in the paintEvent
[Qt Message] QPainter::end: Painter not active, aborted
[Qt Message] QPainter::begin: Cannot paint on a null pixmap
[Qt Message] QPainter::begin: Cannot paint on a null pixmap
[Qt Message] QPainter::pen: Painter not active
[Qt Message] QPainter::setPen: Painter not active
[Qt Message] QPainter::setPen: Painter not active
[Qt Message] QPainter::setPen: Painter not active
[Qt Message] QPainter::setPen: Painter not active
[Qt Message] QPainter::setPen: Painter not active
[Qt Message] QPainter::begin: Cannot paint on a null pixmap


And messages keep popping in the console until a 'data exception' occurs and application crashes.

Process 1106, thread 1107 stopped at 0x79573c1e: A data abort exception has occurred.
Finished.

I tried increasing the QPixmapCacheLimit from 1024 KB to 4096 KB: and it only helped to increase the number of trials before crashing.
I tried clearing the QPixmapCache before closing any widget: did not help, I got worse.

Any other suggestions?

Added after 38 minutes:

An update:
I used this code instead


if (!QPixmapCache::find("background", &m_bg)) {
m_bg.load(":/bg.png");
QPixmapCache::insert("background", m_bg);
}

if (!QPixmapCache::find("bannar", &m_bannar)) {
m_bannar.load(":/bannar.png");
QPixmapCache::insert("bannar", m_bannar);
}

if (!QPixmapCache::find(m_sectionId, &m_title)) {
m_title.load(":/titles/"+m_sectionId+".png");
QPixmapCache::insert(m_sectionId, m_title);
}
// m_bannar.load(":/bannar.png");
// m_bg.load(":/bg.png");
// m_title.load(":/titles/"+m_sectionId+".png");

but no effect at all

wysota
14th January 2011, 12:13
Are you using threads?

Mah.Adly
14th January 2011, 15:42
No, only signals/slots

wysota
15th January 2011, 02:06
Please post the backtrace after a crash.

Mah.Adly
16th January 2011, 09:29
Please post the backtrace after a crash.

Where can I find it?

wysota
16th January 2011, 10:12
In your debugger. It's also sometimes called a "stack trace" and contains a list of function calls leading to the crash.

Mah.Adly
16th January 2011, 12:58
I hope I got it right:

the application has this sequence:
NewsHeadlines ----(choose item)----> NewsDetails
NewsDetails ----(go back)----> NewsHeadlines

this is what happens at the constructor of NewsDetails when an item is selected in NewsHeadlines
http://img9.imageshack.us/img9/7286/onopen.png

and when going back
http://img696.imageshack.us/img696/4114/onback.png

and this is when inside the paintEvent of NewsDetails (and similar one in NewsHeadlines), whether it works fine or crashes
http://img694.imageshack.us/img694/6187/crashmoment.png

wysota
16th January 2011, 13:04
This is not during a crash. This is a trap on some breakpoint. Let the program crash and then dump the backtrace. In textual form, not graphical (open context menu and copy to clipboard).

Mah.Adly
16th January 2011, 16:46
I tried but couldn't get more than this:

Thread 1 (Thread 922):
#0 0x795ac6e4 in ?? ()
#1 0x79934fbe in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
only for the two question marks in the previous images. Once the application crashes, there are no more functions or variables to watch.

So I tried a new way to save some time. I made a dummy project with two windows. Each window has only one button to close itself and open the other window. The code is not more than:

void WindowA::on_pushButton_clicked()
{
WindowB *b = new WindowB();
b->showMaximized();
this->close();
}


void WindowB::on_pushButton_clicked()
{
WindowA *a = new WindowA();
a->showMaximized();
this->close();
}

And tested this application for about 30 times then it crashed just the same as my previous example. And the same QPainter messages.

I attached the example

wysota
17th January 2011, 11:53
Maybe you are running out of memory? You have a serious memory leak - you should be reusing old objects which you are not deleting instead of spawning new ones each time a button is clicked.

An example implementation follows. It's a bit complicated as it uses a state machine but I wanted the code to be minimalisticly short.

#include <QtGui>

int main(int argc, char **argv){
QApplication app(argc, argv);

QWidget w1, w2;
QPushButton *b1 = new QPushButton("Change 1");
QPushButton *b2 = new QPushButton("Change 2");
QVBoxLayout *l1 = new QVBoxLayout(&w1);
QVBoxLayout *l2 = new QVBoxLayout(&w2);
l1->addWidget(b1);
l2->addWidget(b2);

QStateMachine machine;
QState *s1 = new QState; // state: w1 visible
QState *s2 = new QState; // state: w2 visible

s1->assignProperty(&w1, "visible", true);
s1->assignProperty(&w2, "visible", false);
s2->assignProperty(&w1, "visible", false);
s2->assignProperty(&w2, "visible", true);

s1->addTransition(b1, SIGNAL(clicked()), s2); // [ s1, b1.clicked() ] -> {s2}
s2->addTransition(b2, SIGNAL(clicked()), s1); // [ s2, b2.clicked() ] -> {s1}

machine.addState(s1);
machine.addState(s2);
machine.setInitialState(s1); // start with w1 visible
machine.start();
return app.exec();
}

Mah.Adly
23rd January 2011, 11:49
Thanks for this idea. I worked with something like this for while using SlidingStackedWidget. But of course I had to find out "Why?"

A new way appeared. From Qt docs:

"The widget is hidden if it accepts the close event."
So that's why when I open and close many widgets/windows the application crashes and a message of "Memory Full" appears.

Ar first, I had two identical solutions:
1- After closing each widget/window:

this->close();
this->deleteLater();

2- Setting an attribute to the widget in the constructor:

this->setAttribute(Qt::WA_DeleteOnClose);

This way the widget will be deleted not only hidden. These two ways have one flaw till now. When I exit the application, a message appears in the terminal (in case of S60) that "a data abort exception has occurred" or a message appears to user (Desktop) that "the application has stopped working".

And after a couple of trials, I put the

this->deleteLater();
in the destructor and is working fine till now


WindowB::~WindowB()
{
delete ui;
this->deleteLater();
}

wysota
23rd January 2011, 12:30
Putting this->deleteLater() in a destructor doesn't make any sense. It's practically a no-op. If you have identical windows you are swapping between then why do you create new ones each time instead of reusing the existing ones?

Mah.Adly
23rd January 2011, 14:09
Putting this->deleteLater() in a destructor doesn't make any sense. It's practically a no-op.
You're totally right about this one. This was totally useless, my fault.

I'm trying to find a solution because a user can navigate between *many* windows and open each possible one, so I'm trying to avoid this memory problem.. In this part of the appilcation I went into an example of swapping. But there are many more. So loading all windows and switching between them is my last choice.

wysota
23rd January 2011, 16:46
"Many" means how many? 1000? You can always settle for a solution where you cache the recently used windows, something like:

QStackedWidget stack;
QCache<int, QWidget> cache;
cache.setMaxCost(10); // cache last 10 windows
//...
void X::openWindow(int id) {
QWidget *w = cache.object(id);
if(!w) {
w = createWindow(id);
cache.insert(id, w);
}
w->show();
}

QWidget* X::createWindow(int id) {
switch(id) {
case StartWindow: return new StartWindow;
case ConfigWindow:: return new ConfigWindow;
// ...
}
}

Then it's just a matter of calling openWindow() with a proper id based on the set of windows the application provides. The cache will take care of deleting least recently used windows. You can also instruct the cache that different windows may use a different amount of memory by providing the third argument to QCache::insert().

Mah.Adly
26th January 2011, 10:39
Wow, This is a great solution. Thanks a lot !