How to update qml text from two different cpp? - qt

I have an qml project.
There is a text(named id:cnt) on StackPage.qml and I need to update this text from firstclass.cpp and secondclass.cpp.
Q_PROPERTY defines are on firstclass.h and setCntText function is on firstclass.cpp.
I update the text from firstclass.cpp by setCntText(i) and try to update from secondclass.cpp by calling setCntText(0).
I can set the m_cntText variable from secondclass but couldnt update qml text(named id:cnt).
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "firstclass.h"
#include "secondclass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
firstClass frmFirstClass;
engine.rootContext()->setContextProperty("frmFirstClass",&frmFirstClass);
secondClass frmSecondClass;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
firstclass.cpp
#include "firstclass.h"
firstClass::firstClass(QObject *parent) : QObject(parent)
{
QTimer *timer1 = new QTimer();
connect(timer1, &QTimer::timeout, [=]() {setter1();});
timer1->start(2000);
}
int firstClass::cntText() const
{
return m_cntText;
}
void firstClass::setCntText(int cntText)
{
if (m_cntText == cntText)
return;
m_cntText = cntText;
emit cntTextChanged(m_cntText);
}
void firstClass::setter1()
{
static int i = 0;
i++;
qDebug() <<"counter1 : " << i;
setCntText(i);
}
secondclass.cpp
#include "secondclass.h"
firstClass frmFirstClass;
secondClass::secondClass(QObject *parent) : QObject(parent)
{
QTimer *timer = new QTimer();
timer->setSingleShot(true);
timer->start(1000);
connect(timer, &QTimer::timeout, [=]() {
QTimer *timer2 = new QTimer(this);
connect(timer2,SIGNAL(timeout()),this,SLOT(setter2()));
timer2->start(2000);
timer->deleteLater();
} );
}
void secondClass::setter2()
{
frmFirstClass.setCntText(0);
qDebug() << "Checking m_cntText = " << frmFirstClass.m_cntText;
}
firstclass.h
#ifndef FIRSTCLASS_H
#define FIRSTCLASS_H
#include <QObject>
#include <QTimer>
#include <QDebug>
class firstClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int cntText READ cntText WRITE setCntText NOTIFY cntTextChanged)
public:
explicit firstClass(QObject *parent = nullptr);
int cntText() const;
int m_cntText;
signals:
void cntTextChanged(int cntText);
public slots:
void setCntText(int cntText);
private slots:
void setter1();
};
#endif // FIRSTCLASS_H
secondclass.h
#ifndef SECONDCLASS_H
#define SECONDCLASS_H
#include <QObject>
#include <QTimer>
#include <QDebug>
#include "firstclass.h"
extern firstClass frmFirstClass;
class secondClass : public QObject
{
Q_OBJECT
private:
public:
explicit secondClass(QObject *parent = nullptr);
signals:
public slots:
private slots:
void setter2();
};
#endif // SECONDCLASS_H
main.qml
import QtQuick 2.10
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
anchors.fill: parent
color: "black"
StackView{
anchors.fill: parent
initialItem : stackPage
}
Component{
id:stackPage
StackPage{}
}
}
}
StackPage.qml
import QtQuick 2.0
Item {
Rectangle{
anchors.centerIn: parent
width: 200
height: 200
color: "orange"
Text {
id: cnt
text: frmFirstClass.cntText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 40
anchors.fill: parent
}
}
}

Your extern usage is incomplete, so you are effectively creating two instances of firstClass. As extern is a bit misleading at poitns, I suggest you to give a pointer to the "one-and-only" firstClass when creating secondClass (in main). This also gives the code a better representation of intended hierarchy.
Update the following files:
secondClass.h
#include "firstClass.h"
class secondClass : public QObject
{
Q_OBJECT
private:
public:
explicit secondClass(firstClass *theFirstClass, QObject *parent = nullptr);
signals:
public slots:
private slots:
void setter2();
private:
firstClass *myFirstClass_ = nullptr;
};
secondClass.cpp
#include "secondclass.h"
secondClass::secondClass(firstClass *theFirstClass, QObject *parent)
: QObject(parent)
, myFirstClass(theFirstClass)
{
QTimer *timer = new QTimer();
timer->setSingleShot(true);
timer->start(1000);
connect(timer, &QTimer::timeout, [=]() {
QTimer *timer2 = new QTimer(this);
connect(timer2,SIGNAL(timeout()),this,SLOT(setter2()));
timer2->start(2000);
timer->deleteLater();
} );
}
void secondClass::setter2()
{
myFirstClass->setCntText(0);
qDebug() << "Checking m_cntText = " << myFirstClass->m_cntText;
}
main.cpp
int main(int argc, char *argv[])
{
...
firstClass frmFirstClass;
engine.rootContext()->setContextProperty("frmFirstClass", &frmFirstClass);
secondClass frmSecondClass(&frmFirstClass);
...
}
Disclaimer: I did not try to compile this

Related

How can a QThread send a signal from its own thread with an enum as an argument for consumption in QML?

In the following code, if the signal errorHappened is emitted from the main thread it works without problem. However if it is emitted from the QThread thread it fails with the following error:
QObject::connect: Cannot queue arguments of type 'ErrorCode'
(Make sure 'ErrorCode' is registered using qRegisterMetaType().)
Is there a way that the signal can be successfully emitted from the QThread thread? If so, how?
Full code in this Gist
MyClass.h
#import <QThread>
#import <atomic>
class MyClass : public QThread
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = Q_NULLPTR);
virtual ~MyClass() override;
enum ErrorCode {
ErrorA,
ErrorB,
ErrorC
};
Q_ENUM(ErrorCode)
signals:
void errorHappened(ErrorCode errorCode);
public slots:
void mainThreadError();
void otherThreadError();
private:
std::atomic<bool> m_running;
std::atomic<bool> m_signalStop;
std::atomic<bool> m_signalError;
void run() override;
void stop();
};
MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(QObject *parent)
: QThread(parent)
{
start();
}
MyClass::~MyClass()
{
stop();
}
void MyClass::mainThreadError()
{
emit errorHappened(ErrorCode::ErrorA);
}
void MyClass::otherThreadError()
{
m_signalError = true;
}
void MyClass::run()
{
m_running = true;
while (!m_signalStop) {
if (m_signalError) {
emit errorHappened(ErrorCode::ErrorA);
m_signalError = false;
}
msleep(1);
}
m_running = false;
m_signalStop = false;
}
void MyClass::stop()
{
if (m_running) {
m_signalStop = true;
wait();
}
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include "MyClass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView();
qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");
view->setSource((QUrl(QStringLiteral("qrc:/main.qml"))));
view->create();
view->show();
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Controls 2.5
import MyClass 1.0
Rectangle {
id: root
width: 800
height: 600
focus: true
MyClass {
id: tester
onErrorHappened: {
var s
switch (errorCode) {
case MyClass.ErrorA:
s = "Error A happened"
break
}
console.log(s)
}
}
Row {
spacing: 30
Button {
id: mainThreadButton
enabled: !tester.testRunning
text: "Test on main thread"
onClicked: tester.mainThreadError()
}
Button {
id: otherThreadButton
enabled: !tester.testRunning
text: "Test on other thread"
onClicked: tester.otherThreadError()
}
}
}
qmlRegisterType makes the QObject (MyClass) class accessible in QML (Q_PROPERTY, Q_ENUM, Q_SIGNAL, Q_SLOT, Q_INVOKABLE, etc.) but does not allow the transmission of data between threads, for this case it must be registered using qRegisterMetaType<MyClass::ErrorCode>("ErrorCode"):
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include "MyClass.h"
static void registerTypes(){
qRegisterMetaType<MyClass::ErrorCode>("ErrorCode");
qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");
}
Q_COREAPP_STARTUP_FUNCTION(registerTypes)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource((QUrl(QStringLiteral("qrc:/main.qml"))));
view.show();
return app.exec();
}
your type "ErrorCode" defined only in thread (not in main)
For user defined types you can use (read this https://doc.qt.io/qt-5/qmetatype.html)
Q_DECLARE_METATYPE(ErrorCode);
Use standard types for arguments (int,QMap,QString....)

Reference to QML Singleton Class Instance?

I have created a class called LoginService.
I registered it to QT QML file by using qmlRegisterSingletonType, now the problem is I can't get the loginservice instance which instantiated by QML. My current c++ code is:
static QObject *qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
LoginService::m_pThis = new LoginService;
return m_pThis;
}
qmlRegisterSingletonType<LoginService>("com.test.LoginService", 1, 0, "LoginService", &LoginService::qmlInstance);
If you want to access the singleton from C++, create a method that returns an instance other than qmlInstance:
loginservice.h
#ifndef LOGINSERVICE_H
#define LOGINSERVICE_H
#include <QObject>
class QQmlEngine;
class QJSEngine;
class LoginService : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
static LoginService *instance();
static QObject *qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine);
QString name() const;
void setName(const QString &name);
Q_SIGNAL void nameChanged();
private:
explicit LoginService(QObject *parent = nullptr);
static LoginService* m_pThis;
QString mName;
};
#endif // LOGINSERVICE_H
loginservice.cpp
#include "loginservice.h"
#include <QQmlEngine>
LoginService* LoginService::m_pThis = nullptr;
LoginService::LoginService(QObject *parent) : QObject(parent)
{
}
QString LoginService::name() const
{
return mName;
}
void LoginService::setName(const QString &name)
{
if(mName == name)
return;
mName = name;
Q_EMIT nameChanged();
}
LoginService *LoginService::instance()
{
if (m_pThis == nullptr) // avoid creation of new instances
m_pThis = new LoginService;
return m_pThis;
}
QObject *LoginService::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
// C++ and QML instance they are the same instance
return LoginService::instance();
}
main.cpp
#include "loginservice.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<LoginService>("com.test.LoginService", 1, 0, "LoginService", &LoginService::qmlInstance);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// get instance in C++
LoginService *service = LoginService::instance();
qDebug()<<service->name();
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import com.test.LoginService 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component.onCompleted: LoginService.name = "testing"
}

QML Slot not displaying double value from serial input

I'm struggling to get my serial input "analogread2" converted to a double, to display in QML.
Ive added the Q_property, the root context in the main.cpp, and I can add the property in the qml, but I cant get it to display in text format.
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SerialPort serialport;
//engine.rootContext()->setContextProperty("serialport", &serialport);
qmlRegisterType<SerialPort>("SerialPortlib", 1, 0, "SerialPort");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")))
header.
Q_PROPERTY(double newValue MEMBER m_oil_pressure_volt WRITE set_oil_pressure_volt NOTIFY oil_pressure_volt_Changed )
qml
Window {
visible: true
width: 640
height: 480
id: gauge
SerialPort{
// what would I put in here to display the text value (double)
}
}
any help is much appreciated.
To do the test I am assuming that you are sending the data from the arduino in one of the following ways:
Serial.println(data);
Serial.print(data);
Assuming the above has been implemented the serialport class, this has a function called openDefault that scans the devices that are connected through serial and searches for the word "Arduino" within description or manufacturer and if one finds it connects to it.
The complete code can be found at: https://github.com/eyllanesc/stackoverflow/tree/master/Arduino-QML
serialport.h
#ifndef SERIALPORT_H
#define SERIALPORT_H
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
class SerialPort : public QObject
{
Q_OBJECT
Q_PROPERTY(double oil_pressure_volt READ get_oil_pressure_volt WRITE set_oil_pressure_volt NOTIFY oil_pressure_volt_Changed )
public:
SerialPort(QObject *parent = 0);
~SerialPort();
double get_oil_pressure_volt() const;
void set_oil_pressure_volt(double newValue);
public slots:
void onReadData();
signals:
void oil_pressure_volt_Changed(double newValue);
private:
QSerialPort *arduino;
double mOil_pressure_volt;
QSerialPortInfo portInfo;
void openDefault();
};
serialport.cpp
#include "serialport.h"
#include <QDebug>
SerialPort::SerialPort(QObject *parent):QObject(parent)
{
arduino = new QSerialPort(this);
connect(arduino, &QSerialPort::readyRead, this, &SerialPort::onReadData);
openDefault();
}
SerialPort::~SerialPort()
{
delete arduino;
}
void SerialPort::set_oil_pressure_volt(double newValue)
{
if (mOil_pressure_volt == newValue)
return;
mOil_pressure_volt = newValue;
emit oil_pressure_volt_Changed(mOil_pressure_volt);
}
void SerialPort::onReadData()
{
if(arduino->bytesAvailable()>0){
QByteArray data = arduino->readAll();
QString value = QString(data).trimmed();
qDebug()<< value;
bool ok;
double val = value.toDouble(&ok);
if(ok)
set_oil_pressure_volt(val);
}
}
void SerialPort::openDefault()
{
for(auto info: QSerialPortInfo::availablePorts()){
qDebug()<<info.portName()<<info.description()<<info.manufacturer();
if(!info.isBusy() && (info.description().contains("Arduino") || info.manufacturer().contains("Arduino"))){
portInfo = info;
break;
}
}
if(portInfo.isNull()){
return;
}
arduino->setPortName(portInfo.portName());
arduino->setBaudRate(QSerialPort::Baud115200);
arduino->setDataBits(QSerialPort::Data8);
arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);
arduino->setFlowControl(QSerialPort::NoFlowControl);
if(arduino->open(QSerialPort::ReadWrite))
qDebug()<<"Connected to "<< portInfo.manufacturer()<< " on " << portInfo.portName();
else
qCritical()<<"Serial Port error: " << arduino->errorString();
}
double SerialPort::get_oil_pressure_volt() const
{
return mOil_pressure_volt;
}
Example:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "serialport.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<SerialPort>("SerialPortLib", 1, 0, "SerialPort");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import SerialPortLib 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Test")
SerialPort{
onOil_pressure_voltChanged: {
tx.text = "%1".arg(oil_pressure_volt);
}
}
Text {
id: tx
anchors.fill: parent
font.family: "Helvetica"
font.pointSize: 20
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

Update TextArea in QML from C++

My question is pretty simple I think. Nevertheless I was not able to figure it out. I have a TextArea defined in my .qml file, which needs to be updated dynamically from the C++ code.
Unfortunately, I don't know how to access/update the TextArea from within the imserver.cpp class.
Can anyone help me out please?
Here is the .qml file:
import QtQuick 2.2
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("IMServer")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
TextArea {
id: serverInformation
x: 0
y: 0
width: 247
height: 279
}
}
My main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlEngine>
#include <QtQml>
#include "imserver.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
IMServer server(2000);
qmlRegisterUncreatableType<IMServer>("App", 1, 0, "IMServer", "");
engine.rootContext()->setContextProperty("imserver", &server);
server.startServer();
return app.exec();
}
imserver.h
#ifndef IMSERVER_H
#define IMSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QThreadPool>
class IMServer : public QTcpServer {
Q_OBJECT
Q_PROPERTY(QString text WRITE setText NOTIFY textChanged)
public:
explicit IMServer(int port, QObject *parent = 0);
void startServer();
void setText(const QString &txt);
signals:
void textChanged();
public slots:
protected:
void incomingConnection(qintptr fd);
private:
int port;
QThreadPool *pool;
QString m_text;
};
#endif // IMSERVER_H
imserver.cpp:
#include "imserver.h"
#include "clienthandler.h"
IMServer::IMServer(int port, QObject *parent) : QTcpServer(parent) {
this->pool = new QThreadPool(this);
this->pool->setMaxThreadCount(100);
this->port = port;
}
void IMServer::startServer() {
setText("TEST");
if (!this->listen(QHostAddress::Any, this->port)) {
qDebug() << "Server could not be started";
} else {
qDebug() << "Server started, listening...";
}
}
void IMServer::setText(const QString &txt) {
m_text = txt;
emit textChanged();
}
void IMServer::incomingConnection(qintptr fd) {
ClientHandler *client = new ClientHandler();
client->setAutoDelete(true);
client->fd = fd;
this->pool->start(client);
}
There are several ways. Here is how I'd do it.
First you should register your IMServer class:
qmlRegisterUncreatableType<IMServer>("App", 1, 0, "IMServer", "");
Then you add your IMServer instance to QML:
enigne.rootContext()->setContextProperty("imserver", &server);
Your IMServer class then needs a signal, that your TextArea can be connected to, or even better add a property (you need to add the getText() function and textChanged() signal here as well, for a read-only property):
Updated:
Q_PROPERTY(QString text READ getText NOTIFY textChanged)
In the TextArea you can then create a binding:
TextArea {
text: imserver.text
}
Then whenever you emit textChanged in your IMServer class, the TextArea's text will be updated.
For more information: http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html

QQmlListProperty : Cannot assign to non-existent property "lon" lon: "3"

I wish to see how QQmlListProperty is used. I tried the following but I am not sure if this is the correct way to do it. I got an error shown in the title.
aa.h
#ifndef IMO
#define IMO
#include <QQmlListProperty>
#include "DummyClass.h"
class SubClass: public QObject
{
Q_OBJECT
Q_PROPERTY (QQmlListProperty <DummyClass> functionWhat READ functionWhat)
public:
SubClass (QObject *parent = 0);
QQmlListProperty <DummyClass> functionWhat()
{
return QQmlListProperty <DummyClass> (this, dummyList);
}
private:
QList <DummyClass*> dummyList;
};
#endif
aa.cpp
#include "aa.h"
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
SubClass :: SubClass (QObject *parent) : QObject (parent)
{
qDebug ("In Constructor.");
}
void appendDummies (QQmlListProperty<DummyClass> *property, DummyClass *dc)
{
qDebug ("Do nothing. Can't add to a directory using this method.");
}
DummyClass.h
#ifndef IM
#define IM
#include <QObject>
class DummyClass : public QObject
{
private:
Q_OBJECT
Q_PROPERTY (bool functionWhat READ functionWhat WRITE functionSetWhat)
public:
DummyClass (QObject *parent = 0) {}
float lat; float lon;
bool functionWhat ()
{
qDebug ("In functionWhat");
}
public slots:
void functionSetWhat (bool arg)
{
qDebug ("In functionWhatSlot");
}
signals:
void functionWhatChanged (bool arg);
};
#endif
main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "/home/anisha/qmllistproperties/DummyClass.h"
#include <QtQml>
#include "/home/anisha/qmllistproperties/aa.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
const char* ocuui = "OCUUI"; // #uri OCUUI
qmlRegisterType <DummyClass> (ocuui, 1, 0, "DummyClass");
qmlRegisterType <SubClass> (ocuui, 1, 0, "SubClass");
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
viewer.showExpanded();
return app.exec();
}
main.qml
import QtQuick 2.0
import OCUUI 1.0
Rectangle {
width: 360
height: 360
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
SubClass
{
functionWhat:
[
DummyClass
{
lat: "2"
lon: "3"
}
]
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
You seem to be missing the property declarations for lat and lon properties in your DummyClass:
Q_PROPERTY (int lat READ getLat WRITE setLat)
Q_PROPERTY (int lon READ getLon WRITE setLon)
You should study this example.

Resources