如何link QProcess 的执行和 QProgressBar 的推进用于非常繁重的计算循环
How to link the execution of QProcess and the advancement of the QProgressBar for a very heavy computation loop
我要通过 QPushButton
在 GUI 上执行以下 bash 脚本:
#!/bin/bash
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /scan > test_landing_test_2.csv
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /velodyne_points > vel_test_2.csv
脚本将遍历每个文件并提取相关的 .csv
。这个过程很重,需要一点时间。 问题是没有办法知道需要多长时间,除非我放一个QProgressBar
,但我不知道link的执行有多正确QProcess
和 QProgressBar
的进步正确。
截至目前提取成功,但 QProgressBar
并未从 0 移动。
下面我创建了一个最小的可验证示例,为了完整起见,可以在此处找到源代码:
mainwindow.h
#include <QMainWindow>
#include <QProcess>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_PROPERTY(float progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
float progress();
bool running();
bool finished();
public Q_SLOTS:
void startComputation();
void finishComputation();
void updateProgress(int value);
signals:
void progressChanged();
void runningChanged();
void finishedChanged();
private slots:
void on_executeBtn_clicked();
private:
Ui::MainWindow *ui;
QProcess *executeBash;
bool m_running = false;
int m_progressValue = 0;
bool m_finished = false;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->executeBash = new QProcess(this);
this->executeBash->setProcessChannelMode(QProcess::MergedChannels);
/*connect(this->executeBash, &QProcess::readyReadStandardOutput, [script = this->executeBash](){
qDebug() << "[EXEC] DATA: " << script->readAll();
});*/
connect(this->executeBash, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[script = this->executeBash](int exitCode, QProcess::ExitStatus exitStatus){
qDebug() << "[EXEC] FINISHED: " << exitCode << exitStatus;
if(script->bytesAvailable() > 0)qDebug() << "[EXEC] buffered DATA:" << script->readAll();
});
connect(this->executeBash, &QProcess::errorOccurred, [script = this->executeBash](QProcess::ProcessError error){
qDebug() << "[EXEC] error on execution: " << error << script->errorString();
});
connect(this->executeBash, &QProcess::readyReadStandardOutput, [this, script = this->executeBash](){
QString s = QString::fromUtf8(script->readAll());
qDebug() << "[EXEC] DATA: " << s;
auto match = QRegularExpression("Stage (\d+)/(\d+): (.*)").match(s);
if (match.hasMatch()) {
int x = match.captured(1).toInt();
int y = match.captured(2).toInt();
QString stage_info = match.captured(3);
qDebug() << "x = " << x;
qDebug() << "y = " << y;
qDebug() << "info = " << stage_info;
this->updateProgress(x * 100 / y);
}
});
// Initialization of the progressbar
m_running = false;
emit runningChanged();
m_finished = false;
emit finishedChanged();
updateProgress(0);
}
MainWindow::~MainWindow()
{
delete ui;
}
// ProgressBag loading depending on the workload of the .sh file
float MainWindow::progress()
{
return m_progressValue;
}
bool MainWindow::running()
{
return m_running;
}
bool MainWindow::finished()
{
return m_finished;
}
void MainWindow::startComputation()
{
m_running = true;
emit runningChanged();
updateProgress(100);
}
void MainWindow::finishComputation()
{
m_finished = true;
emit finishedChanged();
m_running = false;
emit runningChanged();
}
void MainWindow::updateProgress(int value)
{
m_progressValue = value;
emit progressChanged();
if (m_progressValue == 100)
finishComputation();
ui->progressBarExecuteScript->setValue(value);
}
void MainWindow::on_executeBtn_clicked()
{
qDebug() << "Button clicked!";
this->executeBash->start(QStringLiteral("/bin/sh"), QStringList() << QStringLiteral("/home/emanuele/catkin_docking_ws/devel/lib/test.sh")); //will start new process without blocking
// right after the execution of the script the QProgressBar will start computing
//ui->progressBarExecuteScript->setValue(executeBash->readAll().toInt());
}
到目前为止我做了什么
1) 在设置了所有必要的点后,我遇到了 this source 用户遇到了我现在遇到的同样问题,但通过 QDir
解决了,我不确定这个解决方案不过
2) 在查看官方文档后我发现了 future watcher。我是这个工具的新手,很难理解如何应用它,这就是我创建最小可验证示例的原因。
3) 我尝试setValue
的进度条基于bash文件的执行。或者更好的它的进步如下图:
ui->progressBarExecuteScript->setValue(executeBash->readAll().toInt());
我认为这可以解决问题,但 QProgressBar
保持在 0 值,我不确定为什么。
4) 我查阅了QProgressBar官方文档,但没有找到任何可以帮助我解决问题的东西。
请指出解决此问题的正确方向。
首先修改脚本,输出当前正在做的阶段:
#!/bin/bash
set -e
echo "Stage 1/2: Scan"
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /scan > test_landing_test_2.csv
echo "Stage 2/2: Velodyne"
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /velodyne_points > vel_test_2.csv
我还添加了 set -e
以使其在第一个失败的命令时停止。
现在,您可以在输出流中查找这些 "Stage x/y" 标记,如果是,则调用 updateProgress
。
connect(this->executeBash, &QProcess::readyReadStandardOutput, [this, script = this->executeBash](){
QString s = QString::fromUtf8(script->readAll());
auto match = QRegularExpression("Stage (\d+)/(\d+): (.*)").match(s);
if (match.hasMatch()) {
int x = match.captured(1).toInt();
int y = match.captured(2).toInt();
QString stage_info = match.captured(3);
this->updateProgress(x * 100 / y);
}
});
我要通过 QPushButton
在 GUI 上执行以下 bash 脚本:
#!/bin/bash
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /scan > test_landing_test_2.csv
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /velodyne_points > vel_test_2.csv
脚本将遍历每个文件并提取相关的 .csv
。这个过程很重,需要一点时间。 问题是没有办法知道需要多长时间,除非我放一个QProgressBar
,但我不知道link的执行有多正确QProcess
和 QProgressBar
的进步正确。
截至目前提取成功,但 QProgressBar
并未从 0 移动。
下面我创建了一个最小的可验证示例,为了完整起见,可以在此处找到源代码:
mainwindow.h
#include <QMainWindow>
#include <QProcess>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_PROPERTY(float progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
float progress();
bool running();
bool finished();
public Q_SLOTS:
void startComputation();
void finishComputation();
void updateProgress(int value);
signals:
void progressChanged();
void runningChanged();
void finishedChanged();
private slots:
void on_executeBtn_clicked();
private:
Ui::MainWindow *ui;
QProcess *executeBash;
bool m_running = false;
int m_progressValue = 0;
bool m_finished = false;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->executeBash = new QProcess(this);
this->executeBash->setProcessChannelMode(QProcess::MergedChannels);
/*connect(this->executeBash, &QProcess::readyReadStandardOutput, [script = this->executeBash](){
qDebug() << "[EXEC] DATA: " << script->readAll();
});*/
connect(this->executeBash, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[script = this->executeBash](int exitCode, QProcess::ExitStatus exitStatus){
qDebug() << "[EXEC] FINISHED: " << exitCode << exitStatus;
if(script->bytesAvailable() > 0)qDebug() << "[EXEC] buffered DATA:" << script->readAll();
});
connect(this->executeBash, &QProcess::errorOccurred, [script = this->executeBash](QProcess::ProcessError error){
qDebug() << "[EXEC] error on execution: " << error << script->errorString();
});
connect(this->executeBash, &QProcess::readyReadStandardOutput, [this, script = this->executeBash](){
QString s = QString::fromUtf8(script->readAll());
qDebug() << "[EXEC] DATA: " << s;
auto match = QRegularExpression("Stage (\d+)/(\d+): (.*)").match(s);
if (match.hasMatch()) {
int x = match.captured(1).toInt();
int y = match.captured(2).toInt();
QString stage_info = match.captured(3);
qDebug() << "x = " << x;
qDebug() << "y = " << y;
qDebug() << "info = " << stage_info;
this->updateProgress(x * 100 / y);
}
});
// Initialization of the progressbar
m_running = false;
emit runningChanged();
m_finished = false;
emit finishedChanged();
updateProgress(0);
}
MainWindow::~MainWindow()
{
delete ui;
}
// ProgressBag loading depending on the workload of the .sh file
float MainWindow::progress()
{
return m_progressValue;
}
bool MainWindow::running()
{
return m_running;
}
bool MainWindow::finished()
{
return m_finished;
}
void MainWindow::startComputation()
{
m_running = true;
emit runningChanged();
updateProgress(100);
}
void MainWindow::finishComputation()
{
m_finished = true;
emit finishedChanged();
m_running = false;
emit runningChanged();
}
void MainWindow::updateProgress(int value)
{
m_progressValue = value;
emit progressChanged();
if (m_progressValue == 100)
finishComputation();
ui->progressBarExecuteScript->setValue(value);
}
void MainWindow::on_executeBtn_clicked()
{
qDebug() << "Button clicked!";
this->executeBash->start(QStringLiteral("/bin/sh"), QStringList() << QStringLiteral("/home/emanuele/catkin_docking_ws/devel/lib/test.sh")); //will start new process without blocking
// right after the execution of the script the QProgressBar will start computing
//ui->progressBarExecuteScript->setValue(executeBash->readAll().toInt());
}
到目前为止我做了什么
1) 在设置了所有必要的点后,我遇到了 this source 用户遇到了我现在遇到的同样问题,但通过 QDir
解决了,我不确定这个解决方案不过
2) 在查看官方文档后我发现了 future watcher。我是这个工具的新手,很难理解如何应用它,这就是我创建最小可验证示例的原因。
3) 我尝试setValue
的进度条基于bash文件的执行。或者更好的它的进步如下图:
ui->progressBarExecuteScript->setValue(executeBash->readAll().toInt());
我认为这可以解决问题,但 QProgressBar
保持在 0 值,我不确定为什么。
4) 我查阅了QProgressBar官方文档,但没有找到任何可以帮助我解决问题的东西。
请指出解决此问题的正确方向。
首先修改脚本,输出当前正在做的阶段:
#!/bin/bash
set -e
echo "Stage 1/2: Scan"
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /scan > test_landing_test_2.csv
echo "Stage 2/2: Velodyne"
rostopic echo -b test_LaserScan_PointCloud2_test2.bag -p /velodyne_points > vel_test_2.csv
我还添加了 set -e
以使其在第一个失败的命令时停止。
现在,您可以在输出流中查找这些 "Stage x/y" 标记,如果是,则调用 updateProgress
。
connect(this->executeBash, &QProcess::readyReadStandardOutput, [this, script = this->executeBash](){
QString s = QString::fromUtf8(script->readAll());
auto match = QRegularExpression("Stage (\d+)/(\d+): (.*)").match(s);
if (match.hasMatch()) {
int x = match.captured(1).toInt();
int y = match.captured(2).toInt();
QString stage_info = match.captured(3);
this->updateProgress(x * 100 / y);
}
});