How to check if QProcess is executing correctly? - c++

QProcess process_sdcompare;
QString command_sdcompare;
QStringList args_sdcompare;
command_sdcompare = "diff";
args_sdcompare << "Filename" << "Filename";
process_sdcompare.start(command_sdcompare,args_sdcompare,QIODevice::ReadOnly);
process_sdcompare.waitForFinished();
QString StdOut_sdcompare = process_sdcompare.readAllStandardOutput(); //Reads standard output
QString StdError_sdcompare = process_sdcompare.readAllStandardError(); //Reads standard error
if(StdOut_sdcompare.isEmpty()) //the process output is checked here if empty it is a success
return 1;
I am running the above code. When I check for an error condition after comparing not similar text files, isEmpty() returns false.
How do I check if the QProcess execution occurred without errors?

I use QProcess::error() to query the last error (for quick debugging), however a "nicer" way to do it:
// capture any errors (do this before you run the process)
connect(&process_sdcompare, &QProcess::errorOccurred, this, &ThisClass::processError);
Then define slot:
ThisClass::processError(QProcess::ProcessError error)
{
qDebug() << "error enum val = " << error << endl;
}
update
Or with Lambda:
// No need to define a slot function...
connect(&process_sdcompare, &QProcess::errorOccurred, [=](QProcess::ProcessError error)
{
qDebug() << "error enum val = " << error << endl;
});

I think all the status and error functions are pretty useless to figure out if the process execution was actually successful. Example, trying to execute a program which does not exist (under windows):
error(): QProcess::ProcessError(FailedToStart)
exitStatus(): QProcess::ExitStatus(NormalExit)
exitCode(): 0
errorString(): "Process failed to start: Das System kann die angegebene Datei nicht finden."
If the process was actually executed successfully:
error(): QProcess::ProcessError(UnknownError)
exitStatus(): QProcess::ExitStatus(NormalExit)
exitCode(): 0
errorString(): "Unknown error"
So I would consider "Unknown error" not an indicator for success.
The solution I think could look like this:
QProcess *m_proc = new QProcess(this);
bool errorOccured = false;
QProcess::ProcessError procError;
QObject::connect(m_proc, &QProcess::errorOccurred, [&](QProcess::ProcessError error)
{
procError = error;
errorOccured = true;
});
m_proc->start(...);

When waitForFinished returns, it returns a bool that broadly indicates success. As Karsten recommended, you can then check for actual success or failure using QProcess::exitCode.

This is how you use QProcess
QProcess process_sdcompare;
QStringList args_sdcompare;
args_sdcompare << "Filename" << "Filename";
process_sdcompare.setProgram("diff");
process_sdcompare.setArguments(args_sdcompare);
process_sdcompare.start();
if (process_sdcompare.waitForStarted() && process_sdcompare.waitForFinished()) {
QString StdOut_sdcompare = process_sdcompare.readAllStandardOutput();
QString StdError_sdcompare = process_sdcompare.readAllStandardError();
if(StdOut_sdcompare.isEmpty())
return 1;
}
}

Related

Variable not updating in lambda slot

I need to run a function with arguments (Installer::install) on a QProcess::finished signal. I've decided to use a lambda as slot, resulting in this:
private:
QProcess* unzip = new QProcess(this);`
void Installer::extract(QString source, QString destination) {
QStringList args;
args.append("x");
args.append(source);
args.append(QString("-o%1").arg(destination));
connect(unzip, &QProcess::finished, this, [=](int code, QProcess::ExitStatus status) {
if (code != 0 || status == QProcess::CrashExit) {
qDebug() << "Installer - Error >> Exit code:" << code;
qDebug() << "Installer - Error >> Status:" << status;
emit error();
}
else
install(destination + "/game.exe");
});
unzip->start("path\to\extractor\program.exe", args);
if (!unzip->waitForStarted()) {
qDebug() << "Installer - Error >> Extractor not started";
qDebug() << "Installer - Error Detail >>" << unzip->error();
emit error();
return;
}
qDebug() << "Sot Installer - Updates - Extractor >> Extracting...";
}
Installer::extract function is called at the end of every extraction. Supposing we have four archives to extract, Installer::extract will be called four times, with four different destination.
For the first archive, all works well. From the second one, the destination variable inside the lambda slot has the value of the first destination while it should have the second destination value. Instead, the destination variable outside the slot (e.g. in the last args.append) has the right value. Why is this happening and how can I solve this?
According to #Igor Tandetnik, connections were stacking up and so were the values of destination. Disconnecting all the connections of unzip before running Installer::extract again solved the problem.

How to read when QProcess need user input with Qt

I'm using Qt in order to implement an interface allowing to develop for embedded system.
I'm facing a problem: In order to flash program into the embeded system I use QProcess, in order to use the command "make" and "make flash". To make there isn't any problem and the program compiles successfully.
But when I try to do the same thing for "make flash" there is a problem because the console is waiting for user input, he has to press a button on the embedded system.
But QProcess returns standard output only when the script is finished so I don't have the message "press a button".
So my question is: How can I know when QProcess needs user input? Or if it's impossible, how can I open a console dynamically with Qt and start a command?
I tried to what is said here: https://www.qtcentre.org/threads/47538-QProcess-read-from-stdout-lively
And here is my code :
compilator->start("make flash");
compilator->waitForFinished();
QByteArray errors = compilator->readAllStandardError();
QString stringError(errors);
QByteArray message = compilator->readAll();
QString stringMessage(message);
m_logForm->setText("Project path : "
+ pathProject + "\n"
+ "Flash finished with exit code " + QString::number(compilator->exitCode()) + "\n"
+ stringError + "\n"
+ stringMessage + "\n");
Where m_logFrom is a class used to display console report in my interface
[EDIT] I tried what Vladimir said. Unfortunatly I don't have my material so I can't test it but I've done this script (test.bat) in order to test :
set /p answer=Do you want to create it now (Y/N)?
Here is my new code :
QProcess *compilator = new QProcess(this);
connect(compilator, &QProcess::readyReadStandardOutput, [compilator, this](){
QString output =compilator->readAll();
qDebug() << "output: "<< output;
m_logForm->setText("Flash finished with exit code " + QString::number(compilator->exitCode()) + "\n"
+ output + "\n");
});
connect(compilator, &QProcess::readyReadStandardError, [compilator, this](){
QString err = compilator->readAllStandardError();
qDebug() << "error: "<<err;
m_logForm->setText("Flash finished with exit code " + QString::number(compilator->exitCode()) + "\n"
+ err + "\n");
});
m_logForm->setText("Flashing to serial port "+m_Serial + "\n");
compilator->setWorkingDirectory(pathProject);
compilator->start("test.bat");
}
But it do nothing
My test.bat
set /p answer=Do you want to create it now (Y/N)?
echo "user response:" %answer%
pause
The code starts the batch command, reads it output and writes the answer to the process:
QProcess *compilator = new QProcess(this);
connect(compilator, &QProcess::readyReadStandardOutput, [compilator, this]()
{
QString output = compilator->readAllStandardOutput().simplified();
qDebug().noquote() << "output: " << output;
if (output.contains("(Y/N)?", Qt::CaseInsensitive))
{
compilator->write("Y\n"); // write our answer to the process
}
else if (output.contains(". . .", Qt::CaseInsensitive))
{
compilator->write("\n"); // simulate press any key to process
}
});
connect(compilator, &QProcess::readyReadStandardError, [compilator, this]()
{
QString err = compilator->readAllStandardError().simplified();
qWarning().noquote() << "error: " << err;
});
// Handle the finished() signal:
connect(compilator, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[compilator, this](int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "compilator process finished with exit code =" << exitCode
<< "and exit status =" << exitStatus;
});
compilator->start("test.bat");
if (compilator->waitForStarted()) // use to check start errors
{
qDebug() << "compilator process started ok";
}
else
{
qCritical() << "compilator process start FAILED:" << compilator->errorString();
}

Syntaxe error source with QSqlQuery

I have a problem with a QSqlQuery when I execute a source command on my database.
This code works :
QString myRequest = "CREATE DATABASE MY_DATABASE;";
lQueryResult.Exec(myRequest);
if(!lQueryResult.GetMexec())
{
qDebug() << "Error in the request";
return false;
}
else
{
qDebug() << "The request is OK";
}
This code doesn't works, I have a syntax error :
You have an error in
your SQL syntax; check the manual that corresponds to your MariaDB
server version for the right syntax to use near 'source
./folder/myFile.sql' at line 1 QMYSQL: Unable to execute query
This is the code :
myRequest = "source myFile.sql";
lQueryResult.Exec(myRequest);
if(!lQueryResult.GetMexec())
{
qDebug() << "Error";
qDebug() << lQueryResult.LastError();
return false;
}
else
{
qDebug() << "OK";
}
I can successfully do my source with this command :
QString lCommand("mysql -uUsername -pPassword Data_Base -e \"source " + variablePath + variableFile + ".sql\"");
system(lCommandeProvisoire.toStdString().c_str());
I have no error. But, if I execute this code juste after, I have the error
No database selected QMYSQL: Unable to execute query
:
TheRequest = "SELECT * FROM MyTable;";
QueryResult.Exec(TheRequest);
if(!QueryResult.GetMexec())
{
qDebug() << QueryResult.LastError();
return false;
}
else
{
qDebug() << "OK";
}
But, If I execute a select * from MyTable in shell, it works.
Also, if I execute this in a shell logged in mysql, it works :
source myFile.sql
SOURCE is MySQL client command which means it is executed by MySQL shell, not by MySQL server, so you can't execute it via QSqlQuery. You can work around this by reading whole file and then sending it's contents to server.

Outputting QProcess readAll response to label

I have a QProcess where i would like to output the response in a label. First off, here is what i have tried:
QProcess *proc = new QProcess();
proc->setProcessChannelMode(QProcess::MergedChannels);
proc->start(cmdLineRequest.toUtf8().constData()); // cmdLineRequest is omitted
if (!proc->waitForFinished()) {
qDebug() << "Make failed:" << proc->errorString();
ui->topBarcode->setText(QString(proc->errorString()));
} else {
qDebug() << "Make output:" << proc->readAll();
ui->topBarcode->setText(QString(proc->readAll()) + "asdf");
}
proc->readAll() is a QByteArray and setText accepts a QString. From what i've read, i should be able to cast the QByteArray to a QString, howver it does not work. I have also tried to convert proc->readAll() with the QString class
->setText(QString::fromUtf8(proc->readAll())) // not working
->setText(QString::fromLatin1(proc->readAll())) // not working
->setText(QString::fromLocal8Bit(proc->readAll())) // not working
... etc ...
It seems weird, since i'm adding pictures to labels in nearly the same matter using setPixmap(QPixmap::fromImage(image))
Any help appreciated, thank you.
Update:
If i add a QMessageBox before the end of the method that the above block of code belongs to, i can see the text added to the label. However when i close the QMessageBox, the text dissapears. Am i giving an address position to the label with proc->readAll() or how come this behaviour? Thank you.
The problem here is that you're calling proc->readAll twice; the first for the qDebug output and then again for the string which you set on the label.
{
qDebug() << "Make output:" << proc->readAll();
ui->topBarcode->setText(QString(proc->readAll()) + "asdf");
}
I expect that as QProcess is a QIODevice, it's returning a buffered byte array. When you read it, it removes that from the buffer.
Therefore, create a temporary string and read the buffer once, before calling qDebug and setting the string to the label: -
{
QString output = proc->readAll();
qDebug() << "Make output:" << output;
ui->topBarcode->setText(output + "asdf");
}
You should listen to readyReadStandardOutput() signal and call readAll() when you receive signal.
or you can call
bool waitForReadyRead(int msecs = 30000)
before calling readAll().

Qt: How to catch an error with system call?

I am building a GUI application where I do a system call and call for gnuplot to run a script. Now i want to build in an error message that says when something is wrong (e.g. gnuplot is not installed or in the wrong path).
So I've been thinking about just putting out a QMessageBox, but I don't know how I can check whether the system call succeeded or not.
if(//System call didn't work)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("GNUPLOT was not installed");
msgBox.exec();
}
My system call looks like this:
system(gnuplot script.txt);
Any suggestions?
You should use QProcess rather than low-level system call because it is a nice abstraction in a Qt codebase. Otherwise, you will end up dealing with platform specific bits. QProcess already solves that for you. If you happen to be fine with a blocking approach, aka. sync, you could write something like that code below.
QProcess process;
process1.start("gnuplot arg1 arg2 etc");
// Wait for it to start
if(!process.waitForStarted())
return 0;
bool retval = false;
QByteArray buffer;
while ((retval = process.waitForFinished()));
buffer.append(process.readAll());
if (!retval) {
qDebug() << "Process 2 error:" << process.errorString();
msgBox.setText(buffer);
return 1;
}
If you would not like to block while the gnuplot script of yours is running, you could connect a slot, or simply lambda with C++11 and on, to the readyRead() signal. For sure, you would also need to connect to the error() signal. The code without lambda to work with pre C++11 environments would look something like this:
GnuPlotReader::GnuPlotReader(QQProcess *process, QObject *parent)
: QObject(parent)
, m_process(process)
, m_standardOutput(stdout)
{
connect(m_process, SIGNAL(readyRead()), SLOT(handleReadyRead()));
connect(m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(handleError(QProcess::ProcessError)));
connect(&m_timer, SIGNAL(timeout()), SLOT(handleTimeout()));
m_timer.start(5000);
}
GnuPlotReader::~GnuPlotReader()
{
}
void GnuPlotReader::handleReadyRead()
{
m_readData = m_process->readAll();
if (!m_timer.isActive())
m_timer.start(5000);
}
void GnuPlotReader::handleTimeout()
{
if (m_readData.isEmpty()) {
m_standardOutput << QObject::tr("No data was currently available for reading from gnuplot") << endl;
} else {
m_standardOutput << QObject::tr("GnuPlot successfully run")<< endl;
}
}
void GnuPlotReader::handleError(QProcess::ProcessError processError)
{
if (processError == QProcess::ReadError) {
m_standardOutput << QObject::tr("An I/O error occurred while reading the data, error: %2").arg(m_process->errorString()) << endl;
messageBox.setText(m_readData);
}
}