QProcess state() always returns RUNNING - qt

I do the following to start a process and waits for its completion (I am intentionally avoiding use of SIGNAL/SLOTS in this case).
QProcess *myProcess = new QProcess();
QString program = "test.exe";
QStringList args;
myProcess->start(program, args);
myProcess->waitForStarted();
while( myProcess->state() == QProcess::Running )
{
// Do other things...
}
I can verify that test.exe exits. But process state is for some reason always QProcess::Running. I am using Qt 4.8.2 on Windows.

If "do other things" does not include processing events of the thread's event loop, then the QProcess never gets a chance to update its state.
Either waitForFinished if this needs to block the thread or connect to the finished() signal.

I don't think state() should be used to check for a program that has closed down but you can use something like while(!waitForFinished(1)).

Related

How to read QProcess output

In the main thread of a Gui Application I am starting a QProcess of another GUI application that will log some messages over time in stdout using fputs(). The problem is that after some time the GUI application started with QProcess will freeze because it's output is not consumed by the parent. I know that this is the problem because if I am starting the QProcess with QIODevice::NotOpen or QIODevice::Unbuffered argument, it will not get stuck but the output will never be reached.
I've tried to connect the readyRead, readyReadStandardError, readyReadStandardOutput signals of the subprocess to a slot in the parent, but for some reasons the signals are never emitted. I am also flushing the stdout after each write.
My question is how to force QProcess to send some data in real time without closing it?
The connection of the signals, (T- is a wrapper for QProcess):
process->setWorkingDirectory(workingDir);
process->start(prog, argumentsList);
process->waitForStarted();
T* reciver = new V8QProcess(process);
QObject::connect(process, &QProcess::readyRead, reciver, &V8QProcess::OnMessageRecieved);
QObject::connect(process, &QProcess::readyReadStandardError, reciver, &V8QProcess::OnMessageRecieved);
QObject::connect(process, &QProcess::readyReadStandardOutput, reciver, &V8QProcess::OnMessageRecieved);
The code of the subprocess that will log in stdout:
QByteArray bytes = LogMsg::getDisplayable(logMsg, 0).toUtf8();
fputs(bytes.constData(), stdout);
fflush(stdout);
The code of the OnMessageRecieved:
if (!p) { // p is the QProcess
return;
}
QByteArray output;
output.append(p->readAllStandardError()).append(p->readAll());
QString message = QString::fromStdString(output.toStdString());
This approach is working when running a shell script or other simple program.
I found out what the problem was on my case:
Because I was starting the QProcess in a std::Thread, the events(signals) that occur were skipped because std::Thread don't have a queue of events as QThread or QApplication does.
The solution that I use is:
1. Use QThread instead of std::thread
2. Call QCoreApplication::proccesEvents() from time to time.
The proper solution is to use QThread::exec() in order to create an event loop, but this approach would block the GUI Application, so in my case is no good.

QT QProcess memory management

If I use this in my program to start a ffmpeg command.
QProcess *cmd1 = new QProcess();
cmd1->start(ffmpeg command);
if (!cmd1->waitForStarted())
return false;
if (!cmd1->waitForFinished())
return false;
then on finishing process will memory be deallocated of its own from heap or I have to use
delete cmd1.
Alternatively, or in addition you can set the process to delete itself when it is finished: -
// using Qt 5 connect syntax
connect(cmd1, &QProcess::finished, cmd1, &QProcess::deleteLater);
This is probably the preferred method, if the parent object that you pass to your QProcess object is likely to have a much longer lifetime than the QProcess object requires.
If you set Parent to QProcess like below, then you dont need to delete it manually.
Parent can be the QObject who is creating the QProcess.
QProcess *cmd1 = new QProcess(parent);
But if you can not set parent then you need to delete it.

How to run a synchronous chain of QProcess without freezing the gui?

I want to optimize images with the help of some external programs. The programs must be executed one after one, some of them depending on the output of the last program, some of them depending on the characteristics of the image.
I know, I can use signals, but this is not very useful here, because I would have to use dozens of functions and signals for each and every external program, some of them even multiple times depending on the position, where a program is called in the chain. It would be much easier to execute them one by one. But if I do so, the gui freezes immidiately even without displaying the status message until all processes are finished. What else can I do?
ui->status->setText("Do something with program 1");
QProcess *proc1 = new QProcess;
proc1->setReadChannelMode(QProcess::SeparateChannels);
proc1->start("program 1 -args", QIODevice::ReadWrite);
while(!proc1->waitForFinished(10))
;
ui->status->setText("Do something with program 2");
QProcess *proc2 = new QProcess;
proc2->setReadChannelMode(QProcess::SeparateChannels);
proc2->start("program 2 -args", QIODevice::ReadWrite);
while(!proc2->waitForFinished(10))
;
In this case using Signals is the "correct" way. You just need to chain them.
[...]
ui->status->setText("Do something with program 1");
proc1.setReadChannelMode(QProcess::SeparateChannels);
proc1.start("program 1 -args", QIODevice::ReadWrite);
connect(proc1, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finishedProc1()))
[...]
void finishedProc1()
{
ui->status->setText("Do something with program 2");
proc2.setReadChannelMode(QProcess::SeparateChannels);
proc2.start("program 2 -args", QIODevice::ReadWrite);
connect(proc2, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finishedProc2()))
}
void finishedProc2()
{
[...]
}
Alternativly you could call processEvents in QApplication while you are waiting or do the whole thing in a different QThread.
Create worker thread (several threads, if you need parallel processing) and do waiting there. GUI will not be blocked then.

stdout from QProcess is nowhere near realtime

I want to get the stdout from a QProcess into a QTextEdit.
It works if I do it like this:
QObject::connect(process, SIGNAL(readyReadStandardOutput()),
this, SLOT(readStdOutput()));
and
void myClass::readStdOutput()
{
teOutput->insertPlainText(process->readAllStandardOutput());
}
While the subprogramm continuously prints to stdout (tested in a terminal)
the QTextEdit is only updated every couple of seconds with chunks of output.
From the QIODevice manual:
Certain subclasses of QIODevice, such as QTcpSocket and QProcess, are asynchronous. This means that I/O functions such as write() or read() always return immediately, while communication with the device itself may happen when control goes back to the event loop. QIODevice provides functions that allow you to force these operations to be performed immediately, while blocking the calling thread and without entering the event loop.
Another thing to try would be to disable any buffer, the manual doesn't mention it for QProcess, but you could try it:
Some subclasses, such as QFile and QTcpSocket, are implemented using a memory buffer for intermediate storing of data. This reduces the number of required device accessing calls, which are often very slow.
...
QIODevice allows you to bypass any buffering by passing the Unbuffered flag to open().
Calling waitForReadyRead() seems to trigger an update.
So, to update e.g. 10 times per second, after
QObject::connect(process, SIGNAL(readyReadStandardOutput()),
this, SLOT(readStdOutput()));
add a
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(executeWaitForReadyRead()));
timer->start(100);
and
void YourClass::executeWaitForReadyRead()
{
process->waitForReadyRead();
}

Auto Updater Examples

Well i've been looking how to do an auto updater on google, however no success.
What i would plan is to create an updater (ANother exe called by QProcess though the principal exe) but here ihave some questions:
How do i make the QProcess silent?
If there's a new version, how do i show a notification on the window from where the process has been started (I meant i've create the process in Game.exe, i want to send a notification to Game.exe from Updater.exe that there's a new version available.)
Thanks for the answers.
First, I've never encountered a need to create anything other than a QThread to handle my update needs. The QProcess would be helpful if, once the user updates, you want to download, install, and relaunch the program while the user continues with the main program. (But this can all be achieved with a shell script, python script, even BAT file)
When you use QProcess, you will have to rely on the signals readyReadStandardError() and readyReadStandardOutput(). Then the application that your process is calling should send its output to stderr or stdout. Updater.exe should write to either of these files.
I would imagine your Updater to make use of QNetworkAccessManager::finished(QNetworkReply *reply). When this slot is called, please do something nicer than this:
void Updater::replyFinished(QNetworkReply *reply){
QString r(reply->readAll());
if(r.contains(SERVER_REPLY_UPDATE_AVAILABLE)){
qDebug() << "yes";
}else{
qDebug() << "no";
QApplication::quit();
}
}
If Updater.exe is going to be a full blown GUI application, do not call the show() method unless it's needed and it should appear to run in the background. I would prefer a script, but you know me.
Then your Game.exe will set up a QProcess. You can pass arguments to the process within the QProcess::start() function.
Good arguments that will help direct your update process would be:
Game.exe version number
"check_for_updates"
"ignore_updates"
"download_update"
finally, in Game.exe:
...
connect(process,SIGNAL(readyReadStandardError()),this,SLOT(readProcessReply()));
...
void Game::readProcessReply(){
QString r(process->readAllStandardError());
if(r.contains("yes")){
//show your dialog here
}else{
//do nothing
}
}

Resources