PDA

View Full Version : QProcess - read from stdout lively



dotjan
20th February 2012, 16:21
Hi, I am using using the following code to successfully collect the stout of the program I run with QProcess.



QProcess *Process = new QProcess;
Process->start(program, args);

Process->waitForFinished();

QTextStream rsyncStdoutStream(rsyncProcess->readAllStandardOutput());
while (true)
{
QString line = rsyncStdoutStream.readLine();
if (line.isNull())
break;
else
newFiles.append(line);
}

The problem is that with this code the output is actually collected once the program is finished, so I do not have "live" access to it, which is actually what I am looking for (I would need this for other purpose). Simply not using the waitForFinished() function makes my program to crash.

i thought that using the QProcess::readyReadStandardOutput signal could be a solution, but AFAIK this would need to call a SLOT and with this it is not possible to return any value, which is my need. So I am a bit stuck at this point..

Thanks,
Jan

dotjan
21st February 2012, 16:32
Ok, I investigated it further and I have now the following code.



QProgressDialog *progress = new QProgressDialog("Processing...", "Abort", 0, INT_MAX, this);
progress->setWindowModality(Qt::WindowModal);
progress->setLabelText("Calculating..");
progress->setMinimumSize(400, 40);
progress->setRange(0, 100);
progress->setValue(1); // AFAIU this is what shows the progress dialog

QProcess *rsyncProcess = new QProcess;
rsyncProcess->start(program, args);
rsyncProcess->waitForFinished();

progress->setValue(100); // AFAIU this is where the progress dialog closes


The problem with this is that the dialog seems to be actually shown _after_ the rsyncprocess is finished and so it always appears only for few milliseconds, no matter the time the rsyncProcess actually takes, which is not what I need of course. I have also tried using a busy indicator with range 0, 0 but this does not change the described behavior, the dialog seems to appear after te rsyncprocess is finished.



QProgressDialog *progress = new QProgressDialog("Processing...", "Abort", 0, INT_MAX, this);
progress->setWindowModality(Qt::WindowModal);
progress->setLabelText("Calculating..");
progress->setMinimumSize(400, 40);
progress->setRange(0, 0);
progress->setValue(1); // AFAIU this is what shows the progress dialog

QProcess *rsyncProcess = new QProcess;
rsyncProcess->start(program, args);
rsyncProcess->waitForFinished();

progress->setValue(0); // AFAIU this is where the progress dialog closes



Any suggestion please?

boudie
21st February 2012, 23:21
You are doing it wrong.
Try something like this:



MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
process = new QProcess(this); // create on the heap, so it doesn't go out of scope
connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput())); // connect process signals with your code
connect (process, SIGNAL(readyReadStandardError()), this, SLOT(processOutput())); // same here
process->start(program, args); // start the process
}


// this gets called whenever the process has something to say...
void MainWindow::processOutput()
{
qDebug() << process->readAllStandardOutput(); // read normal output
qDebug() << process->readAllStandardError(); // read error channel
}


Success

ChrisW67
22nd February 2012, 00:01
Be mindful that, depending on how the child program manages its output, you may get partial lines in processOutput() and you may need to cope with that.

dotjan
22nd February 2012, 19:06
Thanks boudie, this clarify a bit about how I should do with the QProcess object. However I do not understand the benefit to have it in the heap so that it does not go out of the scope.. Just to clarify it to myself, having it in the heap is not enough, I also need to have it defined as class member in order to be available also for other functions of the class. Do I get it right?

Also, in my program now I have


progress = new QProgressDialog("Processing...", "Abort", 0, INT_MAX, this);
progress->setWindowModality(Qt::WindowModal);
progress->setLabelText("Calculating..");
progress->setMinimumSize(400, 40);
progress->setRange(0, 100);
progress->setValue(1);


rsyncProcess = new QProcess(this);
connect(rsyncProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(showProgress()));
rsyncProcess->start(program, args);


void SyncHandler::showProgress()
{
static int x = 1;

QStringList newFiles;
QTextStream rsyncStdoutStream(rsyncProcess->readAllStandardOutput());
while (true)
{
QString line = rsyncStdoutStream.readLine();
if (line.isNull()) {
break;
} else {
newFiles.append(line);
progress->setValue(++x);
//qDebug() << x;
}
}
qDebug() << x;


} }

In this way it works as long as x is actually increased exactly to 100, the value set with setRange(0,100). If x is lower than 100 the progress dialog just stay there waiting for the maximum value in the range, if higher a new one progress dialog is created.

Added after 48 minutes:

I have another problem with this. I actuially need to wait for process to finish before continuing as the next part of my code is based on the result of this process. So if I remove rsyncProcess->waitForFinished(); is a problem, my application goes ahead without having the needed information as process is running somehow in background and it is not finished yet.

boudie
22nd February 2012, 22:17
Add this connection:


connect(rsyncProcess, SIGNAL(finished()), this, SLOT(processFinished()));

and create a function processFinished() where you do what has to be done.

You should do yourself a favour and read the docs. It's really a very good investment of your time (and ours...). ;)

dotjan
27th February 2012, 18:50
Hi, finally I could figure it out. I was completely missing the logic and so some pieces in the code, not the function itself which of course I had already come across.... BTW, sometimes the problem is not about not reading docs but fully understanding it, I think that's we novices ask this basic things to you experts.... Thanks for your time anyway...

jeykeu
6th April 2014, 12:45
how do i wrap the codes from line 4 to 7 and bind with `processOutput`?


You are doing it wrong.
Try something like this:



MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
process = new QProcess(this); // create on the heap, so it doesn't go out of scope
connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput())); // connect process signals with your code
connect (process, SIGNAL(readyReadStandardError()), this, SLOT(processOutput())); // same here
process->start(program, args); // start the process
}


// this gets called whenever the process has something to say...
void MainWindow::processOutput()
{
qDebug() << process->readAllStandardOutput(); // read normal output
qDebug() << process->readAllStandardError(); // read error channel
}


Success

how do i wrap the codes from line 4 to 7 and bind with `processOutput`?

ChrisW67
6th April 2014, 23:07
Lines 5 and 6 specifically connect the process output notifier signals to the slot.