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

3
我正在开发一个跨平台应用程序,通过REST API在C++端接收图像,然后通过ImageProvider将它们发送到QML,这似乎会导致内存泄漏。内存泄漏的速度与图像大小和更新间隔成正比。
我尝试禁用QML Image的缓存,但没有改变。我还尝试通过在图像更新时运行gc()来强制进行垃圾回收,但仍然没有成功。
为了完全确定这不是由于我的编码问题等引起的,我创建了一个最小化的演示,它基于这个Qt示例:http://doc.qt.io/qt-5/qquickimageprovider.html 唯一的补充是我增加了图像大小并实现了交换红色图像和黄色图像的方法。一旦你运行应用程序,图像颜色每秒钟就会改变一次,而内存会持续增加。该图像具有10000x10000的尺寸,以便您可以清楚地看到增加情况。即使图像是10x10或任何其他大小,内存泄漏仍会发生。
我已经成功地在Android手机、Macbook和运行Fedora的PC上复制了这个问题。
请告诉我是否有任何原因导致这种情况发生,如果是一个 bug,我需要什么解决方法来将图像发送到 QML。我需要在通过 REST API 接收到这些图像后尽快发送它们,通常大约 30FPS。
非常感谢您的任何帮助!完整的解决方案如下。无论是 Image 还是 Pixmap 提供程序都会导致相同的问题。如果您想测试原始的 Qt 代码,请在 main.cpp 中更改 QQuickImageProvider::Image QQuickImageProvider::Pixmap。

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

你确定你没有遇到与这个相同的问题吗?看起来你的代码没问题,不应该导致内存泄漏。但是对我来说,它只产生黑屏。 - Filip Hazubski
谢谢,黑屏是因为默认提供程序使用图像而不是 Pixmap,当您更改代码以使用 Pixmap 时,您应该会看到彩色图像。看起来这可能是一个错误,我已经提交了报告,并且它刚刚被优先处理,所以问题可能在 Qt 方面。https://bugreports.qt.io/browse/QTBUG-62600感谢您的帮助! - Martin Peniak
做得好 :) - Filip Hazubski
那么,请考虑回答您自己的问题或完全删除它,如果您认为这不值得努力。在我看来,提供对现有错误报告的引用或像本例中新创建的错误报告的答案是非常有用的。 - BaCaRoZzo
1个回答

1

是时候关闭这个问题了! - karlphillip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接