View Full Version : modify a QML Text from C++
neda
13th February 2016, 11:20
Hi,
I see data in application output of Qt Creator, but I can not bind this data to text control.
Please guide me.
Thanks
main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
MySerialPort iSerialPort;
QThread * iTH = new QThread;
iSerialPort.moveToThread(iTH);
iSerialPort.openSerialPort();
iTH->start();
return app.exec();
}
myserialport.cpp:
void MySerialPort::readData()
{
QByteArray data = serial->readAll();
qDebug() << data;
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:MyItem.qml"));
QObject *object = component.create();
object->setProperty("text1Text",data);
}
MyItem.qml:
import QtQuick 2.0
import QtQuick.Controls 1.2
Item {
id: item1
width: 400
height: 400
property alias text1Text: text1.text
Text {
id: text1
width: 400
height: 29
color: "red"
text: "This text should change..."
font.pixelSize: 12
}
}
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MyItem{
}
}
anda_skoa
13th February 2016, 11:52
MySerialPort::readData() runs in a worker thread, it can't instantiate UI elements.
The cleanest way would be to have a model for the strings and use a Repeater in your main.qml to create MyItem instances for each model entry.
The model would need a slot that takes a string and appends it to its data.
You would then connect this slot to a signal in your serial port class which is emitted when it has received a new string.
Cheers,
_
neda
14th February 2016, 06:27
MySerialPort::readData() runs in a worker thread, it can't instantiate UI elements.
The cleanest way would be to have a model for the strings and use a Repeater in your main.qml to create MyItem instances for each model entry.
The model would need a slot that takes a string and appends it to its data.
You would then connect this slot to a signal in your serial port class which is emitted when it has received a new string.
Cheers,
_
Thanks for your reply.
Please guide me with simple code.
I change my code but I don't see any change to text control.
main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
MySerialPort iSerialPort;
QThread* thread = new QThread(app);
iSerialPort.moveToThread(thread);
thread->start();
iSerialPort.myText= engine.rootObjects().at(0)->findChild<QObject*>("text1Text");
iSerialPort.openSerialPort();
return app.exec();
}
serialport.cpp:
void MySerialPort::readData()
{
QByteArray data = serial->readAll();
qDebug() << data;
myText->setProperty("text", data);
}
myserialport.h
#ifndef MYSERIALPORT_H
#define MYSERIALPORT_H
#include <QtSerialPort/QtSerialPort>
#include <QObject>
#include <QApplication>
#include <QQmlApplicationEngine>
class MySerialPort: public QSerialPort
{
Q_OBJECT
public:
MySerialPort();
QObject *myText;
public slots:
void openSerialPort();
void closeSerialPort();
void writeData(const QByteArray &data);
void readData();
void handleError(QSerialPort::SerialPortError error);
private:
void showStatusMessage(const QString &message);
QSerialPort *serial;
};
#endif // MYSERIALPORT_H
main.qml:
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Text {
id: text1Text
objectName: text1Text
width: 400
height: 29
color: "red"
text: "This text should change..."
font.pixelSize: 12
}
}
anda_skoa
14th February 2016, 12:03
That still tries to have the thread interact with the GUI, i.e. you still need the mediator object.
But if you only want to show a single text that is updated with the newest data, then this can be done with a simple property.
So
1) Add a signal to MySerialPort that emits the data in readData()
2) Create a QObject derived class that has a matching slot which updates a QString property, something like this
class SerialData : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text NOTIFY textChanged)
public slots:
void setText(const QString &text)
{
if (text == m_text) return;
m_text = text;
emit textChanged();
}
signals:
void textChanged();
private:
QString m_text;
};
3) Set an instance of that as a context property on the QQmlEngine's rootContext
4) Connect the serial port signal to the slot
5) Use the "text" property in QML as the value of the Text element
Cheers,
_
neda
15th February 2016, 07:19
My problem is solved.
Thanks
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <myserialport.h>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
MyDisplay myDisplay;
myDisplay.setText("neda");
engine.rootContext()->setContextProperty("myDisplay", &myDisplay);
MySerialPort iSerialPort;
iSerialPort.setDisplay(&myDisplay);
iSerialPort.openSerialPort();
return app.exec();
}
mydisplay.h:
#include <QObject>
#ifndef MYDISPLAY_H
#define MYDISPLAY_H
class MyDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(QString newText READ getText WRITE setText NOTIFY textChanged)
public:
MyDisplay();
MyDisplay(QString);
Q_INVOKABLE QString getText() const;
public slots:
void setText(QString text);
signals:
void textChanged(QString);
private:
QString newText;
};
#endif // MYDISPLAY_H
mydisplay.cpp:
#include "mydisplay.h"
#include "qstring.h"
MyDisplay::MyDisplay()
{
newText = "";
}
MyDisplay::MyDisplay(QString text)
{
newText = text;
}
QString MyDisplay::getText() const
{
return newText;
}
void MyDisplay::setText(QString text)
{
if (text != newText)
{
newText = text;
emit textChanged(text);
}
}
serialport.cpp:
void MySerialPort::readData()
{
QByteArray data = serial->readAll();
qDebug() << data;
QString dataString=data;
myDisplay->setText(dataString);
}
void MySerialPort::setDisplay(MyDisplay * m_display)
{
myDisplay = m_display;
}
main.qml:
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Text {
id: text1Text
//objectName: text1Text
width: 400
height: 29
color: "red"
text: myDisplay.newText
font.pixelSize: 12
}
}
myserialport.h
class MySerialPort: public QSerialPort
{
Q_OBJECT
public:
MySerialPort();
public slots:
void openSerialPort();
void closeSerialPort();
void setDisplay(MyDisplay * m_display);
void writeData(const QByteArray &data);
void readData();
void handleError(QSerialPort::SerialPortError error);
private:
void showStatusMessage(const QString &message);
MyDisplay * myDisplay;
QSerialPort *serial;
};
anda_skoa
15th February 2016, 09:32
My problem is solved.
Thanks
I am surprised that this works.
In any case you should not call MyDisplay::setText() from the secondary thread, at least not without proper locking.
See my original suggestion of using a signal/slot connection.
Cheers,
_
neda
16th February 2016, 05:44
> I am surprised that this works.
Why? Which part of the code is illogical?
> In any case you should not call MyDisplay::setText()
Why?
> from the secondary thread
secondary thread? I did not use thread.
> at least not without proper locking.
Please guide me, I do not know about proper locking.
> See my original suggestion of using a signal/slot connection.
> 1) Add a signal to MySerialPort that emits the data in readData()
connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
SLOT(handleError(QSerialPort::SerialPortError)));
MySerialPort::MySerialPort()
{
serial = new QSerialPort(this);
connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
SLOT(handleError(QSerialPort::SerialPortError)));
//connect(socket, SIGNAL(setText(QString)), this, SLOT(readData()));
}
> 2) Create a QObject derived class that has a matching slot which updates a QString property
mydisplay.h:
#include <QObject>
#ifndef MYDISPLAY_H
#define MYDISPLAY_H
class MyDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(QString newText READ getText WRITE setText NOTIFY textChanged)
public:
MyDisplay();
MyDisplay(QString);
Q_INVOKABLE QString getText() const;
public slots:
void setText(QString text);
signals:
void textChanged(QString);
private:
QString newText;
};
#endif // MYDISPLAY_H
> Set an instance of that as a context property on the QQmlEngine's rootContext
engine.rootContext()->setContextProperty("myDisplay", &myDisplay);
> 5) Use the "text" property in QML as the value of the Text element
Text {
id: text1Text
width: 400
height: 29
color: "red"
text: myDisplay.newText
font.pixelSize: 12
}
I am new in Qt, please guide me to write better code.
Thank you for your kindness.
anda_skoa
16th February 2016, 09:04
> I am surprised that this works.
Why? Which part of the code is illogical?
You load the QML file before setting the context property but the QML code accesses the context property.
I would have expect that to cause the QML file to fail loading.
Usually one would set the context property before loading.
> In any case you should not call MyDisplay::setText()
Why?
It is OK now, I didn't see you had removed the usage of QThread.
> from the secondary thread
secondary thread? I did not use thread.
Right, you had thread usage earlier, didn't see that this changed.
That also makes the signal/slot approach unnecessary.
Since you are not using threading anymore you could even add the property to the MySerialPort class and set its instance as the context property.
Cheers,
_
Powered by vBulletin® Version 4.2.5 Copyright © 2024 vBulletin Solutions Inc. All rights reserved.