PDA

View Full Version : QGraphicsView not being updated



Luc4
15th June 2010, 00:04
Hi! I've been noticing this strange behavior and I really can't understand what could it be related to.
I have a worker thread which loads some images in a loop and sends a signal in each iteration. This signal is connected to a slot in the GUI thread which simply shows these images in a QGraphicsView.

This structure has always been working very good, but now I tried it on another system (both are Windows CE), and it seems its not working properly. When I start the application, the images coming from the thread are simply not rendered in the QGraphicsView. They are loaded correctly in the background, but not rendered. After I click anywhere, those which are already loaded appears suddenly, and the others begin to appear correctly as soon as those are loaded.

After this process has finished, everything is OK. I can even start this thread again, and the images are correctly shown as expected, as soon as they are loaded. The problem is only immediately after the application is loaded.

Any idea why this could be happening. As I've recently been taught, I thought it could be a problem of events not processed, but I tried placing QApplication::processEvents() after the update method without any success...
Any other idea?

Thanks for any advice!

wysota
15th June 2010, 02:08
Can we see the code? Remember you can only call QApplication::processEvents() from the main thread.

Luc4
15th June 2010, 09:41
This is the run method of my thread:


for (int i = 0; i < filesList->count(); i++) {
// Load the QImage...
QImage image = imageReader.read();
emit this->imageReady(image);
}

it simply loads one after the other all the images listed. The signal is connected to a slot which calls a method which sets the image in a QGraphicsItem subclass which uses it in its paint method this way:


void Class::setImageLoaded(QImage image) {
// Store the image in the QGraphicsItem
this->currentImage = image;
// Force repainting of this object.
this->update(this->boundingRect());
}

and the paint method draws the image with drawImage of this->currentImage.

wysota
15th June 2010, 10:32
How does the connect statement look like? What is "Class", what is its superclass?

Luc4
15th June 2010, 10:47
The connect statement is this:


connect(loader, SIGNAL(imageReady(QImage)), this, SLOT(imageGenerated(QImage)));

imageGenerated then calls Class::setImageLoaded(QImage image) that I reported above.
Class is a subclass I made which is defined this way:


class IconViewItem : public QObject, public QGraphicsItem {
Q_OBJECT
Q_PROPERTY(QPointF pos READ pos WRITE setPos)
Q_INTERFACES(QGraphicsItem)
public:
...
}

wysota
15th June 2010, 10:55
Please provide more than one or two lines of code. It is important to see the context of a particular call. If you use some variable in a code snippet, please explain what it is, we're not trueseers.

Luc4
15th June 2010, 15:10
Sure, the methods I already posted are more or less like those are in my code. The only difference is that imageReady and the slot imageGenerated accept two parameters instead of one. I didn't report the implementation of imageGenerated which is this:


void Class2::imageGenerated(QImage image, QString string) {
Class* item = itemsMap.value(string);
item->setImageLoaded(image);
}

where Class2 is a subclass of QGraphicsScene and itemsMap is a QMap which maps strings to objects of class Class. Sorry I made a mistake in the other post, in fact IconViewItem was Class.
This method I reported calls the method setImageLoaded of class Class that I reported above (it was complete, it only lacked another assignation of image, no more lines).

Of course the connect statement was:


connect(loader, SIGNAL(imageReady(QImage, QString)), this, SLOT(imageGenerated(QImage, QString)));

The code I reported for the worker thread was complete, I only removed some lines where I set the size I want for imageReader before loading (I report in case it's relevant):


QImageReader imageReader(dir->absoluteFilePath(filesList->at(i)));
if (imageReader.size().height() >= imageReader.size().width())
imageReader.setScaledSize(QSize(imageReader.size() .width()*64/imageReader.size().height(), 64));
else imageReader.setScaledSize(QSize(64, imageReader.size().height()*64/imageReader.size().width()));

where dir and filesList are local pointers to QDir and QStringList respectively.

wysota
15th June 2010, 15:25
So "loader" is an instance of a subclass of QThread?

What does the program do while the thread is working? Are you able to resize windows of the application, scroll the view, etc.?

Luc4
15th June 2010, 15:46
Sorry, forgot to specify loader. loader is a subclass of QThread whose run() method I already reported.
The program works correctly except immediately after it is loaded. Subsequent loaders work correctly. I create it this way:

loader = new ThumbnailLoader(dir, filesList);

where dir and filesList are those I reported.
And I start it in a method this way:


delete loader->dir;
delete loader->filesList;
loader->dir = dir;
loader->filesList = imagesList;
loader->start(QThread::LowestPriority);

The reason why I delete and then reassign is that this method is called many times when the user requests (parameters in the constructor were absolutely unuseful). It works correctly every time except the first which is right after the program loads. After the first call, while the loader works, I can do whatever I want, like scrolling the QGraphicsView, stopping it, etc... The first time instead, it works, but the QGraphicsView is simply not showing anything until I click anywhere on the application. After I click, the images already processed are suddenly rendered and the QGraphicsView starts to show them as soon as they are loaded (which is the expected behavior, and the behavior the program has in some systems, always Windows CE). I hope I've been able to make the behavior clear.

EDIT: I forgot to mention that the above portion of code is placed in a method which is called, the first time (which is the only case in which sometimes it fails to update correctly), in the constructor of a subclass of a QGraphicsScene (the same which has to render the images).

wysota
15th June 2010, 16:14
Please provide a minimal compilable example reproducing the problem.

Luc4
20th June 2010, 01:25
This is a compilable example showing the issue I'm talking about. It doesn't happen always, but quite often. Tried this on Windows Vista and Windows CE.

Under Windows Vista seems to work always correctly. Under Windows CE, sometimes it shows the images as soon as they are loaded, sometimes it is necessary to click in some place before the application shows the images.

In this example I didn't add an update explicitly, in my previous application there was an explicit call to the update method, but it seems this is not needed, at least not under Windows Vista.

If you need to change the path where the images are located and the filter to test it, change whatever you need in workerthread.cpp.

Thanks for any advice!

Luc4
28th June 2010, 00:14
Isn't anyone able to reproduce this issue?

wysota
9th July 2010, 09:56
You can't manipulate QPixmaps in worker threads, neither can they be transfered across threads (use QImage instead). Also remember the WorkerThread object doesn't live in the thread it represents. See if it all helps.

Luc4
9th July 2010, 21:36
You're absolutely right. My bad. I will correct this mistake. I used QPixmaps instead of using QImages. But I did this only in the example. In my code I originally used QImages. Are you able to reproduce this situation anyway?
Thanks for your answers!

Luc4
14th August 2010, 16:48
I made some more investigation on this, and found out that the method in my GUI thread that should have painted the images on the view was not executed until the user clicked. I therefore changed the type of connection to Qt::DirectConnection. Unfortunately, now the method is called immediately, but still the QGraphicsView is not updated anyway until the user clicks.
Any other idea?
Thanks!