6

Following the official documentation I'm trying to do this:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    QThread *thread = new QThread;    
    Worker *worker= new Worker();

    worker->moveToThread(thread);

    //init connections

    thread->start();
}

Worker constructor:

Worker::Worker(QObject *parent) :
    QObject(parent)
{
    serial = new QSerialPort(this);  //passing the parent, which should be the current thread      
}

No compiling errors but when I execute it throws me this:

QObject: Cannot create children for a parent that is in a different thread. 
(Parent is QSerialPort(0x11bd1148), parent's thread is QThread(0x11bd2ef8), current thread is QThread(0x3e47b8)

Namely, it's telling me that serial has as a parent the main thread and not the thread that I have created.

The same result if I don't instantiate serial in the constructor but in the main process, which is triggered after we've called thread->start():

Worker::Worker(QObject *parent) :
    QObject(parent)
{             
}

Worker::doWork()
{
    if(!serial)
        serial= new QSerialPort(this); 

    //...
}

What am I missing?


Send function as an example (a slot):

void Worker::send(const QByteArray &data)
{
    serial->write(data);
    if( serial->waitForBytesWritten(TIMEOUT) )
        qDebug() << "sent: " << data;
}
anat0lius
  • 2,145
  • 6
  • 33
  • 60
  • I thought it should not be necessary. Is QSerialPort class [1]: http://qt-project.org/doc/qt-5/qserialport.html#QSerialPort – anat0lius May 09 '14 at 09:12
  • Yes. Until foo is not created,the error doesn't throws. The program seems to work fine, but that error is neither a good thing. – anat0lius May 09 '14 at 09:46
  • I tested with other type of objects and it seems that there are no problems, so the issue is exclusively of QSerialPort :( – anat0lius May 09 '14 at 11:36
  • Remove those and see if you still get the same message. – thuga May 09 '14 at 11:55
  • Then no, if there is no interaction, no error. – anat0lius May 09 '14 at 12:04
  • Why would you use QtSerialPort in a separate thread? You are aware of the signal/slot mechanism? – László Papp May 09 '14 at 12:28
  • 2
    @LaszloPapp Because I'm running a GUI application, and there is a task on which I must send a file (line by line) through the serial port. If I do that task in the main thread, the GUI freezes. – anat0lius May 09 '14 at 12:34
  • Read [this thread](http://qt-project.org/forums/viewthread/21369). It might help. – Arpegius May 09 '14 at 19:17
  • The error indicates, that you call methods of objects from wrong threads. You don't show such calls, but there are two possible reasons. Either you do direct calls (replace with `invokeMethod` or preferably change design so you don't need direct calls), or you create signal-slot connections where explicitly specify direct connection type (don't specify connection type, use the default). – hyde May 10 '14 at 07:10
  • @LaszloPapp Yes, I guess... I should rewrite all. Sry for the dealy, have been very busy. – anat0lius May 14 '14 at 08:06
  • Hi, I have the same issue (with the freezing UI). It can be reproduced by connecting to device connected to the serial port (USB for ex.) that isn't responding (or it's realetes to serial port driver). So, in this case, the UI freezes (on the QSerialPort::open() or close() methods) until you disconnected the serial port device and after that UI Immediately recovers. Did you find solution for this issue? My environment is: Host: Windows 10, Qt 5.5.12 MCVS19; Device: STM32 based device connected to the USB port; Sorry for the long comment) – svadym Jan 15 '22 at 11:10

1 Answers1

7

In short, it is a bad idea to use the QtSerialPort module like this.

We designed this module based on QIODevice which already provides you non-blocking mechanism for your GUI application to use the QSerialPort class.

You should look into the following signals:

void QIODevice::bytesWritten(qint64 bytes) [signal]

This signal is emitted every time a payload of data has been written to the device. The bytes argument is set to the number of bytes that were written in this payload. bytesWritten() is not emitted recursively; if you reenter the event loop or call waitForBytesWritten() inside a slot connected to the bytesWritten() signal, the signal will not be reemitted (although waitForBytesWritten() may still return true).

and...

void QIODevice::readyRead() [signal]

This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.

readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there's data still to be read in your buffers). Do not emit readyRead() in other conditions.

I wrote two examples for doing this through the command line which you can find in here:

Command Line Writer Async Example

Command Line Reader Sync Example

Community
  • 1
  • 1
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • 5
    I need to read data from a serial port and give it a time stamp, which needs to be somewhat accurate. Since [QT's message loop freezes for example when you hold the left mouse button on the title bar](http://www.qtcentre.org/threads/51858) it is impossible to get accurate time stamps using QT's message loop, hence a thread is required. Is there any way to get reliable time stamps with QtSerialPort besides creating a separate process and then piping the data and time stamps to the main process? – nwp Jan 12 '15 at 12:48
  • 1
    Another problem is that QSerialPort will block when open is called. – blarf Sep 03 '15 at 03:35
  • Is there anyway to call open() asynchronized way? When I unplug and plug USB serial port, it stuck on open() call for 3~4 seconds even if it's called by queued SIGNAL/SLOT way. If using another thread, it complains "event notifiers cannot be enabled from another thread". Is it a bug? – RabbitHole Sep 03 '15 at 06:54
  • 3
    Found solution for my comment about blocking open() call. To prevent open() call from freezing GUI, QSerialPort should be created(!) and opened in a non-gui thread. That means should not create QSerialPort in a constructor of Worker class. And don't pass 'this'(of worker) to QSerialPort's constructor. – RabbitHole Sep 04 '15 at 02:11
  • @lpapp I just found this after posting my question(http://stackoverflow.com/questions/36839369/how-to-know-when-qserialport-is-ready-for-more-data-in-qt5) that I feel is not answered well by the async write example. Since the example just sends some data then quits, it is hard to see how to handle "congestion" over long-haul transmissions. Thanks! – Mr. Developerdude Apr 25 '16 at 11:53
  • 1
    Does someone has more info on that ? I tried to use QSerialPort without putting it in a different thread and it blocked the GUI. I would like to understand how to use it correctly. – ElevenJune Jul 07 '16 at 14:08
  • Would you have an idea @lpapp for this very close question https://stackoverflow.com/questions/72798863/qserialport-with-no-gui-no-thread-qobjectstarttimer-timers-can-only-be-used ? for a non-GUI app, `serial->write(...)` seems to want a timer or a thread. Why? Can we disable this? – Basj Jun 30 '22 at 08:07