QT Serialport GUI and worker thread - c++

First things first, I'm a newbie in QT so don't blame me. I know that many similar questions have been in the forum, but I couldn't solve my problem.
Problem description. I want to have a GUI application that receives and parses data and update some qt widget. Formerly I did them all in the Mainwindow thread, but since it hangs, I tried to make it multi-threaded. But it still hangs when I try to update GUI data as fast as 10 ms.
Now, this is what I have tried.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
class Parser : public QThread , public QRunnable
{
Q_OBJECT
public:
explicit Parser(QThread *parent = nullptr);
~Parser();
signals:
void data1Available(unsigned char*);
void data2Available(unsigned char*);
void finished();
// QRunnable interface
public:
void run();
public slots:
void parse();
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect( &SerialPort, SIGNAL(readyRead()), this, SLOT(ReadData()) );
QThread* thread = new QThread;
thread->setObjectName("Parser Thread");
qInfo()<<"Parser Thread";
Parser* parser= new Parser();
parser->moveToThread(thread);
QObject::connect(thread,&QThread::started,parser,&Parser::run);
QObject::connect(parser,&Parser::finished,parser,&Parser::deleteLater);
QObject::connect(parser,&Parser::finished,thread,&QThread::quit);
QObject::connect(thread,&QThread::finished,thread,&QThread::deleteLater);
QObject::connect(parser,SIGNAL(data1Available(unsigned char *)),this,SLOT(on_data1Available(unsigned char *)));
QObject::connect(parser,SIGNAL(data2Available(unsigned char *)),this,SLOT(on_data2Available(unsigned char *)));
thread->start();
}
void MainWindow::ReadData()
{
QByteArray Data = SerialPort.readAll();
for (unsigned char i=0;i<Data.length();i++)
circBuff.append(Data[i]);
}
void MainWindow::on_data1Available(unsigned char* tempData)
{
ui->label1->setNum(tempData[5]);
}
void MainWindow::on_data2Available(unsigned char* tempData)
{
ui->label2->setNum(tempData[7]);
}
void Parser::run()
{
qInfo()<<this<<Q_FUNC_INFO<<QThread::currentThread();
QScopedPointer<QEventLoop> loop (new QEventLoop);
QScopedPointer<QTimer> timer (new QTimer);
timer->setInterval(5);
connect(timer.data(),&QTimer::timeout,this,&Parser::parse);
connect(this,&Parser::finished,loop.data(),&QEventLoop::quit);
timer->start();
loop->exec();
qInfo()<<this<<"Finished... "<<QThread::currentThread();
}
void Parser::parse()
{
unsigned char tempData[16];
while (1)
{
while (circBuff.size()>=16)
{
if ( )
{
if ()
emit data1Available(tempData);
else
emit data2Available(tempData);
}
}
}
emit finished();
}

Related

Data sharing problem between threads in Qt QML backend C++

I am newbie in Qt/QML world and i am trying a multithread application. I have 2 threads. I want to pass some value from one thread to another but i have a problem with it. I can see the value from setValue() function but i cannot see the value from the showData() thread of the same object. My code is below:
My object changes the value in doWork thread
class MyObject : public QObject
{
Q_OBJECT
public:
myWorker *itsMyWorker;
explicit MyObject(QObject *parent = nullptr){
itsMyWorker= new myWorker;
}
void doSetup(QThread &cThread){
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
signals:
public slots:
void doWork(){
for(int i = 0; i < 100; i++){
if(i % 10 == 0)
itsMyWorker->setValue(i);
}
}
};
I want to see the value from showData thread
class myWorker : public QObject
{
Q_OBJECT
public:
explicit myWorker (QObject *parent = nullptr);
void doSetup(QThread &cThread){
connect(&cThread, SIGNAL(started()), this, SLOT(showData()));
}
void setValue(int i){
myNumber = Number;
qDebug() << "setValue() " << myNumber;
}
void showValue(){
//qDebug() << "showValue() " << myNumber;
}
signals:
public slots:
void showData(){
while(1){
showValue();
}
}
private:
int myNumber;
};
And this is the main
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QThread>
#include "myobject.h"
#include "mynesne.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QThread cThread;
MyObject cObject;
cObject.doSetup(cThread);
cObject.moveToThread(&cThread);
QThread cThread2;
myWorker cWorker;
cWorker.doSetup(cThread2);
cWorker.moveToThread(&cThread2);
cThread2.start();
cThread.start();
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);
engine.load(url);
return app.exec();
}

Get response from QProcess in real time

I am new in c++ programming , so i need help with the logic (code would be awesome). I want to make QTextEdit to work as a terminal like widget.
I am trying to achieve that with QProcess like:
void QPConsole::command(QString cmd){
QProcess* process = new QProcess();
connect(process, &QProcess::readyReadStandardOutput, [=](){ print(process->readAllStandardOutput()); });
connect(process, &QProcess::readyReadStandardError, [=](){ print(process->readAllStandardError()); });
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[=](int exitCode, QProcess::ExitStatus exitStatus){ prepareCommandLine(); return; });
process->setProgram("/bin/bash");
process->start();
process->write(cmd.toStdString().c_str());
// process->write("\n\r");
process->closeWriteChannel();
}
The problem is next:
If i don't close write channel (process->closeWriteChannel) than i am stuked in infinite loop. And if i do close it, than i cannot remember state (kinda makes new session) for example if i do "pwd" i get result, but if i do next "cd .." and then "pwd" the result is same as the first output of "pwd"
So, my question is is it possible to achieve some kind of real time output + having previous state remembered (like in real terminal) with QProcess or i have to do in some other way. In python i did it with subprocess calls with pipes, but i don't know what is the equilavent for c++.
An code example would be great. I have QTextEdit part (which is sending QString param to function), i need part with interaction with console.
The idea is simple: Keep an instance of QProcess and write commands to it followed by \n!
Here is a full example, with a QMainWindow and a Bash class, put it in main.cpp:
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class Bash : public QObject
{
Q_OBJECT
public:
explicit Bash(QObject *parent = nullptr) : QObject(parent)
{
}
~Bash()
{
closePrivate();
}
signals:
void readyRead(QString);
public slots:
bool open(int timeout = -1)
{
if (m_bash_process)
return false;
m_timeout = timeout;
return openPrivate();
}
void close()
{
closePrivate();
}
bool write(const QString &cmd)
{
return writePrivate(cmd);
}
void SIGINT()
{
SIGINTPrivate();
}
private:
//Functions
bool openPrivate()
{
if (m_bash_process)
return false;
m_bash_process = new QProcess();
m_bash_process->setProgram("/bin/bash");
//Merge stdout and stderr in stdout channel
m_bash_process->setProcessChannelMode(QProcess::MergedChannels);
connect(m_bash_process, &QProcess::readyRead, this, &Bash::readyReadPrivate);
connect(m_bash_process, QOverload<int>::of(&QProcess::finished), this, &Bash::closePrivate);
m_bash_process->start();
bool started = m_bash_process->waitForStarted(m_timeout);
if (!started)
m_bash_process->deleteLater();
return started;
}
void closePrivate()
{
if (!m_bash_process)
return;
m_bash_process->closeWriteChannel();
m_bash_process->waitForFinished(m_timeout);
m_bash_process->terminate();
m_bash_process->deleteLater();
}
void readyReadPrivate()
{
if (!m_bash_process)
return;
while (m_bash_process->bytesAvailable() > 0)
{
QString str = QLatin1String(m_bash_process->readAll());
emit readyRead(str);
}
}
bool writePrivate(const QString &cmd)
{
if (!m_bash_process)
return false;
if (runningPrivate())
return false;
m_bash_process->write(cmd.toLatin1());
m_bash_process->write("\n");
return m_bash_process->waitForBytesWritten(m_timeout);
}
void SIGINTPrivate()
{
if (!m_bash_process)
return;
QProcess::startDetached("pkill", QStringList() << "-SIGINT" << "-P" << QString::number(m_bash_process->processId()));
}
bool runningPrivate()
{
if (!m_bash_process)
return false;
QProcess process;
process.setProgram("pgrep");
process.setArguments(QStringList() << "-P" << QString::number(m_bash_process->processId()));
process.setProcessChannelMode(QProcess::MergedChannels);
process.start();
process.waitForFinished(m_timeout);
QString pids = QLatin1String(process.readAll());
QStringList pid_list = pids.split(QRegularExpression("\n"), QString::SkipEmptyParts);
return (pid_list.size() > 0);
}
//Variables
QPointer<QProcess> m_bash_process;
int m_timeout;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
{
connect(&m_bash, &Bash::readyRead, this, &MainWindow::readyRead);
QWidget *central_widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(central_widget);
layout->addWidget(&m_message_board);
layout->addWidget(&m_cmd_edit);
layout->addWidget(&m_sigint_btn);
m_message_board.setReadOnly(true);
m_cmd_edit.setEnabled(false);
m_sigint_btn.setText("Send SIGINT(CTRL+C)");
connect(&m_cmd_edit, &QLineEdit::returnPressed, this, &MainWindow::writeToBash);
connect(&m_sigint_btn, &QPushButton::clicked, this, &MainWindow::SIGINT);
setCentralWidget(central_widget);
resize(640, 480);
QTimer::singleShot(0, this, &MainWindow::startBash);
}
~MainWindow()
{
m_bash.close(); //Not mandatory, called by destructor
}
private slots:
void startBash()
{
//Open and give to each operation a maximum of 1 second to complete, use -1 to unlimited
if (m_bash.open(1000))
{
m_cmd_edit.setEnabled(true);
m_cmd_edit.setFocus();
}
else
{
QMessageBox::critical(this, "Error", "Failed to open bash");
}
}
void writeToBash()
{
QString cmd = m_cmd_edit.text();
m_cmd_edit.clear();
if (!m_bash.write(cmd))
{
QMessageBox::critical(this, "Error", "Failed to write to bash");
}
}
void readyRead(const QString &str)
{
m_message_board.appendPlainText(str);
}
void SIGINT()
{
m_bash.SIGINT();
}
private:
Bash m_bash;
QPlainTextEdit m_message_board;
QLineEdit m_cmd_edit;
QPushButton m_sigint_btn;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"

Redirecting std::cout from DLL in a separate thread to QTextEdit

The goal is to display all the application output shown in QtCreator to a QTextEdit as a debug console window so with the same application only those who have password are allowed to see the console window while normal users cannot. There is an exe with several dlls. All std::cout from DLLs and qDebug are needed to be shown in the debug console window.
To achieve this, I have followed
http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
The code works great for single thread but is hanged when a thread is started to call functions in DLL. I would like to know how to fix the problem.
code
In mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_qd = new Q_DebugStream(std::cout,ui->textEdit); //Redirect Console output to QTextEdit
m_qd->Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
connect(ui->pushButtonSingleThreadTest, SIGNAL(clicked()),this,SLOT(SingleThreadTest()));
connect(ui->pushButtonMultiThreadTest, SIGNAL(clicked()),this,SLOT(MultiThreadTest()));
}
void MainWindow::SingleThreadTest()
{
run();
}
void MainWindow::MultiThreadTest()
{
QThread *workerThread= new QThread;
ThreadWorker *worker = new ThreadWorker();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
In mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "q_debugstream.h"
#include "../ToyProj1/Header.h"
namespace Ui {
class MainWindow;
}
class ThreadWorker : public QObject
{
Q_OBJECT
public:
ThreadWorker()
{
}
private:
signals:
void finished();
public slots:
void doWork()
{
run();
emit finished();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void SingleThreadTest();
void MultiThreadTest();
private:
Ui::MainWindow *ui;
Q_DebugStream* m_qd;
};
#endif // MAINWINDOW_H
in q_debugstream.h
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include "QTextEdit.h"
class Q_DebugStream : public std::basic_streambuf<char>
{
public:
Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
{
log_window = text_edit;
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~Q_DebugStream()
{
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler(){
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
std::cout << msg.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append("");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
QString str(p);
if(str.contains("\n")){
QStringList strSplitted = str.split("\n");
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++){
log_window->append(strSplitted.at(i));
}
}else{
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (str);
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H
In DLL,
int run()
{
std::cout << "Hello World" << std::endl;
return 0;
}
The code sample is uploaded to github for reference. Build ToyProj1 and ToyProj1GUI when repeat the problem.
https://github.com/kuwt/ToyProject.git
With the help comments in question, this solution works well with signal and slot mechanism. std::cout and qDebug are redirected to QTextEdit.
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
// Setup QMessageCatch
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
qInstallMessageHandler(MainWindow::QMessageOutput);
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QTextEdit>
#include "q_debugstream.h"
namespace Ui {
class MainWindow;
}
class ThreadWorker : public QObject
{
Q_OBJECT
public:
ThreadWorker()
{
}
private:
signals:
void finished();
public slots:
void doWork()
{
std::cout<< "Hello World2" <<std::endl;
qDebug() << "Hello World2q" ;
emit finished();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// QMessage
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
static void QMessageOutput(QtMsgType , const QMessageLogContext &, const QString &msg);
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
public slots:
void SingleThreadTest();
void MultiThreadTest();
private:
Ui::MainWindow *ui;
// MessageHandler for display and ThreadLogStream for redirecting cout
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
MessageHandler *msgHandler = Q_NULLPTR;
ThreadLogStream* m_qd;
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>
// Catch QMessage, redirect to cout
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
void MainWindow::QMessageOutput(QtMsgType , const QMessageLogContext &, const QString &msg)
{
std::cout<<msg.toStdString().c_str()<<std::endl;
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Set up ThreadLogStream, which redirect cout to signal sendLogString
// Set up MessageHandler, wgucg catch message from sendLogString and Display
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
m_qd = new ThreadLogStream(std::cout); //Redirect Console output to QTextEdit
this->msgHandler = new MessageHandler(this->ui->textEdit, this);
connect(m_qd, &ThreadLogStream::sendLogString, msgHandler, &MessageHandler::catchMessage);
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
connect(ui->pushButtonSingleThreadTest, SIGNAL(clicked()),this,SLOT(SingleThreadTest()));
connect(ui->pushButtonMultiThreadTest, SIGNAL(clicked()),this,SLOT(MultiThreadTest()));
}
void MainWindow::SingleThreadTest()
{
std::cout<< "Hello World1" <<std::endl;
qDebug() << "Hello World1q" ;
}
void MainWindow::MultiThreadTest()
{
QThread *workerThread= new QThread;
ThreadWorker *worker = new ThreadWorker();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
q_debugstream.h
#ifndef ThreadLogStream_H
#define ThreadLogStream_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
// MessageHandler
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
class MessageHandler : public QObject
{
Q_OBJECT
public :
MessageHandler(QTextEdit *textEdit, QObject * parent = Q_NULLPTR) : QObject(parent), m_textEdit(textEdit){}
public slots:
void catchMessage(QString msg)
{
this->m_textEdit->append(msg);
}
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
private:
QTextEdit * m_textEdit;
};
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
class ThreadLogStream : public QObject, public std::basic_streambuf<char>
{
Q_OBJECT
public:
ThreadLogStream(std::ostream &stream, QObject * parent = Q_NULLPTR) :QObject(parent), m_stream(stream)
{
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~ThreadLogStream()
{
// output anything that is left
if (!m_string.empty())
{
emit sendLogString(QString::fromStdString(m_string));
}
m_stream.rdbuf(m_old_buf);
}
protected:
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
emit sendLogString(QString::fromStdString(m_string));
m_string.erase(m_string.begin(), m_string.end());
}
else
m_string += v;
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
m_string.append(p, p + n);
long pos = 0;
while (pos != static_cast<long>(std::string::npos))
{
pos = static_cast<long>(m_string.find('\n'));
if (pos != static_cast<long>(std::string::npos))
{
std::string tmp(m_string.begin(), m_string.begin() + pos);
emit sendLogString(QString::fromStdString(tmp));
m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
}
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
std::string m_string;
signals:
void sendLogString(const QString& str);
};
#endif // ThreadLogStream_H
for long transactions, you can add:
QCoreApplication::processEvents();
in the catchMessage(QString(msg) method of the class MessageHandler in q_debugstream.h, after the append() call.
This updates textEdit 'as soon as possible'.

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

Qt - Simple example using threads controlled by push buttons

I have been trying to get this simple example using threads activated by pushbuttons to work. It is based off of the solution in the question below:
How to implement frequent start/stop of a thread (QThread)
The main differences between the example solution above and my code below are:
I used a QWidget instead of MainWindow
I changed the name of signals for clarity
My code contains debugging information
I experimented with eliminating the signals created by worker as the didn't appear to do anything
It appears that the start/stop signals are not triggering their corresponding slots, but I am not experienced enough to troubleshoot why.
Additionally, I am unsure of the purpose of the signal:
SignalToObj_mainThreadGUI()
Is that just something that could be used and is not?
I have been trying to get this code to work for some time, so any help would be greatly appreciated.
main.cpp
#include "threadtest.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest w;
w.show();
return a.exec();
}
threadtest.h
#include <QWidget>
#include <QThread>
#include "worker.h"
namespace Ui
{
class ThreadTest;
}
class ThreadTest : public QWidget
{
Q_OBJECT
public:
explicit ThreadTest(QWidget *parent = 0);
~ThreadTest();
signals:
void startWorkSignal();
void stopWorkSignal();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
private:
Ui::ThreadTest *ui;
worker *myWorker;
QThread *WorkerThread;
};
threadtest.cpp
#include "threadtest.h"
#include "ui_threadtest.h"
ThreadTest::ThreadTest(QWidget *parent) :
QWidget(parent),
ui(new Ui::ThreadTest)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
connect(this,
SIGNAL(startWorkSignal()),
myWorker,
SLOT(StartWork())
);
connect(this,
SIGNAL(stopWorkSignal()),
myWorker,
SLOT(StopWork())
);
//Debug
this->dumpObjectInfo();
myWorker->dumpObjectInfo();
}
ThreadTest::~ThreadTest()
{
delete ui;
}
void ThreadTest::on_startButton_clicked()
{
qDebug() << "startwork signal emmitted";
emit startWorkSignal();
}
void ThreadTest::on_stopButton_clicked()
{
qDebug() << "stopwork signal emmitted";
emit stopWorkSignal();
}
worker.h
#include <QObject>
#include <QDebug>
class worker : public QObject {
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
~worker();
signals:
void SignalToObj_mainThreadGUI();
//void running();
//void stopped();
public slots:
void StopWork();
void StartWork();
private slots:
void do_Work();
private:
volatile bool running, stopped;
};
worker.cpp
#include "worker.h"
worker::worker(QObject *parent) : QObject(parent), stopped(false),
running(false)
{
qDebug() << "running: " << running;
qDebug() << "stopped: " << stopped;
}
worker::~worker() {}
void worker::do_Work()
{
qDebug() << "inside do Work";
emit SignalToObj_mainThreadGUI();
if (!running || stopped) return;
// actual work here
/*
for (int i = 0; i < 100; i++)
{
qDebug() << "count: " + i;
}
*/
QMetaObject::invokeMethod(this, "do_Work", Qt::QueuedConnection);
}
void worker::StopWork()
{
qDebug() << "inside StopWork";
stopped = true;
running = false;
//emit stopped();
}
void worker::StartWork()
{
qDebug() << "inside StartWork";
stopped = false;
running = true;
//emit running();
do_Work();
}
You should write
WorkerThread->start();
Or you can use the thread of the ThreadTest object instead the WorkerThread (in this case the WorkerThread is needless):
myWorker->moveToThread(thread()); // this->thread
The slots are not triggered, because you have moved myWork to the thread WorkerThread, but didnot run an event loop in that thread. In threadtest.cpp, add
WorkerThread .start();
after
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);