2

This question stems from a question I asked previously.

In short, the image provider abstract class requires one to implement a method that requests an image via a parameter "url" and returns the image. E.g. a single method requests and returns the image. But Qt's QNetworkAccessManager class is designed strictly for asynchronous use, e.g. you requests the url in one method and intercept it by connecting to the signal emitted upon completion of the request. E.g. it HAS to be broken down in two steps, which rises the question how would one go about implementing the image provider required single method with a class that was intended to be broken down (considering the hack I used to force it to be synchronous resulted in a mess)?

Community
  • 1
  • 1

1 Answers1

2

The general answer is: it can't be safely done. In specific cases though, it can be done, but it requires close scrutiny of the code involved, and some proof of there being no issues due to reentrancy.

As for the use of the synchronous class, you simply need to run a local event loop within the requestXxxx method's implementation. If it runs in a dedicated thread, then the reentrancy problems are less of an issue since you control what objects are active in the thread.

Since your implementation of QQuickImageProvider can specify the ForceAsynchronousImageLoading flag, your provider will run in its own thread, and can safely run its own message loop.

Note that the default QML image provider takes an URI as input, and will be more than happy to load images from the web - thus you don't have to worry about it in this case.

So, even through your custom image provider is completely unnecessary, if you were to create it, here's how you might do it:

class MyImageProvider : public QQuickImageProvider {
public:
  MyImageProvider();
  Flags flags() const { return ForceAsynchronousImageLoading; }
  QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize)
    Q_DECL_OVERRIDE;
}

QImage MyImageProvider::requestImage(
  const QString & id, QSize * size, const QSize & requestedSize)
{
  QImage image;
  QEventLoop loop;
  QNetworkAccessManager mgr;
  QObject::connect(&mgr, &QNetworkAccessManager::finished, 
    [&loop, size](QNetworkReply* reply) {
       image.load(reply, "JPG");
       if (size) *size = image.size();
       loop.quit();
       delete reply;
    });
  mgr.get(QNetworkRequest(QUrl(id)));
  loop.exec();
  return image;
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • You mean that in that particular case `QEventLoop` did not instantiate a thread of its own by default and instead ran in the main thread? –  Sep 19 '14 at 16:21
  • @user3735658 A `QEventLoop` is not instantiating any threads!! But since you need an event loop to run async network access manager, and you need that event loop to run *while* `requestXxxx` executes, it's on you to create one and `exec()` it. – Kuba hasn't forgotten Monica Sep 19 '14 at 16:22
  • So I would also have to instantiate a thread, move objects to that thread but still lock the method until the event loop is done (and how can I tell when that is?)? It is still unclear how to work around that problem. –  Sep 19 '14 at 16:24
  • @user3735658 The presence of the `ForceAsynchronousImageLoading` flag in an image provider makes the QML engine create a worker thread and run the `requestXxx` in that thread. The thread instantiation is done *for you*. – Kuba hasn't forgotten Monica Sep 19 '14 at 16:25
  • If that is the case, why did I get those crashes? I did force async... Was it because of the event loop? If so, how to do it without the loop? –  Sep 19 '14 at 16:27
  • 1
    @user3735658 You won't ever know unless you run it under the debugger and see for yourself where exactly it crashed. Don't ask us :) – Kuba hasn't forgotten Monica Sep 19 '14 at 16:34