如何从分离的QProcess中读取stdout / stderr

时间:2014-03-22 07:39:25

标签: c++ multithreading qt

我是Qt的初学者。我需要从桌面应用程序中的按钮调用命令行程序。该计划下载YouTube视频。我还需要从中读取标准错误。我写了以下代码:

void YoutubeDL::on_downloadButton_clicked()
{
    [...]

    QProcess p;
    p.startDetached("youtube-dl -f " + get + " " + ui->urlBox->text());
    QString perr = p.readAllStandardError();
    if (perr.length())
        ui->descBox->setText("Error during download.\n" + perr);
    else
        ui->descBox->setText("Download completed!");
}

然而,stderr读取不会发生。

另一方面,如果我使用非分离p.start()然后waitForFinished(-1),那么我可以读取stderr,但GUI会冻结,直到下载完成。

如何解决这个问题?

一个相关的问题:我还想要一些方法能够实时读取下载过程的输出,以便我可以在GUI中显示它。 youtube-dl给出了这样的进度报告:

[download]   0.0% of 2.00MiB at 173.22KiB/s ETA 00:12
[download]   0.1% of 2.00MiB at 105.01KiB/s ETA 00:19
[download]   0.3% of 2.00MiB at 96.86KiB/s ETA 00:21
[download]   0.7% of 2.00MiB at 105.23KiB/s ETA 00:19
[download]   1.5% of 2.00MiB at 100.29KiB/s ETA 00:20
...

我希望能够在生成它们时阅读这些内容。

3 个答案:

答案 0 :(得分:5)

由于问题的性质,上述所有答案都不正确。 @aakashjain要求分离进程。你们建议的仅适用于启动过程仍然附加的情况。

QProcess p
p.startDetached(...)

相同
QProcess::startDetached(...)

QProcess :: startDetached()是一个静态方法,它不是任何对象的一部分或返回一个。一旦调用此方法并且成功,该过程将不再以任何方式附加到生成它的过程(您的应用程序)。

我建议你先查看official documentation on this method,然后阅读更多关于interprocess communication以及如何将一个进程的输出传递给另一个进程(在这种情况下是对终端的分离进程)。

我偶然发现了在PyQt4中使用 QProcess :: startDetached()进行基本控制的问题,其中我做了@DmitrySokolov建议的内容。令我惊讶的是,状态始终为零,使用QProcess非静态方法无法设置或检索任何内容。在进行了一些挖掘和询问后,我确切地指出了我在答案开始时所写的内容 - 如果使用 QProcess :: startDetached(),则后面的控件必须通过系统工具(例如 kill 命令,管道等),因为你没有可以实际使用的对象。

QProcess :: startDetached()提供两个重要的返回值:

  • 方法本身的返回值 - 一个布尔值,它告诉您进程的启动是否成功(== true)或失败(== false)
  • 函数的qint64 * pid指针参数 - 如果进程已成功启动, pid 将包含已分离进程的PID(进程标识符)< / LI>

您可以使用此PID与您喜欢的过程进行交互(当然,过程本身也允许您这样做)) - 您可以终止它,管道输出,让它进入睡眠状态等。

现在回到你的问题。这里有三种解决方案:

  • 启动子进程 - 该进程将包含在QProcess对象中,您可以像@DmitrySokolov所述进行交互。但是这会阻止你的主线程,它处理UI直到子进程完成它的任务并且已经停止(主线程在你的Qt应用程序的进程内运行)。我怀疑你是否希望你的用户界面冻结(正如你自己试验的那样),特别是因为我们正在讨论下载可能大量的视频数据......
  • 启动一个独立的过程 - 我在此解决方案列表之前所描述的内容。然而,它需要更多地了解进程间通信。如果你愿意并且想花一些时间研究这个话题(我只能鼓励你这样做!),做吧
  • 在其中启动一个主题和一个子进程 - 不仅您对下载任务有一个非常巧妙的控制,而且您还可以通过插槽和信号向UI提供很好的反馈,这将改善用户体验很多。这是此类任务使用最广泛的方式,我还可以在评论中看到您想要在ui->descBox中输出进度(您还可以添加QProgressbar以使输出更加用户友好并执行标准输出/ stderr在后台的东西只为你看)。我没有对此进行测试,但实际上它应该可行。

答案 1 :(得分:2)

在开始流程之前,将QProcess信号连接到您的广告位:

void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardError()
void readyReadStandardOutput()

finished()信号被触发时,您可以使用sdterr方法读取所有readAllStandardError()输出,或者当stderrreadyReadStandardError()时,您可以阅读部分youtube-dl数据触发。

据我了解,stdout将进度数据输出到readyReadStandardOutput。因此,您可以在触发class YoutubeDL { ... private: QProcess p; void on_process_finished(int exitCode, QProcess::ExitStatus exitStatus); void on_process_readyReadStandardOutput(); }; void YoutubeDL::on_downloadButton_clicked() { ... p.connect(&p, &QProcess::readyReadStandardOutput, this, &YoutubeDL::on_process_readyReadStandardOutput); p.connect(&p, (void (QProcess::*)(int,QProcess::ExitStatus))&QProcess::finished, this, &YoutubeDL::on_process_finished); p.start("youtube-dl -f " + get + " " + ui->urlBox->text()); } void YoutubeDL::on_process_finished(int exitCode, QProcess::ExitStatus exitStatus) { QString perr = p.readAllStandardError(); if (perr.length()) ui->descBox->setText("Error during download.\n" + perr); else ui->descBox->setText("Download completed!"); } void YoutubeDL::on_process_readyReadStandardOutput() { p.setReadChannel(QProcess::StandardOutput); QTextStream stream(&p); while (!stream.atEnd()) { QString line = stream.readLine(); // extract progress info from line and etc. ... } } 信号时阅读并解析。

<强>更新

{{1}}

答案 2 :(得分:1)

不要等完了。而不是使用QProcess信号readyReadStandardError通知您可以读取任何内容。