Not getting past emit command.
-
I have a 3rd party library that im using to run tests on external hardware.
Testing takes 10sec, and during this time the window freezes.
I Have done some reading and moved the 3rd party code to a separate thread to try and prevent the window freeze.
I send a signal from the main thread to the 3rd party code running in a separate thread to start testing.
While the code is executing in the separate thread my main thread should continue to process input events in a while loop, like a Pause button being clicked. Events must be checked until the 3rd party code emits a signal to the main thread telling it that the results are available. Problem is my MainWindow is still freezing and the While loop is only executed when the 3rd party code has finished and results are available. Why is the code not passing the 1st emit statement and continuing with the while loop?@#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QEventLoop>
#include <iostream>#include "mythreadclass.h"
namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();private slots:
void on_StartButton_clicked();
void on_PauseButton_clicked();
void ReceiveResults(int NewValue);private:
Ui::MainWindow *ui;
MyThreadClass *MyThreadObj;
QEventLoop *EventLoopObj;
int Result;signals:
void StartTest();};
#endif // MAINWINDOW_H
@@#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);EventLoopObj = new QEventLoop(parent); MyThreadObj = new MyThreadClass(); connect(this, SIGNAL(StartTest()), MyThreadObj, SLOT(DoTest())); connect(MyThreadObj, SIGNAL(SendResult(int)), this, SLOT(ReceiveResults(int)));
}
MainWindow::~MainWindow()
{
delete ui;
MyThreadObj->deleteLater();
EventLoopObj->deleteLater();
}void MainWindow::on_StartButton_clicked()
{
MyThreadObj->start();
Result = 0;emit StartTest(); //Problem - code execution stopes here until 3rd party command has finished executing. qDebug("Emit Done"); while(Result == 0) { EventLoopObj->processEvents(); } ui->label->setText("Test Done"); MyThreadObj->quit();
}
void MainWindow::on_PauseButton_clicked()
{
ui->Pause_Label->setText("Paused");
}void MainWindow::ReceiveResults(int NewValue)
{
Result = NewValue;
}
@@#ifndef MYTHREADCLASS_H
#define MYTHREADCLASS_H#include <QObject>
#include <QThread>
#include "waitclass.h" //Simulates 3rd party libraryclass MyThreadClass : public QThread
{
Q_OBJECT
private:
WaitClass *WaitObj; //Simulates 3rd party varable
public:
explicit MyThreadClass(QObject *parent = 0);
~MyThreadClass();void run();
signals:
void SendResult(int NewValue);public slots:
void DoTest();};
#endif // MYTHREADCLASS_H
@@#include "mythreadclass.h"
MyThreadClass::MyThreadClass(QObject *parent)
{
WaitObj = new WaitClass();
}MyThreadClass::~MyThreadClass()
{
WaitObj.deleteLater();
}void MyThreadClass::run()
{
exec();
}void MyThreadClass::DoTest()
{
WaitObj->WaitSec(10); //Simulates 3rd party command that takes 10sec to finish
int SimulatedResult = 255; //Simulates value returned by 3rd party commandemit SendResult(SimulatedResult);
}
@ -
You clearly have not done enough reading on the matter.
In this scenario, you should not subclass QThread. Instead, use a worker object and move that to a thread managed by a (vanilla) QThread.
The root of your problem is that you have created a slot on the manager of your thread (the QThread object), but that manager itself lives in the thread that created it in the first place.
There is lots of information to be found on this issue, including in the QThread documentation itself.
-
My Original approach was like you stated to move the worker object to a vanilla QThread. But then I ended up having other issues and compiling errors. People told me that the errors where caused because the object was not created in the 2nd thread where the worker object was moved to. I Will try it again and let you know what happens.
-
Ok. The above code was just an example of the issue I was facing in another bigger program. I Rewrote the code back to the way It was, where I try moving the objects to a vanilla QThread. I Get the same two errors as before.
On first run I usually get:
@QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0xb1fc20), parent's thread is QThread(0x15819a70), current thread is QThread(0xb30d50)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0xb1fc38), parent's thread is QThread(0x15819a70), current thread is QThread(0xb30d50)@on second run I sometimes get this error:
@Invalid parameter passed to C runtime function.
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 16e80d50. Receiver 'MainWindowWindow' (of type 'QWidgetWindow') was created in thread 154b9a70", file kernel\qcoreapplication.cpp, line 521
Invalid parameter passed to C runtime function.
QObject::killTimers: timers cannot be stopped from another thread
X:\MTEK\Programming\Contact Noise GUI\43\build-Contact_Noise-Desktop_Qt_5_3_MinGW_32bit-Debug\debug\Contact_Noise.exe exited with code 3@I Will post some of my code in the next couple of minutes.
-
I have omitted code that I think might be unnecessary.
@class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
bool TestActive;
bool ProcessEvents;
QEventLoop *EventLoopObj;QThread *TestThreadObj; TestClass *TestObj; RelayClass *RelayObj; ScopeClass *ScopeObj; MotorClass *MotorObj; explicit MainWindow(QWidget *parent = 0); ~MainWindow(); void Initialize(); void Uninitialize();
public slots:
void on_StartBtn_clicked();void SLOT_StopEventProcessing();
signals:
void SIGNAL_StartTest();};@
@MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
EventLoopObj = new QEventLoop(parent);TestThreadObj = new QThread(); TestObj = new TestClass(); RelayObj = new RelayClass(); ScopeObj = new ScopeClass(); MotorObj = new MotorClass();
}
MainWindow::~MainWindow()
{
EventLoopObj->deleteLater();MotorObj->deleteLater(); ScopeObj->deleteLater(); RelayObj->deleteLater(); TestThreadObj->deleteLater(); TestObj->deleteLater();
}
void MainWindow::Initialize()
{
//START THREAD OBJs
TestThreadObj->start();//MOVE OBJECTS TO THREADS TestObj->moveToThread(TestThreadObj); ScopeObj->moveToThread(TestThreadObj); MotorObj->moveToThread(TestThreadObj); RelayObj->moveToThread(TestThreadObj); //SET TEST OBJ TestObj->Initialize(); //SET RELAY OBJ RelayObj->setPort(ui->Relay_Com->text()); RelayObj->Initialize(); //SET SCOPE OBJ ScopeObj->Initialize(); //SET MOTOR OBJ MotorObj->SetPort(ui->Motor_Com->text()); //Merge with Initialize MotorObj->Initialize(); //CONNECT UI OBJ connect(this, SIGNAL(SIGNAL_StartTest()), TestObj,SLOT(StartTest())); connect(TestObj, SIGNAL(TestingDone()), this, SLOT(SLOT_StopEventProcessing())); //CONNECT RELAY OBJ connect(TestObj, SIGNAL(SIGNAL_AllRelaysOff()), RelayObj, SLOT(RelaysAllOff())); connect(TestObj, SIGNAL(SIGNAL_RelayOn(int)), RelayObj, SLOT(RelayOn(int))); //CONNECT SCOPE OBJ connect(TestObj, SIGNAL(SIGNAL_SCOPE_Initialize()), ScopeObj, SLOT(Initialize())); connect(TestObj, SIGNAL(SIGNAL_SCOPE_MeasureVavg()), ScopeObj, SLOT(TestVavg())); connect(TestObj, SIGNAL(SIGNAL_SCOPE_MeasureVpp()), ScopeObj, SLOT(TestVpp())); connect(TestObj, SIGNAL(SIGNAL_SCOPE_MeasureVrms()), ScopeObj, SLOT(TestVrms())); //CONNECT MOTOR OBJECT connect(TestObj, SIGNAL(SIGNAL_RunMotor()), MotorObj, SLOT(RunMotor())); TestActive = true; ProcessEvents =true;
}
void MainWindow::Uninitialize()
{
MotorObj->Uninitialize();ScopeObj->Uninitialize(); RelayObj->Uninitialize(); TestObj->Uninitialize(); TestThreadObj->quit();
}
void MainWindow::on_StartBtn_clicked()
{
Initialize();
emit SIGNAL_StartTest();
while(ProcessEvents == true)
{
EventLoopObj->processEvents();
}
Uninitialize();
}void MainWindow::SLOT_StopEventProcessing()
{
ProcessEvents = false;
}
@Inside the MotorObj and RelayObj there are QSerialPort variables.
QSerialPort* ComPort = new (QSerialPort);
From what I have read and understand this is where things go wrong.
The QSerialPorts Object seems to be running in the wrong thread.
I Have also tried to move the ComPort Objects contained in both MotorObj and RelayObj to the TestThread. When I do so The application Freezes when I click on the Start Button @MotorObj->ComPort->moveToThread(TestThreadObj);@ -
I have not studied the complete code sample yet. But does it help if you just create your QSerialPort object with the parent in the first place? QObject::moveToThread also moves all child objects, so that should work. If moving QSerialPort keeps given trouble, I suggest you only create it after the move from within the right thread.
-
Looks like there was noting wrong with my initial approch by moving the objects to a Vanilla QThread. I Have managed to traced down the problem a bit more. I Think the warnings are a bit misleading. Below is the code for the RelayObj. If I comment out the "relays->write()" lines the errors disappear. Also If I create other objects in a similar way in the RelayClass like I do with QSerialPort I don't get any errors at all. Meaning it must be something to do with QSerialPort and how it behaves in a Threaded environment.
-
@#include "relayclass.h"
RelayClass::RelayClass(QObject *parent) : QObject(parent)
{
relays = new (QSerialPort);
}RelayClass::~RelayClass()
{
relays->deleteLater();
}void RelayClass::Initialize()
{
relays->setPortName(COM);if(relays->open(QIODevice::ReadWrite)) { relays->setBaudRate(QSerialPort::Baud9600); relays->setDataBits(QSerialPort::Data8); relays->setParity(QSerialPort::NoParity); relays->setStopBits(QSerialPort::OneStop); relays->setFlowControl(QSerialPort::NoFlowControl); }
}
void RelayClass::Uninitialize()
{
//relays->close();
}void RelayClass::RelaysAllOff()
{
relay_arr.clear();
relay_arr = "all_offx";
//relays->write(relay_arr); //Causes Error
relays->waitForBytesWritten(-1);
}void RelayClass::RelayOn(int Ch_on)
{
relay_arr.clear();
relay_arr.setNum(Ch_on);
relay_arr.prepend("ch");
relay_arr.append("_onx");
//relays->write(relay_arr); //Causes Error
relays->waitForBytesWritten(-1);
}void RelayClass::setPort(QString PORT)
{
COM = PORT;
}
@ -
@#ifndef RELAYCLASS_H
#define RELAYCLASS_H#include <QObject>
#include <QtSerialPort>
#include <QString>#include "waitclass.h"
class RelayClass : public QObject
{
Q_OBJECT
public:
QString COM;
QByteArray relay_arr;
QSerialPort *relays;WaitClass *WaitObj; explicit RelayClass(QObject *parent=0); ~RelayClass(); void Initialize(); void Uninitialize();
public slots:
void RelaysAllOff();
void RelayOn(int Ch_on);
void setPort(QString PORT);
};#endif // RELAYCLASS_H
@ -
Looks like this article may have the answer to my problems.
"LINK":http://stackoverflow.com/questions/23559610/how-to-setup-qserialport-on-a-separate-thread -
Ok. Problem solved.
I Moved the QSerialPort variables to my main thread and now use Signals and Slots to send data to them. I'm not an expert on this, and just quoting what I have read. QSerialPort already provides you non-blocking mechanism for your GUI application. When moving QSeerial Port to another thread this somehow interferes with the way objects communicate between one another and that is where the errors are coming from. If anyone has some extra opinions on this or knows how to move a QSerialPort object into a separate thread, please do share.