How to read when QProcess need user input with Qt - c++

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

Related

Cannot connect to the device with QSerialPort

I develop Qt application in C++ under manjaro linux. The goal is to connect with a qC (Nucleo-L476RG Board) and receive data from accelerometer. App can find device on /dev/ttyACM0, but when i try to connect it fails with error "QSerialPort::DeviceNotFoundError". Checking errorString() gives "No such file or directory", but it still can find it in /dev.
I'm in the right group to read the file:
[1]: https://i.stack.imgur.com/CG1yZ.png
I use Qt v.6.2.4, but have build the code with Qt v.5.15.3. I tried to run another person's app that does the same and works under ubuntu, but it didn't work.
Here is my code with the searching and connecting methods. Method addToLogs() just prints logs in textEdit.
void MainWindow::addToLogs(QString message)
{
QString currDateTime = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss");
ui->textEditLogs->append(currDateTime + "\t" + message);
}
void MainWindow::on_pushButtonSearch_clicked()
{
ui->comboBoxDevices->clear();
this->addToLogs("Szukam urządzeń...");
QList<QSerialPortInfo> devices;
devices = QSerialPortInfo::availablePorts();
if(devices.count() > 0)
{
for(int i=0; i<devices.count(); i++)
{
this->addToLogs(devices.at(i).portName());// + " " + devices.at(i).description());
//ui->comboBoxDevices->addItem(devices.at(i).systemLocation());//portName());
ui->comboBoxDevices->addItem(devices.at(i).portName());
}
}
}
void MainWindow::on_pushButtonConnect_clicked()
{
if(ui->comboBoxDevices->count() == 0)
{
this->addToLogs("Nie wykryto żadnych urządzeń!");
return;
}
if(this->device->isOpen())
{
this->addToLogs("Port jest już otwarty!");
return;
}
QString portName = ui->comboBoxDevices->currentText();
//this->device->setPortName(portName);
this->device->setPortName("/dev/ttyACM0");
qDebug() << this->device->portName();
if(device->open(QSerialPort::ReadWrite))
{
this->device->setBaudRate(QSerialPort::Baud9600);
this->device->setParity(QSerialPort::NoParity);
this->device->setDataBits(QSerialPort::Data8);
this->device->setStopBits(QSerialPort::OneStop);
this->device->setFlowControl(QSerialPort::NoFlowControl);
this->addToLogs("Połączono z urządzeniem " + portName);
}
else
{
this->addToLogs("Otwarcie portu szeregowego się nie powiodło!");
this->addToLogs(device->errorString());
qDebug() << this->device->error();
}
}
I will be thankful for help, cause i've benn sitting on it for last 2 or 3 weeks and nothing works.

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.

How to check if QProcess is executing correctly?

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

Qt GUI freezes while using waitForFinished.. Alternative?

What I need to do
I am building a qt application that run a exe and pipe its output to other exe. (In my case ffmpeg | x265)
What I did
QProcess ffmpeg;
QProcess x265;
ffmpeg.setStandardOutputProcess(&x265);
x265.setProcessChannelMode(QProcess::ForwardedChannels);
ffmpeg.start(ffmpegArgs);
x265.start(x265Args);
if(!ffmpeg.waitForStarted())
return;
bool retval = false;
while ((retval = x265.waitForFinished(-1)))
{}
ffmpeg.close();
x265.close();
Every thing woks fine but the GUI freezes when the process is running.
What I tried to solve the Issue
void Basic::on_btnEncode_clicked()
{
if(fileContainer -> getQueue() -> rowCount() == 0) {
QMessageBox msg;
msg.setText("No Input to Convert");
msg.setIcon(QMessageBox::Information);
msg.exec();
}
QString file;
int bitRate;
QString preset;
QString ffmpegArgs;
QString x265Args;
bitRate = ui->sldBitRate->value();
preset = mapPreset(ui->sldPreset->value());
for(int i = 0; i < fileContainer->getQueue()->rowCount(); ++i)
{
file = QString("\"") + fileContainer->getQueue()->item(i, 0)->text() + QString("\"");
ffmpegArgs = Addons::FFmpegExe() + " -i " + file + " -pix_fmt yuv420p -f yuv4mpegpipe -";
x265Args = Addons::x265Exe() + " --input - --y4m --preset " + preset + " --bitrate " + QString::number(bitRate) + " --output " + QString("\"") + ui->txtDest->text() + "/out.hevc\"";
QProcess *ffmpeg = new QProcess(this);
QProcess *x265 = new QProcess(this);
ffmpeg->setStandardOutputProcess(x265);
x265->setProcessChannelMode(QProcess::ForwardedChannels);
connect(x265, SIGNAL(started()), this, SLOT(processStart()));
connect(ffmpeg, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(deleteLater()));
connect(x265, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinish(int,QProcess::ExitStatus)));
ffmpeg->start(ffmpegArgs);
x265->start(x265Args);
ffmpeg->close();
x265->close();
}
}
void Basic::processStart()
{
qDebug() << "started";
}
void Basic::processFinish(int exitcode, QProcess::ExitStatus staus)
{
qDebug() << "exitcode" << exitcode << "status" << staus;
}
but I always get the output as exitcode 62097 status 1 and nothing happens.
What can I do to run the process so that GUI does not freeze. Please Help.
You are creating the QProcesses on the stack. Meaning that when your function returns, the ffmpeg and x265 objects are destroyed. This also kills those processes.
If you want your process to keep running after your function returns. They must be created on the heap using new:
QProcess * ffmpeg = new QProcess(this);
QProcess * x265 = new QProcess(this);
Now you also need to destruct the ffmpeg and x265 objects yourself, otherwise you would be leaking memory. The following will wait until the processes are finished before their objects are cleaned up.
connect(ffmpeg, SIGNAL(finished(int,QProcess::ExitStatus)), ffmpeg, SLOT(deleteLater()));
connect(x265, SIGNAL(finished(int,QProcess::ExitStatus)), x265, SLOT(deleteLater()));

Query with QProcess

I'm supposed to check whether the service is RUNNING. I've a problem with QProcess query execution, when it comes to executing the following query: SC QUERY "service name" | findstr RUNNING, though this works fine when executed directly in command line in Windows. The code snipet here as follows:
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start("SC QUERY \"Service_name\" | findstr RUNNING", QIODevice::ReadWrite);
// Wait for it to start
if(!process.waitForStarted())
return 0;
QByteArray buffer;
while(process.waitForFinished())
buffer.append(process.readAll());
qDebug() << buffer.data();
Output is:
Can you help me?
It is because using these three lines will not give you the expected results:
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start("SC QUERY \"Service_name\" | findstr RUNNING", QIODevice::ReadWrite);
Based on the official documentation, QProcess is supposed to work for pipe'd commands:
void QProcess::setStandardOutputProcess(QProcess * destination)
Pipes the standard output stream of this process to the destination process' standard input.
In other words, the command1 | command2 shell command command can be achieved in the following way:
QProcess process1;
QProcess process2;
process1.setStandardOutputProcess(&process2);
process1.start("SC QUERY \"Service_name\"");
process2.start("findstr RUNNING");
process2.setProcessChannelMode(QProcess::ForwardedChannels);
// Wait for it to start
if(!process1.waitForStarted())
return 0;
bool retval = false;
QByteArray buffer;
while ((retval = process2.waitForFinished()));
buffer.append(process2.readAll());
if (!retval) {
qDebug() << "Process 2 error:" << process2.errorString();
return 1;
}
qDebug() << "Buffer data" << buffer;