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

SerialPortReader output in a text file



  • Hi there,

    I'm a newbie on Qt and I'm trying to read a serial port and write the datas into a text file. I started from Qt's example "creaderasync" and now I'm blocked when it comes to associate my opened file with the QTextStream m_standardOuput element of the SerialPortReader class.
    I'm using QtCreator 5.15.2 on Windows 10.

    Here is the modified serialportreader.h :

    #ifndef SERIALPORTREADER_H
    #define SERIALPORTREADER_H
    
    #include <QObject>
    #include <QByteArray>
    #include <QSerialPort>
    #include <QTextStream>
    #include <QTimer>
    #include <QFile>
    
    QT_BEGIN_NAMESPACE
    
    QT_END_NAMESPACE
    
    void serial_start(QSerialPort* serialPort,QObject* parent = nullptr);
    
    class SerialPortReader : public QObject
    {
        Q_OBJECT
    
    public:
        explicit SerialPortReader();
        SerialPortReader(QSerialPort *serialPort, QObject* parent = nullptr);
    
    public slots:
        void handleReadyRead();
        void handleTimeout();
        void handleError(QSerialPort::SerialPortError error);
    
    private:
        QSerialPort *m_serialPort = nullptr;
        QByteArray m_readData;
        QFile file;
        QTextStream m_standardOutput;
        QTimer m_timer;
    };
    
    #endif // SERIALPORTREADER_H
    

    and here is the modified serialportreader.cpp :

    #include "serialportreader.h"
    #include <QCoreApplication>
    #include <QDebug>
    #include <QFile>
    #include <QObject>
    #include <QByteArray>
    #include <QSerialPort>
    #include <QTextStream>
    #include <QTimer>
    
    SerialPortReader::SerialPortReader(QSerialPort* serialPort,QObject* parent)
        : QObject(parent), m_serialPort(serialPort), file("file.txt"), m_standardOutput(&file) //construction of FILE and linking with m_standardOutput using QTextStream's  constructor
    
    {
        if(!file.open(QIODevice::WriteOnly | QIODevice::Append)){ //test file opening
            qDebug() << "can't open file.txt \n";
        }
    
        qDebug() << file.fileName() << "opened";
        m_standardOutput << "First test out \n"; //first try writing into the file
    
         //usual SerialPortReader connections
        connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortReader::handleReadyRead);
        connect(&m_timer, &QTimer::timeout, this, &SerialPortReader::handleTimeout);
        connect(m_serialPort, &QSerialPort::errorOccurred, this, &SerialPortReader::handleError);
    
        qDebug() << "connection done\n";
        qDebug() << "timer set\n";
        m_timer.start(5000);
    }
    
    void SerialPortReader::handleReadyRead()
    {
        //qDebug () << "handleReadyRead \n";
        m_standardOutput << "second test out" << "\n"; //second try writing into the file , meant to write serialport's content in the futur
    
    // datas receiving verification
        m_readData = m_serialPort->readAll();
        qDebug () << m_readData;              
        //m_serialPort->flush();
        m_timer.start(5000);
    }
    
    void SerialPortReader::handleTimeout()
    {
    /* Original example's definition...*/
        QCoreApplication::quit();
    }
    
    void SerialPortReader::handleError(QSerialPort::SerialPortError serialPortError)
    {
    /* Original example's definition...*/
            QCoreApplication::exit(1);
        }
    }
    
    

    The buil step is ok. It runs well since I can see my serial port's datas on my qDebug() terminal. The file file.txt is created in my build folder but it contains nothing...
    After some research, I thought it could be because I construct m_standardOutput(&file) before calling file.open(QIODevice...) . But I can't find a solution to do it the opposite way.

    I hope I didn't forget any important detail.

    Thank you in advance for your support !


  • Lifetime Qt Champion

    @Denta1000 There is an example in the documentation (https://doc.qt.io/qt-5/qtextstream.html):

    QFile data("output.txt");
    if (data.open(QFile::WriteOnly | QFile::Truncate)) {
        QTextStream out(&data);
        out << "Result: " << qSetFieldWidth(10) << left << 3.14 << 2.7;
        // writes "Result: 3.14      2.7       "
    }
    

    So, in constructor open the file (make the QFile instance class member) and pass the pointer to it as parameter to QTextStream.


  • Moderators

    @jsulm you can see, the OP actually does that, in the initializer list

    @Denta1000 QTesxtStream has a setDevice() function, use that to set the device after you open it, see if that helps 🤷‍♂️


  • Lifetime Qt Champion

    @J-Hilk Ah, you're right, need more coffee :-)


  • Moderators

    @jsulm said in SerialPortReader output in a text file:

    @J-Hilk Ah, you're right, need more coffee :-)

    😜

    I'm out of coffee, thank god, today is a short work day!



  • @jsulm @J-Hilk thanks for your help,

    I tried using setdevice() but it changed nothing.
    So far, I constructed a second QTextStream linked to the same file, before file.open() to verify my hypothesis :
    *

    QTextStream out(&file); //constructed before file opening
    
    if(!file.open(QIODevice::WriteOnly | QIODevice::Append)){ //test file opening
        qDebug() << "can't open fichier.txt \n";
    }
    
    
    qDebug() << file.fileName() << "opened"; //test opening success
    
    qDebug() << "out associated Device : " << out.device(); //First QTextStream infos
    qDebug() << "out status : " << out.status();
    out << "First test";
    
    m_standardOutput.setDevice(out.device()); // setdevice() after file opening
    qDebug() << "m_standard associated device : " << m_standardOutput.device();
    qDebug ()<< "m_standard status : " << m_standardOutput.status();
    m_standardOutput << "second test \n";*
    

    Here QTextStream out is constructed before file.open() , and yet it does write into file.
    Whereas, m_standardOutput.setdevice() is used after the file opening and m_standarOutput still doesn't write into the file.

    qDebug output shows that the two QTextStreams are similar :

    f6fb079c-d7d3-4da9-b05b-035e7b510d5b-image.png

    According to QTextStream:: , status 0 means "The text stream is operating normally."



  • I tried setting m_standardOutput as a QTextStream pointer :

    QTextStream out(&file);
        m_standardOutput = &out;
    

    and then I call :

    *m_StandardOutput << "Second test";
    

    It does write into the file when used in the SerialPortReader constructor.
    So I called "*m_standardOutput <<" in the SerialPortReader::handleReadyRead().

    void SerialPortReader::handleReadyRead()
    {
    
        //qDebug() << "device inside handleReadyRead : " << m_standardOutput->device();
        *m_standardOutput << "deuxieme test out \n" ; //second try writing into the file , meant to write serialport content in the futur
    
        m_readData = m_serialPort->readAll(); // datas receiving verification
        qDebug () << m_readData;
        //m_serialPort->flush();
        m_timer.start(5000);
    }
    

    But as soon as the program encounters a command involving m_standardOutput inside the HandleReadyRead() part, it blocks...


  • Lifetime Qt Champion

    Hi,

    @Denta1000 said in SerialPortReader output in a text file:

    QTextStream out(&file);
    m_standardOutput = &out;

    You realize that you are taking the address of a local and thus temporary object that will be destroyed at the end of the method it was created in ?



  • Hi, @SGaist
    Do you mean that QTextStream out is destroyed when the SerialPortReader constructor is done and so the link to write into the file ?


  • Lifetime Qt Champion

    Yes it is destroyed since it's variable that is local to the constructor.



  • Well , in fact it sounds logical...
    Then I should juste have to construct the link beetween m_standardOutput and file since they are both part of the SerialPortReader object
    But m_standardOutput doesn't write anything in the file, even during the constructor part.

    SerialPortReader::SerialPortReader(QSerialPort* serialPort,QObject* parent)
        : QObject(parent), m_serialPort(serialPort), file("file.txt"),m_standardOutput(&file) //construction of FILE and linking with QTextStream m_standardOutput
    {
    
        if(!file.open(QIODevice::WriteOnly | QIODevice::Append)){ //test file opening
            qDebug() << "can't open fichier.txt \n";
        }
        //m_standardOutput.setDevice(&file);
        qDebug() << file.fileName() << "opened";
        qDebug() << "file permissions :" << file.permissions() << "\n";
    
        m_standardOutput << "test 2";
    
        qDebug() << "m_standard associated device : " << m_standardOutput.device();
        qDebug ()<< "m_standard status : " << m_standardOutput.status();
    

    This part of the code should be sufficient to write the "test 2" text in the file but it doesn't, even though qDebug shows that the link is correct...

    29e2b616-10a5-46f0-97e9-c94d0bcc3e35-image.png



  • @Denta1000 said in SerialPortReader output in a text file:

    should be sufficient to write the "test 2" text in the file but it doesn't

    I don't really understand why you're saying anything is wrong. It reports status OK, what is it that makes you say something has gone wrong? I wouldn't expect to see anything in the file till you've closed or at least flushed it, if that's what you're looking at.



  • @JonB thanks a lot, it was all because I wouldn't close the file properly. But I thought I read that file.close flushes the file. Doesn't "flush" means the file's content get destroyed ?

    Thanks a lot, even if I feel a bit ashamed now ... ^^'

    How do I make the post Resolved ?


  • Lifetime Qt Champion

    @Denta1000

    Hi
    Flushing a file means making sure all buffers are saved to disk.
    Often file IO has buffers and flushing means to get all data on the disk.

    Often closing the file will also make it flush first.

    • How do I make the post Resolved ?

    In the first post. There is Topic Tool button that make flag as solved.



  • @Denta1000
    When you send bytes to a QTextStream they are in memory in a buffer, not yet in disk file. You can call QTextStream::flush() to

    Flushes any buffered data waiting to be written to the device.

    Flushing here does not mean discarding pending-written data, it only means flushing it from memory to disk.

    I believe that rather than sending the "\n" which you do, endl would flush:

    m_standardOutput << "second test out" << endl;
    

    You should call QFile::close() to close the backing file. There may be buffering going on in QFile as well as in QTextStream, I don't know. There is a QIODevice::Unbuffered flag which can be passed when opening QFile. Nor do I know whether you need to call QTextStream::flush() before closing the file.

    You would have to play to find out how QTextStream and QFile interact for all this buffering/flushing!


Log in to reply