I have some data structure updated in c++ layer. I have to display it in qml and save changes from qml layer to c++ structures. I hope there is a declarative approach to do it but I in desperate to find it.
Here is the part of code:
C++ header:
#ifndef NODEINFO_H
#define NODEINFO_H
#include <QObject>
#include <QString>
class NodeInfo : public QObject {
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
public:
NodeInfo(QObject *parent = 0);
virtual ~NodeInfo() {}
const QString& label() const;
void setLabel(const QString& val);
signals:
void labelChanged();
private:
QString d_label;
};
#endif // NODEINFO_H
C++ body:
#include "nodeinfo.h"
#include <QDebug>
NodeInfo::NodeInfo(QObject *parent) : QObject(parent), d_label("Test string") {
}
const QString &NodeInfo::label() const {
qDebug() << "NodeInfo::label: getter";
return d_label;
}
void NodeInfo::setLabel(const QString &val) {
qDebug() << "NodeInfo::label: setter - " << val;
d_label = val;
emit labelChanged();
}
main.cpp
#include <QGuiApplication>
#include <QDebug>
#include <QQmlContext>
#include <QQuickView>
#include <QQmlApplicationEngine>
#include "nodeinfo.h"
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// qmlRegisterType<NodeInfo>("NodeInfo", 1, 0, "NodeInfo");
NodeInfo nodeDescr;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("nodeData", &nodeDescr);
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
QObject *root = engine.rootObjects().value(0);
if (QWindow *window = qobject_cast<QWindow *>(root))
window->show();
else
return -1;
return app.exec();
}
Qml code:
import QtQuick 2.15
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow {
id: root
width: 360
height: 520
visible: true
// property alias a_label: nodeData.label
Column {
anchors.fill: parent
TextInput {
id: simpleTxt
text: nodeData.label
}
Text {
id: txt
text: nodeData.label
}
Button {
text: "writeProp"
onClicked: nodeData.label = simpleTxt.text
}
// Binding {
// target: nodeData
// property: "label"
// value: simpleTxt.text
// }
}
}
So when I'm editing text in TextInput it should automatically set property in c++ code but it do not. Only if I press button.
There is the Binding way as you see in comments and it works but I it's not a true way I hope.
Let's imagine if I have 15-30 or more data fields in my c++ structure and it's full rubbish if I must do 30 Bindings such way or if I need to write signal/slot on each data field and connect them.
But what is right way?
Any ideas appreciated
A simpler solution is to assign the signal associated to the property text:
Text {
id: txt
text: nodeData.label
onTextChanged: {
if(nodeData.label != simpleTxt.text)
nodeData.label = simpleTxt.text
}
}
text: nodeData.label sets binding nodeData.label --> text (one direction). I.e. text is updated whenever nodeData.label is changed. When the user types some text in the field, this binding is destroyed.
So if you want to update nodeData.label when the user changes text, you need to use onTextChaged event.
Text {
onTextChanged: nodeData.label = text
}
One more note: you need to check if the property is really changed before emitting the appropriate changed signal. So the code should be something like this:
void NodeInfo::setLabel(const QString &val) {
qDebug() << "NodeInfo::label: setter - " << val;
if (d_label != val)
{
d_label = val;
emit labelChanged();
}
}
This will prevent your code from endless binding loops.
Related
I've got a struct with two fields, for example:
struct testStruct
{
Q_GADGET
Q_PROPERTY(QString text MEMBER m_text);
Q_PROPERTY(QString value MEMBER m_value);
public:
QString m_text;
QString m_value;
};
There is a QList<testStruct> m_testStructs member of my "AppEngine" class exposed to QML via
Q_PROPERTY(QList<testStruct> testStructs READ testStructs NOTIFY testStructsChanged).
It is filled like that:
testStruct newStruct1, newStruct2;
newStruct1.m_text = "text1";
newStruct1.m_value = "value1";
newStruct2.m_text = "text2";
newStruct2.m_value = "value2";
m_testStructs << newStruct1 << newStruct2;
So I want to see "text" members in ComboBox list and use "value" members in further operations.
In fact QML ComboBox popup shows me the list of objects names when I set ComboBox's "textRole" property to "text" and "valueRole" to "value", but it does nothing for "currentText" or "currentValue" properties when I click the item, only "currentIndex" changes. Also "displayText" remains blank.
This is what I get in console when clicking those items:
qml: currentIndex: 0; currentText: ; currentValue: undefined
qml: currentIndex: 1; currentText: ; currentValue: undefined
So I see that ComboBox gets members of struct, but doesn't want to work with them. What should I do to make "currentText" and "currentValue" members of ComboBox work as they should?
Here are all the needed files:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "appengine.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/qml_testComboBoxStruct/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
//exposing AppEngine class to QML
AppEngine appEngineObj;
QQmlContext *context = engine.rootContext();
context->setContextProperty("AppEngine", &appEngineObj);
engine.load(url);
return app.exec();
}
my custom class header AppEngine.h
#ifndef APPENGINE_H
#define APPENGINE_H
#include <QObject>
#include <QDebug>
struct testStruct
{
Q_GADGET
Q_PROPERTY(QString text MEMBER m_text);
Q_PROPERTY(QString value MEMBER m_value);
public:
QString m_text;
QString m_value;
};
Q_DECLARE_METATYPE(testStruct)
class AppEngine : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<testStruct> testStructs READ testStructs NOTIFY testStructsChanged);
public:
explicit AppEngine(QObject *parent = nullptr);
QList<testStruct> testStructs();
private:
QList<testStruct> m_testStructs;
signals:
void testStructsChanged();
};
#endif // APPENGINE_H
my custom class cpp file AppEngine.cpp
#include "appengine.h"
AppEngine::AppEngine(QObject *parent)
: QObject{parent}
{
testStruct newStruct1, newStruct2;
newStruct1.m_text = "text1";
newStruct1.m_value = "value1";
newStruct2.m_text = "text2";
newStruct2.m_value = "value2";
m_testStructs << newStruct1 << newStruct2;
qDebug() << "m_testStructs.length():" << m_testStructs.length();
}
QList<testStruct> AppEngine::testStructs()
{
qDebug() << "testStructs()";
return m_testStructs;
}
main.qml
import QtQuick
import QtQuick.Controls
Window {
width: 640
height: 480
visible: true
title: qsTr("C++ struct to QML ComboBox")
ComboBox
{
anchors.centerIn: parent
width: 180
height: 30
id: comboBoxID
textRole: "text"
valueRole: "value"
model: AppEngine.testStructs
onActivated:
{
console.log('currentIndex:', currentIndex, '; currentText:', currentText, ';currentValue:', currentValue);
}
}
}
As I checked in the main.qml model property cant find and understand as you show it is undefined.
qml: currentIndex: 0; currentText: ; currentValue: undefined
qml: currentIndex: 1; currentText: ; currentValue: undefined
from ListView::model property
The model provides the set of data that is used to create the items in
the view. Models can be created directly in QML using ListModel,
ObjectModel, or provided by C++ model classes. If a C++ model class is
used, it must be a subclass of QAbstractItemModel or a simple list.
For example, you can have this :
import QtQuick
import QtQuick.Controls
Window {
width: 640
height: 480
visible: true
title: qsTr("C++ struct to QML ComboBox")
ComboBox
{
id: comboBoxID
anchors.centerIn: parent
width: 180
height: 30
textRole: "text"
valueRole: "value"
model: ListModel {
id : model
ListElement { text: "text1" ; value : "value1" }
ListElement { text: "text2" ; value : "value2" }
ListElement { text: "text3" ; value : "value3" }
ListElement { text: "text4" ; value : "value4" }
}
onActivated:
{
console.log('currentIndex:', currentIndex, '; currentText:', currentText, '; currentValue:', currentValue);
}
}
}
Because you use QML ListModel if you want to define your model from C++ it must be a subclass of QAbstractItemModel or a simple list.
updated :
you need to use QStandardItemModel which inherits from QAbstractItemModel you cant inherit from the abstract interface because of that I use QStandardItemModel
in appengine.h:
#ifndef APPENGINE_H
#define APPENGINE_H
#include <QObject>
#include <QDebug>
#include <QStandardItemModel>
struct testStruct: public QStandardItemModel
{
Q_OBJECT
Q_PROPERTY(QString text MEMBER m_text);
Q_PROPERTY(QString value MEMBER m_value);
public:
QString m_text;
QString m_value;
};
Q_DECLARE_METATYPE(testStruct)
class AppEngine : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<testStruct *> testStructs READ testStructs NOTIFY testStructsChanged);
public:
explicit AppEngine(QObject *parent = nullptr);
QList<testStruct *> testStructs();
private:
QList<testStruct *> m_testStructs;
signals:
void testStructsChanged();
};
#endif // APPENGINE_H
In appengine.cpp
#include "appengine.h"
AppEngine::AppEngine(QObject *parent)
: QObject{parent}
{
testStruct *newStruct1 = new testStruct;
testStruct *newStruct2 = new testStruct;
newStruct1->m_text = "text1";
newStruct1->m_value = "value1";
newStruct2->m_text = "text2";
newStruct2->m_value = "value2";
m_testStructs << newStruct1 << newStruct2;
qDebug() << "m_testStructs.length():" << m_testStructs.length();
}
QList<testStruct *> AppEngine::testStructs()
{
qDebug() << "testStructs()";
return m_testStructs;
}
I have a Label in QML and I want to change its text value when I click on a button. I have tried many different ways to achieve this, but nothing seems to work properly. I have used QObject::setProperty() and it seems to work when I print the new text value with qDebug(), but it does not refresh on the GUI. What am I doing wrong?
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QSettings>
#include <QQuickStyle>
#include <QtQuickControls2>
#include <QQmlContext>
#include <QIcon>
#include "Controllers/Network/network.hpp"
#include "Controllers/NFC/nfc.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QIcon::setThemeName("gallery");
QQuickStyle::setStyle("Material");
// Property bindings:
qmlRegisterType<RFS::Communication::Network>("rfs.communication.network", 1, 0, "Network");
qmlRegisterType<RFS::Communication::NFC>("rfs.communication.nfc", 1, 0, "NFC");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("availableStyles", QQuickStyle::availableStyles());
engine.load(QUrl("qrc:/main.qml"));
if (engine.rootObjects().isEmpty()) return -1;
return app.exec();
}
nfc.hpp:
#include <QObject>
#include <QtNfc/qnearfieldmanager.h>
#include <QtNfc/qnearfieldtarget.h>
namespace RFS::Communication
{
class NFC : public QObject
{
Q_OBJECT
public:
explicit NFC(QObject *parent = nullptr);
Q_INVOKABLE bool getStatus() { return pairingStatus; }
Q_INVOKABLE void changeTextValue();
private:
bool pairingStatus;
};
}
nfc.cpp:
#include <QtQuick>
#include <QQuickView>
#include "Controllers/NFC/nfc.hpp"
void RFS::Communication::NFC::changeTextValue()
{
QQuickView view;
view.setSource(QUrl("qrc:/Views/overview.qml"));
QObject *rootObject = view.rootObject();
QList<QObject*> list = rootObject->findChildren<QObject*>();
QObject *testLabel = rootObject->findChild<QObject*>("testLabel");
qDebug() << "Object" << testLabel->property("text"); // Successfully prints old value
testLabel->setProperty("text", "test1");
qDebug() << "Object" << testLabel->property("text"); // Successfully prints new value
QQmlProperty(testLabel, "text").write("test2");
qDebug() << "Object" << testLabel->property("text"); // Successfully prints new value
}
overview.qml:
import QtQuick 2.12
import QtQuick.Controls 2.12
import rfs.communication.nfc 1.0
Page {
id: page
NFC {
id: nfc
}
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Pane {
id: overviewTab
width: swipeView.width
height: swipeView.height
Button {
id: pairButton
text: qsTr("Pair new receiver")
onClicked: {
nfc.changeTextValue()
}
}
Label {
id: testLabel
objectName: "testLabel"
text: "hei" // I want to change this value
}
}
}
}
Is there any better way to achieve this? Thanks a lot in advance.
Anyone looking for a simple solution, I just came up with this trying to achieve changing label's text on c++ side by pressing a button in QML
Add to main.cpp:
Backend backend;
engine.rootContext()->setContextProperty("backend", &backend);
Create a new class (backend.h & backend.cpp)
In backend.h:
#ifndef CONTROLLERS_H
#define CONTROLLERS_H
#include <Qt>
#include <QObject>
#include <QCoreApplication>
#include <QWindow>
#include <QString>
class Backend : public QObject
{
Q_OBJECT
public:
explicit Backend(QObject *parent = nullptr);
public slots:
void setText();
QString getText();
};
#endif // CONTROLLERS_H
In backend.cpp:
#include "backend.h"
QString text = ""; // don't forget this
Backend::Backend(QObject *parent) : QObject(parent)
{
}
QString Backend::getText()
{
return text; //returns the text you set or didnt in setText()
}
void Backend::setText()
{
text = "text"; // or do anything you want with global QString text
}
In QML:
Label{
id: myLabel
}
Button{
id: myButton
onClicked:{
backend.setText()
myLabel.text = backend.getText()
}
}
So I'm new to Qt and I'm trying to improve my C++ skills, so I decided to start a project where I can search items in a QStringList using a textfield. I got the search function working and I was able to move the result of the search into another QStringList, where I can use it to display to the user in a function that is declared as a "public slot".
The main Idea is that the list will auto update as soon as the user enters a character into the textfield, which it already does. So I managed to get the resulted list into the Slot function to be able to display a different list every time and character gets typed in the textfield.
In the function where I pass in the list of search results, I am trying to use this
m_context->setContextProperty("resultModel",QVariant::fromValue(m_resultList));
where resultModel is the name of my model in QML and m_resultList is where the results of the search are being stored, to display the list in the ListView. My program compiles but it crashes after I run it.
So, my true question is: Is there any way that I can display a C++ QStringList not in the main.cpp into a QML ListView?
The reason why I'm asking for it to not be in the main is because I tried to use that same line above in the main.cpp with a hard coded QStringList and the list was able to display, so there must be an issue with it not being in the main. Also because I would not be able to use the slot function in SearchClass to auto update.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "searchclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<SearchClass>("b9c.backend", 1, 0, "BackEnd");
QQmlApplicationEngine engine;
SearchClass obj;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlContext *context = engine.rootContext();
obj.getContext(context);
//the line below works if provided with a qstringlist
//context->setContextProperty("resultModel", QVariant::fromValue(resultList));
return app.exec();
}
SearchClass.h
#ifndef SEARCHCLASS_H
#define SEARCHCLASS_H
#include <QObject>
#include <QQmlContext>
class SearchClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString userSearch READ userSearch WRITE setUserSearch NOTIFY userSearchChanged)
public:
SearchClass(QObject *parent = 0);
QStringList resultList;
QString userSearch();
void setUserSearch(QString &userSearch);
void getFilenameAndInput(QString inputString);
QString CompareInputAndFilename(QString inputString, QString filename);
QStringList getFileName();
//get context
void getContext(QQmlContext *context);
signals:
void userSearchChanged();
public slots:
void setUserSearch();
private:
QStringList m_resultList;
QString m_userSearch;
QQmlContext* m_context;
};
#endif // SEARCHCLASS_H
SearchClass.cpp
#include "searchclass.h"
#include <QDebug>
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
SearchClass::SearchClass(QObject *parent) : QObject(parent)
{
connect(this, SIGNAL(userSearchChanged()), this, SLOT(setUserSearch()));
}
//the result should be displayed in this SLOT when ever the user types in a character into the textfield
void SearchClass::setUserSearch(){
qDebug() << "SLOT: " << m_resultList;
//The line below makes the program crash. It works when implemented in the main.cpp
// m_context->setContextProperty("resultModel", QVariant::fromValue(m_resultList));
}
QString SearchClass::userSearch()
{
return m_userSearch;
}
void SearchClass::setUserSearch(QString &userSearch)
{
if (userSearch == m_userSearch)
return;
m_userSearch = userSearch;
qDebug() << "Input: " <<m_userSearch;
getFilenameAndInput(m_userSearch);
emit userSearchChanged();
}
QStringList SearchClass::getFileName(){
//Returns the items that will be searched for...
}
void SearchClass::getFilenameAndInput(QString inputString){
//Puts the search results into class variable m_resultList...
m_resultList = resultList;
}
QString SearchClass::CompareInputAndFilename(QString inputString, QString filename){
//Search processing...
}
//gets context to use setProperty in the above signal, but it crashes
void SearchClass::getContext(QQmlContext *context){
m_context = context;
}
main.qml
import QtQuick 2.6
import QtQuick.Controls 2.0
import b9c.backend 1.0
import QtQuick.Window 2.2
ApplicationWindow {
id: root
width: 300
height: 480
visible: true
BackEnd { id: backend }
TextField {
id: txtfield
text: backend.userSearch
placeholderText: qsTr("Search...")
width: parent.width
onTextChanged: backend.userSearch = text
}
ListView {
id:view
height: parent.height
width: parent.width
y: 5 + txtfield.height
model: resultModel
delegate: Rectangle {
border.color: "lightblue"
height: 25
width: parent.width
Text {
anchors.centerIn: parent
text: modelData
}
}
}
}
You are doing it wrong. In every possible way. You even name getContext() the function that actually sets the context.
m_resultList is never set to anything in that code you have provided. So there is no way to tell you why your application is crashing, because the actual data is a mystery.
You also have a QObject derived class - your SearchClass. So you should expose that as a context property, and then have the string list interfaced to QML by being implemented as a Q_PROPERTY of SearchClass.
Here is a simple example:
// the equivalent of your SearchClass
class Test : public QObject {
Q_OBJECT
Q_PROPERTY(QStringList model MEMBER m_model NOTIFY modelChanged)
QStringList m_model;
public slots:
void setModel(QString m) {
m_model = m.split(" ");
modelChanged();
}
signals:
void modelChanged();
};
// in main.cpp
Test t;
engine.rootContext()->setContextProperty("Test", &t);
// in main.qml
Column {
TextField {
onTextChanged: Test.setModel(text)
}
ListView {
width: 200; height: 300
spacing: 5
model: Test.model
delegate: Rectangle {
height: 25
width: 200
color: "lightgray"
Text { text: modelData; anchors.centerIn: parent }
}
}
}
As you type the text string is sent to Test::setModel(), which then splits it into space separated tokens and sets the QStringList, which is used as a model source for the list view.
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.
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
}