如何将 QProcess 的执行与 QProgressBar 的推进联系起来以实现非常繁重的计算循环
Posted
技术标签:
【中文标题】如何将 QProcess 的执行与 QProgressBar 的推进联系起来以实现非常繁重的计算循环【英文标题】:How to link the execution of QProcess and the advancement of the QProgressBar for a very heavy computation loop 【发布时间】:2020-05-13 21:32:18 【问题描述】:我有以下 bash 脚本要通过 QPushButton
在 GUI 上执行:
#!/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
,但我不知道如何正确链接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官方文档,但没有找到任何可以帮助我解决问题的东西。
请指出正确的方向来解决这个问题。
【问题讨论】:
我认为不可能获得QProcess
的推进,因为您不知道该过程可以存在多少时间。所以一个可能的解决方案是将进度条的最小值、最大值和当前值设置为 0,以获得无限的进度
感谢您阅读问题。您的意思是在此期间无限进展正在工作,何时完成将值设置为 100?
@Emanuele QProgressBar 只显示进度(它是一个小部件),它不计算它。计算的任务是你的工作,例如,如果你下载 100 个文件,那么你可以指出每个文件是 1%,但另一个开发人员可以计算所有文件的大小(以字节为单位),然后将进度计算为下载百分比bytes 显然这两个数字是不同的,所以如果你不知道如何计算它,那么其他人是不可能帮助你的。在这些中最好设置progressbar->setRange(0, 0)
,这将成为一个忙碌的酒吧
@eyllanesc,感谢您阅读问题。问题是我不知道第一个命令何时结束(即我不知道提取.csv
的物理时间)并且我不知道第二个命令何时结束(即开始第二次提取及其持续时间)将发生。
看来QProcess
不是一个好选择?
【参考方案1】:
首先,修改脚本以输出当前正在执行的阶段:
#!/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);
);
【讨论】:
感谢您访问并阅读问题。您的解决方案部分有效,实际上在终端上我可以看到两个标志正在正确处理:)。但是QProgressBar
不会从 0 开始移动。我错过了什么吗?我更新了问题,添加了正确触发 QProcess
的答案
我认为你的第一个回调(使用[EXEC] DATA
)在第二个回调之前吃掉了输出。
我找到了丢失的部分!基本上我没有正确更新进度条并在函数updateProgress
中添加了ui->progressBarExecuteScript->setValue(value);
。也是的,你是对的,第一个回调(带有[EXEC] DATA
)正在吃掉输出。
我在问题中发布了正确的工作代码,添加了您的建议并添加了进度条的缺失部分。感谢您的帮助和时间,非常感谢!另外,如果您认为我的问题很清楚,请考虑给我一个大拇指:)。以上是关于如何将 QProcess 的执行与 QProgressBar 的推进联系起来以实现非常繁重的计算循环的主要内容,如果未能解决你的问题,请参考以下文章