首页 > 解决方案 > 将图像从 c++ 更新到 QML

问题描述

我正在尝试将 QImage 从 c++ 更新为 QML,

我使用的方法来自:https ://www.huber.xyz/?p=477

我实现了 QQuickPaintedItem - 显示了初始 QImage,但是如果我在那里收到信号(FileWatcher),我找不到从 c++ 更新 QImage 的方法。

我的实现看起来像:

QML:

ImageItem {
 id: liveImageItem
 height: parent.height
 width: parent.width            
 objectName: "liveImageItem"
}

我用以下方式注册图像:

qmlRegisterType<QUIQImageItem>("imageItem", 1, 0, "ImageItem");

图像的实现:

ImageItem::ImageItem(QQuickItem *parent) : QQuickPaintedItem(parent) {
  qDebug() << Q_FUNC_INFO << "initializing new item, parent is: " << parent;

  this->current_image = QImage(":/resources/images/logo.png");
}

void ImageItem::paint(QPainter *painter) {
  qDebug() << Q_FUNC_INFO << "paint requested...";

  QRectF bounding_rect = boundingRect();
  QImage scaled = this->current_image.scaledToHeight(bounding_rect.height());
  QPointF center = bounding_rect.center() - scaled.rect().center();

  if (center.x() < 0)
    center.setX(0);
  if (center.y() < 0)
    center.setY(0);
  painter->drawImage(center, scaled);
}

QImage ImageItem::image() const {
  qDebug() << Q_FUNC_INFO << "image requested...";
  return this->current_image;
}

void ImageItem::setImage(const QImage &image) {
  qDebug() << Q_FUNC_INFO << "setting new image...";

  this->current_image = image;
  emit imageChanged();
  update();
}

如何在 c++ 端获取 ImageItem 的引用以通过 setImage 管理 Image 的更新?

这种方式可行还是我应该尝试其他解决方案?

我试图通过

QList<ImageItem*> res = engine->findChildren<ImageItem*>();

并且:

QList<ImageItem*> res = engine->findChildren<ImageItem*>("liveImageItem");

ImageItems (res) 列表始终为空。

标签: c++qtqml

解决方案


一般来说,您应该避免直接从 C++ 修改在 QML 中创建的项目,在此之前我将通过将图像添加为 qproperty 来改进您的实现:

*。H

#ifndef IMAGEITEM_H
#define IMAGEITEM_H

#include <QImage>
#include <QQuickPaintedItem>

class ImageItem : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
    ImageItem(QQuickItem *parent = nullptr);
    QImage image() const;
    void setImage(const QImage &image);

    void paint(QPainter *painter);
signals:
    void imageChanged();
private:
    QImage m_image;
};
#endif // IMAGEITEM_H

*.cpp

#include "imageitem.h"
#include <QDebug>
#include <QPainter>

ImageItem::ImageItem(QQuickItem *parent):QQuickPaintedItem(parent)
{
    qDebug() << Q_FUNC_INFO << "initializing new item, parent is: " << parent;
    setImage(QImage(":/resources/images/logo.png"));
}

QImage ImageItem::image() const
{
    qDebug() << Q_FUNC_INFO << "image requested...";
    return m_image;
}

void ImageItem::setImage(const QImage &image)
{
    qDebug() << Q_FUNC_INFO << "setting new image...";
    if(image == m_image)
        return;
    m_image = image;
    emit imageChanged();
    update();
}

void ImageItem::paint(QPainter *painter)
{
    if(m_image.isNull())
        return;
    qDebug() << Q_FUNC_INFO << "paint requested...";

    QRectF bounding_rect = boundingRect();
    QImage scaled = m_image.scaledToHeight(bounding_rect.height());
    QPointF center = bounding_rect.center() - scaled.rect().center();

    if (center.x() < 0)
        center.setX(0);
    if (center.y() < 0)
        center.setY(0);
    painter->drawImage(center, scaled);
}

在这一部分中,我将回答您的直接问题,尽管这不是最好的,因为如果您不知道如何处理,您可能会遇到问题,例如,如果您在StackViewPage 中设置项目,因为每次更改时都会创建和删除它们页。

QObject *obj = engine.rootObjects().first()->findChild<QObject*>("liveImageItem");

if(obj){
    QImage image = ...;  
    QQmlProperty::write(obj, "image", image);
}

示例: main.cpp

#include "imageitem.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlProperty>

#include <QTime>
#include <QTimer>

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

    qsrand(QTime::currentTime().msec());
    qmlRegisterType<ImageItem>("com.eyllanesc.org", 1, 0, "ImageItem");
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *obj = engine.rootObjects().first()->findChild<QObject*>("liveImageItem");

    QTimer timer;
    if(obj){
        QObject::connect(&timer, &QTimer::timeout, [obj](){
            QImage image(100,100, QImage::Format_ARGB32);
            image.fill(QColor(qrand()%255, qrand()%255, qrand()%255));
            QQmlProperty::write(obj, "image", image);
        });
        timer.start(1000);
    }

    return app.exec();
}

对我来说,一个更好的主意是实现一个 Helper 并在 QML 中建立连接:

#include "imageitem.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlProperty>
#include <QQmlContext>

#include <QTime>
#include <QTimer>

class Helper: public QObject{
    Q_OBJECT
    Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
    QImage image() const{ return m_image; }
    void setImage(const QImage &image){
        if(m_image == image)
            return;
        m_image = image;
        emit imageChanged();
    }
signals:
    void imageChanged();
private:
    QImage m_image;
};

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

    qsrand(QTime::currentTime().msec());
    qmlRegisterType<ImageItem>("com.eyllanesc.org", 1, 0, "ImageItem");
    QGuiApplication app(argc, argv);
    Helper helper;

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("helper", &helper);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&helper](){
        QImage image(100,100, QImage::Format_ARGB32);
        image.fill(QColor(qrand()%255, qrand()%255, qrand()%255));
        helper.setImage(image);
    });
    timer.start(1000);

    return app.exec();
}

#include "main.moc"

*.qml

...
ImageItem{
    id: liveImageItem
     height: parent.height
     width: parent.width
}
Connections{
    target: helper
    onImageChanged: liveImageItem.image = helper.image
}
...

推荐阅读