Using QtConcurrentRun without introducing memory leaks
-
I have an application which has an image list with thumbnails which show a small preview of the images to the user.
These thumbnails are cached to user's disk and thus, after the 1st time the user sees them they load up quickly.
Once the user clicks on a thumbail of the list, the program reads the original image and shows a preview of it on a larger screen on the left. Screenshot for clarity: !http://i.imgur.com/QuRT2fV.png(Clarity Image)!
Once the user clicks another thumbnail, having a 2nd label below the first on the screen on the right, we have achieved a transition effect by playing with the opacities of the 2 images.
The problem: The program must remain responsive while the original image is being read from the disk, resized and set on the image on the right. While this isn't a problem with small images, the program is used for controlling the desktop background, and thus many high resolution images are used. This slows down the process of reading, resizing and setting the image to the label by far.
The solution: I thought of using QtConcurrentRun with QFutureWatcher, like this:
@
// -- mainwindow.h
QFutureWatcher<QImage*> *scaleWatcher_;// -- mainwindow.cpp
//constructor
scaleWatcher_ = new QFutureWatcher<QImage*>(this);
connect(scaleWatcher_, SIGNAL(finished()), this, SLOT(scaleFinished()));//image selection changed, fire up the scale function
//some other image was selected and the transition is not yet ready.
//I understand that QtConcurrent::run cannot be cancelled, but I do it anyway
//to manually check inside the corresponding functions whether their caller has been cancelled
if(scaleWatcher_->isRunning()){
scaleWatcher_->cancel();
}QFuture<QImage*> future = QtConcurrent::run(this, &MainWindow::scaleWallpapersPreview, filename);
scaleWatcher_->setFuture(future);
@The scaleWallpapersPreview function reads the image filename from the disk and scales it down using QImageReader. Returns a pointer to the resulted image.
The scaleFinished slot:
@void MainWindow::scaleFinished(){
if(scaleWatcher_->isCanceled()){
//last moment cancel, if we don't return, program might crash
return;
}
setupAnimationsAndChangeImage(scaleWatcher_->future().result());
}@The setupAnimationsAndChangeImage takes care of the transition animation and sets the new image to the screen on the right.
The problem(s) of the solution: Memory leaks everywhere. I understand that I cannot cancel the QtConcurrent::run but I think that I delete the pointers where I use them. My thought is that the result of QFutureWatcher doesn't go anywhere when I cancel and thus I should call for waitForFinished() after I cancel, which hangs my application while the image is being loaded.
Sum up: I need my program to NOT hang while loading an image on the screen, on the right of the window. There are no memory leaks if I let my program hang while loading the image, but there are memory leaks if I try to use QtConcurrentRun to prevent my program from hanging.
PS 1: A link of the application leaking memory: "http://i.imgur.com/gCNTqt1.png":http://i.imgur.com/gCNTqt1.png I managed to leak 600MB just by browsing the thumbnails using the arrow keys for half a minute.
PS 2: You can see the full application code at "https://code.launchpad.net/~wallch/wallpaper-changer/trunk":https://code.launchpad.net/~wallch/wallpaper-changer/trunk
Revision 139 uses QtConcurrent::run and has major memory leakage, while 138 doesn't. Clicking on a revision number enables various options like browsing the files on that revision.
-
Hi,
Might be a bit short of an answer but why don't you reuse the technique of the "QtConcurrent Image Scaling Example" ? No pointers to QImage needed
-
Yes, you are correct. I got rid of the pointers and now it works just fine :)
-
Nice,
Then please update the thread title prepending [solved] so other forum users may know a solution has been found :)