Display Image in QML from response to XmlHttpReque

2019-05-28 22:57发布

I need to fetch a jpeg image from a REST API call. I use XMLHttpRequest as the request requires authentication headers (i.e. I can't just create an Image and set the source to the URL with user:passwd@url).

I thought I'd be able to use a Canvas and call drawImage by setting the REST data to a CanvasImageData object. However, it doesn't draw anything, nor does it produce an error. The REST call returns Content-Type: image/jpeg and the Transfer-Encoding: chunked.

Should this approach work, or am I missing something else? Any better suggestions?

// map_request.imageBytes is a property that holds the binary response from the REST query
Canvas {
id: location_map
width: 2400
height: 1500
contextType: '2d'

onPaint: {
  if (context && map_request.imageBytes)
  {
    var cid = context.createImageData(width, height);
    cid.data = map_request.imageBytes;
    context.drawImage(cid, 0, 0);
  }
}

标签: qt qml
1条回答
\"骚年 ilove
2楼-- · 2019-05-28 23:58

The proper solution is to create a QQuickImageProvider as @folibis instructed. However, since I am using Qt5.5, I can't make a QQuickAsyncImageProvider (which is introduced in Qt5.6). Instead, you have to set the Flags when constructing the QQuickImageProvider to QQmlImageProviderBase::ForceAsynchronousImageLoading. This flag ensures that calling requestImage doesn't block the main GUI thread.

However, requestImage expects the Image to be returned, causing a challenge to fetch the Image data from the network without blocking that thread. QNetworkAccessManager returns its status with signals, and QQuickImageProvider isn't a QObject, so I made a helper class to monitor the signals for the QNetworkReply.

class ReplyMonitor : public QObject
{
  Q_OBJECT
public:
  ReplyMonitor(QNetworkAccessManager *);

public Q_SLOTS:
  void handleReplyFinished();
  void handleSslErrors(QNetworkReply *, const QList<QSslError> &);
  void handleAuthenticationRequired(QNetworkReply *, QAuthenticator *);

public:
  bool finished;
};

and

ReplyMonitor::ReplyMonitor(QNetworkAccessManager *mgr)
  : finished(false)
{
  connect(mgr, SIGNAL( finished(QNetworkReply *) ), this, SLOT( handleReplyFinished() ));
  connect(mgr, SIGNAL( sslErrors(QNetworkReply *, const QList<QSslError> &) ),
          this, SLOT( handleSslErrors(QNetworkReply*, const QList<QSslError> &) ));
  connect(mgr, SIGNAL( authenticationRequired(QNetworkReply *, QAuthenticator *) ),
          this, SLOT( handleAuthenticationRequired(QNetworkReply*, QAuthenticator*) ));
}

void ReplyMonitor::handleReplyFinished()
{
  finished = true;
}

Then in requestImage() I check finished and call

  while (!monitor->finished)
  {
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
  }

before I create the Image to return with

  if (reply->error() == QNetworkReply::NoError)
  {
    image.loadFromData(reply->readAll());
  }

I omitted the details of creating a QNetworkRequest, as that is well-documented.

查看更多
登录 后发表回答