Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Usage of QSerialport in a QThread (Cannot create children for a parent that is in a different thread)



  • Hello,

    I am trying to access QSerialPort from another thread.
    Sadly even with a queued connection this is not working and I have no clue why.

    In the following you find a minimal working example which fails if a real port is opened.

    I get the following error:
    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QSerialPort(0x2ccdc763b70), parent's thread is QThread(0x2ccd9beac80), current thread is QThread(0xfcff2ff690)

    Thank you very much :-)

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

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QThread>
    
    class Drive;
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
        Drive *port;
    
    signals:
        void signalGetInfo();
    
    public slots:
        void process();
    
    };
    
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "serialCon.h"
    #include <QTimer>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        QTimer *timer = new QTimer(this);
            connect(timer, SIGNAL(timeout()), this, SLOT(process()));
            timer->start(1000);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::process()
    {
        port = new Drive;
        QThread t2;
        port->moveToThread(&t2);
        t2.start();
    
        connect(this, &MainWindow::signalGetInfo, port, &Drive::connect_device, Qt::QueuedConnection);
        emit signalGetInfo();
    }
    
    

    serialCon.h

    #ifndef SERIALCONNECTION_H
    #define SERIALCONNECTION_H
    
    #include <QSerialPort>
    
    class Drive : public QObject
    {    
        Q_OBJECT
    
    public:
        Drive();
    
    private:
        QSerialPort *device;
    
    public slots:
        bool connect_device();
    };
    
    #endif // SERIALCONNECTION_H
    
    

    serialCon.cpp

    #include "serialCon.h"
    
    Drive::Drive()
    {
        device = new QSerialPort;
    }
    
    bool Drive::connect_device()
    {
            device->setPortName("name");
            device->open(QSerialPort::ReadWrite);
            device->setBaudRate(QSerialPort::Baud115200);
            device->setDataBits(QSerialPort::Data8);
            device->setParity(QSerialPort::NoParity);
            device->setStopBits(QSerialPort::OneStop);
            device->setFlowControl(QSerialPort::NoFlowControl);
        return true;
    }
    
    

  • Lifetime Qt Champion

    Hi
    You seems to have killed by scope issue

    void MainWindow::process()
    {
        port = new Drive;
        QThread t2; <<<<< local variable. will die at last }
        port->moveToThread(&t2);
        t2.start();
    
        connect(this, &MainWindow::signalGetInfo, port, &Drive::connect_device, Qt::QueuedConnection);
        emit signalGetInfo();
    } // t2 is gone here
    


  • @mrjj said in Usage of QSerialport in a QThread (Cannot create children for a parent that is in a different thread):

    Hi
    You seems to have killed by scope issue

    Hi,
    thanks for pointing out this issue!
    I now added the thread as a member variable and changed the timer to single shot.

    The issue is still there.
    The following line leads to the error:

    device->open(QSerialPort::ReadWrite);
    

    The error only occurs if an existing serial port is opened.
    If I put a random string as port name no error is showing up.

    Any ideas?


  • Moderators

    @robro
    Couple of things,

    first give your Serialport a parent, so it get moved also to the thread

    new QSerialPort(this);

    second
    your QTimer is going to create a new serialport class and thread each second
    you should change that.



  • The timer issue I already noticed and changed.

    Thank you all very much!
    Giving it a parent solved it!

    I have to say, you saved my day :-)



  • @J-Hilk
    i have the same problem and i just try to use Qt, so i don't so much
    can you explain more about:

    first give your Serialport a parent, so it get moved also to the thread
    second
    your QTimer is going to create a new serialport class and thread each second
    you should change that

    and how i can do it??
    thank you!!


  • Lifetime Qt Champion

    @Thanh-Tung why do you need a thread at all?

    just use QSerialPort with signals&slots - and without a thread.

    Regards



  • @aha_1980 i need thread because i have a button1 that will run a while loop to communicate with device through serial port, and button2 that will run a another thread( this thread also use serial port, so a need kill the thread of button 1).


  • Lifetime Qt Champion

    @Thanh-Tung said in Usage of QSerialport in a QThread (Cannot create children for a parent that is in a different thread):

    i have a button1 that will run a while loop to communicate with device through serial port, and button2 that will run a another thread( this thread also use serial port, so a need kill the thread of button 1).

    That sounds like a horrible design - you should overthink it.

    As said, you don't need threads, as QSerialPort is asynchronous. Just use the signals bytesWritten and readyRead. See for example here: https://doc.qt.io/qt-5/qtserialport-terminal-example.html

    Regards


  • Moderators

    hello @Thanh-Tung and welcome.

    @Thanh-Tung said in Usage of QSerialport in a QThread (Cannot create children for a parent that is in a different thread):

    @J-Hilk
    i have the same problem and i just try to use Qt, so i don't so much
    can you explain more about:

    first give your Serialport a parent, so it get moved also to the thread
    second

    Here is the thing, if you don't know what the parent child relationship in Qt is, you should not attempt using multiple threads. IMHO.

    Take a look at the documentation:
    https://doc.qt.io/qt-5/qtcore-index.html
    and especially
    https://doc.qt.io/qt-5/objecttrees.html

    your QTimer is going to create a new serialport class and thread each second
    you should change that

    and how i can do it??
    thank you!!

    You need to freshen up your C++ knowledge! Are you new to programming in general ?

    new is a key word in almost all programming languages - that I use/know of at least - that indicates a the. creation of a new object.
    https://en.cppreference.com/w/cpp/language/new

    So when you new an object (QSerialPort in this case) the object is created and the pointer is stored in a variable, in this example.
    BUT just because you assign a new object to the old variable does not mean that the old object is destroyed!!! In fact it's still there, taking up memory.
    Or worse, in your case where you have infinite while loops running, its not only taking up memory but cpu time as well, keeping the os busy with thread swapping and consumes electricity. DON'T do it!

    c++ does not have fancy garbage collectors like python/JS etc. YOU have to manage your own garbage!

    To get back to your question:
    @robro wrote:

    QTimer *timer = new QTimer(this);
            connect(timer, SIGNAL(timeout()), this, SLOT(process()));
            timer->start(1000);
    
    ....
    void MainWindow::process()
    {
        port = new Drive;
        QThread t2;
        port->moveToThread(&t2);
        t2.start();
    
        connect(this, &MainWindow::signalGetInfo, port, &Drive::connect_device, Qt::QueuedConnection);
        emit signalGetInfo();
    }
    

    timer is QTimer object that is created on start up of the class and will call every 1000ms the slot process -> each second port = new Drive; is executed -> each second a new Drive instance is created and the old one not deleted.

    If you're not running a microcontroller, but have an over arching operating system than there is no reason to not use the asynchronous api of QSerialPort.

    I still have hope that the synchronous api will be removed in Qt6. @aha_1980 please make it happen 😉🙏



  • @J-Hilk said in Usage of QSerialport in a QThread (Cannot create children for a parent that is in a different thread):

    If you're not running a microcontroller, but have an over arching operating system than there is no reason to not use the asynchronous api of QSerialPort.

    And if you are running onto a microcontroller and you are not bitbanging, you'd better implement an asynchronous interface to the serial port anyway ;-)

    I still have hope that the synchronous api will be removed in Qt6. @aha_1980 please make it happen 😉🙏

    Another vote!



  • @aha_1980
    Thanks for your advice, i may overthink about it, and now i use the Qtimer instead Qthread.
    @J-Hilk
    i usually use C and this is the first project i use C++ and Qt :)))
    thanks you so much for your help and all your recommendation!!!


Log in to reply