15
I’m creating an application using QML and the Qt 5.2. In her a ListView
displays multiple items, each with an image and associated text. The image is built based on data uploaded from a server by HTTP. Simply put, I have the following code:
MyProvider::MyProvider() :
QQuickImageProvider(QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading)
{ }
QImage MyProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
{
// Obter dados JSON que me explicam como montar a imagem
QNetworkAccessManager manager;
QNetworkRequest request(QUrl("http://myserver.com/api/imagedata/" + id));
request.setRawHeader("Accept", "application/json");
QNetworkReply* reply = manager.get(request);
// Aguardar pela resposta. Aqui está o problema
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
// Ler a resposta e montar uma imagem com ela
QImage img = produceImageFromJsonData(reply->readAll());
delete reply;
// Ajustar para o tamanho requisitado
if (requestedSize.isValid())
img = img.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
*size = img.size();
return img;
}
The MyProvider
is then recorded and used in QML as source
of each Image
from the list. The problem with this code is that there is a race condition in it. The requestImage
is executed in a thread different from the rest of the application. At the moment I create a QEventLoop
and run it, I’m allowing my thread to receive and process any event occurring. Since there can be more than one thread running this loop, two events can be sent to the same object at the same time by different threads. I get a crash difficult to reproduce (it happened for the first time today, after almost a month of development).
The problem can also be reproduced with this smaller code, showing that the simple existence of a QEventLoop
triggers the crash:
MyProvider::MyProvider() :
QQuickImageProvider(QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading)
{ }
QImage MyProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
{
QEventLoop loop;
loop.exec();
return QImage();
}
I found a bugreport what date of the Qt 4.7.1 where the following is said:
The problem is that the
QEventLoop
that is created in the imageprovider causes Events to be Delivered to the image Reader which shares the thread. It receives These Events while still Processing a Previous Event. [...] It is not Valid to run an Event loop in the image Provider.
In short, I cannot use the QEventLoop
in my role. So how can I wait for the QNetworkReply
and only return from the function when the answer arrives?
Qnetworkreply has an 'finished' sign (http://qt-project.org/doc/qt-4.8/qnetworkreply.html#finished) which is emitted when the response has finished processing. You tried to use it directly?
– Luiz Vieira
That’s the one I used
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
. The problem is that waiting for any sign implies receiving events.– Guilherme Bernal
Yes, but what I meant was, did you try to connect the signal to a method in your own class and put the 'produceImageFromJsonData' call there (something like connect(reply, SIGNAL(finished()), this /* slot in this, and not loop */, SLOT(finished()));)? In other words, you really need the loop?
– Luiz Vieira
@Luizvieira, does not solve the problem, because Qquickimageprovider expects me to return to Qimage for that function. The moment I return the thread may be destroyed.
– Guilherme Bernal
@Bacco, this is exactly the same as the loop Event. But note that this is not in the same thread as the UI, so I don’t have to worry about freezing. The problem is that I cannot process events and Qnetworkreply needs me to process them to work (merely using a loop with a Sleep inside does not work).
– Guilherme Bernal
@You’re right, I’m sorry. Well, I have another test suggestion for you: try using Qt::Queuedconnection as the connection type. The default is 'auto', and since Qnetworkreply and Qeventloop are in the same thread maybe the bug report problem you cited (It receives These Events while still Processing a Previous Event) is occurring because of direct sending. Who knows if by forcing the finish signal queuing the problem no longer occurs (although it is difficult to reproduce, as you mentioned).
– Luiz Vieira
@Luizvieira Nothing to apologize, is helping! To what I understood mine
QEventLoop
is receiving and processing signals from other objects of other threads (some internal QML that is in the UI thread). As there is more than one thread of mine, this QML object receives two signals at the same time from separate threads. Also the Bugger showed that the crash happened with some other signal, even before the signalQNetworkReply
be eviado. I can reproduce the problem by justQEventLoop().exec();
inside that function, deleting everything else.– Guilherme Bernal
@Guilhermebernal What a hair problem, huh? hehe The error still occurs (or changes) if you use Qeventloop(). exec(Qeventloop::Exclude serinputevents); ?
– Luiz Vieira
@Luizvieira Yes, I tried that too. Unfortunately I have the same crash.
– Guilherme Bernal
@Guilhermebernal I imagined that you had already tested, but it wouldn’t hurt to ask. : ) Well, I will research more as soon as I have more time. If you have any more suggestions I will post. Good luck there.
– Luiz Vieira