How to properly modify QML objects through C++?
-
Hello,
i am trying to recreate a behaviour in my Widgets application using a QuickWidget and the Image QML type instead of a QLabel.
The QML file is very small just an image with an additional QObject which the Image binds to and that i want to modify using c++.import QtQuick 2.15 import image Rectangle { id: root ImageModel { id: imageModel objectName: "image_model" } Image { id: image objectName: "image" source: imageModel.path width: root.width height: root.height fillMode: Image.PreserveAspectFit } }
To start with, I just wanted to adjust the url path to ensure that the setup works.
The ImageModel class should just represent the properties that i want to expose to c++:class ImageModel : public QObject { Q_OBJECT Q_PROPERTY(QString path READ path NOTIFY pathChanged) QML_ELEMENT public: explicit ImageModel(QObject* parent = nullptr); QString path() const; void setPath(const QString &newPath); signals: void pathChanged(); private: QString m_path; }; ImageModel::ImageModel(QObject* parent) : QObject(parent), m_path(QString{""}) {} QString ImageModel::path() const { return m_path; } void ImageModel::setPath(const QString &newPath) { if (m_path == newPath) return; qWarning() << "Update image to " + newPath; m_path = newPath; emit pathChanged(); }
The setup is in my widgets constructor:
ImageDisplayQuick::ImageDisplayQuick(QWidget *parent) : QWidget(parent), ui(new Ui::ImageDisplayQuick) { ui->setupUi(this); _quickWidget = findChild<QQuickWidget*>("quick_widget"); _loadTimer = new QTimer(this); _loadTimer->setTimerType(Qt::PreciseTimer); connect(_loadTimer, &QTimer::timeout, this, &ImageDisplayQuick::requestNextImage); _imageModel = _quickWidget->rootObject()->findChild<ImageModel*>("image_model"); QTimer::singleShot(2000, this, &ImageDisplayQuick::startImageCapture); } void ImageDisplayQuick::startImageCapture() { _loadTimer->start(1000); } void ImageDisplayQuick::requestNextImage() { _currentIndex = (_currentIndex + 1) % 360; _imageModel->setPath(QString("file:///D:/_Data/3000x3000_16bit_png/%1.png").arg(_currentIndex, 4, 10, QChar('0'))); }
The app crashes during QString::operator= because of
std::_Atomic_address_as<long,std::_Atomic_padded<int> >(...) returned 0xFFFFFFFFFFFFFFFF
and sometimes during doActivate() in qobject.cpp (also read access violation).
I figured out that I have to get a new pointer to the object before updating the source:_imageModel = _quickWidget->rootObject()->findChild<ImageModel*>("image_model");
I assume that I am doing it not the intended way as i always have to get a ref to the element. How am I supposed to modify the GUI or the underlying bindings in c++?
-
@ian28 the way you are doing it is how one would implement a new QML component in C++ that would tend to be purely used in QML - i.e. you wouldn't normally interact with it from the C++ side once it is created.
If you wanted to continue with that sort of approach, it might be better to create the timer in QML and have that update the path of your
Image
.However a better fit might be to expose your "model" via a "singleton instance" (see
qmlRegisterSingletonInstance
). This is the more modern approach that is advised rather than using the oldersetContextProperty
, but you will probably find more information out there about usingsetContextProperty
. It's not massively different in principle and it might be easier for you to use the older approach in order to get started.