简体   繁体   中英

Capture stdout in a Qt Application

I am trying to capture output of my Qt application but without success. (I am calling an external lib that outputs to console, and I want to show this in my UI. Its NOT a QProcess, its a calss instance in my own process).

The problem: my lambda slot when writing to std::cout never gets called.

Here is my code distilled to a small testable application.

This is my latest attempt, and so far I am getting the "furthest" with it meaning, that the std::cout output is successfully redirected to the QFile. What I don't understand however, is why the QTextStream connected to this file is not being triggered?

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QTimer>
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    
    QFile file("output.txt");
    file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate);
    
    // Redirect stdout to the QFile
    std::freopen(file.fileName().toStdString().c_str(), "w", stdout);
    QTextStream stream(&file);
    std::string output; //to spy on in debugger
    //this lambda never gets called!?
    QObject::connect(&file, &QFile::readyRead, [&stream,&output] {
        // Read from the QTextStream whenever new data is available
        output += stream.readLine().toStdString();
        qApp->quit();
    });

    //This succefully writes to the QFile on disk
    QTimer::singleShot(100, [] {
        std::cout << "This will be written to output.txt" << std::endl<<std::flush;
    });
    
    
    app.exec();
    
    // Close the stream
    std::fclose(stdout);
    file.close();
}

My previous attempts are listed below, and are still not answered, so if you know how to fix any of the approaches, I will be really thankful!

Note at the end of the code, I am outputting directly to std::cout and to the QTextStream. All of these outputs are seen on the console, including the output to stream - which means, that QTextStream object is correctly initialized with stdout.

Any ideas why the lambda slot is not getting called? (I am aware of the recursion that will happen in the lambda due to outputting to stdout in it. But as this is just a test code its ok - the problem is that the lambda is not getting called at all)

#include <QCoreApplication>
#include <iostream>
#include <QTextStream>
#include <QTimer>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    
    QTextStream stream(stdout);

    QObject::connect(stream.device(), &QIODevice::bytesWritten, [&stream](qint64 bytes) {
        // This lambda function is called when new data is available to be read
        std::cout<<"in lambda"<<std::endl;
     });
    
    QTimer::singleShot(100, [&stream]{
        std::cout << "Hello, world!" << std::endl<<std::flush;
        std::cout << "This is some output." << std::endl<<std::flush;
        stream<< "Stream output\n";
        stream.flush();
    });

    return app.exec();

}

After some more thought, it occurred to me to try listening on stdout with QSocketNotifier. With this approach, the notifier slot gets triggered, however, two things:

  1. The notifier slot gets called before the QTimer slot - repeatedly like in an endless loop.
  2. I get nothing from the stream which is really confusing me - I am being notified when stdout is being written to (even though nothing is writing to it, yet) and am I getting nothing when trying to read that data? (the fact that I get nothing is not that surprising since I didn't write anything (yet) to stdout it's more the fact it is being triggered all the time)

Anyone knows how to explain this?

Here is the code:

#include <QCoreApplication>
#include <iostream>
#include <QTextStream>
#include <QTimer>
#include <QSocketNotifier>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    
    QTextStream stream(stdout);

    int stdoutFd = fileno(stdout);

    std::string output; //allows me to spy the content in the debuger

    QSocketNotifier notifier(stdoutFd, QSocketNotifier::Write );
    QObject::connect(&notifier, &QSocketNotifier::activated, [&stream, &output] {
        auto s = stream.readAll(); 
        output += s.toStdString();
    });

   
    QTimer::singleShot(100, [&stream]{
        std::cout << "This is some output." << std::endl<<std::flush;
        stream << "Stream output\n";
    });
    return app.exec();
}

Many thanks in advance!

I have managed to achieve what I wanted, not very elegant, but it works.
I am putting my solution here for others that might also encounter this problem.
On one hand I like this solution since its basically pure C++ and does not require Qt.
On the other, it requires polling, which is quite "clunky".
Thus, I am leaving my OP open, with the hope that a more elegant solution will be offered, and I am curious as to why my Qt solutions didn't work.

This solution uses std::stringstream and std::streambuf . I am putting here in a simple example main() , you can then adapt it to what ever application you have as it is really simple.
https://onlinegdb.com/AixVsxUyy

#include <sstream>
#include <iostream>
int main(int argc, char *argv[]) {

    //Redirect stdout to streambuf - this "grabs" the stdout
    std::stringstream buffer;
    std::streambuf *streamBuff = std::cout.rdbuf(buffer.rdbuf());

    std::cout << "this is redirected to streambuf" << std::endl;
   
    //Read our buffer
    std::string text = buffer.str();

    // Release stdout back to original buffer
    std::cout.rdbuf(streamBuff);

   
    std::cout << "Grabbed text:" << text << std::endl;
    std::exit(0);

}

Since this is synchronous, I resorted to polling buffer.str() . If anyone knows how can I be notified when streambuff has new data, please share.

Note: When polling, if between the calls no new data was written to stdout, the same string will be read again as the buffer is still intact. If you want to clear the buffer when you read it call:

buffer.clear();
buffer.str("");

I hope this helps someone!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM