Many active QTimers timekeeping causing GUI lag - c++

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();
}

Related

Data Appending while sending Data through Qt NamedPipe

Data Appending Problem While sending the data through Namedpipe using QT
localserver.h
#ifndef LOCALSERVER_H
#define LOCALSERVER_H
#include "QLocalSocket"
#include "QLocalServer"
class QLocalSocket;
class LocalServer : public QLocalServer
{
Q_OBJECT
public:
explicit LocalServer(QObject *parent = 0);
void send(QString &msj);
QLocalSocket *mSocket;
private slots:
void receive();
private:
};
#endif // LOCALSERVER_H
localserver.cpp
#include "localserver.h"
#include "QLocalSocket"
#include "QTextStream"
#include "QMessageBox"
LocalServer::LocalServer(QObject *parent) : QLocalServer(parent)
{
mSocket = nullptr;
connect(this,&LocalServer::newConnection,[&](){
mSocket = nextPendingConnection();
});
qDebug()<<"Hello World";
}
void LocalServer::send(QString &msj)
{
qDebug()<<"Sending Data";
if(mSocket)
{
QTextStream T(mSocket);
T<<msj.toStdString().c_str();
mSocket->flush();
}
}
void LocalServer::receive()
{
qDebug()<<"Im here";
QDataStream in(mSocket);
in.setVersion(QDataStream::Qt_5_2);
mSocket->readAll();
qDebug()<<"Data Received";
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QProcess"
#include "QTimer"
#include "QLocalSocket"
#include "localserver.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void Connection();
private slots:
void on_start_clicked();
void on_send_clicked();
void on_quit_clicked();
void detect_connection();
void connect_terminate();
void connected();
void send_data();
void receive_data();
QString RandomData();
void on_pbtn_Stop_clicked();
private:
Ui::Widget *ui;
LocalServer *mLocalServer;
QTimer *timer;
QLocalSocket *mSocket;
bool first;
QString data_received;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "QProcess"
#include "QDebug"
#include "localserver.h"
#include "QMessageBox"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
Connection();
first = true;
}
void Widget::Connection()
{
mLocalServer = new LocalServer(this);
mLocalServer->listen("Server1");
connect(mLocalServer,SIGNAL(newConnection()),this,SLOT(detect_connection()));
first = true;
timer = new QTimer(this);
timer->setInterval(8);
connect(timer,SIGNAL(timeout()),this,SLOT(send_data()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_start_clicked()
{
timer->start();
}
void Widget::on_send_clicked()
{
timer->stop();
}
void Widget::on_quit_clicked()
{
timer->stop();
this->close();
}
void Widget::detect_connection()
{
qDebug()<<"Write Pipe Established";
mSocket = new QLocalSocket(this);
mSocket->connectToServer("Server2");
connect(mSocket,SIGNAL(disconnected()),this,SLOT(connect_terminate()));
connect(mSocket,SIGNAL(connected()),this,SLOT(connected()));
qDebug()<<"Connection Established, Ready to Read";
connect(mSocket,&QLocalSocket::readyRead, [&]() {
QTextStream T(mSocket);
ui->receivedText->addItem(T.readAll());
});
}
void Widget::connect_terminate()
{
qDebug()<<"Read Server Connection Terminated";
QString string = "Stop_Session";
mLocalServer->send(string);
ui->sentText->addItem(string);
}
void Widget::connected()
{
qDebug()<<"Socket Connected to Server";
QString s=" Connection Done ";
mLocalServer->send(s);
}
void Widget::send_data()
{
QString string = RandomData();
mLocalServer->send(string);
ui->sentText->addItem(string);
}
QString Widget::RandomData()
{
srand(time(NULL));
int random_number = rand()%(1920-0 + 1) + 0; // rand() return a number between ​0​ and RAND_MAX
int random_number1 = rand()%(1080-0 + 1) + 0;
int random_number2 = rand()%(10-0 + 1) + 0;
QString s="Contour,"+QString::number(random_number)+","+QString::number(random_number1)+","+QString::number(random_number2);
// Basically this data in "s" gives the actual contours detected data but am using a random generator to test the Local Server and Socket .
return s;
}
void Widget::receive_data()
{
QString string;
QTextStream T(mSocket);
T.readLine();
if(T.readAll()=="Stop")
{
qDebug()<<"Socket About to Close";
QString string = "Stop";
mLocalServer->send(string);
}
else
{
qDebug()<<"Program can continue"<<T.readAll();
}
}
void Widget::on_pbtn_Stop_clicked()
{
timer->stop();
}
At Receiver SIde Program
Receiver.cpp
void Receiver ::Declaration_Of_Server2()
{
// Declaration Of LocalServer i.e Server2
ServerIS = new LocalServer(this);
ServerIS->listen("Server2");
// Connect To a LocalSocket TO The Above LocalServer i.e Server_IS with Server_VS
connect(ServerIS,SIGNAL(newConnection()),this,SLOT(DetectConnection_WithServer1()));
}
void CoFire_MainPage::DetectConnection_WithServer1()
{
// Declaration of LocalSocket i.e Server_VS
SocketIS = new QLocalSocket(this);
SocketIS->connectToServer("Server1");
connect(SocketIS,SIGNAL(readyRead()),this,SLOT(Receiving_VS_Data()));
}
void CoFire_MainPage::Receiving_VS_Data()
{
// Receiving Vs Data From Socket "Socket_IS"
QTextStream msg(SocketIS);
QString str = msg.readLine();
qDebug()<<" VS DATA : "<<str;
}
By this am trying to send data ( example : Contour,1000,800,1 ) from one program to other program
with a speed of 1 data / 8msec but in real scenario it might even go to 1msec data transfer.
but by this at receving end every now and then incomming data is been appended
(example: Contour,1000,800,1Contour,200,400,1Contour,500,650,1 etc... ).
But actually expentected data is
1st
Contour,1000,800,1
next
Contour,200,400,1
next
Contour,500,650,1
etc....
This way the data is getting appeneded before i capture and process the data at receiver .
By this am loosing the precious information .
how do i solve this issue .
readLine() waits for an end-of-line ("\n" or "\r\n"), but in your case you do not send it, you must change to the following:
QString Widget::RandomData(){
srand(time(NULL));
int random_number = rand()%(1920-0 + 1) + 0; // rand() return a number between ​0​ and RAND_MAX
int random_number1 = rand()%(1080-0 + 1) + 0;
int random_number2 = rand()%(10-0 + 1) + 0;
return QString("Contour,%1,%2,%3\n")
.arg(random_number)
.arg(random_number1)
.arg(random_number2);
}
On the other hand it is not necessary to use the reference of the QString, and also it is unnecessary to use toStdString().c_str().
void LocalServer::send(const QString &msj)
{
qDebug()<<"Sending Data";
if(mSocket)
{
QTextStream T(mSocket);
T << msj;
mSocket->flush();
}
}

QT Update variable of worker thread from mainwindow

I am displaying opencv video using QT multithreading concept as suggested here
I also wish to update private "path" variable of worker thread class on button click. So I added
connect(this, SIGNAL(send_to_worker(QString)),workers[view], SLOT(get_from_main(QString)));
However, get_from_main(QString) function never gets called.
May I please know what is the safest method to update worker thread class variable from mainwindow ?
Here is full code..
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QString path, int id);
~Worker();
public slots:
void readVideo(QString path = "");
void get_from_main(QString path);
signals:
// frame and index of label which frame will be displayed
void frameFinished(cv::Mat frame, int index);
void finished(int index);
private:
QString filepath;
int index;
};
//worker.cpp
#include "worker.h"
#include <QDebug>
#include <QThread>
#include <QTime>
Worker::Worker(QString path, int id) : filepath(path), index(id)
{
}
Worker::~Worker()
{
}
void Worker::get_from_main(QString path)
{
qDebug() << "updating";
}
void Worker::readVideo(QString path)
{
if (path.length() > 0)
filepath = path;
cv::VideoCapture cap(filepath.toStdString());
if (! cap.isOpened())
{
qDebug() << "Can't open video file " << filepath;
emit finished(index);
return;
}
cv::Mat frame;
while (true)
{
cap >> frame;
if (frame.empty())
{
frame = cv::Mat(cv::Size(720, 576), CV_8UC3, cv::Scalar(192, 0, 0));
emit frameFinished(frame, index);
break;
}
emit frameFinished(frame.clone(), index);
QThread::msleep(30);
}
emit finished(index);
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <opencv2/opencv.hpp>
#include "worker.h"
#define MAX_NUM_CAM 8
namespace Ui {
class MainWindow;
}
class QThread;
class QLabel;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void init();
private slots:
void displayFrame(cv::Mat frame, int index);
void file_open_clicked();
signals:
send_to_worker(QString path);
private:
Ui::MainWindow *ui;
int numCams;
QLabel *labels[MAX_NUM_CAM];
QThread* threads[MAX_NUM_CAM];
Worker* workers[MAX_NUM_CAM];
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>
#include <QLabel>
#include <QGridLayout>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qRegisterMetaType< cv::Mat >("cv::Mat");
qDebug() << "Main thread " << QThread::currentThreadId();
init();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::init()
{
QGridLayout *grid = new QGridLayout;
int numCols = 2;
numCams = 4;
int row = 0, col = 0;
for (int i = 0; i < numCams; i++)
{
labels[i] = new QLabel;
row = i / numCols;
col = i % numCols;
grid->addWidget(labels[i], row, col);
threads[i] = new QThread;
workers[i] = new Worker(QString("/home/shang/Videos/%1.mp4").arg(i+1), i);
workers[i]->moveToThread(threads[i]);
connect(workers[i], SIGNAL(frameFinished(cv::Mat, int)), this, SLOT(displayFrame(cv::Mat,int)));
connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));
connect(workers[i], SIGNAL(finished(int)), threads[i], SLOT(quit()));
connect(workers[i], SIGNAL(finished(int)), workers[i], SLOT(deleteLater()));
connect(threads[i], SIGNAL(finished()), threads[i], SLOT(deleteLater()));
threads[i]->start();
}
this->centralWidget()->setLayout(grid);
}
void MainWindow::file_open_clicked(){
QString Path = QFileDialog::getSaveFileName( this,tr("OpenVideo"),"","Video (*.avi)");
if(Path.isEmpty())
return;
view =3;
connect(this, SIGNAL(send_to_worker(QString)),workers[view], SLOT(get_from_main(QString)));
emit this->send_to_worker(recorder_Path);
}
void MainWindow::displayFrame(cv::Mat frame, int index)
{
QPixmap p = QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped());
p = p.scaled(QSize(frame.cols/2, frame.rows/2));
labels[index]->setPixmap(p);
}
Qt + OpenCV play videos with std::thread
You're trying to perform an operation periodically whilst still processing events -- that's simply calling out for a QTimer.
(Note that the following code is untested.)
Change your Worker class to make use of a QTimer rather than a loop that blocks the event queue...
class Worker: public QObject {
Q_OBJECT;
public:
Worker (QString path, int id);
~Worker();
public slots:
void readVideo(QString path = "");
void get_from_main(QString path);
signals:
// frame and index of label which frame will be displayed
void frameFinished(cv::Mat frame, int index);
void finished(int index);
private:
QString filepath;
int index;
QTimer timer;
cv::VideoCapture cap;
};
Worker::Worker (QString path, int id)
: filepath(path)
, index(id)
, timer(this)
, cap(filepath.toStdString())
{
/*
* Connect QTimer::timeout to the readVideo slot that will read a
* single frame on each signal at 30ms intervals.
*/
connect(&timer, &QTimer::timeout, this, &Worker::readVideo);
timer.start(30);
}
Worker::~Worker ()
{
}
void Worker::get_from_main (QString path)
{
qDebug() << "updating";
filepath = path;
cap = cv::VideoCapture(filepath);
if (!cap.isOpened()) {
qDebug() << "Can't open video file " << filepath;
emit finished(index);
}
}
void Worker::readVideo ()
{
cv::Mat frame;
cap >> frame;
if (frame.empty())
{
frame = cv::Mat(cv::Size(720, 576), CV_8UC3, cv::Scalar(192, 0, 0));
emit frameFinished(frame, index);
break;
}
emit frameFinished(frame.clone(), index);
}
Now Worker::readVideo simply reads a single frame from the capture and then returns to the event loop.
Also remove the line...
connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));
from MainWindow::init. As I stated above this is untested and probably needs a lot more error checking. But it should give you a good idea as to what's required.

use QAudioOutput from QThread to play sound asynchronously

I am trying for a long time to play asynchronously a generated sound with QAudioOutput, from a QThread. To use QAudioOutput into QThread, we need QThread to have its own event loop. So, I start from doc example by using QObject::moveToThread() method.
Sound is generated ok. Because if I play sound and wait to finish sound->playSound(true); then the sound is played ok. But if I use sound->playSound(false); then there is no sound, like there is no event loop.
I use while(1){} because in original code, Interpreter is a long while() loop in which many things happen (graphics, sounds, etc.).
1) If I wait the sound sound->playSound(true); I got this output:
try playSound
handleAudioStateChanged ActiveState
handleAudioStateChanged IdleState
return from playSound
2) If I play the sound asynchronously sound->playSound(false); I got this output:
try playSound
handleAudioStateChanged ActiveState
return from playSound
How can I play asynchronously a sound with QAudioOutput from a QThread while doing a lot of other things expensive into an infinite loop?
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
void Interpreter::doWork() {
bool result = false;
sound = new SoundSystem();
qDebug() << "try playSound";
sound->playSound(true); //bool wait=true/false
qDebug() << "return from playSound";
while(1){}
emit resultReady(result);
}
Controller::Controller() {
interpreterThread = new QThread();
Interpreter *i = new Interpreter;
connect(interpreterThread, &QThread::finished, i, &QObject::deleteLater);
connect(this, &Controller::startInterpreter, i, &Interpreter::doWork);
connect(i, &Interpreter::resultReady, this, &Controller::stopRunFinalized);
i->moveToThread(interpreterThread);
interpreterThread->start();
}
Controller::~Controller() {
interpreterThread->quit();
interpreterThread->wait();
}
void Controller::stopRunFinalized(bool i){
qDebug() << "stopRunFinalized";
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){
Controller *t = new Controller;
t->startInterpreter();
}
MainWindow::~MainWindow(){}
mainwindow.h
#include <QMainWindow>
#include <QThread>
#include <QDebug>
#include "Sound.h"
class Interpreter : public QObject {
Q_OBJECT
public:
SoundSystem *sound;
public slots:
void doWork();
signals:
void resultReady(bool);
};
class Controller : public QObject {
Q_OBJECT
public:
Controller();
~Controller();
QThread *interpreterThread;
//public slots:
void stopRunFinalized(bool);
signals:
void startInterpreter();
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
Sound.cpp - used to generate a simple wave sound for 2 seconds
#include "Sound.h"
#include <QDebug>
void SoundSystem::playSound(bool wait) {
double wave;
double wavebit;
short s;
char *cs = (char *) &s;
QByteArray* array = new QByteArray();
QBuffer buffer(array);
const double pi = 4*atan(1.0);
buffer.open(QIODevice::ReadWrite|QIODevice::Truncate);
wave=0.0;
wavebit=0.0;
// lets build a sine wave into buffer
int length = 44100 * 440 / 1000;
wavebit = 2 * pi / (44100.0 / 1000.0);
for(int i = 0; i < length; i++) {
s = (int16_t) (0x7fff * sin(wave));
buffer.write(cs,sizeof(int16_t));
wave+=wavebit;
}
buffer.seek(0);
// setup the audio format
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleSize(16); // 16 bit audio
format.setCodec("audio/pcm");
format.setSampleType(QAudioFormat::SignedInt);
audio = new QAudioOutput(format);
connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleAudioStateChanged(QAudio::State)));
audio->start(&buffer);
if(wait){
QEventLoop *loop = new QEventLoop();
QObject::connect(audio, SIGNAL(stateChanged(QAudio::State)), loop, SLOT(quit()));
do {
loop->exec(QEventLoop::WaitForMoreEvents);
} while(audio->state() == QAudio::ActiveState);
delete (loop);
}
}
void SoundSystem::handleAudioStateChanged(QAudio::State newState){
qDebug() << "handleAudioStateChanged" << newState;
}
Sound.h
#include <math.h>
#include <QObject>
#include <QAudioOutput>
#include <QBuffer>
#include <QEventLoop>
class SoundSystem : public QObject
{
Q_OBJECT
public:
void playSound(bool wait);
QAudioOutput* audio;
QAudioFormat format;
private slots:
void handleAudioStateChanged(QAudio::State);
};

Scope issues with Qt Quick and C++ classes

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();
}

How to fix the runtime crash - QObject::setParent: Cannot set parent, new parent is in a different thread?

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"