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 ClickedDirectly Written
Captured: COUT from Button! 2.3368
Captured: In subfunction COUT.. 2.3392
Captured: In subufnction PRINTF w/ DELAY! 3.8394How 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_OBJECTsignals:
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);
}
}
}@ -
hello CodeMonkey99, I am trying to do the exactly same thing you tried to do, i.e. redirecting output from std::cout to a QTextBrowser. I tried to run the code
proc = new QProcess( this );
and then connecting the readyReadStandardOutput() signal to my self-defined slot function. After that I tried QProcess::waitForStarted(). However by calling QProcess::state() it turned out that the QProcess has never started! is this really the right way to do it?
I tried to sift through the code and discussion that follows your post, however they appear to be extremely disorganized and I do not understand what was going on.
I'd appreciate it if you may tell me how you have overcome this problem.
Thank you.
-
@OlegOleg said in Catching all stdout (cout and maybe printf?) to QtextEdit:
it turned out that the QProcess has never started
Then you should check why the process doesn't start.
Connect a slot to https://doc.qt.io/qt-5/qprocess.html#errorOccurred and use https://doc.qt.io/qt-5/qiodevice.html#errorString in the slot to check what happened.
Also, you can show how you're using QProcess.