将图像从c ++更新为QML

时间:2018-05-28 12:54:50

标签: c++ qt 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");

Image的实现:

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)列表始终为空。

2 个答案:

答案 0 :(得分:1)

一般来说,你应该避免直接从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);
}

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

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
}
...

答案 1 :(得分:1)

要稍微改进@eyllanesc的解决方案,Helper类应该保留状态,而ImageItem应该只是图像的哑表示。

此外,您不需要单独的Connection元素。

我的设置如下:

LiveImage.h

#ifndef LIVEIMAGE_H
#define LIVEIMAGE_H

#include <QImage>
#include <QQuickPaintedItem>
#include <QPainter>

class LiveImage : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QImage image MEMBER m_image WRITE setImage)

    // Just storage for the image
    QImage m_image;

public:
    explicit LiveImage(QQuickItem *parent = nullptr);
    void setImage(const QImage &image);
    void paint(QPainter *painter) override;
};

#endif // LIVEIMAGE_H

LiveImage.cpp

#include "LiveImage.h"

LiveImage::LiveImage(QQuickItem *parent) : QQuickPaintedItem(parent), m_image{}
{}

void LiveImage::paint(QPainter *painter)
{
    painter->drawImage(0, 0, m_image);
}

void LiveImage::setImage(const QImage &image)
{
    // Update the image
    m_image = image;

    // Redraw the image
    update();
}

ImageProvider.h

#ifndef IMAGEPROVIDER_H
#define IMAGEPROVIDER_H

#include <QObject>
#include <QImage>

class ImageProvider : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QImage image MEMBER m_image READ image WRITE setImage NOTIFY imageChanged)

    QImage m_image;

public:
    explicit ImageProvider(QObject *parent = nullptr);
    void setImage(QImage const &image);
    QImage image() const;

signals:
    void imageChanged();
};

#endif // IMAGEPROVIDER_H

ImageProvider.cpp

#include "ImageProvider.h"

ImageProvider::ImageProvider(QObject *parent)
    : QObject(parent)
{}

void ImageProvider::setImage(QImage const &image)
{
    m_image = image;
    emit imageChanged();
}

QImage ImageProvider::image() const
{
    return m_image;
}

然后在您的main函数中,将LiveImage注册为可实例化的QML类型,并使ImageProvider的实例也可从QML使用:

qmlRegisterType<LiveImage>("MyApp.Images", 1, 0, "LiveImage");
ImageProvider provider{};
engine.rootContext()->setContextProperty("LiveImageProvider", &provider);

QTimer::singleShot(1000, [&provider](){
    QImage image{480, 480, QImage::Format_ARGB32};
    image.fill(Qt::yellow);
    provider.setImage(std::move(image));
});

最后,您的QML如下所示:

import MyApp.Images

...

LiveImage {
    width: 480
    height: 480
    x: 0
    y: 0
    image: LiveImageProvider.image
}