QProcess调用外部进程

前言

  在Qt程序开发过程中,往往我们会调用一些外部的程序,实现特定的功能,并减少开发工作量。例如有一个项目需要使用Https接口下载一些文件和上传下载一些数据,Qt本身虽然封装了OpenSSL的接口,但是安装包中SDK并不带编译完成的OpenSSL的库文件,需要用户自己手动编译对应版本的OpenSSL(Qt对应的OpenSSL版本号可以调用QSslSocket::sslLibraryBuildVersionString()查看),msvc版本相对比较好编译,但是mingw版本就很费事。关于mingw编译三方库可以参考我的另外一篇。所以最后项目采用wget for windows,利用wget的功能去实现我们想要的功能。wget的压缩包里面已经自带了OpenSSL的库文件。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
bool bRet = false;
QStringList args;
//当前wget版本不支持timeout,后续如果有需求需要下载新版wget
args << "--no-check-certificate" << "-c" << "-O" << "wechat.exe" << m_url;

connect(m_pWgetProcess, &QProcess::readyReadStandardOutput, [&]() {
//这里需要死循环处理,不然当wget下载完成,输出还没有读完,导致无法通知进度
while (m_pWgetProcess->bytesAvailable() > 0) {
QString text = m_pWgetProcess->readLine();
QRegExp reg("[\\s0-9K]+[\\.\\s]+([0-9]{1,3})%\\s+([0-9\\.]+[KM])\\s+([0-9hms]+).*");
reg.setCaseSensitivity(Qt::CaseInsensitive);
if (reg.exactMatch(text)) {
int progress = reg.cap(1).toInt();
QString curSize = reg.cap(2).toLocal8Bit();
QString time = reg.cap(3).toLocal8Bit();
emit s_UpdateProgress(progress, time);
} else if (text.contains("100%")) {
emit s_UpdateProgress(100, "0s");
} else if (text.contains("saved") && text.contains(QString("[%1/%2]").arg(m_iFileSize).arg(m_iFileSize))) {
emit s_haveDone();
}

}
});
connect(m_pWgetProcess, &QProcess::readyReadStandardError, [&]() {
qWarning() << m_pWgetProcess->readAllStandardError();
emit s_haveError(2, m_pWgetProcess->readAllStandardError());
});
m_pWgetProcess->setReadChannel(QProcess::StandardOutput);
m_pWgetProcess->setProcessChannelMode(QProcess::MergedChannels);
m_pWgetProcess->start(m_wgetFilePath, args);
bRet = m_pWgetProcess->waitForFinished(-1);
if (!bRet) {
qDebug() << m_pWgetProcess->errorString();
emit s_haveError(2, m_pWgetProcess->errorString());
}

要点

&emsp;&emsp;上述代码非常简单,采用wget下载一个微信程序,实时通知下载进度并通知下载失败或完成,需要注意以下几点:

  • QProcess启动子进程推荐使用cmd和args分离的形式,适合参数比较复杂的情况

  • 需要获取QProcess输出的时候,一般只读StandardOutput通道即可,StandardErro通道会有无意义的错误返回输出,导致解析难度加大

  • start方式启动的子进程需要调用waitForFinished或其他wait接口才能获取到输出,默认是阻塞30s,如果任务超时就会自动退出。需要等待子进程自己退出那么需要将参数传入-1。

  • 在获取输出时采用readLine()按行读取解析时,如果输出过于庞大,会出现输出还没有获取完,但是子进程已经退出了(注意此时QProcess其实已经获取了全部输出在缓存里面了,在wait接口后面可以readAll获取到,但是我们是希望在QProcess::readyReadStandardOutput的槽函数里面同步处理)。这里就可以采用while循环的方式,将全部输出全部按行解析。


QProcess调用外部进程
http://yoursite.com/2020/05/06/QProcess调用外部进程/
作者
还在输入
发布于
2020年5月6日
许可协议