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_OBJECT

    public:
    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 library

    class 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 command

    emit 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_OBJECT

    public:
    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.



  • Ok. I Will get back to you. Thanks for your help.



  • 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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.