简体   繁体   中英

How to correctly use Qt QML Image Provider

I am developing a cross-platform application that receives images via REST API on the C++ side and then sends them to QML via ImageProvider, which seems to be causing memory leaks. The speed at which the memory leaks is proportional to the size of the image and update interval.

I tried disabling caching of QML Image but did not change a thing. I also tried forcing garbage collection by running gc() on image updates but still no luck.

To be completely sure that this is not something caused by my bad coding, etc. I have created a minimal demo, which is based on this Qt example: http://doc.qt.io/qt-5/qquickimageprovider.html

The only addition is that I have increased the image sizes and implemented means of swapping the red coloured image for the yellow coloured image. Once you run the application the image will change colour every second and the memory will keep increasing. The image has 10000x10000 dimension such that you can see the increase clearly. Even if the image is 10x10 or any other size the memory leak still occurs.

I have managed to replicate this problem on Android phone, Macbook as well as a PC running Fedora.

Please let me know if you see any reason why this is occurring and if it is a bug what workaround I can use to send images to QML. I need to send these images as soon as they are received via REST API so usually around 30FPS.

Any help will be very much appreciated! The complete solution is below. Both the Image and Pixmap providers cause the same issue. If you want to test the original Qt code then change QQuickImageProvider::Image QQuickImageProvider::Pixmap in the main.cpp.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QThread>

#include "imageProvider.h"

class MyThread : public QThread
{
public:
    MyThread(QObject* object) : m_object(object)
    {
    }

    virtual void run()
    {
        QVariant colour = "red";

        while (isRunning())
        {
            QMetaObject::invokeMethod(
                m_object, "updateViewport", Q_ARG(QVariant, colour));

            if (colour == "red")
            {
                colour = "yellow";
            }
            else
            {
                colour = "red";
            }

            QThread::sleep(1);
        }
    }

private:
    QObject* m_object;
};

int main(int argc, char* argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.addImageProvider(QLatin1String("imageprovider"),
                            new ImageProvider(QQuickImageProvider::Image));
    QQmlComponent component(&engine, "qrc:/main.qml");
    QObject* object = component.create();
    MyThread* myThread = new MyThread(object);
    myThread->start();

    return app.exec();
}

imageProvider.h

#ifndef IMAGE_PROVIDER_H
#define IMAGE_PROVIDER_H

#include <QQuickImageProvider>
#include <QPixmap>
#include <QPainter>

class ImageProvider : public QObject, public QQuickImageProvider
{
    Q_OBJECT
public:
    explicit ImageProvider(ImageType type, Flags flags = 0);
    QPixmap requestPixmap(const QString& id,
                          QSize* size,
                          const QSize& requestedSize);
    QImage requestImage(const QString& id,
                        QSize* size,
                        const QSize& requestedSize);
};
#endif // IMAGE_PROVIDER_H

imageProvider.cpp

#include "imageProvider.h"

using namespace std;

ImageProvider::ImageProvider(ImageType type, Flags flags)
    : QQuickImageProvider(type, flags)
{
}

QPixmap ImageProvider::requestPixmap(const QString& id,
                                     QSize* size,
                                     const QSize& requestedSize)
{
    int width = 10000;
    int height = 10000;

    if (size)
    {
        *size = QSize(width, height);
    }

    QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
                   requestedSize.height() > 0 ? requestedSize.height() :
                                                height);
    pixmap.fill(QColor(id).rgba());
    QPainter painter(&pixmap);
    QFont f = painter.font();
    f.setPixelSize(20);
    painter.setFont(f);
    painter.setPen(Qt::black);
    if (requestedSize.isValid())
        painter.scale(requestedSize.width() / width,
                      requestedSize.height() / height);
    painter.drawText(QRectF(0, 0, width, height), Qt::AlignCenter, id);

    return pixmap;
}

QImage ImageProvider::requestImage(const QString& id,
                                   QSize* size,
                                   const QSize& requestedSize)
{
    return QImage(10000, 10000, QImage::Format_ARGB32);
}

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("MemoryLeakDemo")

    function updateViewport(colour) {
        image.source = "image://imageprovider/" + colour;
    }

    Image {
        id: image
        cache: false
    }
}

memoryLeakDemo.pro

QT += qml quick

CONFIG += c++11

SOURCES += main.cpp \
    imageProvider.cpp

RESOURCES += qml.qrc

DEFINES += QT_DEPRECATED_WARNINGS

qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += imageProvider.h

Qt has confirmed that this is a bug so hopefully it will get fixed soon:

https://bugreports.qt.io/browse/QTBUG-62600

In the meantime, you could try to apply te patches and compile the framework from source:

https://codereview.qt-project.org/#/c/200715/

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM