如何使用 QProcess 避免 waitForStarted 以阻止 GUI 冻结?

Posted

技术标签:

【中文标题】如何使用 QProcess 避免 waitForStarted 以阻止 GUI 冻结?【英文标题】:How to avoid waitForStarted with QProcess to stop GUI from freezing? 【发布时间】:2016-07-25 20:00:05 【问题描述】:

我正在使用 QProcess 运行 wscript 来运行将 Excel 文件转换为制表符分隔的文本文件的 VB 脚本。该脚本运行良好,一切正常,但 GUI 冻结,用户在很长一段时间内无法与其交互。代码如下:

/* Create txt files and store paths */
for (int i = 0; i < excelFilepaths.size(); ++i)     
    wscript->start("wscript.exe", QStringList() << vbs.fileName() << excelFilepaths.at(i) << newDir.absolutePath() + "/" + QString::number(i + 1));
    wscript->waitForFinished();
    payloadPaths.push_back(newDir.absolutePath() + "/" + QString::number(i + 1));

所以我有多个 excel 文件路径和一个在堆上分配的 QProcess。此 QProcess 运行将 excel 文件转换为文本文件的 VB 脚本,然后存储新文本文件的路径。这需要很长时间(4 个 excel 文件大约需要 20 秒)。在此期间,GUI 被冻结。我希望用户能够使用不干扰该过程的部分 GUI。

现在我怀疑这个问题的原因是

QProcess::waitForFinished()

我在网上阅读了有关连接 QProcess 的 finished() 和 error() 信号以消除此问题的信息。但是我一直很难做到这一点。我将此代码作为继承自 QObject 并包含 Q_OBJECT 宏的类的方法运行,因此应设置所有内容。我只是需要一些帮助把剩下的部分放在一起。我怎样才能让它在 QProcess 运行时我的 GUI 不会冻结?请帮忙。

【问题讨论】:

但是我在这样做时遇到了困难 你有什么尝试?什么失败了? “我怎样才能让我的 GUI 在 QProcess 运行时不会冻结?” - 通过 “连接完成()和错误()信号QProcess" 到本地槽,并删除同步的waitForFinished 调用。 我该如何连接这些信号呢?这如何消除我的问题?我想我会做类似 connect(process, &QProcess::finished, this, &CustomClass::slotHere) 之类的事情。或者我可以摆脱自定义插槽并执行 connect(process, &QProcess::finished, lambdaHere)。一个例子会很有帮助 【参考方案1】:

引用Synchronous Process API部分的文档:

waitForStarted() 阻塞,直到进程开始。 waitForReadyRead() 阻塞,直到有新数据可用于在当前读取通道上读取。 waitForBytesWritten() 阻塞,直到一个有效负载数据写入进程。 waitForFinished() 阻塞直到进程完成。

从主线程(调用 QApplication::exec() 的线程)调用这些函数可能会导致您的用户界面冻结。

请记住这一点。但是,您可以使用 that 之类的方法解决此问题:

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus) /* ... */ );

请注意,还有更多 signals 可能适合任何所需用途。

【讨论】:

【参考方案2】:

我遇到了同样的问题,但使用的是 QSerialPort。但是,我认为解决方案是相同的。我找不到让“serial->waitForReadyRead()”不冻结 GUI 的方法,所以我实现了自己的函数。

void Research::WaitSerial(int MilliSecondsToWait)

    QTime DieTime = QTime::currentTime().addMSecs(MilliSecondsToWait);
    flag = 0;
    while(QTime::currentTime() < DieTime && !flag)
    
        QCoreApplication::processEvents(QEventLoop::AllEvents,100);
        if(BufferSerial != "")
        
            flag++;
        
    

当然,你的问题类似但不一样。只需更改 if 即可获得“停止条件”。希望这会有所帮助。


编辑:这最初不是我的想法。我在某处的论坛上找到了它。所以我不拿学分。

【讨论】:

与往常一样,轮询只是次优解决方案。 Qt 使用signals & slots 来驱动其基于事件的框架。 你能解释一下这里发生了什么吗?在我看来,您在 QProcess 运行时手动停止了程序。对吗? @Dillydill123 嗯,是的。我正是这样做的。我只是暂停程序,直到串行发送一些东西。就我而言,它与WaitForReadyRead 相同。我不知道如何使用信号和插槽,抱歉。 使用QCoreApplication::processEvents(QEventLoop::AllEvents,100);makes 这样GUI就不会因为处理当前线程的所有事件而冻结。例如,允许您在窗口等待时移动它。至于QTime DieTime = QTime::currentTime().addMSecs(MilliSecondsToWait);,这只是它等待的时间。剩下的只是我的停留点。如上所述,这不是最好的选择.....但它是一种快速且易于理解的选择。

以上是关于如何使用 QProcess 避免 waitForStarted 以阻止 GUI 冻结?的主要内容,如果未能解决你的问题,请参考以下文章

React 测试库如何使用 waitFor

Flux waitFor() 和异步操作,如何建模。

如何使用 QProcess 同时运行多个 python 脚本

如何使用 QProcess 运行命令行?

如何读取 QProcess 输出

如何使用 QProcess 启动 Shell 脚本?