I am trying to update an QImage from c++ to QML,
I am using the approach from: https://www.huber.xyz/?p=477
I implemented the QQuickPaintedItem - the initial QImage is shown, but I don't find a way to update the QImage from c++ if I receive a signal there (FileWatcher).
My implementation looks like:
QML:
ImageItem {
id: liveImageItem
height: parent.height
width: parent.width
objectName: "liveImageItem"
}
I register the image with:
qmlRegisterType<QUIQImageItem>("imageItem", 1, 0, "ImageItem");
The implementation of the 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();
}
How could I get the reference of the ImageItem on c++ side to manage an update of the Image via setImage?
Is this way possible or should I try another solution?
I tried to get the item by
QList<ImageItem*> res = engine->findChildren<ImageItem*>();
and also:
QList<ImageItem*> res = engine->findChildren<ImageItem*>("liveImageItem");
the list of ImageItems (res) is always empty.
In general you should avoid modifying an item created in QML from C ++ directly, before that I will improve your implementation by adding the image as 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);
}
In this part I will answer your direct question, although it is not the best because if you do not know how to handle you could have problems, for example if you set the item in a StackView
Page since they are created and deleted every time you change pages.
QObject *obj = engine.rootObjects().first()->findChild<QObject*>("liveImageItem");
if(obj){
QImage image = ...;
QQmlProperty::write(obj, "image", image);
}
Example: 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();
}
For me a better idea is to implement a Helper and make the connection in 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
}
...
To slightly improve upon @eyllanesc's solution, the Helper
class should probably hold the state while the ImageItem
should just be a dumb representation of the image.
Also, you don't need the separate Connection
element.
My setup is then as follows:
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;
}
And then in you main
function, register the LiveImage
as an instantiable QML type, and make instances of ImageProvider
available from QML as well:
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));
});
Finally, your QML would look like this:
import MyApp.Images
...
LiveImage {
width: 480
height: 480
x: 0
y: 0
image: LiveImageProvider.image
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.