Solved 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; }
-
Hi
You seems to have killed by scope issuevoid 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 issueHi,
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?
-
@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 thatand how i can do it??
thank you!! -
@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).
-
@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
andreadyRead
. See for example here: https://doc.qt.io/qt-5/qtserialport-terminal-example.htmlRegards
-
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
secondHere 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.htmlyour QTimer is going to create a new serialport class and thread each second
you should change thatand 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/newSo 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 secondport = 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!
-