Home > Software design >  How to update QPlainTextEdit in real time?
How to update QPlainTextEdit in real time?

Time:02-22

I am very new to Qt.

I created a widget with Run and Cancel button. Run launches a bash script via QProcess, and Cancel button is meant to send SIGINT signal to disrupt the process in bash (does not work properly).

QProcess which launches the script is declared in my constructor and connected to append functions as:

runScript = new QProcess;
connect(runScript , SIGNAL(readyReadStandardError()), this, SLOT(updateProcessError()));
connect(runScript , SIGNAL(readyReadStandardOutput()), this, SLOT(updateProcessText()));

where appending slots are simply updating log and logError QPlainTextEdits (also fields of MyWidget class):

void MyWidget::updateProcessError()
{
    QString output(runScript ->readAllStandardError());
    errorLog->appendPlainText(output);
    errorLog->verticalScrollBar()->setValue(log->verticalScrollBar()->maximum());};

void MyWidget::updateProcessText()
{
    QString output(runScript ->readAllStandardOutput());
    log->appendPlainText(output);
    log->verticalScrollBar()->setValue(log->verticalScrollBar()->maximum());
};  

Now this does not work in real time. It does append all output, shell script is outputting on the terminal, along the errors, but only after script is finished.

So while the script is running, everything is frozen, I cannot even click on Cancel button.

So first question is: How to append QPlainTextEdit in real time, where appending is done by reading QProcess outputs from terminal?

I know why my Cancel button does not work and I have an idea for the solution. The Run button launches the runScript process and is waiting until it finishes. So naturally slot can't finish until script does. I feel the problem in my implementation is that I am trapping the Run button into QProcess that is running on the terminal. I could try to figure out a bash way to output script's output and errors to log files or fork the script execution and do something with its process, but I'd like an advice on how to do this properly (I only started with Qt 10 days ago so everything is new). I think if I run my script with & in the end, I can somehow figure out how to get its id from qt (should be logged) and then Cancel button just needs to kill the process? This is how roughly slot for run button looks like:

void MyWidget::handleRunButton()
{
    runButton->setEnabled(false);   
    QString program = "MyShellScript "   _scriptOptions;
    runScript->start("/bin/sh "   program);
    runScript->waitForFinished(-1);
}

I coded Cancel button to release SIGINT signal as:

void MyWidget::handleCancelButton()
{
    QProcess *runCtrlC = new QProcess;
    runCtrlC->start("/bin/bash -c \" kill -INT $$ \" ");
    runCtrlC->waitForFinished(-1);  

    runScript->terminate();
    emit finished(0);
};

So my next question is, how to interrupt a running shell script from Qt on a button a click, that has started on another button click?

I feel one solution is to launch script as separate process in bash on Run button click, and on Cancel button extract shell process id and kill it. If I do this, how would I then access output of process that is running the script in qt? I'd like the optimal answer.

CodePudding user response:

As docs says, QProcess::waitForFinished blocks until the process has finished. Delete this line and it should work as you expect.

void MyWidget::handleRunButton()
{
    runButton->setEnabled(false);   
    QString program = "MyShellScript "   _scriptOptions;
    runScript->start("/bin/sh "   program);
//    runScript->waitForFinished(-1);         <-- DELETE THIS LINE
}

EDIT:

I missed about the second question. To kill the process, You can simply call QProcess::kill.

void MyWidget::handleCancelButton()
{
    runScript->kill();
    runScript->waitForFinished(-1);
    emit finished(0);
};

QProcess::terminate might work as well. It depends on your MyShellScript. See the doc for details.

  • Related