How to setup QSerialPort on a separate thread?

12,337

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

Share:
12,337
anat0lius
Author by

anat0lius

Updated on June 26, 2022

Comments

  • anat0lius
    anat0lius almost 2 years

    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;
    }
    
  • nwp
    nwp over 9 years
    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 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?
  • blarf
    blarf over 8 years
    Another problem is that QSerialPort will block when open is called.
  • RabbitHole
    RabbitHole over 8 years
    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
    RabbitHole over 8 years
    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.
  • Lennart Rolland
    Lennart Rolland about 8 years
    @lpapp I just found this after posting my question(stackoverflow.com/questions/36839369/…) 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!
  • ElevenJune
    ElevenJune almost 8 years
    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.
  • Basj
    Basj almost 2 years
    Would you have an idea @lpapp for this very close question stackoverflow.com/questions/72798863/… ? for a non-GUI app, serial->write(...) seems to want a timer or a thread. Why? Can we disable this?