Catching all stdout (cout and maybe printf?) to QtextEdit



  • Having seen this very topic in my search of many other threads, I still can't get my code to work. Any guidance or advice would be very much appreciated by this Qt-newbie.

    My intent is to capture each line of standard output cout (and possibly printf) from a library (or sub-function) in a QtextEdit box. I would like to get the "live" updates (otherwise I would just return a string from the library function and post that to the Qtextedit). I suspect the problem with my code is that I'm not actually launching a seperate process exactly: instead of launching a command line program, I'm just calling a library or sub-function. (I think I'm not properly using the Qprocess object.)

    Code follows:

    @#include "uic.h"
    #include "ui_uic.h"
    #include <qobject.h>
    #include <qprocess.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <iostream>
    #include <qstring.h>
    #include <qdebug.h>
    #include <assert.h>

    QProcess* proc; // global

    ////##############################################
    UIC::UIC(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::UIC)
    {
    ui->setupUi(this);

    // Directly write to QtextEdit box
    ui->UIC_text->append("Directly Appending!");
    
    // QProcess related code
    proc = new QProcess( this );
    proc->waitForStarted();
    
    // make connections
    assert( connect( proc, SIGNAL(readyReadStandardOutput()),
               this, SLOT(my_readFromStdOut()) ));
    
    assert( connect( proc, SIGNAL(readyRead()),
               this, SLOT(my_readFromStdOut()) ));
    
    
    std::cout << "\n Test1\n";
    printf("\n Test2 \n");
    

    }
    ////##############################################
    UIC::~UIC()
    {
    delete proc;
    delete ui;
    }
    ////##############################################
    void UIC::my_readFromStdOut()
    {
    qDebug()<< "Reached Connected Slot!!!!!";

    ui->UIC_text->append( proc->readAll() );
    ui->UIC_text->append( proc->readAllStandardOutput() );
    ui->UIC_text->append( proc->readAllStandardError() );
    

    }
    ////##############################################
    void UIC::on_pb_clicked() // on pushbutton clicked
    {
    qDebug()<< "Got to clicked!";
    std::cout << "Some Text from Button! \n";
    do_something();
    printf("\n Again \n");
    }
    ////##############################################
    void do_something(void)
    {
    std::cout << "\nIn Subfunction Test1\n";
    printf("\n In Subfunction Test2\n");
    }
    ////##############################################

    @

    The slot "my_readFromStdOut" is never reached and no standard output reaches the Qtextbox.

    I'm using Qt Creator 3.2.2 based on Qt 5.3.2 on a Win7 machine. Again, I would sincerely appreciate any guidance or help!



  • DBoosalis, Ok - that clears things up for me a bit. I have been reading the Qt documentation and all, but some of these big-picture type things are tough to figure out. Thank you.

    MuldeR, Wow - thank you very much for that code you pasted. It's been a little tricky trying to get the output from the new thread back to the QtTextEdit display, but I think I'm almost there. When it's all working I will post the code here.



  • [quote author="CodeMonkey99" date="1416522519"] It's been a little tricky trying to get the output from the new thread back to the QtTextEdit display[/quote]

    The sample code was just to show how it can be done in principle. In Qt, I'd use a QThread for this purpose and return the captured strings via a queued signal.



  • That makes more sense.. I've had a little time to get back to this. I think I'm almost there: I can catch stdout in "real-time" (as shown in the time stamp form the output). However, though the text is captured real time, it isn't displayed real time. It only shows up on the Qtextbox in one giant chunk when the "do_something" function completes and then the pushbutton slot finishes execution. In other words all of the captured text shows up as one big block with no delay (which I would expect with intermiediate output).

    Output:

    Main thread constructor:: 0x13f8
    Captured: Test1 0.0002
    Captured: Test2 0.0002
    --------------------------------Button Clicked

       Directly Written
    

    Captured: COUT from Button! 2.3368
    Captured: In subfunction COUT.. 2.3392
    Captured: In subufnction PRINTF w/ DELAY! 3.8394

    How do properly connect it such that the output is displayed as soon as the string_out signal is sent?

    Code and listening class below:

    @#ifndef H_GET_STD
    #define H_GET_STD

    #include <Windows.h>
    #include <io.h>
    #include <fcntl.h>
    #include <qthread.h>
    #include <qdebug.h>

    #define BUFF_SIZE 4096

    // structure for timing functions
    typedef struct
    {
    LARGE_INTEGER freq;
    LARGE_INTEGER start;
    LARGE_INTEGER stop;
    } TICTOC;
    ////##############################################
    class stdout_worker : public QObject
    {
    Q_OBJECT

    signals:
    void string_out(const QString &value);
    void update_display();

    public:
    int fd_pipe[2];

    FILE* fPipeRd;
    FILE* fPipeWr;
    TICTOC tt;
    ////##############################################
    void stdout_worker::qtic (TICTOC* timer)
    {
    QueryPerformanceFrequency(&timer->freq);
    QueryPerformanceCounter(&timer->start);
    }

    ////##############################################
    double stdout_worker::qtoc (TICTOC* timer)
    {

    QueryPerformanceCounter(&timer->stop);
    return (double) ((timer->stop.QuadPart)-(timer->start.QuadPart))/(timer->freq.QuadPart);

    }
    ////##############################################
    void stdout_worker::setup_monitoring()
    {
    //Create the pipe
    if(_pipe(fd_pipe, BUFF_SIZE, _O_TEXT) != 0)
    {
    abort();
    }

    //Get FILE pointers
    fPipeRd = _fdopen(fd_pipe[0], "r");
    fPipeWr = _fdopen(fd_pipe[1], "w");
    if(!(fPipeRd && fPipeWr))
    {
    abort();
    }

    //Replace stdout and stderr streams
    *stdout = *fPipeWr;
    *stderr = *fPipeWr;

    //Disable buffering
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
    }
    ////##############################################
    private slots:
    void monitor_stdout()
    {
    //Init the buffer
    char buffer [BUFF_SIZE];
    size_t currentPos = 0;

    this->qtic(&tt);

    //Process captured text
    while(!(feof(fPipeRd) || ferror(fPipeRd)))
    {
    const int c = fgetc(fPipeRd);
    if((c != EOF) && (c != '\r') && (c != '\n') && (c != '\b'))
    {
    if(currentPos < BUFF_SIZE)
    {
    buffer[currentPos++] = c;
    }
    }
    else
    {
    if(currentPos > 0)
    {
    buffer[(currentPos < BUFF_SIZE) ? currentPos : (BUFF_SIZE - 1)] = '\0';
    QString txt;
    txt.sprintf("%s %s %0.4lf", "Captured: ", buffer, this->qtoc(&tt));
    emit string_out(txt);
    Sleep(1);
    emit update_display();

     currentPos = 0;
    }
    

    }
    }
    }
    };

    ////#######################################################
    ////#######################################################
    ////#######################################################

    #endif

    @

    Main code:

    @
    #define BUFF_SIZE 4096

    ////##############################################
    UIC::UIC(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::UIC)
    {
    ui->setupUi(this);
    QString txt;

    txt.sprintf("Main thread constructor:: %#x", QThread::currentThreadId());
    ui->UIC_text->append(txt);

    // private element in UIC
    stdout_thread = new QThread(); // listening thread for stdout

    // private element in UIC
    dsp_thread = new QThread(); // display thread

    // private element in UIC, instantiate listening object
    my_stdout = new stdout_worker();

    // move to thread
    my_stdout->moveToThread(stdout_thread);

    // setup listening object;
    my_stdout->setup_monitoring();

    // move display to display thread
    ui->UIC_text->moveToThread(dsp_thread);

    // when thread is started, launch onSTD
    connect(stdout_thread, SIGNAL(started()), my_stdout, SLOT(monitor_stdout()));

    stdout_thread->start();
    dsp_thread->start();

    //// link emitted text to text box
    //connect(my_stdout, SIGNAL(string_out(QString)), ui->UIC_text, SLOT(append(QString)), Qt::DirectConnection);
    //connect(my_stdout, SIGNAL(update_display()), ui->UIC_text, SLOT(repaint()), Qt::DirectConnection);

    // link emitted text to text box
    connect(my_stdout, SIGNAL(string_out(QString)), ui->UIC_text, SLOT(append(QString)), Qt::QueuedConnection);
    connect(my_stdout, SIGNAL(update_display()), ui->UIC_text, SLOT(repaint()), Qt::QueuedConnection);

    //connect(my_stdout, SIGNAL(string_out(QString)), ui->UIC_text, SLOT(update_text(QString)));

    printf("Test1 \n");
    std::cout << "Test2 \n ";

    }
    ////##############################################
    UIC::~UIC()
    {
    delete ui;
    }
    ////##############################################
    void UIC::on_pb_clicked()
    {
    ui->UIC_text->append("--------------------------------Button Clicked\n");
    ui->UIC_text->append(" Directly Written\n");
    std::cout << "COUT from Button! \n";
    ui->UIC_text->repaint();
    do_something();

    }
    ////##############################################

    void UIC::update_text(const QString &value)
    {
    ui->UIC_text->append(value);
    ui->UIC_text->repaint();

    }
    ////##############################################
    void do_something(void)
    {

    std::cout << "In subfunction COUT..\n"; fflush(stdout);
    

    Sleep(1500);
    printf("In subufnction PRINTF w/ DELAY!\n");
    }
    ////##############################################
    @



  • Sure, if you call a function that takes a lot of time from the main (GUI) thread, this will make your whole GUI "freeze" until the function has returned and thus control flow returns returns to the Qt event loop! That's why functions that compute longer than a few seconds should be moved into a "background" worker thread. Either that, or QApplications::processEvents() needs to be called in short intervals by the lengthy function. The latter needs to be used with care!


    Try:
    @void do_something(void)
    {
    for(int i = 0; i < 10; i++)
    {
    printf("In subufnction PRINTF w/ DELAY!\n");
    for(j = 0; j < 15; j++)
    {
    qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
    Sleep(100);
    }
    }
    }@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.