Search code examples
qtqmlqwebengineview

Load image in QML WebEngineView using QQuickImageProvider


I'm injecting HTML content into a QML WebEngineView using the loadHtml method, and I'm trying to get it to load the images through a QQuickImageProvider.

Up to now, we've been successfully loading images from a Qt resource container (qrc), but this is not flexible enough.

contentimageprovider.cpp

#include "contentimageprovider.h"

#include <QDebug>

ContentImageProvider::ContentImageProvider() : QQuickImageProvider(QQuickAsyncImageProvider::Image)
{

}

QImage ContentImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    qDebug() << __FUNCTION__ << id;
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtWebEngine/QtWebEngine>

#include "contentimageprovider.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    QtWebEngine::initialize();

    engine.addImageProvider(QLatin1String("content-images"), new ContentImageProvider);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import QtWebEngine 1.4

Image {
    source: "image://content-images/this-image-is-requested";
}

WebEngineView {
    Component.onCompleted: {
        loadHtml("<img src='qrc://images/this-image-is-displayed.png' /><img src='image://content-images/this-image-should-also-be-requested' />", "/");
    }
}

Expected output

requestImage "this-image-is-requested"
requestImage "this-image-should-also-be-requested"

Actual output

requestImage "this-image-is-requested"

And the image loaded via qrc in the WebEngineView is displayed, and a broken image is shown for the other one.

Has anyone been able to get this to work?


Solution

  • Thanks to @Xplatforms who pointed out the initial error in assuming that the Chromium engine under the QML WebEngineView would interact with the QML Quick engine and trigger the image provider.

    The solution was to implement a QWebEngineUrlSchemeHandler:

    void ImageRequestHandler::requestStarted(QWebEngineUrlRequestJob *request)
    {
        // request->requestUrl() is a QUrl
        QFile *image  = new QFile(QDir::currentPath() + "/storage/content/" + request->requestUrl().path() + ".png");
    
        // makes sure the image deletes itself when closing the file
        connect(image, &QIODevice::aboutToClose, image, &QObject::deleteLater);
        // close the file when the request job is done
        connect(request, &QObject::destroyed, image, &QIODevice::close);
    
        QMimeDatabase mimeDB;
        QMimeType mimeType = mimeDB.mimeTypeForFile(image->fileName());
    
        request->reply(mimeType.name().toUtf8(), image);
    }
    

    main.cpp

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        // web engine to provide content display
        QtWebEngine::initialize();
    
        // intercept requests from the web engine to provide locally loaded content and images
        ImageRequestHandler *imageRequestHandler = new ImageRequestHandler();
        QQuickWebEngineProfile::defaultProfile()->installUrlSchemeHandler("image", imageRequestHandler);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        return app.exec();
    }