When signal emitted from C++ send data to QML - c++

How I can do this:
When signal finishedreply(from c++) send variable replydata(from c++) to TextArea(qml)
How i can connect this? Maybe Q_PROPERTY is a good way?
I use Qt 5.3
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
SendGetSMS *Connection = new SendGetSMS();
engine.rootContext()->setContextProperty("abc1", Connection);
QObject::connect(Connection,&SendGetSMS::finishedReply,engine,...);

from the documentation
in the c++:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
return m_author;
}
private:
QString m_author;
};
Message msg;
engine.rootContext()->setContextProperty("msg", &msg);
in the qml:
Text {
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
}

Related

QEvent on property set in QML

I would like to catch QEvent in my custom cpp QObject (MyObject) if some of the properties is changed (QEvent::DynamicPropertyChange):
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(bool value MEMBER mValue)
public:
MyObject();
bool event(QEvent *e) override
{
qDebug() << "EVENT RECIEVED" << e->type();
return QObject::event(e);
}
private:
bool mValue = false;
};
It works great if i do this in cpp:
MyObject obj = MyObject();
obj.setProperty("val", true);
But it does not if I try to change property in QML:
MyObject {
id: obj
}
Button{
id: button
text: "set value"
onClicked: function () {
console.log('button clicked');
obj.value = true;
}
}
This is a simple github example of this.
Any ideas ?

Connect qml object property changes to signal/slot mechanism

I got this basic project:
main.qml
Rectangle{
Text {
id: text1
objectName: "showStr"
property string _textField: "passive"
text: _textField
}
Button{
id: _button
signal buttClick(string str)
anchors.top: text1.bottom
text: "Button"
onClicked:
{
_button.buttClick("state: active")
}
}
}
myClass.cpp
class myClass : public QObject{
Q_OBJECT
public:
explicit myClass(QObject *parent = nullptr): QObject(parent) {}
public slots:
void setTextProperty(const QString& s) {this->str = s;}
void getStrToQObj()
{
//TODO: set updated str into qml
}
signals:
void strChanged(const QString& time);
private:
QString str;
};
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
QObject* showTime = item->findChild<QObject*>("showTime");
QObject* butt = item->findChild<QObject*>("start");
myClass mc(item);
QObject::connect(butt, SIGNAL(buttClick(QString)),
&mc, SLOT(setTextProperty(QString)));
return app.exec();
}
Connection of Qml signal buttClick to setTextProperty(const QString& s) works fine and myClass::str is changed.The question is how to connect Text property _textField to update every time, when myClass::str is changed?
Do not export objects from QML to C++, instead do the opposite: export the C ++ objects to QML since this way the connection is simple and there is no need to use the old connection syntax:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
myClass mc;
engine.rootContext()->setContextProperty("mc", &mc);
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
return app.exec();
}
Rectangle {
Text {
id: text1
text: "passive"
}
Button {
id: _button
anchors.top: text1.bottom
text: "Button"
onClicked: {
mc.setTextProperty("state: active");
}
}
Connections {
function onTimeChanged(text) {
text1.text = text;
}
target: mc
}
}
It is old syntax of connection, but according to separations logiv from GUI, and for ones who do not know JS well,turns out you can call parent() method to you class because it is inherit QObject class. This return to you Qobject* you put in constructor of your class, and then you can findChild and work with it:
void myClass::f(){
//do fancy stuff with your myClass::str
emit activityChanged(this->parent())
}
void getStrToQObj(QObject* qo)
{
auto tmp = qo->findChild<QObject*>("showTime");
tmp->setProperty("_textField", this->str);
}
And connect this in main.cpp like:
QObject::connect(&mc, SIGNAL(strChanged(QObject*)),
&mc, SLOT(getStrToQObj(QObject*)));

How to update a QML text with c++

I have a small problem. Can someone show me how can I update a qml text from c++. I have an example using threads but I don't want to apply this method because I don't know how to set a
parameter in run() function.To fully understand me here is my code.In the main function when I start the thread I want to put my custom text or a string variable that has a text.
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent=nullptr);
~Thread() override;
Q_SLOT void stop();
Q_SIGNAL bool textChanged(const QString & text);
protected:
void run() override;
};
#endif // THREAD_H
thread.c
#include "thread.h"
#include <QDebug>
Thread::Thread(QObject *parent):
QThread(parent)
{
}
Thread::~Thread()
{
}
void Thread::stop()
{
requestInterruption();
wait();
}
void Thread::run() //here I want to specify a QString variable such that in main function to call the function with my text, and not specified the text from here
{
int i=0;
while(!isInterruptionRequested()){
QString text;
text = QString("I changed the text"); // I don't want to declare from here the text.
Q_EMIT textChanged(text);
QThread::msleep(20);
qDebug() <<i++;
}
}
main.cpp
...
Thread thread;
QQmlApplicationEngine engine;
QObject::connect(&app, &QGuiApplication::aboutToQuit, &thread, &Thread::stop);
thread.start();
engine.rootContext()->setContextProperty("thread", &thread);
engine.load(QUrl("qrc:/main.qml"));
thread.stop();
...
main.qml
.....
Text {
objectName: "myLabel"
id: txt
width: 200
height: 29
color: "#faf9f9"
text: qsTr("Text")
font.pixelSize: 12
}
Connections{
target: thread
onTextChanged: txt.text = text
}
.....
You can send data from C++ to qml using signals, like this:
//main.cpp
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: gapi
onSignalData: {
console.log("Data: " + data)
textToChange.text = "Changed to: " + data
}
}
Text {
id: textToChange
text: "beforeChange"
}
}
//yourClass.h
class YourClass : public QObject
{
signals:
// Signal from YourClass to QML
void signalData(QString data);
}
//yourClass.cpp
emit signalData("Hello QML"); // Signal from GAPI to QML
This page https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml have a complete tutorial about "How to Expose a Qt C++ Class with Signals and Slots to QML"

Qt, how to know the request from which page with CustomScheme

I's using the QWebEngineView to render a h5 page, the h5 page use our custom scheme protocol to communicate with native.
For example, when the native receive the 'scheme_name://close' message from h5 page, the native will close the h5 page.
How can i know the message from which page?
Firstly, i use QDesktopServices::setUrlHandler() to set a url handler, but it's a global handler, and the callback must accepts a single QUrl argument, i can not know the page which the request from.
class Dialog : public QDialog {
public:
void Close() {
web_view_->close();
close();
}
private:
QWebEngineView *web_view_;
}
void OnCustomScheme(const QUrl &url) {
if (url.scheme() != "scheme_name") {
return;
}
if (url.host() == "close") {
// call Dialog::Close();
// who->Close();
}
}
Secondly,i try to use QWebEngineProfile::installUrlSchemeHandler, the receive can use a member of my page, like:
class SchemeHandler : public QWebEngineUrlSchemeHandler {
public:
void requestStarted(QWebEngineUrlRequestJob *job) override {
emit SignalSchemeRequest(job->requestUrl());
}
signals:
void SignalSchemeRequest(const QUrl &url);
};
class Dialog : public QDialog {
public:
Dialog() {
handler_ = new SchemeHandler(this);
connect(handler_, &SchemeHandler::SignalSchemeRequest,
this, &Dialog::SlotSchemeRequest);
profile_ = new QWebEngineProfile(this);
profile_->installUrlSchemeHandler("scheme_name", handler_);
}
void LoadPage(const QUrl &url) {
QWebEnginePage *page = new QWebEnginePage(profile_, this);
web_view_->setPage(page);
page->load(url);
}
void Close() {
web_view_->close();
close();
}
private slots:
coid SlotSchemeRequest(const QUrl &url) {
if (url.host() == "close") {
Close();
}
}
private:
QWebEngineView *web_view_;
SchemeHandler *handler_;
QWebEngineProfile *profile_;
}
Does it has any better way to do?
Everytime to load a page, i have to new a QWebEnginePage.

Give data to a ChartView (LineSeries) - QML

I recently started with QML and I'm searching for a way to give data from c++ to my ChartView in QML.
I would prefer a solution where I can send a QMap with something like Q_PROPERTY so it updates automatically.
I've searched and found that you can do a function where you then can use 'append()' to added values to the chart. But I seem to be unable to send some kind of list to the QML...
QML file:
ChartView {
theme: ChartView.ChartThemeQt
antialiasing: true
DateTimeAxis {
id: dateTimeAxisX
}
ValueAxis{
id: valueAxisY
min: 0
max: 15
titleText: "Voltage (V)"
}
LineSeries {
id: voltageSeries
axisX: dateTimeAxisX
axisY: valueAxisY
name: "Battery Voltage"
}
}
robot.h:
class Robot: public QObject
{
...
Q_PROPERTY(QMap<int, double> list_battery_voltages READ getList_battery_voltages NOTIFY listBatteryVoltagesChanged)
...
}
main.cpp:
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QApplication app(argc, argv);
QQmlApplicationEngine engine;
// Load custom class inside engine
QScopedPointer<Robot> robot(new Robot(app.applicationDirPath() + "/robot_settings.ini"));
engine.rootContext()->setContextProperty("robot", robot.data());
QQmlComponent component(&engine, QUrl(QLatin1String("qrc:/main.qml")));
component.create();
return app.exec();
}
Are there elegant solutions that I'm overseeing?
I have solved it by using QVariantMap. It is not the most ellegant solution but it works :)
I have a javascript function in my QML file that fills the lineseries.
This function is connected to my robot signal that will be send when new data have arrived.
robot.h:
class Robot: public QObject
{
...
Q_PROPERTY(QVariantMap list_battery_voltages READ getList_battery_voltages NOTIFY listBatteryVoltagesChanged)
...
}
QML:
ChartView {
...
Connections {
target: robot
onMapBatteryVoltagesChanged: insertVoltages()
}
Component.onCompleted: {
insertVoltages()
}
...
function insertVoltages() {
var voltages_map = robot.map_battery_voltages
for (var prop in voltages_map) {
voltageSeries.append(prop, voltages_map[prop])
}
}
...
}
As I remember Javascript in QML operates with C++ array using QVariantList and QVariantMap only. They are both not good for you as I see. So I advice you to store your map in C++ in a way you like and provide some convenient way to access the values. For example, using singleton:
mysingleton.h
class MySingleton : public QObject
{
Q_OBJECT
public:
explicit MySingleton(QObject *parent = nullptr);
Q_INVOKABLE int count();
Q_INVOKABLE double getX(int index);
Q_INVOKABLE double getY(int index);
private:
QList<QPair<double, double>> m_map;
};
mysingleton.cpp
MySingleton::MySingleton(QObject *parent) : QObject(parent)
{
QRandomGenerator *generator = QRandomGenerator::global();
int count = generator->generate() % 10;
double xAccumulated = 0;
for(int i = 0;i < count;i ++)
{
double x = generator->generateDouble() / (double)count;
xAccumulated += x;
double y = generator->generateDouble();
m_map.append(QPair<double, double>(xAccumulated, y));
}
}
int MySingleton::count()
{
return m_map.count();
}
double MySingleton::getX(int index)
{
return m_map.at(index).first;
}
double MySingleton::getY(int index)
{
return m_map.at(index).second;
}
registering (main.cpp):
static QObject *my_singleton_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
return new MySingleton(engine);
}
main() {
qmlRegisterSingletonType<MySingleton>("Qt.MyTest", 1, 0, "MySingleton", my_singleton_provider);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
}
And so now you can use this in QML:
import QtQuick 2.9
import QtCharts 2.1
import Qt.MyTest 1.0
ChartView {
anchors.fill: parent
LineSeries {
id: series
Component.onCompleted: {
for(var i = 0;i < MySingleton.count();i ++) {
series.append(MySingleton.getX(i), MySingleton.getY(i));
}
}
}
}
Using this way could give you some additional advantages like data change signal etc.