How to read QProcess output - qt

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.

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.

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

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

Correct way to quit a Qt program?

How should I quit a Qt Program, e.g when loading a data file, and discovered file corruption, and user need to quit this app or re-initiate data file?
Should I:
call exit(EXIT_FAILURE)
call QApplication::quit()
call QCoreApplication::quit()
And difference between (2) and (3)?
QApplication is derived from QCoreApplication and thereby inherits quit() which is a public slot of QCoreApplication, so there is no difference between QApplication::quit() and QCoreApplication::quit().
As we can read in the documentation of QCoreApplication::quit() it "tells the application to exit with return code 0 (success).". If you want to exit because you discovered file corruption then you may not want to exit with return code zero which means success, so you should call QCoreApplication::exit() because you can provide a non-zero returnCode which, by convention, indicates an error.
It is important to note that "if the event loop is not running, this function (QCoreApplication::exit()) does nothing", so in that case you should call exit(EXIT_FAILURE).
You can call qApp->exit();. I always use that and never had a problem with it.
If you application is a command line application, you might indeed want to return an exit code. It's completely up to you what the code is.
While searching this very question I discovered this example in the documentation.
QPushButton *quitButton = new QPushButton("Quit");
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection);
Mutatis mutandis for your particular action of course.
Along with this note.
It's good practice to always connect signals to this slot using a
QueuedConnection. If a signal connected (non-queued) to this slot is
emitted before control enters the main event loop (such as before "int
main" calls exec()), the slot has no effect and the application never
exits. Using a queued connection ensures that the slot will not be
invoked until after control enters the main event loop.
It's common to connect the QGuiApplication::lastWindowClosed() signal
to quit()
If you're using Qt Jambi, this should work:
QApplication.closeAllWindows();
if you need to close your application from main() you can use this code
int main(int argc, char *argv[]){
QApplication app(argc, argv);
...
if(!QSslSocket::supportsSsl()) return app.exit(0);
...
return app.exec();
}
The program will terminated if OpenSSL is not installed
//How to Run App
bool ok = QProcess::startDetached("C:\\TTEC\\CozxyLogger\\CozxyLogger.exe");
qDebug() << "Run = " << ok;
//How to Kill App
system("taskkill /im CozxyLogger.exe /f");
qDebug() << "Close";
example

Resources