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

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.

Related

How to express the delayed operation of QProcess with QProgressBar? [duplicate]

This question already has an answer here:
How to show progress bar while saving file to excel in python?
(1 answer)
Closed 2 years ago.
I use QProcess to call the Ping command, and use waitForFinished to wait for the end of the command execution.
Now I want to use a QProgressBar progress bar connect_gress to indicate that the range is (0, 100) in the initial state, and it becomes (0, 0) when QProcess starts to start, which is the busy state, and the command called by QProcess ends. Time to change back to (0, 100)
void web::gress_begin(){
ui->connect_gress->setRange(0, 0);
}
void web::gress_finish(){
ui->connect_gress->setRange(0, 100);
ui->connect_gress->setValue(100);
}
This is part of QProcess
QProcess excping;
connect(&excping, SIGNAL(started()), this, SLOT(gress_begin()));
connect(&excping, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(gress_finish()));
excping.start(cmdstr);
excping.waitForStarted();
excping.waitForFinished(-1);
But when the program is running, I press the button, the connect_gress on the main interface does not enter the busy state, and until the command ends, the value of connect_gress will become 100, that is, gress_finish is executed and successfully displayed, and gress_start is also It was executed, but it did not show up successfully.
QProcess::waitForFinished blocks the current thread. And I believe you are calling it in main tread aka GUI thread. So main thread never has a change to process events including paintEvent. Also GUI is frozen while ping process running. So user can't do anything at this time, I don't know if it is your intend or not.
Easiest way to solve it not waiting for finish at all. But in this case user can play around with ui while pinging. excping is a stack variable so it might die before emitting finished in this case. We can create it in heap and delete after the finished signal.
auto excping = new QProcess(this);
connect(excping, SIGNAL(started()), this, SLOT(gress_begin()));
connect(excping, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(gress_finish()));
connect(excping, SIGNAL(finished(int, QProcess::ExitStatus)), excping, SLOT(deleteLater()));
excping->start(cmdstr);
excping->waitForStarted();
// no waitForFinished
Another easy way to solve with blocking is forcing event loop to process events. I am not a favor of processEvents. Documentations also discourage to use that. So please don't make it habit to toss around like a some sort of magic sauce.
void web::gress_begin(){
ui->connect_gress->setRange(0, 0);
QCoreApplication::processEvents();
}
Also you can tweak around with the execution delay of functions. Checkout Qt::QueuedConnection and QMetaObject::invokeMethod.

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.

QProcess state() always returns RUNNING

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)).

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();
}

QT: make a function to pause at some moment for some time

I've got a QT problem.
I want to make my program stop at place where I define, let`s say for 3 seconds. I couldn't manage to do that. I need that because earler my program generates file and it is used by a program which I call a bit later. Problem is, that file doesn't seem have enough time to create. My code looks like this:
void MainWindow::buttonHandler()
{
QFile ..... (creating a text file);
//Making a stream and writing something to a file
//A place where program should pause for 3 seconds
system("call another.exe"); //Calling another executable, which needs the created text file, but the file doesn`t seem to be created and fully written yet;
}
Thanks in advance.
Some possibilities:
1) Use another slot for the things to do after the sleep:
QTimer::singleShot(3000, this, SLOT(anotherSlot());
...
void MyClass::anotherSlot() {
system(...);
}
2) Without another slot, using a local event loop:
//write file
QEventLoop loop;
QTimer::singleShot(3000, &loop, SLOT(quit()) );
loop.exec();
//do more stuff
I would avoid local event loop and prefer 1) though, local event loops can cause a plethora of subtle bugs (During the loop.exec(), anything can happen).
Try void QTest::qSleep ( int ms ) or void QTest::qWait ( int ms )
Looking into the source of these functions is also useful if you do not want the overhead of QTest.
More info at http://doc.qt.io/qt-5/qtest.html#qSleep
Maybe you just need to close the written file before you call the other program:
QFile f;
...
f.close();
(This also flushes internal buffers so that they are written to disk)

Resources