how pass TextField text from QString in C++? - c++

Hi Guys, i need take the text of TextField QML then pass to QString in C++.
Firstpage.qml:
Component {
id: mainView
Row {
Image {
id: logo
anchors.fill: parent
source: "Imagens/jscalcadosLogo.png"
}
ColumnLayout {
id: layoutLogin
anchors.centerIn: parent
anchors.margins: 3
spacing: 3
TextField {
id: login
objectName: "login"
Layout.fillWidth: true
placeholderText: "Username"
}
Main.cpp:
int main(int argc, char *argv[]){
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQuickView *view = new QQuickView;
view->setSource(QUrl::fromLocalFile("FirstPage.qml"));
QObject *object = view->rootObject();
QObject *textin = object->findChild<QObject*>("login");
//QString input = textin->Property("text").toString(); // crash the program
return app.exec();
}
When i try convert Textin to QString the program crash
any idea ? i'm sorry for my bad english.

The general rule is: Don't read or write something in QML from C++
Instead, you shall create a QObject-derived instance in C++ that has a property for that text. You expose that QObject to QML, either by setting it as a context property or registering it as a singleton.
Then in QML you set afore-mentioned property - et voila - you have pushed the text from the QML world to the C++.
There are various questions on StackOverflow regarding the interaction of C++ and QML (I might search for them later, and probably flag your question as duplicate, for I am sure your question is not the first).
Also the documentation has more information on this.

Related

QML timer not triggered

I have a timer in qml (view in StackView) which I try to start from C++ code, calling javascript function. But my timer is never triggered. What Am I doing wrong? My flow is a.qml -> b.qml (on Button clicked)
File b.qml :
Item {
function connectionConfirmed() {
myTimer.start()
console.log("started timer")
}
Timer {
interval: 1000; running: false; repeat: false
id: myTimer
onTriggered: {
console.log("timer triggered")
}
}
}
file main.qml:
ApplicationWindow {
id: root
visible: true
width: 320
height: 530
StackView {
id: stackView
initialItem: "qrc:/a.qml"
anchors.fill: parent
}
}
file a.qml
MouseArea{
anchors.fill: parent
onClicked: {
stackView.push("qrc:/b.qml")
}
}
C++ part:
connect(&myObjectInstance, &X::somethingHappend, this, [this](){
QQmlComponent component(&engine_, "qrc:/b.qml");
QObject *obj = component.create();
QVariant returnedValue;
QMetaObject::invokeMethod(obj, "connectionConfirmed",
Q_RETURN_ARG(QVariant, returnedValue));
delete obj;
});
Output is:
started timer
When I tried to set running: true timer is triggered, does it mean that I am not able to start the timer from JS function?
The problem is caused because you are assuming that the component created on the C++ side is the same that is created on the QML side. The .qml file is the source code, when you invoke it, they generate a different object each time.
Explanation of the behavior:
connect(&myObjectInstance, &X::somethingHappend, this, [this](){
QQmlComponent component(&engine_, "qrc:/b.qml");
QObject *obj = component.create();
QVariant returnedValue;
QMetaObject::invokeMethod(obj, "connectionConfirmed",
Q_RETURN_ARG(QVariant, returnedValue));
delete obj;
});
In this code you are first creating the component, and then you create the object, call the function written in js, that function has just been executed and print the message, and after that you delete it, so when you delete it, it will never fire. new Timer, for that reason it seemed that the one that printed the first message was the item created in QML, but it is not, the new item printed it.
Calling a C ++ QML function brings these problems because many times you do not access the object you want.
The recommendation indicated by Qt is, on the contrary, to make the connection on the QML side, for this you must export the C ++ object using setContextProperty().
In the following example using its implementation of QML, we create a class that has a signal called mysignal, it is triggered every 5 seconds for the purpose of testing. An object of that class is exported to QML and connected so that it invokes the js function from QML
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
class Helper: public QObject{
Q_OBJECT
public:
using QObject::QObject;
signals:
void mysignal();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Helper helper;
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](){
emit helper.mysignal();
});
timer.start(5000);
return app.exec();
}
#include "main.moc"
b.qml
import QtQuick 2.0
Item {
id: it
function connectionConfirmed() {
myTimer.start()
console.log("started timer")
}
Timer {
interval: 1000; running: false; repeat: false
id: myTimer
onTriggered: {
console.log("timer triggered")
}
}
Connections{
target: helper
onMysignal: it.connectionConfirmed()
}
}
Finally, in a StackView, items are created and destroyed each time a page is changed.

Emitting signal not updating the values in GUI

I'm trying to make a very basic timetable app just to learn the basics of Qt. I've researched this quite a bit and cant seem to get an answer to my problem, I emit the signal when I change the value of my subject but the emitting signal does not update the GUI. It definitely is changing the value in the code but the QML GUI does not update it during run time.
I have a class called Day with the following Q_PROPERTY's:
Q_PROPERTY(Period *getPeriod READ getPeriod WRITE setPeriod NOTIFY periodChanged)
Q_PROPERTY(QString getDay READ getDay WRITE setDay NOTIFY dayChanged)
and a member to hold the periods
Period *m_period = new Period[10]; //Of type Period (my other class) which holds my subject string
I also have getters and setters for the days and periods(as seen in the Q_PROPERTY) and these two for setting/getting the subject:
Q_INVOKABLE QString getSubject(int t){
return m_period[t].getSub();
};
Q_INVOKABLE void setSubject(int t, QString subj){
m_period[t].setSub(subj);
emit periodChanged();
};
With the following signals:
void periodChanged();
void dayChanged();
I also have a class called Period with the following Q_PROPERTY:
Q_PROPERTY(QString getSub READ getSub WRITE setSub NOTIFY subChanged)
and a member to hold the subject names:
QString subject;
My Period class hold the functions called in day to actually change the QString subject:
QString getSub(){
return subject;
};
void setSub(QString sub){
subject = sub;
emit subChanged();
};
With the following signal:
void subChanged();
So surely when the subject gets changed using setSubject() in my QML, shouldn't it be emitting the periodChanged() and the subChanged() signals, which should update the GUI? I thought it would but it doesn't seem to be working.
For reference, this is basically how I implemented it in QML:
Label { text: monday.getSubject(2) } //How I display the Subject, the parameter being the period number
Button{
text: "Enter"
onClicked:{
monday.setSubject(2, "RANDOM_TEXT") //How I set the subject at run time, with the first argument being the period number and second the text I want to change it to
}
Here are the main and class files:
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<Day>("Timetable", 1,0, "Day");
qmlRegisterType<Period>("Timetable", 1,0, "Period");
QQmlApplicationEngine engine;
Day monday;
engine.rootContext()->setContextProperty("monday", &monday);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
component.create();
return app.exec();
}
Day.h
class Day : public QObject
{
Q_OBJECT
Q_PROPERTY(Period *getPeriod READ getPeriod WRITE setPeriod NOTIFY periodChanged)
private:
Period *m_period = new Period[10];
public:
Day(QObject *parent = 0);
~Day();
Period* getPeriod();
Q_INVOKABLE void setPeriod(Period *p);
Q_INVOKABLE QString getSubject(int t){
return m_period[t].getSub();
};
Q_INVOKABLE void setSubject(int t, QString subj){
m_period[t].setSub(subj);
emit periodChanged();
};
signals:
void periodChanged();
};
Period.h
class Period: public QObject
{
Q_OBJECT
Q_PROPERTY(QString getSub READ getSub WRITE setSub NOTIFY subChanged)
private:
QString subject;
public:
Period(QObject *parent = 0);
~Period();
QString getSub(){
return subject;
};
void setSub(QString sub){
subject = sub;
emit subChanged();
};
signals:
void subChanged();
};
main.qml
ApplicationWindow {
id: mainWindow
visible: true
title: "Timetable App"
property int margin: 11
width: mainLayout.implicitWidth + 2 * margin
height: mainLayout.implicitHeight + 2 * margin
minimumWidth: 800
minimumHeight: 600
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: margin
GroupBox{
id: timetable
title: "Timetable"
Layout.fillWidth: true
Layout.fillHeight: true
GridLayout {
id: gridLayout
rows: 11
flow: GridLayout.TopToBottom
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
Label { }
Label { text: "8:00" }
Label { text: ...} // Added Labels for times from 8:00 to 17:00
Label { text: "Monday" }
Label { text: monday.getSubject(0) }
Label { text: monday.getSubject(1) }
Label { text: ...} // Added Labels to display subjects for monday at different times, also did the same for the rest of the week
}
}
RowLayout{
Button{
text: "Enter"
onClicked:{
monday.setSubject(1, "RANDOM_TEXT") // Set the second period of monday to "RANDOM_TEXT"
console.log(monday.getSubject(1)) // To check if actually set
}
}
}
}
}
Your problem is that you are retrieving values via method calls in QML, not via property bindings.
A QML code like this should update
text: monday.getPeriod.getSub
Obviously calling properties "getSomething" is a bit weird.
Now, your m_period suggests that a single Day object will have more than one Period, which would suggest that you might want a list property instead of a single Period object. Something like
Q_PROPERTY(QList<Period*> periods READ getPeriods NOTIFY periodsChanged);
Using that from QML a bit like this
text: monday.periods[0].getSub
Or even with e.g. a Repeater
Label { text: "Monday" }
Repeater {
model: monday.periods
Label {
text: modelData.getSub
}
}
Not related to your update problem but also important to consider:
NOTIFY signals trigger binding updates, so you don't want to emit them unnecessarily. I.e. setters that emit those should first check if the new value is actually different than the old value and only emit if they are.

QML object property changed from c++, but I can't see the result

So I could change the property of a certain QML object via C++ code, but I couldn't see the result on screen.
I have an item repeated 64 times, and I want a certain image to be displayed only for the 32nd item (from C++) so I used invokeMethod to access the object via C++ then I used setProperty to change the visibility, if I view it with qDebug the property "visible" did change, but I notice no difference on the screen I still cannot see the image, but if I change the visibility from qml, I can see it.
This is the C++ code:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl("qrc:///main.qml"));
view.show();
QQuickItem* child;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///Board.qml")));
QObject *rootObject = engine.rootObjects().first();
QQuickItem *qmlObject = rootObject->findChild<QQuickItem*>("grid")->findChild<QQuickItem*>("repeter");
QMetaObject::invokeMethod(qmlObject,"itemAt",Qt::DirectConnection, Q_RETURN_ARG (QQuickItem*,child), Q_ARG(int,32));
child=child->findChild<QQuickItem*>("pleaseWork");
qDebug() << child->property("visible");
child->setProperty("visible","true");
qDebug() << child->property("visible");
return app.exec();
}
I used qDebug to verify the property changed
This is the QML code :
Item
{
id: root
width: 8*45
height: 8*45
Grid
{
id: grid
objectName: "grid"
rows: 8
Repeater
{
objectName: "repeter"
model: 64
Image
{
objectName: "test"
width: 45; height: 45
source: "images/dark_square.png"
Image
{
id: isit
objectName: "pleaseWork"
visible: false
source: "images/avail_dark.png"
}
}
}
}
}
QQuickView and QQmlApplicationEngine are alternative ways to load and show QML views. What you are loading into QQmlApplicationEngine has nothing to do with the visible output of QQuickView.
In order to get things running, you need to change the top element of the QML file from Item to Window and show it on screen:
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///Board.qml")));
// end of your code
QObject *rootObject = engine.rootObjects().first();
QQuickWindow *window = qobject_cast<QQuickWindow *>(rootObject);
if (!window) {
qDebug() << "Error: Your root item has to be a window.";
return -1;
}
window->show();
// continue with your code
QQuickItem *qmlObject = rootObject->findChild<QQuickItem*>("grid")->findChild<QQuickItem*>("repeter");

Set property of qml object using c++

I have tried with
widget->setProperty("text1Text", QVariant("After..."));
in my C++,
and
Button
{
property alias text1Text: text1.text
Text
{
id: text1
text: "Initial"
}
}
in qml.
widget is a QQuickWidget object. What am I doing wrong?
See Interacting with QML Objects from C++.
If you are using QQmlEngine:
// Using QQmlComponent
QQmlApplicationEngine engine;
...
QObject * root = engine.rootObjects().first();
If you are using QQuickView:
QQuickView view;
...
QObject * root = view.rootObject();
Getting text1:
// Update Qml file
Text
{
id: text1
text: "Initial"
objectName: id
}
// Find text1 in c++
QObject * o1 = root->findChild<QObject *>(QStringLiteral("text1"));
QQuickItem *text1 = qobject_cast<QQuickItem*>(o1);
// Set property
text1->setProperty("text", QVariant());

How to set QML properties from 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);