首页 Qt 学习之路 2 Qt 学习之路 2(69):进程

Qt 学习之路 2(69):进程

17 2.7K

进程是操作系统的基础之一。一个进程可以认为是一个正在执行的程序。我们可以把进程当做计算机运行时的一个基础单位。关于进程的讨论已经超出了本章的范畴,现在我们假定你是了解这个概念的。

在 Qt 中,我们使用QProcess来表示一个进程。这个类可以允许我们的应用程序开启一个新的外部程序,并且与这个程序进行通讯。下面我们用一个非常简单的例子开始我们本章有关进程的阐述。

//!!! Qt5
QString program = "C:/Windows/System32/cmd.exe";
QStringList arguments;
arguments << "/c" << "dir" << "C:\\";
QProcess *cmdProcess = new QProcess;
QObject::connect(cmdProcess, &QProcess::readyRead, [=] () {
    QTextCodec *codec = QTextCodec::codecForName("GBK");
    QString dir = codec->toUnicode(cmdProcess->readAll());
    qDebug() << dir;
});
cmdProcess->start(program, arguments);

这是一段 Qt5 的程序,并且仅能运行于 Windows 平台。简单来说,这段程序通过 Qt 开启了一个新的进程,这个进程相当于执行了下面的命令:

C:\\Windows\\System32\\cmd.exe /c dir C:\\

注意,我们可以在上面的程序中找到这个命令的每一个字符。事实上,我们可以把一个进程看做执行了一段命令(在 Windows 平台就是控制台命令;在 Linux 平台(包括 Unix)则是执行一个普通的命令,比如 ls)。我们的程序相当于执行了 dir 命令,其参数是 C:\,这是由arguments数组决定的(至于为什么我们需要将 dir 命令作为参数传递给 cmd.exe,这是由于 Windows 平台的规定。在 Windows 中,dir 命令并不是一个独立的可执行程序,而是通过 cmd.exe 进行解释;这与 ls 在 Linux 中的地位不同,在 Linux 中,ls 就是一个可执行程序。因此如果你需要在 Linux 中执行 ls,那么program的值应该就是 ls )。

上面程序的运行结果类似于:

驱动器 C 中的卷是 SYSTEM

卷的序列号是 EA62-24AB

 C:\ 的目录

2013/05/05  20:41             1,024 .rnd
2013/08/22  23:22    <DIR>          PerfLogs
2013/10/18  07:32    <DIR>          Program Files
2013/10/30  12:36    <DIR>          Program Files (x86)
2013/10/31  20:30            12,906 shared.log

2013/10/18  07:33    <DIR>          Users
2013/11/06  21:41    <DIR>          Windows
               2 个文件         13,930 字节
               5 个目录 22,723,440,640 可用字节

上面的输出会根据不同机器有所不同。豆子是在 Windows 8.1 64位机器上测试的。

为了开启进程,我们将外部程序名字(program)和程序启动参数(arguments)作为参数传给QProcess::start()函数。当然,你也可以使用setProgram()setArguments()进行设置。此时,QProcess进入Starting 状态;当程序开始执行之后,QProcess进入Running 状态,并且发出started()信号。当进程退出时,QProcess进入NotRunning状态(也是初始状态),并且发出finished()信号。finished()信号以参数的形式提供进程的退出代码和退出状态。如果发送错误,QProcess会发出error()信号

QProcess允许你将一个进程当做一个顺序访问的 I/O 设备。我们可以使用write()函数将数据提供给进程的标准输入;使用read()readLine()或者getChar()函数获取其标准输出。由于QProcess继承自QIODevice,因此QProcess也可以作为QXmlReader的输入或者直接使用QNetworkAccessManager将其生成的数据上传到网络。

进程通常有两个预定义的通道:标准输出通道(stdout)和标准错误通道(stderr)。前者就是常规控制台的输出,后者则是由进程输出的错误信息。这两个通道都是独立的数据流,我们可以通过使用setReadChannel()函数来切换这两个通道。当进程的当前通道可用时,QProcess会发出readReady()信号。当有了新的标准输出数据时,QProcess会发出readyReadStandardOutput()信号;当有了新的标准错误数据时,则会发出readyReadStandardError()信号。我们前面的示例程序就是使用了readReady()信号。注意,由于我们是运行在 Windows 平台,Windows 控制台的默认编码是 GBK,为了避免出现乱码,我们必须设置文本的编码方式。

通道的术语可能会引起误会。注意,进程的输出通道对应着QProcess 通道,进程的输入通道对应着QProcess 通道。这是因为我们使用QProcess“读取”进程的输出,而我们针对QProcess的“写入”则成为进程的输入。QProcess还可以合并标准输出和标准错误通道,使用setProcessChannelMode()函数设置MergedChannels即可实现。

另外,QProcess还允许我们使用setEnvironment()为进程设置环境变量,或者使用setWorkingDirectory()为进程设置工作目录。

前面我们所说的信号槽机制,类似于前面我们介绍的QNetworkAccessManager,都是异步的。与QNetworkAccessManager不同在于,QProcess提供了同步函数:

  • waitForStarted():阻塞到进程开始;
  • waitForReadyRead():阻塞到可以从进程的当前读通道读取新的数据;
  • waitForBytesWritten():阻塞到数据写入进程;
  • waitForFinished():阻塞到进程结束;

注意,在主线程(调用了QApplication::exec()的线程)调用上面几个函数会让界面失去响应。

17 评论

渡世白玉 2013年11月10日 - 08:00

赞!!!!豆子老师威武、、

回复
tomisacat 2013年11月10日 - 09:40

标准库里string用string str("abc")初始化好,QString是不是也是这样?

回复
豆子 2013年11月10日 - 10:17

这是 C++ 标准的要求,就看有没有类似的构造函数。在这里,QString 也可以使用类似的语句进行初始化,例如 QString str("abc")

回复
xiaopp 2013年11月12日 - 08:53

其实这是一个效率问题
string str;
str = "abc";
实际上执行了2个动作,一是构造了str,第二是赋值

string str("abc");
是用了string的其中一个构造函数,只是做了一个动作

再几百万次的时候会有明显的区别

回复
datde 2014年6月4日 - 12:36

如何打开 windows记事本? 需要写绝度路径么

回复
豆子 2014年6月5日 - 09:40

如果能够在 path 中找到笔记本路径就不需要绝对路径了。也就是说,如果你在 Windows 运行中能够使用 notepad 打开笔记本,使用 Qt 应该也是可以的。具体没有测试 ;-P

回复
大川 2014年8月20日 - 10:08

您好,这个例子执行以后,我的怎么是乱码呢。

回复
豆子 2014年8月20日 - 21:07

乱码一般是因为编码格式,注意编码是不是 GBK,如果不是,需要按照实际编码修改代码。

回复
eckelcn 2014年9月1日 - 16:52

QObject::connect(cmdProcess, &QProcess::readyRead ... 这句是不是放到cmdProcess->start(program, arguments);前面更妥一些?

回复
豆子 2014年9月2日 - 09:47

应该放在前面更好一些,多谢提醒

回复
Shuai99 2014年10月30日 - 10:01

请问这个问题怎么解决,我刚学Qt
error C2248: “QIODevice::readyRead”: 无法访问 protected 成员(在“QIODevice”类中声明)

回复
豆子 2014年11月5日 - 12:50

不清楚你是怎么使用的?是不是用的 Qt 4 的版本?

回复
Shuai99 2014年11月5日 - 12:55

是的,Qt4.8,

回复
daguo 2016年8月11日 - 00:01

这个问题怎么解决的

回复
风搁浅 2016年6月29日 - 18:54

你们按照上面的没有问题吗?我的lambda()表达式就是出错了

回复
PG134ever 2020年1月15日 - 11:39

请问write() read() 这一部分有参考例程吗

回复
豆子 2020年1月17日 - 21:11

目前没有实际的例子,就是按照文档来编写的

回复

回复 豆子 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2