Passing a pointer to QML string property to C++ - c++

How do you pass a pointer to string property from QML to C++?
When I attempted to do it the obvious way, I got this error:
qrc:/NewAccount.qml:236: Error: Unknown method parameter type: QString*
Which means, QML engine can't convert new_account.error_string property to C++ when calling save_account (Q_INVOKABLE) method
This is my code in QML:
import myproject.aewm.ethkey 1.0
import myproject.aewm.walletaccount 1.0
...
id: new_account
property EthKey key: EthKey{}
property WalletAccount account: WalletAccount{}
property string error_string: ""
....
if (aewm_obj.save_account(key,account,new_account.error_string)) {
wallet_accounts_tabbar.currentIndex=0
} else {
console.log("error occurred:"+new_account.error_string)
}
Where aewm_obj is a C++ type (registered in QML) and save_account is declared in C++ as :
Q_INVOKABLE bool save_account(EthKey *key, WalletAccount *account, QString *err_str);
The docs say that a (QML) string property is a QString object, and that these types are automatically converted from from C++ and QML . I am passing pointers to my custom QObject-derived classes without any problems after qmlRegisterType() call, so why I can't do the same with strings?
I thought that maybe string pointers are not supported and I tried to add this line:
qRegisterMetaType<QString*>("QString*");
but after this change, the pointer I received at C++ side was 0x0 and I got a segfault.
So, how do you pass pointers to QML string properties from QML to C++ ?
Or, do you think I should register QString class with qmlRegisterType() too? I tried it but I have encountered some compilation issues, so I think it won't compile.
The last solution would be to create a custom object with a QString inside, and send a pointer to it from QML to C++. But, this would be an overkill, if QString exists why not find a way to use it?
Will appreciate very much your comments. I definitely want to use pointers , it is safer than dealing with object ownership when exchanging data between C++ and QML.

As I said in the comments, QML only passes pointers from the QObjects, and QString is not a QObject.
I think you are giving an incorrect approach to the problem, you could create a property in the object that performs the calculations that have the error message as shown below.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
class Foo: public QObject{
Q_OBJECT
Q_PROPERTY(QString errorMessage READ errorMessage)
public:
using QObject::QObject;
Q_INVOKABLE bool process(int a, int b, int res){
bool status;
// some operation
status = (a+b) == res;
mErrorMessage = status? "": QString("error message: %1 + %2 is different to %3").arg(a).arg(b).arg(res);
return status;
}
QString errorMessage() const{
return mErrorMessage;
}
private:
QString mErrorMessage;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Foo>("com.eyllanesc.org", 1, 0, "Foo");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import com.eyllanesc.org 1.0
Window {
visible: true
width: 640
height: 480
Foo{ id: foo }
Row{
SpinBox{ id: a }
SpinBox{ id: b }
SpinBox{ id: c }
Button{
text: "process"
onClicked: {
var message;
if(foo.process(a.value, b.value, c.value)){
message = "correct"
console.log("successful")
}
else{
message = foo.errorMessage
console.log("error is "+ message)
}
txt.text = message
}
}
Label{ id: txt }
}
}

With Qt, the C++ API and the QML API are completely different, and practically incompatible layers. There is a lot of conversion of data back and forth in order to make the whole thing work. And you don't really have control over it when it comes to primitives like strings. So just get that idea out of your head.
If you want to access a particular property of a particular object, you need to pass the object that will be received as a pointer, and the property name, which then you can access via its name string via QObjects property() and setProperty().
But in your case that is entirely redundant, simply pass the string itself.

Related

Connecting signals from QML to Qt: var to QList<QString>

I am trying to connect a signal from QML to a SLOT from Qt. The signal passes a QList variable.
QML:
property var cases: ["A", "B", "C"]
signal casesClicked(list cases)
Qt:
d->m_view->setSource(QUrl("qrc:/qml/main.qml"));
d->m_item = d->m_view->rootObject();
QObject::connect(d->m_item, SIGNAL(casesClicked(QList<QString>)), this, SLOT(onCasesClicked(QList<QString>)));
The issue I am having is that I don't know how to declare QList from the QML side, so it is taken directly. If I declare it with:
signal casesClicked(var cases)
then, the signal is not connected, and I if I declare it as a list or an Array it says "Invalid signal parameter type: list/Array"
Any tip? I don't have any problems with single int, bool or string. Thanks,
I do not think it is appropriate to make the connection on the C ++ side since when compiling it does not know the signal created in QML, a possible solution is to make the connection on the QML side. In the following I show an example
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include <QDebug>
class Test: public QObject{
QQuickView *view;
Q_OBJECT
public:
Test(QObject *parent=Q_NULLPTR):QObject(parent)
{
view = new QQuickView;
view->rootContext()->setContextProperty("Test", this);
view->setSource(QUrl("qrc:/main.qml"));
view->show();
}
public slots:
void onCasesClicked(QVariantList cases){
qDebug()<<cases;
}
};
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Test test;
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
Item {
id: it
visible: true
width: 640
height: 480
signal casesClicked(var cases)
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: it.casesClicked(["B", 1, "D"])
}
Connections{
target: it
onCasesClicked: Test.onCasesClicked(cases)
}
// or
// onCasesClicked: Test.onCasesClicked(cases)
// if the connection is made in the same item
}
Output:
(QVariant(QString, "B"), QVariant(int, 1), QVariant(QString, "D"))
IIRC JS arrays are converted to QVariantList in C++, so try using that instead.
EDIT:
Ok, it appears that although the above is the default automatic conversion, it doesn't work for some reason when the connection is made from C++.
In this scenario the signal value is converted to a QVariant instead, which can directly be converted to a QStringList which is convenient. However that approach will not work if you have a JS array with different data types in it, which is perfectly legal and quite frequently used in JS.
QVariantList will still work if you pass the JS array as a parameter to a C++ function call thou. So you can iterate each value and handle a "polymorphic" array.
Whether or not it is recommended practice to create connections from to QML objects from C++ depends on the usage scenario, overall, I'd say that it isn't. The recommended practice is to expose the C++ core interface to QML and do the connections from there and to avoid touching any QML from C++ whatsoever. But there are a few corner cases where exceptions can be made.

How to catch C++ signal in QML signal handler after type registration?

I am developing a basic qml-cpp application to understand how one interacts with another. I have a MessageSetter C++ class and one main.qml. Since I wish to understand two-way communication, I exposed MessageSetter properties to qml using setContextProperty and also registered MessageSetter class with qml (instantiable registration). Exposed properties work fine. Now when a qml button is clicked, then the signal (qmlBtnClicked) is successfully caught in a MessageSetter slot(onQmlButtonClicked). This slot further emits another MessageSetter signal (colorChanged). This new (C++) signal should be caught in qml registered MessageSetter's signal handler (onColorChanged) but it does not arrive here in any case. Below is main.cpp code:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<MessageSetter>("com.SkillLotto.MessageSetter", 1, 0, "SetMessage");
MessageSetter data;
engine.rootContext()->setContextProperty("msgSetter", &data);
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
QObject *object = component.create()->findChild<QObject*>("setTextBtn");
QObject::connect(object, SIGNAL(qmlBtnClicked()), &data, SLOT(onQmlButtonClicked()));
return app.exec();
}
This is MessageSetter slot that emits another signal:
void MessageSetter::onQmlButtonClicked()
{
emit colorChanged("red");
}
This is qml code, this signal handler never gets called:
SetMessage{
onColorChanged: {
rect.color = color //rect is some rectangle in this file.
}
}
As I stated, qml signal is successfully caught in C++ slot but I am unable to catch this C++ signal in qml signal handler. Any help please.
This question, as I see, is focussed on qmlRegisterType() and should not be duplicate of this question? I also want to know whether qmlRegisterType() and setContextProperty() cant be used simultaneously or not ?
I think your code should work well.
I don't have the whole code so I don't know if you have the right methods implemented.
In order to get the signal using qmlRegisterType you need some requirements. Check if you have the Q_PROPERTY macro call implemented. Any property that is writable should have an associated NOTIFY signal that is emitted whenever the property value has changed.
If so, when you change the color property in the SetMessage component, the signal onColorChanged should be triggered.
Here you have an example where two signals are emitted: the first one when the size property is updated and the second one if the C++ mouseClick method is called using a MouseArea.
By the way, you don't need setContextProperty to integrate your C++ class with QML. qmlRegisterType should be enough. Or vice versa, depending on your needs. You can use both, but then you will have two different objects to work with. It really depends on what you want to achieve.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "customitem.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<CustomItem>("CustomItem", 1,0, "CustomItem");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
customitem.h
#ifndef CUSTOMITEM_H
#define CUSTOMITEM_H
#include <QObject>
class CustomItem: public QObject
{
Q_OBJECT
/*
* Any property that is writable should have an associated NOTIFY signal.
* Ref: http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html#exposing-properties
*/
Q_PROPERTY(int size READ size WRITE setSize NOTIFY sizeChanged)
public:
CustomItem(QObject *parent = 0);
int size() const;
void setSize(int);
Q_INVOKABLE void mouseClick();
private:
int m_size;
signals:
void sizeChanged(int size);
void clicked();
public slots:
};
#endif // CUSTOMITEM_H
customitem.cpp
#include "customitem.h"
#include <QDebug>
CustomItem::CustomItem(QObject *parent)
: QObject(parent), m_size(0)
{
}
int CustomItem::size() const
{
return m_size;
}
void CustomItem::setSize(int size)
{
m_size = size;
emit sizeChanged(m_size);
}
void CustomItem::mouseClick()
{
qDebug() << "CustomItem::mouseClick()";
emit clicked();
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import CustomItem 1.0
Window {
visible: true
TextInput {
id: mySize
x: 0
y: 0
text: "100"
}
CustomItem {
id: customItem
size: mySize.text
onSizeChanged: console.log("size changed:", size)
onClicked: console.log("onClicked!")
}
Rectangle {
id: rect
x: 50
y: 50
width: customItem.size
height: customItem.size
color: "red"
MouseArea {
anchors.fill: parent
onClicked: { customItem.mouseClick() }
}
}
}
Because you are using two different instances of your MessageSetter, one is data in main.cpp and other is new instance SetMessage. Use only one to connect both signals/slots.
You are expecting onColorChanged signal from SetMessage but the signal is coming from data (in main.cpp).
Why do you need instantiable if you want to create a context property?
Add this in your main.qml file
Connections {
target: msgSetter
onColorChanged: {
console.log("received color changed signal");
}
}

QAbstractVideoSurface example

I'm trying to make myself a QML Camera item which has more functions, and also provide a source to the VideoOutput element. Such as this:
VideoOutput{
source:mycamera
}
MyCustomCamera{
id:mycamera
}
in the document it says
If you are extending your own C++ classes to interoperate with
VideoOutput, you can either provide a QObject based class with a
mediaObject property that exposes a QMediaObject derived class that
has a QVideoRendererControl available, or you can provide a QObject
based class with a writable videoSurface property that can accept a
QAbstractVideoSurface based class and can follow the correct protocol
to deliver QVideoFrames to it.
I have tried giving my object a private property mediaObject, which is of type QCamera, but looks like QCamera does not have a QVideoRenderControl (or its my fault not knowing how to do it correctly).
I need to achieve the effect I've shown in the beginning, anyway is welcomed.
Or otherwise can anyone give me a short example on what is meant by "a writable videoSurace property that accept blablabla and follow the correct protocol"?
I can't help you with your main concern but i can give you an example usage of the videoSurface.You can use the "writable videoSurface" like this:
My example consists of three main steps:
You write a class that has a QAbstactVideoSurface property. This class will be your video provider which can display frames on the VideoOutput via calling its present() function.
videoadapter.h
#ifndef VIDEOADAPTER_H
#define VIDEOADAPTER_H
#include <QObject>
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>
#include <QTimer>
class VideoAdapter : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface NOTIFY signalVideoSurfaceChanged)
public:
explicit VideoAdapter(QObject *parent = nullptr);
QAbstractVideoSurface *videoSurface() const;
void setVideoSurface(QAbstractVideoSurface *videoSurface);
signals:
void signalVideoSurfaceChanged();
private slots:
void slotTick();
private:
void startSurface();
private:
QAbstractVideoSurface *mVideoSurface;
QVideoSurfaceFormat *mSurfaceFormat;
QImage *mImage;
QTimer mTimer;
};
#endif // VIDEOADAPTER_H
videoadapter.cpp
#include "videoadapter.h"
#include <QDebug>
VideoAdapter::VideoAdapter(QObject *parent)
: QObject(parent), mVideoSurface(nullptr), mSurfaceFormat(nullptr)
{
mTimer.setInterval(1000);
connect(&mTimer, &QTimer::timeout, this, &VideoAdapter::slotTick);
}
QAbstractVideoSurface *VideoAdapter::videoSurface() const
{
return mVideoSurface;
}
void VideoAdapter::setVideoSurface(QAbstractVideoSurface *videoSurface)
{
if(videoSurface != mVideoSurface)
{
mVideoSurface = videoSurface;
emit signalVideoSurfaceChanged();
startSurface();
// This is the test timer that will tick for us to present the image
// on the video surface
mTimer.start();
}
}
void VideoAdapter::slotTick()
{
QVideoFrame frame(*mImage);
mVideoSurface->present(frame);
}
void VideoAdapter::startSurface()
{
mImage = new QImage("../resources/images/test.jpg");
auto pixelFormat = QVideoFrame::pixelFormatFromImageFormat(mImage->format());
mSurfaceFormat = new QVideoSurfaceFormat(mImage->size(), pixelFormat);
if(!mVideoSurface->start(*mSurfaceFormat))
{
qDebug() << "Surface couldn't be started!";
}
}
This class only loads an image file and displays it with the usage of a timer but in your case you will be having a frame source so you can change this to suit your needs. If you can convert your frame to QImage of QVideoFrame you can display it like this.
You have to make this class usable in QML. In my case i created an object and made it visible to QML via setting it as a property.
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlDebuggingEnabler enabler;
VideoAdapter adapter;
// When you do this this object is made visible to QML context with the
// given name
engine.rootContext()->setContextProperty("videoAdapter", &adapter);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
You give this object to the VideoOutput as source in QML.
Window {
visible: true
width: 640
height: 480
color: "black"
title: qsTr("Video Player")
VideoOutput {
id: videoPlayer
anchors.fill: parent
source: videoAdapter
}
}
This example as i said is a simple one that only loads an image and only displays that one image periodically.
This question is an old one and you probably moved on but hope this can at least help other people.
The code provided by #U.Tuken works fine, except if I change the name of property name in Q_PROPERTY from "videoSurface" to any other word, it doesn't work. That is very strange behaviour cause from Qt's point of view "videoSurface" is just a name.
Additionally I got error
"qt.gui.icc: fromIccProfile: failed minimal tag size sanity".
This error pops up if the imported "JPG" is not of correct format
as per this link.
Changing the "JPG" file helped me get rid of the above warning.

How can I send signals from C to QML? [duplicate]

I want to send a Signal from C++ to a Slot in my QML File.
I already got it working without and primitive type parameters, although if I want to send a QString to my QML Slot I get an error whilst connecting.
I connect in main.cpp
QObject *contentView = rootObject->findChild<QObject*>(QString("contentView"));
QObject::connect(&myObj, SIGNAL(finishedGatheringDataForItem(QString)),
contentView, SLOT(updateViewWithItem(QString)));
the relavant part of my qml File
Rectangle {
objectName: "contentView"
function updateViewWithItem(string) { console.log('got some Items'); } // slot
}
Error:
Object::connect: No such slot QDeclarativeRectangle_QML_2::updateViewWithItem(QString)
You should use Connections in this case (maybe it's the only way to connect).
Put your object myObj to QML file by setContextProperty
qmlVectorForm->rootContext()->setContextProperty("YourObject", myOb);
Your signal is
finishedGatheringDataForItem(QString signalString)
In QML file, add Connectios likes below:
Connections {
target: YourObject
onFinishedGatheringDataForItem: {
qmlString = signalString
}
}
I think it would be best if you check this tutorial:
http://doc.qt.io/qt-4.8/qtbinding.html
especially this section:
http://doc.qt.io/qt-4.8/qtbinding.html#receiving-signals
I think your mistake in this case might either be that you didn't declare it as a slot or you didn't make it invocable. Both options are explained in the Qt Tutorial.
Also, you need to use a QVariant in order to exchange data between C++ and QML.
You can also register types, e.g. Widgets and stuff, so that you can use them in QML as a "native" type like a rectangle. In most cases this is not recommended, except if you need some certain extern class or some data that you cannot display otherwise in your QML Interface.
The reason for the QVariant is the Script based approach of QML. The QVariant basically contains your data and a desription of the data type, so that the QML knows how to handle it properly. That's why you have to specify the parameter in QML with String, int etc.. But the original data exchange with C++ remains a QVariant
I have used the qmlRegisterType before, but it is a very inconvenient Solution for simple data types. It is rather used for more complex data, such as custom Widgets, Canvas or Video elements that QML does not natively support or extended QStandardItemModels . It is a more convenient way to exchange data between QML and C++ and does not need Signals or Slots in first instance, because the QStandardItemModel updates the GUI automatically. For using the QStandardItemModel you need to register the Type with qmlRegisterType.. . The Model can then be used in Model based Views such as the ListView etc.
I attached a tutorial for this topic, it describes how to use the QListModel.
http://doc.qt.io/qt-4.8/qdeclarativemodels.html
For those who also stumbled upon this question, I want to say that Everything is much simpler. You just need the signal from C++ to have QVariant arguments. For example:
QObject::connect(&recipient, SIGNAL(resTalk(QVariant)), engine.rootObjects().at(0)->findChild<QObject*>("winSettings"),
SLOT(showWithErrorNetwork(QVariant)));
My signal is declared like this:
signals:
void resTalk(QVariant res);
So I'm calling the signal:
emit resTalk(true); //For more complex types, use 'emit yourSignal(QVariant(yourArg))'
And here is the slot I have in QML:
function showWithErrorNetwork(isNoError=false) {
if(!isNoError) {
visible = true
warningText.text = "Network error. Check the data."
warningText.visible = true
}
}
Solution without Connections and any context is by connecting not signal-slot, but signal-signal. Found here.
Example code is as follows.
qml:
Window{
signal qmlSend(string textOut)
signal qmlReceive(string textIn)
onQmlReceive:{
console.log(textIn)
}
}
Header file of Background class contains
public signals:
void cppSend(QString textOut);
public slots:
void cppReceive(QString textIn);
And main.cpp connects them in this way:
1.From qml to cpp:
QObject::connect(qmlRootObject, SIGNAL(qmlSend(QString)),
backgroundObject, SLOT(cppReceive(QString)));
2.From cpp to qml:
QObject::connect(backgroundObject, SIGNAL(cppSend(QString)),
qmlRootObject, SIGNAL(qmlReceive(QString)));
I have tried a lot of solutions to succeed in just update QML from a C++ signal but many did not work.
This solution works and has been tested, it is based on this answer: https://stackoverflow.com/a/59502860/2486332 (by #Adriano Campos)
You can send data from C++ to qml using signals, like this:
main.cpp:
#include <QQmlContext>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// Class init
YourClass yourObject;
// Embedding C++ Objects into QML with Context Properties
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("yourObject", &yourObject);
return app.exec();
}
main.qml:
import QtQuick 2.6
Window {
id: mainWindow
Connections {
target: yourObject
onSignalData: {
console.log("Data: " + signal_param)
textToChange.text = "Changed to: " + signal_param
}
}
Text {
id: textToChange
text: "beforeChange"
}
}
yourClass.h:
class YourClass : public QObject
{
Q_OBJECT
signals:
// Signal from YourClass
void signalData(QString signal_param);
}
yourClass.cpp:
emit signalData("Hello QML"); // Signal from yourClass
A complete tutorial about "How to Expose a Qt C++ Class with Signals and Slots to QML" is available on this page: https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml
Why not use rootContext?
in c++ side you have:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
//--------------------------------------------------------
#include <myClass.h>
//--------------------------------------------------------
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
//--------------------------------------------------------
myClass * myobj = new myClass(&app);
//--------------------------------------------------------
//--------------------------------------------------------
engine.rootContext()->setContextProperty("myobj",myobj);
//--------------------------------------------------------
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
and in qml side you have:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
//--------------------------------------------------------
Component.onCompleted: {
myobj.onSomeSignal.connect(signalHandling)
}
//--------------------------------------------------------
//--------------------------------------------------------
function signalHandling(){
console.log("Signal emitted from c++ side")
}
//--------------------------------------------------------
}

Communication between C++ and QML

This page shows how to call C++ functions from within QML.
What I want to do is change the image on a Button via a C++ function (trigger a state-change or however it is done).
How can I achieve this?
UPDATE
I tried the approach by Radon, but immediately when I insert this line:
QObject *test = dynamic_cast<QObject *>(viewer.rootObject());
Compiler complains like this:
error: cannot dynamic_cast '((QMLCppBinder*)this)->QMLCppBinder::viewer.QDeclarativeView::rootObject()' (of type 'struct QGraphicsObject*') to type 'class QObject*' (source is a pointer to incomplete type)
In case it is relevant, QMLCppBinder is a class that I try to build to encapsulate the connections from several QML pages to C++ code. Which seems to be trickier than one might expect.
Here is a skeleton class to give some context for this:
class QMLCppBinder : public QObject
{
Q_OBJECT
public:
QDeclarativeView viewer;
QMLCppBinder() {
viewer.setSource(QUrl("qml/Connect/main.qml"));
viewer.showFullScreen();
// ERROR
QObject *test = dynamic_cast<QObject *>(viewer.rootObject());
}
}
If you set an objectName for the image, you can access it from C++ quite easy:
main.qml
import QtQuick 1.0
Rectangle {
height: 100; width: 100
Image {
objectName: "theImage"
}
}
in C++:
// [...]
QDeclarativeView view(QUrl("main.qml"));
view.show();
// get root object
QObject *rootObject = dynamic_cast<QObject *>(view.rootObject());
// find element by name
QObject *image = rootObject->findChild<QObject *>(QString("theImage"));
if (image) { // element found
image->setProperty("source", QString("path/to/image"));
} else {
qDebug() << "'theImage' not found";
}
// [...]
→ QObject.findChild(), QObject.setProperty()
So, you could set your C++ object as a context property on the QDeclarativeView in C++, like so:
QDeclarativeView canvas;
ImageChanger i; // this is the class containing the function which should change the image
canvas.rootContext()->setContextProperty("imgChanger", &i);
In your ImageChanger class, declare a signal like:
void updateImage(QVariant imgSrc);
Then when you want to change the image, call emit updateImage(imgSrc);.
Now in your QML, listen for this signal as follows:
Image {
id: imgToUpdate;
}
Connections {
target: imgChanger;
onUpdateImage: {
imgToUpdate.source = imgSrc;
}
}
Hope this helps.