I have a project that I am working on, that will hopefully result in C++ code with a QML UI as a console, communicating with other consoles and equipment.
My test setup is based on a RaspBerry Pi running UDP comms (works fine into a normal Qt application).
I have tried to port to Qt Quick, and use a simple QML UI, but if I declare the subclass early in my "main.cpp" it doesn't connect properly.
If I declare it in the main.cpp "main" function, then I have scope issues with the subclass functions that I want to use to transfer data.
Open to suggestions / critique......and I am 100% sure that there is a "proper" way of doing this, so if anyone wants to point me at it......I will be enraptured!
Qt version is Qt5.3.
import QtQuick 2.2
import QtQuick.Controls 1.1
Rectangle {
width: 440; height: 150
color: "orange"
Column {
anchors.fill: parent; spacing: 20
Text {
text: rootItem.theChange
font.pointSize: 12; anchors.horizontalCenter: parent.horizontalCenter
}
}
}
// signalgrab.h
#ifndef SIGNALGRAB_H
#define SIGNALGRAB_H
#include <QObject>
#include <QUdpSocket>
#include <QString>
class SignalGrab : public QObject
{
Q_OBJECT
public:
explicit SignalGrab(QObject *parent = 0);
int SendValue();
void setupUDP();
signals:
public slots:
void readyRead();
private:
QUdpSocket *socket;
QHostAddress groupAddress;
};
#endif // SIGNALGRAB_H
// signalgrab.cpp
#include "signalgrab.h"
int i3 = 0;
SignalGrab::SignalGrab(QObject *parent) :
QObject(parent)
{
// create a QUDP socket
socket = new QUdpSocket(this);
socket->bind(QHostAddress("0.0.0.0"), 45454);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
qDebug() << "Socket connected ok!";
}
void SignalGrab::readyRead()
{
// when data comes in
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
socket->readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
std::string s1 (buffer);
std::string s2 = s1.substr(26,sizeof(s1));
i3 = atoi(s2.data());
SendValue();
}
int SignalGrab::SendValue()
{
qDebug() << "sending i3 as: "<< i3;
return i3;
}
// main.cpp
#include <QtGui>
#include <QApplication>
#include <QQmlContext>
#include <QQuickView>
#include <QString>
#include "signalgrab.h"
int y=13;
// ///////////////////////////////////
//SignalGrab nc;
// ///////////////////////////////////
class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(QString theChange READ getTheChange NOTIFY changeOfStatus)
public:
Object()
{
changeMe = false;
myTimer = new QTimer(this);
myTimer->start(5000);
connect(myTimer, SIGNAL(timeout()), this, SLOT(testSlot()));
}
QString getTheChange()
{
if (theValue == 0)
{
return "The Acid QML Test";
}
if (theValue == 1)
{
y = (fetchValue());
qDebug() << "New y!" << y;
return QString("Returning %1").arg(y);
}
return "nothing has happened yet";
}
int fetchValue()
{
// return (nc::SendValue());
return y;
}
Q_INVOKABLE void someFunction(int i)
{
if ( i == 0) {
theValue = 0;
}
if (i == 1) {
theValue = 1;
}
emit changeOfStatus(i);
}
signals:
void changeOfStatus(int i) ;
public slots:
void testSlot()
{
if (changeMe)
{
someFunction(0);
} else {
someFunction(1);
}
changeMe = !changeMe;
}
private:
bool changeMe;
int theValue;
QTimer *myTimer;
};
#include "main.moc"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Object myObj;
// //////////////////////////////
SignalGrab nc; //added
// //////////////////////////////
nc.SendValue();
QQuickView view;
view.rootContext()->setContextProperty("rootItem", (QObject *)&myObj);
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}
Related
Problems with QML Connections
qrc:/main.qml:12:5: QML Connections: Detected function "onTop" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name.
qrc:/main.qml:13: ReferenceError: classA is not defined
as the picture shows,I cannot link to my Qt Signal
The problem is that QML cannot recognize my Qt signal void onTop(), how can I solve it? In Qt 6
I feel that QML has a lot of problems, it is not as easy to use as traditional QWidget, and there are few related materials.
head file
#pragma once
#include <QOBject>
#include <QUdpSocket>
class CupSingleHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(NOTIFY onTop)
public:
CupSingleHelper(QObject *parent = nullptr);
~CupSingleHelper();
public:
void initSocket();
void setAppPid(qint64 app_pid);
void readData();
Q_INVOKABLE void printStr2();
public:
QUdpSocket* udp_socket = nullptr;
qint64 pid;
public slots:
void printStr();
signals:
void onTop();
};
cpp file
#include "CupSingleHelper.h"
#include <QNetWorkDatagram>
#include <qmessagebox.h>
#include <QDebug>
CupSingleHelper::CupSingleHelper(QObject* parent)
{
}
void CupSingleHelper::printStr()
{
qDebug() << "1111111";
}
void CupSingleHelper::printStr2()
{
qDebug() << "22222";
}
void CupSingleHelper::setAppPid(qint64 app_pid)
{
this->pid = app_pid;
}
void CupSingleHelper::initSocket()
{
QString data = ":start";
data.insert(0, QString::number(pid));
QByteArray byte_data = data.toLatin1();
udp_socket = new QUdpSocket(this);
bool sStatus = false;
sStatus = udp_socket->bind(QHostAddress::LocalHost, 8898);
if (sStatus == false) { udp_socket->writeDatagram(byte_data, QHostAddress::LocalHost, 8889); exit(1); }
connect(udp_socket, &QUdpSocket::readyRead, this, &CupSingleHelper::readData);
udp_socket->writeDatagram(byte_data, QHostAddress::LocalHost, 8889);
}
void CupSingleHelper::readData()
{
while (udp_socket->hasPendingDatagrams())
{
QNetworkDatagram datagram = udp_socket->receiveDatagram();
QString receive_data = datagram.data().data();
QString temp = receive_data.section(":", 0, 0);
if (temp.toInt() == pid)
{
break;
}
temp = receive_data.section(":", 1, 1);
if (temp.compare("start") == 0)
{
emit onTop();
}
}
}
CupSingleHelper::~CupSingleHelper()
{
if(udp_socket != nullptr)
{
delete[]udp_socket;
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTranslator>
#include <QLocale>
#include "CupSingleHelper.h"
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
CupSingleHelper cup_single_helper;
qint64 app_pid = QCoreApplication::applicationPid();
cup_single_helper.setAppPid(app_pid); //set process id
cup_single_helper.initSocket();//init udpsocket to listen
QTranslator t;
QLocale ql;
//Check system language and load
if (ql.language() == QLocale::Chinese)
{
bool status = t.load(":/x64/Debug/cuptools_zh.qm");
}
if (ql.language() != QLocale::English)
{
app.installTranslator(&t);
}
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QQmlContext* rootContext = engine.rootContext();
rootContext->setContextProperty("classA",&cup_single_helper);
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 500
height: 720
title: qsTr("CupTools")
property var strd: "22222"
Connections {
target: classA
function onTop(){
label12.text = strd
}
}
Text {
id: label12
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: 20
}
text:"11111"
}
Button {
id: mbutton
anchors.centerIn:parent
text: "Click"
onClicked: classA.printStr()
}
}
Your code has the following problems:
If you are going to declare a signal then you should not use Q_PROPERTY. Remove Q_PROPERTY(NOTIFY onTop).
You must establish the contexproperties before loading the .qml since otherwise at the time of loading those objects will not be defined.
If you want to connect a signal then the syntax is on<signal> where <signal> must be in camelcase format, in your case it must be onOnTop.
In Qt6 the versions of the QML modules are not necessary.
Since udp_socket is a child of the class then it is not necessary to eliminate it since it will cause a segmentation fault since you are eliminating the pointer 2 times.
#ifndef CUPSINGLEHELPER_H
#define CUPSINGLEHELPER_H
#include <QObject>
class QUdpSocket;
class CupSingleHelper : public QObject
{
Q_OBJECT
public:
CupSingleHelper(QObject *parent = nullptr);
~CupSingleHelper();
public:
void initSocket();
void setAppPid(qint64 app_pid);
void readData();
Q_INVOKABLE void printStr2();
public:
QUdpSocket* udp_socket = nullptr;
qint64 pid;
public slots:
void printStr();
signals:
void onTop();
};
#endif // CUPSINGLEHELPER_H
#include "cupsinglehelper.h"
#include <QNetworkDatagram>
#include <QUdpSocket>
CupSingleHelper::CupSingleHelper(QObject* parent):QObject(parent)
{
}
void CupSingleHelper::printStr()
{
qDebug() << "1111111";
}
void CupSingleHelper::printStr2()
{
qDebug() << "22222";
}
void CupSingleHelper::setAppPid(qint64 app_pid)
{
this->pid = app_pid;
}
void CupSingleHelper::initSocket()
{
QString data = ":start";
data.insert(0, QString::number(pid));
QByteArray byte_data = data.toLatin1();
udp_socket = new QUdpSocket(this);
bool sStatus = false;
sStatus = udp_socket->bind(QHostAddress::LocalHost, 8898);
if (sStatus == false) { udp_socket->writeDatagram(byte_data, QHostAddress::LocalHost, 8889); exit(1); }
connect(udp_socket, &QUdpSocket::readyRead, this, &CupSingleHelper::readData);
udp_socket->writeDatagram(byte_data, QHostAddress::LocalHost, 8889);
}
void CupSingleHelper::readData()
{
while (udp_socket->hasPendingDatagrams())
{
QNetworkDatagram datagram = udp_socket->receiveDatagram();
QString receive_data = QString::fromUtf8(datagram.data());
QString temp = receive_data.section(":", 0, 0);
if (temp.toInt() == pid)
{
break;
}
temp = receive_data.section(":", 1, 1);
if (temp.compare("start") == 0)
{
emit onTop();
}
}
}
CupSingleHelper::~CupSingleHelper()
{
}
#include "cupsinglehelper.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
CupSingleHelper cup_single_helper;
qint64 app_pid = QCoreApplication::applicationPid();
cup_single_helper.setAppPid(app_pid); //set process id
cup_single_helper.initSocket();//init udpsocket to listen
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
QQmlContext* rootContext = engine.rootContext();
rootContext->setContextProperty("classA",&cup_single_helper);
engine.load(url);
return app.exec();
}
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
visible: true
width: 500
height: 720
title: qsTr("CupTools")
property string strd: "22222"
Connections {
target: classA
function onOnTop(){
label12.text = strd
}
}
Text {
id: label12
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: 20
}
text:"11111"
}
Button {
id: mbutton
anchors.centerIn:parent
text: "Click"
onClicked: classA.printStr()
}
}
I have created a simple app for monitoring the connected devices. This app shows the connection status of 25 client devices.
This app implements a TCP server and listens at port 7777 and as many as 25 clients can be connected to this application. If no data is received from a client for 30 seconds, the app marks the device as "Offline".
For this purpose, QTimer for each connected device is started for 30 sec when some client connects and the payload is received. Each timer is connected to a common SLOT refreshOfflineDevices() Soon as any timer timeout occurs, refreshOfflineDevices() is called and the non-running timers corresponding to the device are marked as "Offline" in the GUI.
The app works fine and the GUI is updated instantly when the connected device count is not more than 4 or 5. As the connected devices rise, (greater than 8 or 9) the lag in the GUI update becomes obvious.
After some desk research, I assume that the parallel timers would need to be moved to a thread to avoid GUI lags. For that, I created a CyclicWorker class for separating the QTimer but not sure how this will work in this case
I need help with moving and managing all timekeeping events to a thread. Also, I need advise on my assumption of GUI lag correctness
my app GUI
monitor.h
#ifndef CENTRALMONITOR_H
#define CENTRALMONITOR_H
#include <QtCore>
#include <QMainWindow>
#include "ui_device_display.h"
#include "tcp_server.h"
#include "cyclic_worker.h"
#define MAX_DEVICES (25)
#define DEVICE_KEEP_ALIVE_MS (30*1000) // keeps track of the connection before marking "Offline"
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Ui::MainWindow *ui;
TCPServer *ptr_server = nullptr;
QTimer *ptr_deviceTimer[MAX_DEVICES] = {nullptr};
void GUI_update(const int device_number, const QString device_status);
CyclicWorker timerThread;
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
UIDeviceDisplay *ptr_devices[MAX_DEVICES] = {nullptr};
public slots:
void parseJSON(QString response);
void refreshOfflineDevices();
};
#endif // CENTRALMONITOR_H
monitor.cpp
#include "monitor.h"
#include "ui_monitor.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
system("clear");
ui->setupUi(this);
// MyServer instance (TCPServer contains TCPClient instance).
ptr_server = new TCPServer();
connect(ptr_server, SIGNAL(uiPayloadReady(QString)), this, SLOT(parseJSON(QString)));
// draw (MAX_DEVICES) (initialize as offline)
for(int i=0 ; i<5 ; i++) // 5 rows
{
for(int j=0 ; j<5 ; j++) // 5 columns
{
ptr_devices[i*5 + j] = new UIDeviceDisplay(this, j, i);
ptr_devices[i*5 + j]->show();
QString text_device_number = QString("").append(QString::number( i*5 + j + 1) );
ptr_devices[i*5 + j]->ptr_label_device_number->setText(text_device_number);
}
}
// connect keep alive timers to use later
for(int device_idx=0; device_idx<MAX_DEVICES; device_idx++)
{
ptr_deviceTimer[device_idx] = new QTimer();
connect(ptr_deviceTimer[device_idx], SIGNAL(timeout()), this, SLOT(refreshOfflineDevices()));
this->ptr_deviceTimer[device_idx]->setSingleShot(true);
ptr_deviceTimer[device_idx]->setTimerType(Qt::PreciseTimer);
}
timerThread.start();
timerThread.threadFlag = 1;
}
MainWindow::~MainWindow()
{
delete ui;
}
/*
#brief This slot is emitted by ptr_socket readReady() signal by the TCP client handler
#param The received payload for updating GUI
*/
void MainWindow::parseJSON(const QString response)
{
const QJsonDocument jsonDocument = QJsonDocument::fromJson(response.toUtf8());
const QJsonObject jsonObjectRecords = jsonDocument.object();
const int device_number = jsonObjectRecords.value("device_number").toInt();
const QString device_status = jsonObjectRecords.value("device_status").toString();
// start time keeper for current device.
ptr_deviceTimer[device_number-1]->start(DEVICE_KEEP_ALIVE_MS);
GUI_update(device_number, device_status);
}
/*
#brief This method updates the GUI with provided params.
#param GUI update params
*/
void MainWindow::GUI_update(const int device_number, const QString device_status)
{
const int device_idx = device_number-1;
// update device label.
ptr_devices[device_idx]->ptr_label_device_status->setText(device_status);
// refresh online devices label.
int onlineCount =0;
for(int device_idx=0; device_idx<MAX_DEVICES; device_idx++)
{
if( ptr_deviceTimer[device_idx]->isActive() )
onlineCount++;
}
ui->label_online_devices->setText(QString("Online devices: %1").arg(onlineCount));
}
/*
#brief This method is called upon every device_timer expiration. It updates GUI for all the devices that are offline
*/
void MainWindow::refreshOfflineDevices()
{
for(int device_number=1; device_number<=MAX_DEVICES; device_number++)
{
// if device timer is not running, the device is offline
if( !ptr_deviceTimer[device_number-1]->isActive() )
{
GUI_update(device_number, "Offline");
}
}
}
cyclic_worker.h
#include <QDebug>
#include <QThread>
class CyclicWorker : public QThread
{
Q_OBJECT
public:
bool threadFlag; // variable used to control thread execution
CyclicWorker();
void run();
void quit();
private:
};
cyclic_worker.cpp
#include "cyclic_worker.h"
CyclicWorker::CyclicWorker()
{
qDebug() << "\nCyclicWorker object created";
threadFlag = false;
}
/*----------------------------------------------------------------------------
* void run()
*
* Return value : none
*
* Description : this function runs after thread start
*----------------------------------------------------------------------------*/
void CyclicWorker::run()
{
qDebug("Thread invoked . . .");
while(threadFlag)
{
}
}
/*----------------------------------------------------------------------------
* void quit()
*
* Return value : none
*
* Description : this function stops the running thread
*----------------------------------------------------------------------------*/
void CyclicWorker::quit()
{
qDebug() << "Thread stopped . . .";
}
ui_device_display.h
#ifndef UI_DEVICE_DISPLAY_H
#define UI_DEVICE_DISPLAY_H
#include <QtWidgets>
#define X_PADDING (30) // this is the base container widget co-ordinates
#define Y_PADDING (110)
class UIDeviceDisplay : public QFrame
{
Q_OBJECT
public:
UIDeviceDisplay(QWidget *parent = nullptr, int x=0, int y=0);
QLabel *ptr_label_device_number = nullptr;
QLabel *ptr_label_device_status = nullptr;
static const int frameWidth = 240;
static const int frameHeight = 190;
static const int deviceLabelWidth = 70;
static const int deviceLabelHeight = 50;
static const int statusLabelWidth = 150;
static const int statusLabelHeight = 30;
};
#endif
ui_device_display.cpp
#include "ui_device_display.h"
UIDeviceDisplay::UIDeviceDisplay(QWidget *parent, int x, int y) : QFrame(parent)
{
//QFrame containing all the elements.
this->setGeometry(QRect( X_PADDING + frameWidth*x, Y_PADDING + frameHeight*y, frameWidth, frameHeight));
this->hide();
//QLabel for bed number.
ptr_label_device_number = new QLabel(this);
ptr_label_device_number->setGeometry(QRect((frameWidth/2)-(deviceLabelWidth/2), 20, deviceLabelWidth, deviceLabelHeight));
ptr_label_device_number->setAlignment(Qt::AlignCenter);
//QLabel that displays the device status.
ptr_label_device_status = new QLabel(this);
ptr_label_device_status->setText("Offline");
ptr_label_device_status->setGeometry(QRect(45, 90, statusLabelWidth, statusLabelHeight));
ptr_label_device_status->setAlignment(Qt::AlignCenter);
}
tcp_server.h
#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QAbstractSocket>
#include "tcp_client_handler.h"
class TCPServer : public QTcpServer
{
Q_OBJECT
public:
explicit TCPServer(QObject *parent=0);
protected:
void incomingConnection(int handle);
signals:
void uiPayloadReady(QString uiPayload);
public slots:
void payloadReady(QString payload);
};
#endif
tcp_server.cpp
#include "tcp_server.h"
TCPServer::TCPServer(QObject *parent) :
QTcpServer(parent)
{
if(listen(QHostAddress::Any,7777))
qDebug("DEBUG: Server listening at 7777");
else
qDebug("DEBUG: Could not start server");
}
void TCPServer::incomingConnection(int handle)
{
TCPClientHandler *ptr_client = new TCPClientHandler(this);
ptr_client->SetSocket(handle);
connect(ptr_client, SIGNAL(payloadReady(QString)), this, SLOT(payloadReady(QString)));
}
void TCPServer::payloadReady(QString payload)
{
emit uiPayloadReady(payload);
}
tcp_client_handler.h
#ifndef TCP_CLIENT_HANDLER_H
#define TCP_CLIENT_HANDLER_H
#include <QObject>
#include <QTcpSocket>
#include <QDebug>
class TCPClientHandler : public QObject
{
Q_OBJECT
public:
explicit TCPClientHandler(QObject *parent = nullptr);
void SetSocket(int Descriptor);
QTcpSocket *ptr_socket = nullptr;
signals:
void payloadReady(QString payload);
public slots:
void connected();
void disconnected();
void readReady();
private:
};
#endif
tcp_client_handler.cpp
#include "tcp_client_handler.h"
TCPClientHandler::TCPClientHandler(QObject *parent) : QObject(parent)
{
}
void TCPClientHandler::SetSocket(int Descriptor)
{
ptr_socket = new QTcpSocket(this);
connect(ptr_socket, SIGNAL(connected()), this, SLOT(connected()));
connect(ptr_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(ptr_socket, SIGNAL(readyRead()), this, SLOT(readReady()));
ptr_socket->setSocketDescriptor(Descriptor);
}
void TCPClientHandler::connected()
{
//qDebug("DEBUG: client connect event");
}
void TCPClientHandler::disconnected()
{
//qDebug("DEBUG: client disconnect event");
}
void TCPClientHandler::readReady()
{
const QByteArray byteArrayResponse = ptr_socket->readAll();
const QString stringResponse = QString(byteArrayResponse);
//qDebug() << "DEBUG: " << stringResponse;
emit payloadReady(stringResponse);
}
main.cpp
#include "monitor.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
w.show();
return a.exec();
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I know this question as been multiple times . I have browsed and trying for 5 days to link my C++ signal to a QML slot via Connections in QML . Here is my code at the moment and I don't understand why I always get :Cannot assign to non-existent property "ondashsetupChanged"
Please tell me what i am doing wrong ? The complete code is here :
https://github.com/BastianGschrey/PowerTune/tree/Simplification
Here is my code:
my main.cpp :
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtQml>
#include "connect.h"
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QApplication app(argc, argv);
app.setOrganizationName("Power-Tune");
app.setOrganizationDomain("power-tune.org");
app.setApplicationName("PowerTune");
QQmlApplicationEngine engine;
qmlRegisterType<Connect>("com.powertune", 1, 0, "ConnectObject");
engine.rootContext()->setContextProperty("Connect", new Connect(&engine));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
connect.cpp :
#include "datalogger.h"
#include "connect.h"
#include "calculations.h"
#include "sensors.h"
#include "AdaptronicSelect.h"
#include "AdaptronicCAN.h"
#include "Apexi.h"
#include "HaltechCAN.h"
#include "Nissanconsult.h"
#include "obd.h"
#include "AdaptronicCAN.h"
#include "HaltechCAN.h"
#include "Apexi.h"
#include "AdaptronicSelect.h"
#include "dashboard.h"
#include "serialport.h"
#include "appsettings.h"
#include "gopro.h"
#include "gps.h"
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QByteArrayMatcher>
#include <QProcess>
int ecu; //0=apex, 1=adaptronic;2= OBD; 3= Dicktator ECU
int logging; // 0 Logging off , 1 Logging to file
int connectclicked =0;
QByteArray checksumhex;
QByteArray recvchecksumhex;
Connect::Connect(QObject *parent) :
QObject(parent),
m_serialport(Q_NULLPTR),
m_dashBoard(Q_NULLPTR),
m_gopro(Q_NULLPTR),
m_gps(Q_NULLPTR),
m_adaptronicselect(Q_NULLPTR),
m_apexi(Q_NULLPTR),
m_nissanconsult(Q_NULLPTR),
m_OBD(Q_NULLPTR),
m_sensors(Q_NULLPTR),
m_haltechCANV2(Q_NULLPTR),
m_adaptronicCAN(Q_NULLPTR),
m_datalogger(Q_NULLPTR),
m_calculations(Q_NULLPTR)
{
getPorts();
m_dashBoard = new DashBoard(this);
m_appSettings = new AppSettings(this);
m_gopro = new GoPro(this);
m_gps = new GPS(m_dashBoard, this);
m_adaptronicselect= new AdaptronicSelect(m_dashBoard, this);
m_apexi= new Apexi(m_dashBoard, this);
m_nissanconsult = new Nissanconsult(m_dashBoard, this);
m_OBD = new OBD(m_dashBoard, this);
m_sensors = new Sensors(m_dashBoard, this);
m_haltechCANV2 = new HaltechCAN(m_dashBoard, this);
m_adaptronicCAN = new AdaptronicCAN(m_dashBoard, this);
m_datalogger = new datalogger(m_dashBoard, this);
m_calculations = new calculations(m_dashBoard, this);
QQmlApplicationEngine *engine = dynamic_cast<QQmlApplicationEngine*>( parent );
if (engine == Q_NULLPTR)
return;
engine->rootContext()->setContextProperty("Dashboard", m_dashBoard);
engine->rootContext()->setContextProperty("AppSettings", m_appSettings);
engine->rootContext()->setContextProperty("GoPro", m_gopro);
engine->rootContext()->setContextProperty("GPS", m_gps);
engine->rootContext()->setContextProperty("Nissanconsult",m_nissanconsult);
engine->rootContext()->setContextProperty("Sens", m_sensors);
engine->rootContext()->setContextProperty("Logger", m_datalogger);
}
Connect::~Connect()
{
}
void Connect::checkifraspberrypi()
{
QString path = "/sys/class/backlight/rpi_backlight/brightness";
if (QFileInfo::exists(path))
{
m_dashBoard->setscreen(true);
}
else
{
m_dashBoard->setscreen(false);
}
}
void Connect::readdashsetup()
{
qDebug()<<"c++ file read";
QString path = "UserDash.txt";// this is just for testing
QFile inputFile(path);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while (!in.atEnd())
{
QString line = in.readLine();
QStringList list = line.split(QRegExp("\\,"));
m_dashBoard->setdashsetup(list);
qDebug()<< list;
}
inputFile.close();
}
}
void Connect::setSreenbrightness(const int &brightness)
{
//This works only on raspberry pi
QFile f("/sys/class/backlight/rpi_backlight/brightness");
//f.close();
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
QTextStream out(&f);
out << brightness;
//qDebug() << brightness;
f.close();
}
void Connect::setUnits(const int &units)
{
switch (units)
{
case 0:
m_dashBoard->setunits("metric");
break;
case 1:
m_dashBoard->setunits("imperial");
break;
default:
break;
}
}
void Connect::setWeight(const int &weight)
{
m_dashBoard->setWeight(weight);
qDebug() << "weight" << m_dashBoard->Weight();
}
void Connect::getPorts()
{
QStringList PortList;
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
PortList.append(info.portName());
}
setPortsNames(PortList);
// Check available ports every 1000 ms
QTimer::singleShot(1000, this, SLOT(getPorts()));
}
//function for flushing all Connect buffers
void Connect::clear() const
{
// m_Connectport->clear();
}
//function to open Connect port
void Connect::openConnection(const QString &portName, const int &ecuSelect)
{
ecu = ecuSelect;
//Apexi
if (ecuSelect == 0)
{
m_apexi->openConnection(portName);
}
//Adaptronic
if (ecuSelect == 1)
{
m_adaptronicselect->openConnection(portName);
}
//OBD
if (ecuSelect == 2)
{
m_OBD->openConnection(portName);
}
//Nissan Consult
if (ecuSelect == 3)
{
m_nissanconsult->LiveReqMsg(1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
m_nissanconsult->openConnection(portName);
}
//Adaptronic ModularCAN protocol
if (ecuSelect == 5)
{
m_adaptronicCAN->openCAN();
}
//Haltech V2 CAN protocol
if (ecuSelect == 6)
{
m_haltechCANV2->openCAN();
}
}
void Connect::closeConnection()
{
//Apexi
if (ecu == 0)
{
m_apexi->closeConnection();
}
//Adaptronic Select
if (ecu == 1)
{
m_adaptronicselect->closeConnection();
}
//OBD
if (ecu == 2)
{
m_OBD->closeConnection();
}
//Nissan Consult
if (ecu == 3)
{
m_nissanconsult->closeConnection();
}
//Adaptronic ModularCAN protocol
if (ecu == 5)
{
m_adaptronicCAN->closeConnection();
}
//Haltech V2 CAN protocol
if (ecu == 6)
{
m_haltechCANV2->closeConnection();
}
}
void Connect::update()
{
m_dashBoard->setSerialStat("Update started");
QProcess *process = new QProcess(this);
connect(process , SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(updatefinished(int, QProcess::ExitStatus)));
process->start("/home/pi/updatePowerTune.sh");
process->waitForFinished(6000000); // 10 minutes time before timeout
}
void Connect::updatefinished(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "code" <<exitCode;
qDebug() << "status" <<exitStatus;
QString fileName = "/home/pi/build/PowertuneQMLGui";
QFile file(fileName);
if(QFileInfo::exists(fileName))
{
m_dashBoard->setSerialStat("Update Successful");
file.close();
}
else
{
m_dashBoard->setSerialStat("Update Unsuccessful");
}
}
connect.h
#ifndef CONNECT_H
#define CONNECT_H
#include <QtSerialPort/QSerialPort>
#include <QObject>
#include <QModbusDataUnit>
#include <QTimer>
#include <QProcess>
#include "calculations.h"
class SerialPort;
class Sensors;
class DashBoard;
class AdaptronicCAN;
class AdaptronicSelect;
class Apexi;
class HaltechCAN;
class Nissanconsult;
class OBD;
class datalogger;
class calculations;
class AppSettings;
class GoPro;
class GPS;
class OBD;
class Connect : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList portsNames READ portsNames WRITE setPortsNames NOTIFY sig_portsNamesChanged)
public:
~Connect();
explicit Connect(QObject *parent = 0);
Q_INVOKABLE void checkifraspberrypi();
Q_INVOKABLE void readdashsetup();
Q_INVOKABLE void setSreenbrightness(const int &brightness);
Q_INVOKABLE void setUnits(const int &units);
Q_INVOKABLE void setWeight(const int &weight);
Q_INVOKABLE void clear() const;
Q_INVOKABLE void openConnection(const QString &portName, const int &ecuSelect);
Q_INVOKABLE void closeConnection();
Q_INVOKABLE void update();
public:
QStringList portsNames() const { return m_portsNames; }
private:
SerialPort *m_serialport;
DashBoard *m_dashBoard;
AppSettings *m_appSettings;
GoPro *m_gopro;
GPS *m_gps;
AdaptronicSelect *m_adaptronicselect;
Apexi *m_apexi;
Nissanconsult* m_nissanconsult;
OBD* m_OBD;
Sensors *m_sensors;
HaltechCAN *m_haltechCANV2;
AdaptronicCAN *m_adaptronicCAN;
datalogger *m_datalogger;
calculations *m_calculations;
QStringList m_portsNames;
QStringList *m_ecuList;
QThread* CALCThread;
QProcess process;
signals:
void sig_portsNamesChanged(QStringList portsNames);
public slots:
void updatefinished(int exitCode, QProcess::ExitStatus exitStatus);
void getPorts();
void setPortsNames(QStringList portsNames)
{
if (m_portsNames == portsNames)
return;
m_portsNames = portsNames;
emit sig_portsNamesChanged(portsNames);
}
};
#endif // CONNECT_H
dashbboard.cpp
#include <dashboard.h>
#include <QStringList>
#include <QDebug>
DashBoard::DashBoard(QObject *parent)
: QObject(parent)
{
}
void DashBoard::setdashsetup(const QStringList &dashsetup)
{
if (m_dashsetup == dashsetup)
return;
m_dashsetup = dashsetup;
emit dashsetupChanged(dashsetup);
}
//User Dashboard Stringlist
QStringList DashBoard::dashsetup() const { return m_dashsetup; }
dashboard.h
#ifndef DASHBOARD_H
#define DASHBOARD_H
#include <QStringList>
#include <QObject>
class DashBoard : public QObject
{
Q_OBJECT
//User Dashboard Stringlist dashsetup
Q_PROPERTY(QStringList dashsetup READ dashsetup WRITE setdashsetup NOTIFY dashsetupChanged)
public:
DashBoard(QObject *parent = 0);
//User Dashboard Stringlist
QStringList dashsetup() const;
signals:
QStringList m_dashsetup;
};
#endif // DASHBOARD_H
main.qml:
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import com.powertune 1.0
ApplicationWindow {
visible: true
width: 800
height: 480
minimumWidth: 800
minimumHeight: 480
title: qsTr("PowerTune ") + Dashboard.Platform + " Beta 24"
// visibility: "FullScreen"
color: "black"
Connections{
target: Dashboard
ondashsetupChanged: console.log("Dashboard has changed")
}
Item {
id: name
Component.onCompleted: Connect.checkifraspberrypi()
}
SwipeView {
id: view
currentIndex: 0
anchors.fill: parent
Loader {
id: firstPageLoader
source: ""
}
Loader {
id: secondPageLoader
source: ""
}
Loader {
id: thirdPageLoader
source: ""
}
Loader {
id: fourthPageLoader
source: ""
}
Item {
id:lastPage
SerialSettings{}
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
The first letter of the property name is always capitalised in signal handlers:
onDashsetupChanged: console.log("Dashboard has changed")
Property Change Signal Handlers explains this:
A signal is automatically emitted when the value of a QML property changes. This type of signal is a property change signal and signal handlers for these signals are written in the form onChanged where is the name of the property, with the first letter capitalized.
I have some problems with qt + qml: Sometimes I get segmentation fault trying to clear QQmlengine. And even if program doesn't crashes it has memory leaks. (sorry for my english). I think so because the larger qml file (many nested classes) the larger application process even after QQmlengine was deleted. So my question is what am I doing wrong in the code below?
//main.cpp
#include "qmlwindow.h"
#include <QApplication>
int QMLwindow::someValue = 0;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
QSharedPointer<QMLwindow> w0(new QMLwindow);
QSharedPointer<QMLwindow> w1(new QMLwindow);
QSharedPointer<QMLwindow> w2(new QMLwindow);
w0.data()->createWindow();
w0.data()->createWindow();
w1.data()->createWindow();
w2.data()->createWindow();
return a.exec();
}
//qmlwindow.h
#ifndef QMLWINDOW_H
#define QMLWINDOW_H
#include <QObject>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QSharedPointer>
#include <QDebug>
#include <QQmlEngine>
#include <QQmlContext>
class QMLwindow : public QObject
{
Q_OBJECT
private:
QSharedPointer<QQmlEngine> engine;
QQmlComponent *component;
QObject *windowObj;
QQuickWindow *loginWindow;
QVariant count;
public:
static int someValue;
explicit QMLwindow(QObject *parent = 0);
~QMLwindow();
void createWindow();
signals:
public slots:
void deleteWindow(QQuickCloseEvent *event);
};
#endif // QMLWINDOW_H
//qmlwindow.cpp
#include "qmlwindow.h"
QMLwindow::QMLwindow(QObject *parent) : QObject(parent)
{
someValue++;
count = someValue;
qDebug() << "construction of QMLwindow " << count.toInt();
}
QMLwindow::~QMLwindow()
{
qDebug() << "destruction of QMLwindow " << count.toInt();
}
void QMLwindow::createWindow(){
if(engine.isNull()){
engine = QSharedPointer<QQmlEngine>(new QQmlEngine);
component = new QQmlComponent(engine.data());
component->loadUrl(QUrl(QStringLiteral("qrc:/SomeWindow.qml")));
Q_ASSERT(component->isReady());
windowObj = component->create();
loginWindow = qobject_cast<QQuickWindow *> (windowObj);
engine.data()->rootContext()->setContextProperty("someValue", count) ;
connect(loginWindow, SIGNAL(closing(QQuickCloseEvent*)), SLOT(deleteWindow(QQuickCloseEvent*)));
loginWindow->show();
}
else loginWindow->show();
}
void QMLwindow::deleteWindow(QQuickCloseEvent*event){
Q_UNUSED(event)
if(!engine.isNull()) {
qDebug() << "loginWindow destruction";
engine.clear();
}
this->deleteLater();
}
//SomeWindow.qml
import QtQuick 2.3
import QtQuick.Window 2.0
Window {
width: 200
height: 200
color: "white"
Text{
text: someValue
anchors.centerIn: parent
}
}
I have written a QT - webkit application. this application fires a callback when my pSeudo driver gets the character 'l'. However, the application crashes during a firecallback - it says - QObject::setParent: Cannot set parent, new parent is in a different thread. I don't know to fix this, I tried doing moveToThread, but it doesn't help. Please help me here.
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject {
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JS_ADDED();
void loadFinished(bool);
private:
};
class DemoThread : public QThread {
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
private :
MyJavaScriptOperations *m_pJavascriptOp;
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
read(filedesc,&buff, 1);
qDebug()<<"The code received is "<< buff;
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
m_pJavascriptOp->firecb();
}
qDebug()<<"Running Thread.";
sleep(6);
}
}
void MyJavaScriptOperations::JS_ADDED()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
// firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JS_ADDED()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
jvs->moveToThread(thread);
thread->start();
return a.exec();
}
#include "main.moc"
This is the crash-error I get -
./QT_DEMO
MyJavaScriptOperations::MyJavaScriptOperations()
loaded the Generic plugin
The code received is 156
Running Thread.
The code received is 166
void MyJavaScriptOperations::firecb()
QObject::setParent: Cannot set parent, new parent is in a different thread
There are few articles on internet how to make multithreaded applications in Qt. Best explanation can be found here:
http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/
You could read also other articles:
https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Well, I got a solution for my problem. Please tell me, am I complicating the solution.
I am using signal and slot. The thread will emit the signal and the slot of other class will emit the callback to the Qtwebkit - the javascript function. IS it right?
Because, I have suggestion using event loop - exec().
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
/** for reading my driver **/
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject
{
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JsAdded();
void alertReceived();
void loadFinished(bool);
private:
};
class DemoThread : public QThread
{
Q_OBJECT
private:
MyJavaScriptOperations *m_pJavascriptOp;
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
signals:
void alertSendSignal();
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
connect(this, SIGNAL(alertSendSignal()), m_pJavascriptOp, SLOT(alertReceived()));
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
if( 1 != read(filedesc,&buff, 1))
{
qDebug()<<"Read Invalid Data";
}
qDebug()<<"The code received is "<< buff;
/** In my laptop, the 166 means the character 'l' **/
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
emit alertSendSignal();
}
qDebug()<<"Running Thread.";
}
}
void MyJavaScriptOperations::JsAdded()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
}
void MyJavaScriptOperations::alertReceived()
{
qDebug()<<"Sending Firecallback now";
firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JsAdded()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
thread->start();
return a.exec();
}
#include "main.moc"