Properly using Qt QProcess - qt

I'm considering to use QProcess to call a command line app (gpio) multiple times. Every time user clicks a button then a command is issued.
And the app output will be monitored and redirected to screeen. The code looks like the following.
void Gpio::command(QString argument)
{
// if(process)
// delete process;
process = new QProcess(this);
connect(process, SIGNAL(started()), this, SLOT(onStart()));
connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinish(int,QProcess::ExitStatus)));
connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(readGpio()));
QString program("gpio");
QStringList list = argument.split(" ");
process->start(program, list);
}
Question: Should I delete process? Doing so I got:
QProcess: Destroyed while process is still running.
Monitoring exitCode and exitStatus I see they are always 0.
This question concerns more about the proper use of QProcess while "QProcess and shell : Destroyed while process is still running" focus on the specific error.

as you don't want to run multiple processes concurrently (as per the comments), you don't need to create / delete the QProcess multiple times.
gpio.h
QProcess* m_gpioProcess;
gpio.cpp file
Gpio::Gpio(.....),
.....(),
m_gpioProcess(new QProcess(this))
{
m_gpioProcess->setProgram("gpio");
connect(m_gpioProcess, SIGNAL(started()), this, SLOT(onStart()));
connect(m_gpioProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinish(int,QProcess::ExitStatus)));
connect(m_gpioProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readGpio()));
}
void Gpio::command(const QString& args)
{
if (m_gpioProcess->state() != QProcess::NotRunning) {
qDebug() << "Process already running, ignoring the request";
return;
}
m_gpioProcess->setArguments(args.split(" "));
m_gpioProcess->start();
if (m_gpioProcess->waitForStarted()) {
qDebug() << "Process started with arguments:" << m_gpioProcess->arguments();
}
}
if you want to prevent user clicking the button multiple times, consider enabling / disabling the button as per m_gpioProcess state.
for Qt 4.8, just remove this line
m_gpioProcess->setProgram("gpio");
and this line
m_gpioProcess->setArguments(args.split(" "));
and change this line
m_gpioProcess->start();
to
m_gpioProcess->start("gpio", args.split(" "));

Well, It seems I was forgetting process->waitForFinished(); after the call to start. This seems to solve the problem.

add following code maybe helpful:
process->close();

Related

How to properly connect a QProgressBar with a QFutureWatcher?

I want the progress of the function to be displayed on progressBar. Following the guides, I wrote the code below. But during the execution of the function, the program freezes, then progress bar's value becomes equal to one.
The program itself will not produce errors. The result of the function is correct.
I think my problem is that I don’t know how to connect the value of the progress of a function with the value of a progress bar.
(Form.h)
public:
MyObject object;
QFutureWatcher<QBitArray> FutureWatcher;
QFuture<QBitArray> future;
(Form.cpp) Form constructor body:
ui->setupUi(this);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
connect(&this->FutureWatcher, SIGNAL(progressValueChanged(int)), ui->progressBar, SLOT(setValue(int)));
(Form.cpp) Function on_button_clicked():
void Form::on_button_clicked()
{
QString message = ui->textEdit->toPlainText();
future = QtConcurrent::run(&this->object, &MyObject::longFunction, message);
this->FutureWatcher.setFuture(future);
QBitArray bitresult = future.result();
}
The problem is that you're immediately calling future.result(). The problem with this is that result() will wait until the QFuture has finished.
The Qt documentation says (https://doc.qt.io/qt-5/qfuture.html#result):
If the result is not immediately available, this function will block and wait for the result to become available.
The solution is to connect a slot to QFutureWatcher::finished():
auto *watcher = new QFutureWatcher<QBitArray>(this);
connect(watcher, &QFutureWatcher::finished, this, [=]() {
auto result = watcher->result();
// do something with the result
// important: delete watcher again
watcher->deleteLater();
});
// now start the task
QString message = ui->textEdit->toPlainText();
watcher->setFuture(QtConcurrent::run(myObject, &MyObject::longFunction, message));

Is there a way to read a line from a file and get pause for few seconds, during pause a function checks whether user enters the same word or not?

I am working to develop typing tutor project. i am unable to read a specific line from file and allow user to enter the word. so how can i fix this??
void MainWindow::on_pushButton_clicked()
{
QFile file("D:/programs/QT/file_handle/file.txt");
if(!file.open(QFile::ReadOnly | QFile::Text))
QMessageBox::warning(this,"title","file not open");
QTextStream in(&file);
while(!in.atEnd()){
line=in.readLine();
ui->textBrowser->setPlainText(line);
}
file.close();
}
void MainWindow::on_pushButton_2_clicked()
{
QString str=ui->textEdit->toPlainText();
if(line==str)
ui->label->setText("they are same");
else
ui->label->setText("they are not same");
}
i expect to pause and pass control to textEdit where user enters a word after reading the first line. but the actual output appears reading the final line of the file
You'll need to do this asynchronously.
Whether you read the whole file into a buffer / list or you keep the file open doesn't matter.
You would need to make file (or buffer / list) a member of MainWindow.
In on_pushButton_clicked() you would only read the first / next line and display it.

Project hangs on QProcess::start when starting QtAssistant

I am using QProcess::start to launch Qt Assistant with my custom help project file. It works fine until i load project(not help project file) to my programm. Programm generates images from specific data using custom library. Even when all processes ends and i see generated images and nothing else happens, when i trying to launch Qt Assistant, my programm hangs at QProcess:start function when trying to start process. The code is:
show() function(public):
if (!run())
return false;
QByteArray ba("setSource ");
ba.append("qthelp://insyn_help/doc/");
proc->write(ba + page.toLocal8Bit() + '\n');
return true;
run() function(private):
if (!proc)
proc = new QProcess();
if (proc->state() == QProcess::Running)
return true;
QString app = QString(QT_BIN_DIR) + QDir::separator() + QString("assistant");
QString path = QString(PREFIX) + QString(HELP_INSTALL_PATH) + QString("/help_project.qhc");
QStringList args;
args << QLatin1String("-collectionFile")
<< QLatin1String(path.toLatin1())
<< QLatin1String("-enableRemoteControl");
QFileInfo help_project(path);
if (help_project.exists()) {
proc->start(app,args);
if (!proc->waitForStarted()) {
m_exitCode = 1;
emit closed();
return false;
}
}
This code is a part of AssistantLauncher class which was registered using qmlRegisterType and added to main.qml as a member of application window. My programm doesn't touch it anywhere (except calling a method show()). It is separate object (except it is a part of appWindow). The question is why does the process can not start only after my programm did some work? And why QProcess::start even dont have timeout.
UPD: I moved proc->start(app,args); to the child process, which i getting by using fork() and now my programm hangs on pid_t child = fork(). So the problem is that new process can not be created.
The answer is to do not use fork() because it is dangerous in big projects. More at http://www.evanjones.ca/fork-is-dangerous.html . posix_spawn also hangs my project. Now i decided to fork() new process at the beginning and send commands to it through the pipe.

Getting Pixmap is a null pixmap on calling a function 500 times

I am showing a image in qt label. Below is my code:
void MyClass::onPushButtonClicked(QString myurl)
{
this->setCursor(Qt::WaitCursor);
ui.qtImageLabel->clear();
qDebug()<<QTime::currentTime()<<"MyClass: onPushButtonClicked";
QNetworkAccessManager *qnam_push_button_clicked_show_image;
QNetworkReply *reply;
QNetworkRequest request;
request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
QUrl url(myurl);
request.setUrl(url);
qnam_push_button_clicked_show_image = new QNetworkAccessManager(this);
if(qnam_push_button_clicked_show_image)
{
QObject::connect(qnam_push_button_clicked_show_image, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onPushButtonClickedRequestCompleted(QNetworkReply*)));
reply = qnam_push_button_clicked_show_image->post(request, url.encodedQuery());
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
}
void MyClass::onPushButtonClickedRequestCompleted(QNetworkReply *reply)
{
qDebug()<<QTime::currentTime()<<"MyClass: onPushButtonClickedRequestCompleted request completed";
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
this->setCursor(Qt::ArrowCursor);
return;
}
QByteArray data = reply->readAll();
QPixmap pixmap;
pixmap.loadFromData(data);
int width;
int height;
//application size can be changed
QRect rec = QApplication::desktop()->screenGeometry();
height = rec.height();
width = rec.width();
qDebug()<<QTime::currentTime()<<width<<","<<height;
QSize *size = new QSize(width,height);
if(size)
{
QPixmap scaledPixmap = pixmap.scaled(*size);
ui.qtImageLabel->setPixmap(scaledPixmap);
}
if(size)
{
delete size;
size = NULL;
}
data.clear();
this->setCursor(Qt::ArrowCursor);
reply->deleteLater();
return;
}
On clicking push button It will send a request to server and will show a different image received from server. It is working fine if it does't exceeds 500 times. If it exceeds that first this error has been shown
QPixmap::scaled: Pixmap is a null pixmap
and it doesn't show the image. Then if someone again sends a request for an image then it shows the following error:
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
re implement QApplication::notify() and catch all exceptions there.
I am not getting what is the error in the above code. Can someone please tell me how to solve this?
The obvious leak is qnam_push_button_clicked_show_image = new QNetworkAccessManager(this);, which doesn't have a balanced delete anywhere. QNAMs should typically created once, then reused for the lifetime of the application rather than created for a single request. So by turning qnam_push_button_clicked_show_image in a class member (same as ui) you'll fix both your leak and improve the efficiency of the code.
That said, I don't think that's what causes your QPixmap error. If you're running this code on X11, then QPixmap is backed by an X Pixmap resource, which is limited by various factors (software and hardware). Even though from your code there's no obvious leak, it could be that repeatedly allocating large pixmaps slowly fragments the memory pool managed by X, up to the point where it can't allocate a block large enough for the scaled pixmap and then triggers the error. Or it could be a driver bug somewhere in the graphics stack. Have you tried if changing the scaled size increases or decreases the limit before it starts breaking? If so, switching to QImage might help relieving the pressure on X.
Aside from that, the code could use some cleanup, especially that superfluous QEventLoop usage. I'm guessing it's a way to prevent the button from being clicked several times until the new image has been loaded, but I'd much rather implement this using button.setEnabled(false) while the image is downloading, because nested event loops combined with network events is a recipe for countless reentrancy issues and hard to debug crashes/bugs.
I'm also confused about why size is allocated on the heap , especially when it's deleted right after, and these if (size) are really confusing, as they can be understood as if (size->isValid()) while what they really mean is if (size != nullptr), which is pretty much guaranteed as the chance of getting an OOM on that line is infinitesimally low. (if you did eventually run out of memory, my guess is it would likely happen in the readAll() or loadFromData() calls above).
ps: good luck pressing that button another 500 times to check if fixing the leak helped ;)

QT 5.5 - QNetworkReply empty data

I already took at others questions but I didn't find an answer.
I have a problem to print HTML code I download with a QNetworkAccessManager.
I need to log into a website to retrieve this code.
I have a slot like this:
void Aims::slotRequestFinished(QNetworkReply* requestReply)
{
QString data = QString(requestReply->readAll());
qDebug() << data;
}
For the first two steps (connection), I can see the HTML code in the console.
The last step doesn't get any data. There is no redirection nor error.
Now, the stranger part is that when I change my code to show the page into a webview, qDebug doesn't show anything, but the code loaded is shown correctly in the webview.
void Aims::slotRequestFinished(QNetworkReply* requestReply)
{
QString data = QString(requestReply->readAll());
qDebug() << data;
ui->webView->setHtml(data);
}
Well, I can save the content into a file. But I would really like to understand why I can't see anything in qDebug

Resources