How to create thumbs in background(thread) from slot while moving through a QScrollArea?
-
Hello Forum,
I am currently working on a small project which is viewing Thumbnails of images which are puched into a QScrollArea.
My problem is, that when I load a big list of files, my main thread blocks until all thumbs are created and all entries (QLabel) are added into the ScrollArea. The actual adding does not take too much time but the resizing and image generation can be slow.
First of all I would like to have the main thread not block so that the User can still interact even if not all thumbs are visible yet. Therefore I would like to create a thread which does the job. So far, thats not too much of a big deal, but I would also like to only load the thumbs for the QLabels that are currently visible (not all).
Therefore I would like to connect a SIGNAL from the scrollbar to a Slot that is somehow telling the thread, which thumbs have to be created now, BUT without blocking itself obviously. I know that I could generate a list of jobs and let the threat pull for work (like once every second) but I don't like that hack.Is there any way to connect a signal with a threaded slot? Or is there maybe a better solution to my problem?
Thanks for any suggestions,
Charmaman
-
First of all: it certainly is possible.
I think I'd go with a task-based design. On scrolling your list (or otherwise changing it), you will have to create tasks to load the images for the items that are then visible, and put those tasks in a queue. Scrolling or changing of the list may change the queue. I'd create some sort value-function that orders the queue based on position in or near the viewport.
Each task loads and scales a particular image. However, as long as no image has been loaded, your labels should just display a default placeholder image of the right size. That way, your layout will not change as images get loaded.
A threadpool will take items out of your queue (highest priority first) for as long as your queue is filled. If the queue is empty, the threads will simply go to sleep untill there are new tasks to do. Qt offers some nice classes for this. As each thumbnail is loaded, the result is signalled to an object that takes care of actually setting the image on the right label.
Note that you can connect signals and slots across threads, as long as the receiving thread has an eventloop.
-
Hmm somewhat just describted my porblem again, without providing any solution. As I said, that this way is no problem actually. But I do NOT want the thread to pull for jobs in the queue and no "sleeping" and checking from time to time.
I already tested an signal->threadedSlot implementation (ignoring the current viewport) that is supposed to load all images at once.
Unfortunatly the slot is blocking eventhough I used queued connection!I made something like this:
@
// my object with the slot that is meant to be threadedclass ImageLoader : public QObject
{
Q_OBJECTpublic:
ImageLoader()
{}
~ImageLoader()
{}public slots:
void createImages(duptiListe entries)
{
qDebug() << "Receive Signal!!";QHash<QString, DuptiEntry>::iterator entry; for(entry = entries.begin(); entry != entries.end(); entry++) { // do something meaningfull to load the image } }
};
// and here is the way I connected it within my mainwindow-class-constructor:
// Start the ImageLoader m_thread = new QThread(); m_imgLoader = new ImageLoader(); m_imgLoader->moveToThread(m_thread); qRegisterMetaType<duptiListe>("duptiListe"); QObject::connect(this, SIGNAL(contentChanged(duptiListe)), m_imgLoader, SLOT(createImages(duptiListe)), Qt::QueuedConnection); m_thread->start(QThread::LowPriority);
@
My main thread is reading the list of images and prepares QLabels with pixmaps for all of them and puts them into the horizontal scrollarea. This is very fast and done withing no noticeable time. But then my window "freezes" until the slot "createImages()" is done.
I think if I could get this slot being REALLY threaded, the further adjustment should not be to much of a problem...
I know that the official documentations tell to derive from QThread, but this led to an not working code at all. I followed this guide which clears up the wrong documentation:
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ -
For going to sleep and waking up again, you should use a [[doc:QWaitCondition]]. If the queue is emtpy, enter a wait state. When items are added to it, wake the waiting threads. Your design seems to be using only a single thread for your image processing. That is a different solution to what I was proposing.
I don't see how your tread is signalling that it is done with loading images. Your design also doesn't use multiple threads, but only a single worker thread that is also managing the maps. So, as long as your thread is busy with an image, it can't manage it's map.