With this program I press the 'run' button and a for loop cycles 100 times (with a 100ms delay)
and prints the cycle count in a txt field
I have successfully done it with a MyThread object derived from QThread. It works. I can
interrupt the cycle with the 'stop' button.
However it is sternly warned that deriving an object from QThread is very bad. So I did it
the other way they suggested, the "right" way.
And it doesn't work. I can get the loop cycle numbers out on the console but not into the text box
The 'run' button goes down and never comes up again until the 100 cycles are done.
And the text field shows 99.
Here is the code, what am I doing wrong?
// MyWidget.h SF022
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtWidgets/QWidget>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include "../MyThread.h"
#include "../MyObject.h"
#include <QThread>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget* parent = 0);
~MyWidget();
QPushButton* pbRun;
QPushButton* pbStop;
QPushButton* pbExit;
QLineEdit* txtCount;
QThread* cThread;
MyObject* myObject;
public slots:
void onNumberChanged(int);
private slots:
void pbRun_Slot();
void pbStop_Slot();
void pbExit_Slot();
};
#endif /* MYWIDGET_H_ */
------------------------------------------------
// MyWidget.cpp
#include "MyWidget.h"
#include "../K.h"
#include <iostream>
MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {
pbRun = new QPushButton(this);
pbRun->setObjectName(QStringLiteral("pbRun"));
pbRun->setGeometry(QRect(20, 20, 80, 40));
pbRun->setText("Run");
connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));
pbStop = new QPushButton(this);
pbStop->setObjectName(QStringLiteral("pbStop"));
pbStop->setGeometry(QRect(20, 80, 80, 40));
pbStop->setText("Stop");
connect(pbStop, SIGNAL(clicked()), this, SLOT(pbStop_Slot()));
pbExit = new QPushButton(this);
pbExit->setObjectName(QStringLiteral("pbExit"));
pbExit->setGeometry(QRect(20, 140, 80, 40));
pbExit->setText("Exit");
connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));
txtCount = new QLineEdit(this);
txtCount->setGeometry(QRect(20, 200, 80, 40));
txtCount->setStyleSheet("QLineEdit{background: white;}");
// myObject holds the cycling mechanism
myObject = new MyObject(this);
// the myObject sends each new cycle number out here
connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
}
MyWidget::~MyWidget() {
}
void MyWidget::pbRun_Slot() {
// start thread
cThread = new QThread(this);
myObject->doSetup(*cThread);
myObject->moveToThread(cThread);
cThread->start();
}
void MyWidget::pbStop_Slot() {
// stop the thread
myObject->Stop = true;
}
void MyWidget::pbExit_Slot() {
// a static pointer to the main window
(K::SfMainWin)->close();
}
// a slot
void MyWidget::onNumberChanged(int j) {
// output the cycle count to a text field
txtCount->setText(QString::number(j));
}
----------------------------------------------------------
// MyObject.h
#ifndef MYOBJECT_H_
#define MYOBJECT_H_
#include <QObject>
#include <QThread>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject* parent = 0);
~MyObject();
void doSetup(QThread&);
bool Stop;
signals:
void numberChanged(int);
public slots:
void doWork();
};
#endif /* MYOBJECT_H_ */
----------------------------------------------------------
// MyObject.cpp
#include "MyObject.h"
#include <QMutex>
#include <iostream>
#include "string.h"
MyObject::MyObject(QObject* parent) : QObject(parent) {
Stop = false;
}
MyObject::~MyObject() {
}
void MyObject::doSetup(QThread& cThread) {
Stop = false;
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::doWork() {
for (int i = 0; i < 100; i++) {
QMutex mutex;
mutex.lock();
if (this->Stop) {
break;
}
// output into a text field
emit numberChanged(i);
// output on the console
std::cout << "running " << (QString::number(i)).toStdString() << std::endl;
mutex.unlock();
QThread::msleep(100);
}
}
myObject is never moved to the thread you created. Everything is being executed in the main thread. Because
myObject = new MyObject(this);
To move a QObject to another thread, he should not have a parent. If it does Qt will silently tell you something went wrong ( by printing on the output, same with incorrect connections). It is the framework design to not panic over this type of warnings...
It should have been
myObject = new MyObject(0);
Now that this is cleared, you have other defects in the code.
QMutex mutex; is local and will always be acquired by the same thread. which means he has no purpose. Instead it should be a private member of MyObject
MyWidget::pbStop_Slot should be a method of MyObject instead, otherwise you have a race condition when accessing Stop member. Remember the mutex above? It is the time to use it. By the way, with your implementation call directly the method, because the even loop of cThread is only executing doWork
MyObject::pbStop_Slot()
{
mutex.lock()
Stop = true;
mutex.unlock()
}
Now your program should be technically correct. But It suck that you cannot use signals and slots because your thread is blocked executing doWork. Also, can we do something about that lock. In fact, yes. The way I will go is to use a Qtimer as a heartbeat ech 100ms rather than asking the thread to sleep. But to not change much your code you can use QAbstractEventDispatcher * QAbstractEventDispatcher::instance ( QThread * thread = 0 ) directly
MyObject::pbStop_Slot() //becomes a real slot again
{
// no more mutex
Stop = true;
}
....
//this connection is changed
connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
....
void MyObject::doWork() {
for (int i = 0; i < 100; i++) {
//no mutex
if (this->Stop) {
break;
}
// output into a text field
emit numberChanged(i);
// output on the console
std::cout << "running " << (QString::number(i)).toStdString() << std::endl;
//process events, to allow stop to be processed using signals and slots
QAbstractEventDispatcher::instance(cThread)->processEvents();
QThread::msleep(100);
}
}
A warning about processEvents. As it is now, if the user presses run while dowork is being executed it will be called within itself. You have a nasty code right now. An easy way to circumvent this is to put a boolean which is checked and set at the beginning of dowork.
dowork(){
if(isdoingwork)
return;
isdoingwork = true
for(...
This is a poor man way of achieving reentrancy . You will see the word reentrant quite often in Qt documentation.
Good luck in your multithreading trip.
Very good UmNyobe!
For reference I've added the corrected code here.
// MyWidget.h
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtWidgets/QWidget>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include "../MyObject.h"
#include <QThread>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget* parent = 0);
~MyWidget();
QPushButton* pbRun;
QPushButton* pbStop;
QPushButton* pbExit;
QLineEdit* txtCount;
QThread* wThread;
MyObject* myObject;
public slots:
void onNumberChanged(int);
private slots:
void pbRun_Slot();
// void pbStop_Slot();
void pbExit_Slot();
};
#endif /* MYWIDGET_H_ */
-----------------------------------------
// MyWidget.cpp
#include "MyWidget.h"
#include "../K.h"
#include <iostream>
MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {
pbRun = new QPushButton(this);
pbRun->setObjectName(QStringLiteral("pbRun"));
pbRun->setGeometry(QRect(20, 20, 80, 40));
pbRun->setText("Run");
connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));
pbStop = new QPushButton(this);
pbStop->setObjectName(QStringLiteral("pbStop"));
pbStop->setGeometry(QRect(20, 80, 80, 40));
pbStop->setText("Stop");
pbExit = new QPushButton(this);
pbExit->setObjectName(QStringLiteral("pbExit"));
pbExit->setGeometry(QRect(20, 140, 80, 40));
pbExit->setText("Exit");
connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));
txtCount = new QLineEdit(this);
txtCount->setGeometry(QRect(20, 200, 80, 40));
txtCount->setStyleSheet("QLineEdit{background: white;}");
myObject = new MyObject(0);
connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
}
MyWidget::~MyWidget() {
delete myObject;
delete wThread;
}
void MyWidget::pbRun_Slot() {
// start QThread*, wThread in the MyWidget class
wThread = new QThread(this);
myObject->doSetup(wThread);
myObject->moveToThread(wThread);
wThread->start();
}
void MyWidget::pbExit_Slot() {
// a static pointer of the main window
(K::SfMainWin)->close();
}
void MyWidget::onNumberChanged(int j) {
txtCount->setText(QString::number(j));
}
---------------------------------------------------------
// MyObject.h
#ifndef MYOBJECT_H_
#define MYOBJECT_H_
#include <QObject>
#include <QThread>
#include <QMutex>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject* parent = 0);
~MyObject();
void doSetup(QThread*);
int hold;
bool Stop;
int inx;
int lastUsedInx;
signals:
void numberChanged(int);
public slots:
void doWork();
void pbStop_Slot();
private:
bool isdoingwork;
QThread* pThread;
QMutex mutex;
};
#endif /* MYOBJECT_H_ */
----------------------------------------------------
// MyObject.cpp SF022
#include "MyObject.h"
#include <iostream>
#include <QAbstractEventDispatcher>
MyObject::MyObject(QObject* parent) : QObject(parent) {
Stop = false;
isdoingwork = false;
inx = 0;
lastUsedInx = 0;
}
MyObject::~MyObject() {
}
void MyObject::doSetup(QThread* thread) {
pThread = thread;
Stop = false;
isdoingwork = false;
connect(pThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::pbStop_Slot() {
mutex.lock();
Stop = true;
isdoingwork = false;
mutex.unlock();
}
void MyObject::doWork() {
if(isdoingwork) {
return;
}
isdoingwork = true;
for (inx = lastUsedInx + 1; inx < 100; inx++) {
if (this->Stop) {
break;
}
// output into a text box
emit numberChanged(inx);
lastUsedInx = inx;
// process events, to allow stop to be processed using signals and slots
(QAbstractEventDispatcher::instance(pThread))->processEvents(QEventLoop::AllEvents);
QThread::msleep(800);
}
isdoingwork = false;
}
Related
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();
}
QThread documentation suggests two ways to make code run in a separate thread. If I subclass QThread and reimplement run(), then I get
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) Is the timer being started from another thread?
2) Why I don't get that warning when QBasicTimer is replaced by QTimer?
3) Why I don't get that warning when using moveToThread?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
Regarding the first (non-moveToThread) example...
A quick look at the Qt source for QBasicTimer::start shows the following...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
So it expects its second argument obj to have a thread affinity equal to the current thread.
In your Worker::run implementation, however, you have...
timer.start(1000, this);
In this context the current thread is the new thread created by the QThread instance but this refers to the QWorker instance created by the MainWidget on the main GUI thread. Hence the warning.
Edit 1:
To the question...
why it works with moveToThread()?
Consider the implementation of the MainWidget ctor...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
By the time Worker::run is called the Worker instance has been moved to the new thread. So when the line...
timer.start(1000, this);
executes, this (which refers to the Worker instance) is on the current thread and the thread affinity test in QBasicTimer::start passes without warning.
Sorry if the above is a bit convoluted but the important thing is to consider the thread affinity of the second arg to QBasicTimer::start: it must be the currently running thread.
This is the code i'm using now the issue is when i press on the pushbutton
the thread is starting and the value in line edit is updating.
but it slows down the GUI overall.
I am learning QThread so implemented this code and facing difficulties in it.
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
qDebug()<<"pd1";
work->moveToThread(thread);
connect(work, SIGNAL(finished()), work, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), work, SLOT(process()));
connect(work, SIGNAL(datar(int)), this, SLOT(display(int)));
connect(work, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
qDebug()<<"pd2";
}
void MainWindow::display(int i)
{
ui->lineEdit->setText(QString::number(i));
}
void MainWindow::on_pushButton_2_clicked()
{
qDebug()<<"In push button - 2";
for(int i = 0; i < 200; i++)
{
qDebug()<<i;
ui->lineEdit_2->setText(QString::number(i));
}
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <worker.h>
#include <QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void display(int i);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private:
Ui::MainWindow *ui;
worker* work = new worker();
QThread* thread = new QThread;
};
worker.cpp
#include "worker.h"
#include <QDebug>
worker::worker(QObject *parent) : QObject(parent)
{
}
void worker::process()
{
int index = 0;
qDebug()<<"In here";
while(true)
{
qDebug("Hello World!");
index += 1;
if(index > 10000)
{
index = 0;
}
emit datar(index);
}
emit finished();
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class worker : public QObject
{
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
signals:
void finished();
void datar(int);
public slots:
void process();
};
#endif // WORKER_H
What i wanted was to update the line edit continusoly from thread such that it doesn't affect the GUI performance.
It would be great if you identify the mistake and suggest me the changes to do.
Consider your worker::process implementation...
void worker::process()
{
int index = 0;
qDebug()<<"In here";
while(true)
{
qDebug("Hello World!");
index += 1;
if(index > 10000)
{
index = 0;
}
emit datar(index);
}
emit finished();
}
It emits the datar signal continuously and without any intervening delays. But the signal emitter and receiver are on different threads meaning the signal will be delivered to the receiver via its event queue. So you are basically saturating the GUI thread's event loop with events from the datar signal.
Try putting even a slight delay between signals with something like...
QThread::msleep(10);
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);
I want to modify a Qlist by reference in a thread because the sorting could take a few seconds.
But it seems like that I can't modify the List. Outside of the thread the List has not beend changed.
In QMainwindow:
QThread *thread = new QThread();
FarmSortWorker *farmsort_worker = new FarmSortWorker();
farmsort_worker->setFarmData(farm_list);
farmsort_worker->moveToThread(thread);
connect(farmsort_worker, &FarmSortWorker::error, [=](QString error_msg){
qDebug() << error_msg;
logger->logEvent(error_msg, Logger::ERROR);
});
connect(thread, &QThread::started, farmsort_worker, &FarmSortWorker::processSort);
connect(farmsort_worker, &FarmSortWorker::finished, thread, &QThread::quit);
connect(farmsort_worker, &FarmSortWorker::finished, farmsort_worker, &QThread::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
My Thread (.h)
#ifndef FARMSORTWORKER_H
#define FARMSORTWORKER_H
#include <QObject>
#include "../framcontainer.h"
#include <QList>
#include <qDebug>
#include <QString>
class FarmSortWorker : public QObject
{
Q_OBJECT
public:
explicit FarmSortWorker(QObject *parent = 0);
~FarmSortWorker();
void setFarmData(QList<FramContainer> &farm_container);
private:
QList<FramContainer> farm_container;
signals:
void error(QString error);
void finished();
public slots:
void processSort();
};
#endif // FARMSORTWORKER_H
My Thread (.cpp)
#include "farmsortworker.h"
FarmSortWorker::FarmSortWorker(QObject *parent) :
QObject(parent)
{
}
FarmSortWorker::~FarmSortWorker()
{
}
void FarmSortWorker::setFarmData(QList<FramContainer> &farm_container)
{
this->farm_container = farm_container;
}
void FarmSortWorker::processSort()
{
qDebug() << "count:" << farm_container.size();
for(int i = 0; i < farm_container.size(); i++)
{
FramContainer park = farm_container.at(i);
qDebug() << "original:" << park.getFarmName();
}
for(int i = 0; i < farm_container.size(); i++)
{
FramContainer *park =& farm_container[i];
park->setFarmName("test");
}
for(int i = 0; i < farm_container.size(); i++)
{
FramContainer park = farm_container.at(i);
qDebug() << "neu:" << park.getFarmName();
}
emit finished();
}
Thank you all in advance.
It's because you save a copy of the list in your thread class, not a reference.
Instead of having a separate function to set the list, pass it (by reference) to the constructor, and have it set the reference variable:
class FarmSortWorker : public QObject
{
Q_OBJECT
public:
explicit FarmSortWorker(QList<FramContainer>& list, QObject *parent = 0);
...
private:
QList<FramContainer>& farm_container; // Store as a reference
...
};
...
FarmSortWorker::FarmSortWorker(QList<FramContainer>& list, QObject *parent) :
QObject(parent), farm_container(list)
{
}
...
QThread *thread = new QThread();
FarmSortWorker *farmsort_worker = new FarmSortWorker(farm_list);
...