How to set QML properties from C++ - c++

I'm trying to do a simple task as changing a property (text: ) of some QML object from C++ yet I'm failing miserably. Any help appreciated.
I'm not getting any errors, the window shows up, just the text property doesn't change as (at least I think) it should.
Is even anything I'm NOT doing wrong here?!!
What I was trying is this:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQuickItem>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QString>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
QObject *object = component.create();
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QString thisString = "Dr. Perry Cox";
object->setProperty("text", thisString); //<--- tried instead of thisString putting "Dr. ..." but nope.
delete object;
return app.exec();
}
main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
visible: true
width: 360
height: 360
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
Text {
id: whot
text: ""
anchors.centerIn: parent
font.pixelSize: 20
color: "green"
}
}

When you call QObject *object = component.create(); you get access to the root context, which is the Window component and its properties.
To get access to Text properties, you can create property alias like this:
Window {
property alias text: whot.text
...
Text {
id: whot
text: ""
...
}
}
That will give you access to whot's text property from within the Window's context.
There is another slightly more round-about way. Assign objectName property instead of id (or both if you still need id) to whot:
Text {
id: whot // <--- optional
objectName: "whot" // <--- required
text: ""
...
}
Now you can do this in code:
QObject *whot = object->findChild<QObject*>("whot");
if (whot)
whot->setProperty("text", thisString);
On a side note: I don't think you are supposed to delete the object until after calling app.exec(). Otherwise, it will ... well, be deleted. :)

#include <QQmlContext>
#include <qquickview.h>
#include <qdir.h>
QQmlApplicationEngine engine;
QString root = QCoreApplication::applicationDirPath();
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow*>(topLevel);
window->setProperty("root", root);
for qml
ApplicationWindow {
property string root
onRootChanged: {
console.log("root : "+ root);
}
}

For QML properties you should use QQmlProperty instead:
QQmlProperty::write(whot, "text", thisString);

Related

QML. How to change MapPolyline path from C++?

Here is a QML map:
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 14
MapPolyline {
line.width: 3
line.color: 'red'
path: [
{ latitude: -27, longitude: 153.0 },
{ latitude: -27, longitude: 154.1 },
{ latitude: -28, longitude: 153.5 },
{ latitude: -29, longitude: 153.5 }
]
}
}
How to change path from C++/qt? I tried this:
QML:
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 14
MapPolyline {
line.width: 3
line.color: 'red'
path: map_path
}
}
C++:
map = new QQuickWidget();
map->setSource(QUrl("qrc:map.qml"));
QQmlContext *qml_map = map->rootContext();
QGeoPath *path = new QGeoPath();
path->addCoordinate(*(new QGeoCoordinate(56.0831528053, 92.8405031454)));
path->addCoordinate(*(new QGeoCoordinate(56.1, 93)));
qml_map->setContextProperty("map_path", path);
But I got a exception:
calling a private constructor of class 'QVariant':
qml_map->setContextProperty("map_path", path);
and
attempted to use of deleted function:
qml_map->setContextProperty("map_path", path);
UPD:
#hi-im-frogatto suggested that it was necessary to do so:
qml_map->setContextProperty("map_path", QVariant::fromValue(path));
It helped, the error no longer occurs. But the line is not drawn. Got error:
qrc:map.qml:30: ReferenceError: map_path is not defined
qrc:map.qml:21:5: QML Map: Plugin is a write-once property, and cannot be set again.
As it says #HiI'mFrogatto you should use QVariant::fromValue(), but it can not be used directly on the .qml side:
QGeoPath geopath;
geopath.addCoordinate(QGeoCoordinate(56.006355, 92.860984));
geopath.addCoordinate(QGeoCoordinate(56.1, 93));
geopath.addCoordinate(QGeoCoordinate(56.1, 92.777));
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("geopath", QVariant::fromValue(geopath));
what you have to do is access each element through a loop:
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 10
MapPolyline {
id: pl
line.width: 3
line.color: 'red'
}
Component.onCompleted: {
var lines = []
for(var i=0; i < geopath.size(); i++){
lines[i] = geopath.coordinateAt(i)
}
pl.path = lines
}
}
But with this case we can not update the values of the QGeoPath, it is appropriate to implement a class that inherits from QObject and has as property to QGeoPath, since you can manipulate it from C++ or from QML.
main.cpp
#include <QGeoPath>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
class PathController: public QObject{
Q_OBJECT
Q_PROPERTY(QGeoPath geopath READ geoPath WRITE setGeoPath NOTIFY geopathChanged)
public:
PathController(QObject *parent=0):QObject(parent){}
void test(){
mGeoPath.addCoordinate(QGeoCoordinate(56.006355, 92.860984));
mGeoPath.addCoordinate(QGeoCoordinate(56.1, 93));
mGeoPath.addCoordinate(QGeoCoordinate(56.1, 92.777));
QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, [this](){
mGeoPath.translate(0.001, -0.01);
emit geopathChanged();
});
timer->start(1000);
}
QGeoPath geoPath() const{return mGeoPath;}
void setGeoPath(const QGeoPath &geoPath){
if(geoPath == mGeoPath)
return;
mGeoPath = geoPath;
emit geopathChanged();
}
signals:
void geopathChanged();
private:
QGeoPath mGeoPath;
};
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
PathController controller;
controller.test();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("pathController", &controller);
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 QtLocation 5.6
import QtPositioning 5.6
Window {
visible: true
width: 640
height: 480
Plugin {
id: osmMapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 10
MapPolyline {
id: pl
line.width: 10
line.color: 'red'
}
}
function loadPath(){
var lines = []
for(var i=0; i < pathController.geopath.size(); i++){
lines[i] = pathController.geopath.coordinateAt(i)
}
return lines;
}
Connections{
target: pathController
onGeopathChanged: pl.path = loadPath()
}
Component.onCompleted: pl.path = loadPath()
}
In this link you will find the complete example.
QQmlContext::setContextProperty accepts a QVariant and there's no any implicit conversion from QGeoPath to QVariant. However using the following method you can do this:
qml_map->setContextProperty("map_path", QVariant::fromValue(path));
More info: http://doc.qt.io/archives/qt-5.5/positioning-cpp-qml.html
Based on the answer I found here - How to set QML MapPolyline Path Property - I wrote a simple test-case which is correct, works and, most importantly, is simpler than the accepted answer on this question.
The principle is simple - we can wrap our QGeoCoordinates in QVariants. We can add the QVariants to a QVariantList, and pass that directly to the QML MapPolyline.path property.
Even better, if we do this with a C++ class' Q_PROPERTY value, we don't even have to have QML logic to read or re-read the property when it changes.
Finally, with this method you can call setContextProperty after the QQmlWidget has loaded its source and begun displaying, which is useful if, for example, your QQmlWidget is part of a Qt Designer form file instead of added explicitly in C++.
Lets take a look at how this looks in practice - I'm going to create a class called RouteProvider which provides the path:
#ifndef ROUTEPROVIDER_H
#define ROUTEPROVIDER_H
#include <QObject>
#include <qqml.h>
#include <QMetaClassInfo>
#include <QGeoPath>
class RouteProvider : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList path READ path NOTIFY pathUpdated)
QML_ELEMENT
public:
explicit RouteProvider(QObject *parent = nullptr);
QVariantList path();
signals:
void pathUpdated();
private:
QVariantList m_path;
};
#endif // ROUTEPROVIDER_H
To pass this class as a context property, the following needs to be true:
The class must be a subclass of QObject
This might actually be something you can skip using Q_GADGET and Q_REGISTER_METATYPE but that way lies madness
the class must use the QML_ELEMENT macro in the private part of its declaration
Note that your m_path and class functions can, in theory, be anything and do anything. The important aspects are:
The path function should return the QVariantList of QVariant::fromValue(QGeoCoordinate) values
Functions that change the path should emit the pathUpdated signal.
Looking at the .cpp file for this example is very simple:
#include "routeprovider.h"
RouteProvider::RouteProvider(QObject *parent) : QObject(parent)
{
m_path.append(QVariant::fromValue(QGeoCoordinate(51.50648, -0.12927)));
m_path.append(QVariant::fromValue(QGeoCoordinate(51.50826, -0.12599)));
}
QVariantList RouteProvider::path()
{
return m_path;
}
For the sake of this demonstration, I'm just creating a line between two coordinates and returning the path.
I'm going to create a QMainWindow with a QQmlWidget as its central widget, then set an instance of this class as a context property.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "routeprovider.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
RouteProvider m_provider;
};
#endif // MAINWINDOW_H
To demonstrate this method still works even after the QML file has been loaded, I'm loading the QML file first, then setting the context property:
#include "mainwindow.h"
#include <QQuickWidget>
#include <QQmlContext>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto centralWidget = new QQuickWidget(this);
centralWidget->setSource(QUrl{"qrc:/CentralMap.qml"});
centralWidget->rootContext()->setContextProperty("routeProvider", &m_provider);
centralWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
this->setCentralWidget(centralWidget);
this->resize(400, 400);
}
MainWindow::~MainWindow()
{
}
And here's the simplest kind of QML file which still demonstrates this in action:
import QtQuick 2.0
import QtPositioning 5.12
import QtLocation 5.15
Item {
Plugin {
id: mapPlugin
name: "osm"
}
Map {
anchors.fill: parent
zoomLevel: 14
plugin: mapPlugin
MapPolyline {
id: line
path: routeProvider.path
line.width: 15
line.color: 'red'
}
}
}
Place it in a .qrc file to make it available to the program at run-time.
Finally, a default-generated main function starts our application:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Result:

Connecting Signal QML to C++ (Qt5)

I would like to change a the color of a rectangle when I click a button. They are both in the main.qml file. I'd like to send a signal to C++ backend to change the color of the rectangle. I can't seem to figure it out from the code given in the documentation
main.qml:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
id:root
signal mysignal()
Rectangle{
anchors.left: parent.left
anchors.top: parent.top
height : 100
width : 100
}
Button
{
id: mybutton
anchors.right:parent.right
anchors.top:parent.top
height: 30
width: 50
onClicked:root.mysignal()
}
}
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include<QtDebug>
#include <QQuickView>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot() {
qDebug() << "Called the C++ slot with message:";
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyClass myClass;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QPushButton *mybutton = engine.findChild("mybutton");
engine.connect(mybutton, SIGNAL(mySignal()),
&myClass, SLOT(cppSlot()));
return app.exec();
}
Any help would be appreciated!
QPushButton *mybutton = engine.findChild("mybutton");
First, QObject::findChild finds QObjects by object name, not id (which is local to a context anyhow). Hence in QML you need something like:
objectName: "mybutton"
Second, I think you need to perform that findChild not on the engine itself, but on its root objects as returned from QQmlApplicationEngine::rootObjects().
// assuming there IS a first child
engine.rootObjects().at(0)->findChild<QObject*>("myButton");
Third, a Button in QML is represented by a type that you don't have available in C++. So you can't just assign the result to a QPushButton *, but you need to stick with the generic QObject *.
I had to create a seperate class and header file and then connect it to the signal in main.cpp
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include<QtDebug>
#include <QQuickView>
#include<QPushButton>
#include<myclass.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *topLevel = engine.rootObjects().at(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
MyClass myClass;
QObject::connect(window,
SIGNAL(mysignal()),
&myClass,
SLOT(cppSlot())
);
return app.exec();
}
myclass.h
#ifndef MYCLASS
#define MYCLASS
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot();
};
#endif // MYCLASS
myclass.cpp
#include<myclass.h>
void MyClass::cppSlot(){
qDebug()<<"Trying";
}

QML: Type Error with custom QObject

I'm trying out something in QML to try and make it alittle easier to merge the two more seamlessly; to be precise, I'm trying to link an object with structured data to QML.
I have the following setup:
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "dataobject.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<DataObject>("DO", 1,0,"DataObject");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
dataobject.h:
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QVariant>
#include <iostream>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal a MEMBER a NOTIFY aChanged)
Q_PROPERTY(qreal b MEMBER b NOTIFY bChanged)
public:
explicit DataObject(QObject *parent = 0);
signals:
void aChanged();
void bChanged();
public slots:
void printState() {
using namespace std;
cout << a << ", " << b << endl;
}
private:
qreal a;
qreal b;
};
#endif // DATAOBJECT_H
dataobject.cpp:
#include "dataobject.h"
DataObject::DataObject(QObject *parent) :
QObject(parent)
{
connect(this, SIGNAL(aChanged()), this, SLOT(printState()));
connect(this, SIGNAL(bChanged()), this, SLOT(printState()));
}
main.qml:
import DO 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
Text {
text: qsTr("Hello World")
MouseArea {
property DataObject myData;
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
}
}
qml.qrc:
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
Now, what I'd hoped was that values generated by QML could be feed into a object in C++ directly (i.e. the onReleased handler in the MouseArea trying to write to the myData field). However, this basic proof of concept doesn't work, but I don't really understand why.
The error I get (on drag and release of the mouse button) is:
qrc:///main.qml:29:TypeError:Type error
Which matches up with the line "myData.a = mouse.x;", so it fails straight away.
Any idea's where I'm going wrong? I've tried with the fields being int, double, QVariant, and qreal, none of which have worked. Is in a fundamental inability in QML to link objects like that? If so, any idea how, for example, anchors.fill is implemented in the Qt source code?
It helps if you break down the expression on the line where the error is coming from. Try just printing myData.a first:
print(myData.a)
myData.a = mouse.x;
myData.b = mouse.y;
qrc:///main.qml:31: TypeError: Cannot read property 'a' of null
So, myData is null. We can verify this with another QObject-based type:
MouseArea {
property DataObject myData;
property Item item
anchors.fill: parent
drag.target: parent
onReleased: {
print(item)
myData.a = mouse.x;
myData.b = mouse.y;
}
}
qml: null
So, you can fix this error by initialising the property:
property DataObject myData: DataObject {}
You can think of QObject-based properties as pointers for JavaScript; they can be null or point to a valid object... or be undefined. :) I can't find anything mentioned about this here, but that's where this behaviour should be mentioned.
If you'd like to simplify things, you can have the object default-constructed for you by making it a child object of the MouseArea, rather than a property:
MouseArea {
DataObject {
id: myData
}
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
Currently, you won't be able to refer to this property from C++. However, you can achieve this in two ways:
Declare a property alias to the item.
Give it an objectName and use QObject::findChild to find it.

How to connect QQuickitem with Qml Object?

I am trying to make a simple program that allow user to connect to the specific websites via clicking the image.
Here is my code:
account.h:
#ifndef ACCOUNTS_H
#define ACCOUNTS_H
#include <QObject>
#include <QUrl>
#include <QDesktopServices>
class accounts : public QObject
{
Q_OBJECT
public:
explicit accounts(QObject* parent = 0) : QObject(parent){}
public slots:
void gmailOpen(const QString &msg)
{
QUrl gmailUrl(msg);
QDesktopServices::openUrl(gmailUrl);
}
};
#endif // ACCOUNTS_H
main.cpp:
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "accounts.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView;
QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink");
accounts *gmailAccount = new accounts;
QObject::connect(gmail, SIGNAL(gmailSignal(QString)),gmailAccount,SLOT(gmailOpen(QString)));
view->setSource(QUrl::fromLocalFile("/Users/yudelin/Documents/MyCrazyProjects/Managers4ManyAccounts/main.qml"));
view->show();
return app.exec();
}
main.qml:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
color: "silver"
Image {
id:gmailLink
objectName: "gmailLink"
width: 102
height: 199
fillMode: Image.PreserveAspectFit
source: "...." //the url is too long so I omit it.
anchors.centerIn: parent
signal gmaiSignal (string msg)
MouseArea {
anchors.fill: parent
onClicked:
gmailLink.gmailSignal("http://mail.google.com")
}
}
}
As you can see, I am trying to connect Qml Image with c++ object accounts.
So I use QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink"); to fetch the qml object.
But it does not work. I guess it is suit for the older Qt version.
How could I fix this problem?
You want gmailOpen slot to be called on clicking on the Image. There are simpler and preferred ways of doing it other than doing this.
QObject *gmail = view->rootObject().findChild("gmailLink");
As #Kunal pointed out, you can use Qt.openUrlExternally(string).
If you want to open the Url by using QDesktopServices::openUrl from the gmailOpen slot, you can call the slot directly by setting the context property.
4, QObject *gmail = view->rootObject().findChild<QObject*>("gmailLink");... Here before setting the qml file, you are trying to get a reference for the gmailLink object. Its not created yet.
Following code explains my take on the problem
5a. accounts.h file
#ifndef ACCOUNTS_H
#define ACCOUNTS_H
#include <QObject>
#include <QUrl>
#include <QDesktopServices>
class accounts : public QObject
{
Q_OBJECT
public:
explicit accounts(QObject* parent = 0) : QObject(parent){}
public slots:
void gmailOpen(const QString &msg)
{
QUrl gmailUrl(msg);
QDesktopServices::openUrl(gmailUrl);
}
};
#endif // ACCOUNTS_H
5b. main.cpp file
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml>
#include "accounts.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
accounts *gmailAccount = new accounts;
QQuickView *view = new QQuickView;
view->engine()->rootContext()->setContextProperty("accounts",gmailAccount);
view->setSource(QUrl::fromLocalFile("qml/SO_OpenExternalLink/main.qml"));
view->show();
return app.exec();
}
5c. main.qml file
import QtQuick 2.1
import QtQuick.Dialogs 1.0
Rectangle {
width: 360
height: 360
color: "silver"
Image {
id:gmailLink
objectName: "gmailLink"
width: 102
height: 199
fillMode: Image.PreserveAspectFit
source: "http://upload.wikimedia.org/wikipedia/commons/4/41/Flag_of_India.svg"
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked:Qt.openUrlExternally("http://mail.google.com")
}
}
Image{
id:secondImage
width:102
height:199
fillMode:Image.PreserveAspectFit
source: "http://upload.wikimedia.org/wikipedia/commons/5/55/Emblem_of_India.svg"
anchors.left:gmailLink.right
anchors.top: gmailLink.top
MouseArea {
anchors.fill: parent
onClicked:fileDialog.visible = true
}
}
FileDialog {
id: fileDialog
title: "Please choose a file"
nameFilters: [ "Image files (*.jpg *.png)", "All files (*)" ]
visible:false;
selectMultiple:false
onAccepted:secondImage.source = fileDialog.fileUrl
}
}
5d. You need to change the following from the above code
i. Image source
ii. QML file path in the main.cpp
iii. Possibly import QtQuick 2.0 if you don't have QtQuick 2.1 yet.
If you just want to open URL then you can use Qt.openUrlExternally(string) directly from QML. Here is documentation. No need to create a QObject for this.
And for your code. I don't see any method named gmailSignal.
You should be calling gmailOpen.
like
gmailLink.gmailOpen("http://mail.google.com")

How to access a nested QML object from C++?

Here is a reproducible example:
main.qml
import QtQuick 2.0
Item {
id : root
width: 360
height: 360
Text {
id : t1
text: qsTr("Hello World")
property int someNumber: 1000
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
main.cpp
#include <QtGui/QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QDebug>
#include "qtquick2applicationviewer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
viewer.showExpanded();
QQmlEngine engine;
QQmlComponent component(&engine, "qml/untitled/main.qml");
QObject *object = component.create();
qDebug() << "Property value:" << QQmlProperty::read(object, "root.t1.someNumber").toInt();
return app.exec();
}
I wish to access the property somenumber of the text of the QML Item.
The above method isn't producing the desired result.
How to do it?
You have two ways (at least) to accomplish this depending on your personal preference.
QML code extension
You can add a property alias to the root item as follows:
import QtQuick 2.0
Item {
id : root
width: 360
height: 360
property alias mySomeNumber: t1.someNumber // This is the addition
Text {
id : t1
text: qsTr("Hello World")
property int someNumber: 1000
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
C++ code extension
Since the QML items are QObject, you can look for the children explicitly as well, just as you would do it in a C++ QObject hierarchy. The code would be something like this:
#include <QtGui/QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QDebug>
#include "qtquick2applicationviewer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
viewer.showExpanded();
QQmlEngine engine;
QQmlComponent component(&engine, "qml/untitled/main.qml");
QObject *object = component.create();
// This line is added
QObject *childObject = object->findChild<QObject*>("SomeNumberText");
// The following line is modified respectively
qDebug() << "Property value:" << QQmlProperty::read(childObject, "someNumber").toInt();
return app.exec();
}
However, this means you will need to add the objectName: "SomeNumberText" line to your Text child item in the qml file.
Here you can find a recursive method looking for a QML item by objectName and starting at QQmlApplicationEngine::rootObjects():
////
static QQuickItem* FindItemByName(QList<QObject*> nodes, const QString& name)
{
for(int i = 0; i < nodes.size(); i++){
// search for node
if (nodes.at(i) && nodes.at(i)->objectName() == name){
return dynamic_cast<QQuickItem*>(nodes.at(i));
}
// search in children
else if (nodes.at(i) && nodes.at(i)->children().size() > 0){
QQuickItem* item = FindItemByName(nodes.at(i)->children(), name);
if (item)
return item;
}
}
// not found
return NULL;
}
///
static QQuickItem* FindItemByName(QQmlApplicationEngine* engine, const QString& name)
{
return FindItemByName(engine->rootObjects(), name);
}
What is the use case for this? It might be better to just treat the [text, someNumber] struct or object as the model. Then you only need to find the model object. Or you could create the model object on the C++ side and set it in the QML context. You could access the model and its nested properties in QML:
Text {
text: model.text
property int someNumber: model.someNumber
}