I'm creating an application using QML and Qt 5.2 . In it 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 . In a simplified way, 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 registered and used in QML as source
of each Image
of the list. The problem with this code is that there is a race condition in it. requestImage
runs in a thread than the rest of the application. By the time I create a QEventLoop
and run it, I'm allowing my thread to receive and process any event that occurs. Because there may 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 hard 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 dating to 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 summary, I can not use QEventLoop
in my role. So how can I wait for the QNetworkReply
and only return from the function when the response arrives?