简体   繁体   English

如何正确使用Qt QML图像提供程序

[英]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. 我正在开发一个跨平台应用程序,该应用程序通过C ++端的REST API接收图像,然后通过ImageProvider将它们发送到QML,这似乎会导致内存泄漏。 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. 我尝试禁用QML Image的缓存,但没有改变。 I also tried forcing garbage collection by running gc() on image updates but still no luck. 我还尝试通过在图像更新上运行gc()来强制进行垃圾收集,但是仍然没有运气。

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 为了完全确定这不是由我的错误编码等引起的。我创建了一个最小演示,它基于以下Qt示例: 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. 图片的尺寸为10000x10000,因此您可以清楚地看到它的增加。 Even if the image is 10x10 or any other size the memory leak still occurs. 即使图像是10x10或任何其他大小,仍然会发生内存泄漏。

I have managed to replicate this problem on Android phone, Macbook as well as a PC running Fedora. 我设法在Android手机,Macbook以及运行Fedora的PC上复制了此问题。

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. 如果您发现发生这种情况的任何原因,并且如果是错误,请告诉我,可以使用哪种解决方法将图像发送到QML。 I need to send these images as soon as they are received via REST API so usually around 30FPS. 我需要在通过REST API接收到这些图像后立即发送这些图像,因此通常在30FPS左右。

Any help will be very much appreciated! 任何帮助将不胜感激! The complete solution is below. 完整的解决方案如下。 Both the Image and Pixmap providers cause the same issue. Image和Pixmap提供程序都引起相同的问题。 If you want to test the original Qt code then change QQuickImageProvider::Image QQuickImageProvider::Pixmap in the main.cpp. 如果要测试原始的Qt代码,请在main.cpp中更改QQuickImageProvider :: Image QQuickImageProvider :: Pixmap。

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 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 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 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 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: Qt已经确认这是一个错误,因此希望它将尽快得到解决:

https://bugreports.qt.io/browse/QTBUG-62600 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/ https://codereview.qt-project.org/#/c/200715/

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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