如何正确使用 Qt QML Image Provider

Posted

技术标签:

【中文标题】如何正确使用 Qt QML Image Provider【英文标题】:How to correctly use Qt QML Image Provider 【发布时间】:2017-08-18 11:32:59 【问题描述】:

我正在开发一个跨平台应用程序,它通过 C++ 端的 REST API 接收图像,然后通过 ImageProvider 将它们发送到 QML,这似乎会导致内存泄漏。内存泄漏的速度与图像大小和更新间隔成正比。

我尝试禁用 QML 图像的缓存,但没有改变任何事情。我还尝试通过在图像更新上运行 gc() 来强制垃圾收集,但仍然没有运气。

为了完全确定这不是我的错误编码等造成的。我创建了一个最小的演示,它基于这个 Qt 示例: http://doc.qt.io/qt-5/qquickimageprovider.html

唯一的补充是我增加了图像大小并实现了将红色图像交换为黄色图像的方法。 运行应用程序后,图像将每秒更改一次颜色,并且内存将不断增加。该图像具有 10000x10000 尺寸,因此您可以清楚地看到增加。即使图像是 10x10 或任何其他尺寸,仍然会发生内存泄漏。

我已经成功地在 android 手机、Macbook 以及运行 Fedora 的 PC 上复制了这个问题。

如果您发现发生这种情况的任何原因以及如果这是一个错误,请告诉我,我可以使用什么解决方法将图像发送到 QML。我需要在通过 REST API 接收到这些图像后立即发送这些图像,因此通常在 30FPS 左右。

任何帮助将不胜感激!完整的解决方案如下。 Image 和 Pixmap 提供程序都会导致相同的问题。如果要测试原始 Qt 代码,请更改 main.cpp 中的 QQuickImageProvider::Image QQuickImageProvider::Pixmap。

ma​​in.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);

ma​​in.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

【问题讨论】:

你确定你没有遇到和this一样的问题吗?看来您的代码没问题,应该不会导致内存泄漏。但是对我来说它只会产生黑屏。 谢谢,显示黑屏是因为默认提供程序使用的是图像而不是像素图,当您将代码更改为使用像素图时,您应该会看到彩色图像。看起来这可能是一个错误,我已经提交了一份报告,并且它刚刚得到了优先级,所以看起来问题可能出在 Qt 方面。 bugreports.qt.io/browse/QTBUG-62600 感谢您的帮助! 干得好 :) 那么,如果您认为不值得努力,请考虑回答您自己的问题或完全删除它。 IMO 答案提供了对现有错误报告的引用——或在这种情况下新创建的错误报告——真的很有用。 【参考方案1】:

Qt 已确认这是一个错误,因此希望它会尽快得到修复:

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

同时,您可以尝试应用补丁并从源代码编译框架:

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

希望这会有所帮助!

【讨论】:

是时候结束这个问题了!

以上是关于如何正确使用 Qt QML Image Provider的主要内容,如果未能解决你的问题,请参考以下文章

Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算

[Qt5] Develop openCV3 by QML on Qt-creator

如何在 Qt 小部件中以 PreserveAspectFit 的比例显示图像

使用 Qt.createQmlObject() 创建自定义 qml 对象实例

如何使“真正透明”的窗口成为光标,最好是在纯 QML 上? (Qt 5.7)

如何在 Python 和 Qt Quick QML 应用程序中实现简化的双向数据绑定