1. 概述
使用
Qt
进行应用程序开发,主要是通过
QProcess
类用于启动外部程序并与其进行通信.
1.1. 运行进程
要启动进程,需要运行的程序的名称和命令行参数作为参数传递给
start()
。参数以
QStringList
形式提供。
start()
方法原型:
voidstart(const QString &program,const QStringList &arguments, OpenMode mode = ReadWrite)voidstart(const QString &command, OpenMode mode = ReadWrite)
或者,也可以使用
setProgram()
和
setArguments()
设置要运行的程序,然后调用
start()
或
open()
。
以下是
setProgram()
、
setArguments()
、
open()
函数原型:
// 以何种方式打开boolopen(OpenMode mode = ReadWrite) Q_DECL_OVERRIDE;// 获取program
QString program()const;// 设置programvoidsetProgram(const QString &program);// 获取参数
QStringList arguments()const;// 设置参数,参数以QStringList形式传递voidsetArguments(const QStringList & arguments);
例如,以下代码片段在X11平台上通过将包含“-style”和“fusion”的字符串作为参数列表中的两个项来以Fusion样式运行模拟时钟示例:
QObject *parent;...
QString program ="./path/to/Qt/examples/widgets/analogclock";
QStringList arguments;
arguments <<"-style"<<"fusion";
QProcess *myProcess =newQProcess(parent);
myProcess->start(program, arguments);
然后,
QProcess
进入启动状态,并在程序启动时进入运行状态并发出
started()
信号。
QProcess
允许将进程视为顺序I/O设备。因此可以像使用
QTcpSocket
访问网络连接一样写入和读取进程。然后,通过调用
write()
写入进程的标准输入,并通过调用
read()
、
readLine()
和
getChar()
读取标准输出。由于继承了
QIODevice
,
QProcess
也可以作为
QXmlReader
的输入源,或者用于生成要使用
QNetworkAccessManager
上传的数据。
当进程退出时,
QProcess
将重新进入
NotRunning
(初始状态),并发出
finished()
信号。
finished()
信号以进程的
退出码
和
退出状态
作为参数提供,还可以调用
exitCode()
获取最后完成的进程的退出码,以及
exitStatus()
获取其退出状态。如果在任何时间点发生错误,
QProcess
将发出
errorOccurred()
信号。还可以调用
error()
查找上次发生的错误类型,并调用
state()
查找当前进程状态。
注意:
QProcess
不支持
VxWorks
、
iOS
、
tvOS
、
watchOS
或
通用Windows平台
。
1.2. 通过通道进行通信
进程具有两个预定义的输出通道:
标准输出通道(stdout)
提供常规控制台输出,
标准错误通道(stderr)
通常提供进程打印的错误。这些通道代表了两个独立的数据流。
可以通过调用
setReadChannel()
在它们之间切换。
当当前读取通道上有数据可用时,
QProcess
发出
readyRead()
信号。当有新的标准输出数据可用时,它还会发出
readyReadStandardOutput()
,当有新的标准错误数据可用时,会发出
readyReadStandardError()
。之后可以通过调用
readAllStandardOutput()
或
readAllStandardError()
显式地从两个通道中读取所有数据,可以不再调用
read()
、
readLine()
或
getChar()
。
通道可以理解为对象交互之间的看得见或看不见的某种联系。请注意,进程的输出通道对应于
QProcess
的读通道,而进程的输入通道对应于
QProcess
的写通道。这是因为我们使用
QProcess
读取的是进程的输出,我们写入的是进程的输入。
QProcess
可以合并两个输出通道,使运行进程的标准输出和标准错误数据都使用标准输出通道。在启动进程之前调用
setProcessChannelMode()
并传递
MergedChannels
作为参数来激活此功能。还可以选择将运行进程的输出转发给调用主进程,通过传递
ForwardedChannels
作为参数。也可以仅转发其中一个输出通道 - 通常会使用
ForwardedErrorChannel
,但也存在
ForwardedOutputChannel
。
请注意,在
GUI
应用程序中使用通道转发通常是一个不好的主意 - 应该以图形方式呈现错误。
void QProcess::setProcessChannelMode(ProcessChannelMode)
用法:
将
QProcess
标准输出和标准错误通道的通道模式设置为指定的模式。此模式将在下次调用
start()
时使用。例如:
QProcess builder;
builder.setProcessChannelMode(QProcess::MergedChannels);
builder.start("make",QStringList()<<"-j2");if(!builder.waitForFinished())qDebug()<<"Make failed:"<< builder.errorString();elseqDebug()<<"Make output:"<< builder.readAll();
某些进程需要特殊的环境设置才能运行。可以通过调用
setProcessEnvironment()
为进程设置环境变量。要设置工作目录,请调用
setWorkingDirectory()
。默认情况下,进程在调用进程的当前工作目录中运行。
void QProcess::setProcessEnvironment(const QProcessEnvironment &environment)
用法:
设置
QProcess
将传递给子进程的环境。
例如,下面的代码添加了环境变量TMPDIR:
QProcess process;
QProcessEnvironment env =QProcessEnvironment::systemEnvironment();
env.insert("TMPDIR","C:\\MyApp\\temp");// Add an environment variable
process.setProcessEnvironment(env);
process.start("myapp");
注意:在
QNX
上,设置工作目录可能会导致除
QProcess
调用线程之外的所有应用程序线程在生成过程中暂时冻结,这是由于操作系统的限制。
2. 常用方法
2.1. 公共函数
- QStringList arguments() const:获取进程的参数列表。
- void closeReadChannel(ProcessChannel channel):关闭指定的读通道。
- void closeWriteChannel():关闭写通道。
- CreateProcessArgumentModifier createProcessArgumentsModifier() const:获取创建进程参数修改器。
- QProcess::ProcessError error() const:获取进程的错误状态。
- int exitCode() const:获取进程的退出码。
- QProcess::ExitStatus exitStatus() const:获取进程的退出状态。
- InputChannelMode inputChannelMode() const:获取输入通道的模式。
- QString nativeArguments() const:获取原生参数。
- ProcessChannelMode processChannelMode() const:获取进程通道的模式。
- QProcessEnvironment processEnvironment() const:获取进程的环境变量。
- qint64 processId() const:获取进程的ID。
- QString program() const:获取程序的路径。
- QByteArray readAllStandardError():读取并返回所有标准错误输出。
- QByteArray readAllStandardOutput():读取并返回所有标准输出。
- ProcessChannel readChannel() const:获取当前读取通道。
- void setArguments(const QStringList &arguments):设置进程的参数列表。
- void setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier):设置创建进程参数修改器。
- void setInputChannelMode(InputChannelMode mode):设置输入通道的模式。
- void setNativeArguments(const QString &arguments):设置原生参数。
- void setProcessChannelMode(ProcessChannelMode mode):设置进程通道的模式。
- void setProcessEnvironment(const QProcessEnvironment &environment):设置进程的环境变量。
- void setProgram(const QString &program):设置程序的路径。
- void setReadChannel(ProcessChannel channel):设置当前的读取通道。
- void setStandardErrorFile(const QString &fileName, OpenMode mode = Truncate):将标准错误输出重定向到指定文件。
- void setStandardInputFile(const QString &fileName):将标准输入重定向到指定文件。
- void setStandardOutputFile(const QString &fileName, OpenMode mode = Truncate):将标准输出重定向到指定文件。
- void setStandardOutputProcess(QProcess *destination):将标准输出重定向到另一个进程。
- void setWorkingDirectory(const QString &dir):设置工作目录。
- void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite):启动指定的程序。
- void start(const QString &command, OpenMode mode = ReadWrite):启动指定的命令。
- void start(OpenMode mode = ReadWrite):启动进程。
- QProcess::ProcessState state() const:获取进程的状态。
- bool waitForFinished(int msecs = 30000):等待进程完成,最多等待指定的毫秒数。
- bool waitForStarted(int msecs = 30000):等待进程启动,最多等待指定的毫秒数。
- QString workingDirectory() const:获取工作目录。
2.2. 信号
- void errorOccurred(QProcess::ProcessError error):当进程发生错误时触发。
- void finished(int exitCode, QProcess::ExitStatus exitStatus):当进程结束时触发。
- void readyReadStandardError():当标准错误输出有可读数据时触发。
- void readyReadStandardOutput():当标准输出有可读数据时触发。
- void started():当进程开始时触发。
- void stateChanged(QProcess::ProcessState newState):当进程状态发生变化时触发。
##2.3. 静态公共成员
- int execute(const QString &program, const QStringList &arguments):执行指定的程序。
- int execute(const QString &command):执行指定的命令。
- QString nullDevice():获取空设备的路径。
- bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(), qint64 *pid = Q_NULLPTR):以分离模式启动指定的程序。
- bool startDetached(const QString &command):以分离模式启动指定的命令。
- QStringList systemEnvironment():获取系统的环境变量。
3. 示例之调用进程
调用进程实现相对简单。
首先是调用端,先创建一个UI,如下:
在头文件里添加:
QProcess* m_pProcess =nullptr;
然后在“调用客户端”槽函数内,添加:
if(!m_pProcess){
m_pProcess =newQProcess(this);// 完成时调用connect(m_pProcess,static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),this,[=](int exitCode, QProcess::ExitStatus exitStatus){Q_UNUSED(exitCode)Q_UNUSED(exitStatus)});// 进程错误时触发connect(m_pProcess,static_cast<void(QProcess::*)(QProcess::ProcessError)>(&QProcess::error),this,[=](QProcess::ProcessError error1){Q_UNUSED(error1)});// 读取connect(m_pProcess,&QProcess::readyRead,this,[this](){if(!m_pProcess)return;
QString strOutput =QString("[客户端发送输出 ] %1").arg(QString(m_pProcess->readAllStandardOutput()));
ui->textBrowser->append(strOutput);});// 读取标准错误信息connect(m_pProcess,&QProcess::readyReadStandardError,this,[=](){
QString strError =QString("[客户端发送错误 ] %1").arg(QString(m_pProcess->readAllStandardError()));
ui->textBrowser->append(strError);});// 状态改变时触发connect(m_pProcess,&QProcess::stateChanged,this,[=](QProcess::ProcessState state){Q_UNUSED(state)});}// 启动进程 m_pProcess->start("C:/Users/Desktop/process.exe");
然后是被调用的进程端:
UI:
启动后,显示如下;
点击“调用客户端”
4. 进程间的通信
进程间的通信,当通过进程调用后,调用端如下方代码所示,通过绑定
readyRead
和
readyReadStandardError
可以获取输出和错误
// 读取connect(m_pProcess,&QProcess::readyRead,this,[this](){if(!m_pProcess)return;
QString strOutput =QString("[客户端发送输出 ] %1").arg(QString(m_pProcess->readAllStandardOutput()));
ui->textBrowser->append(strOutput);});// 读取标准错误信息connect(m_pProcess,&QProcess::readyReadStandardError,this,[=](){
QString strError =QString("[客户端发送错误 ] %1").arg(QString(m_pProcess->readAllStandardError()));
ui->textBrowser->append(strError);});
但调用端发送信息后,被调用端需要处理接收到的数据。
调用端,在构造里,启动一个线程
QtConcurrent::run(this,&MainWindow::readstdin);
voidMainWindow::readstdin(){bool ok =true;char chBuf[4096];
DWORD dwRead;
HANDLE hStdinDup;const HANDLE hStdin =GetStdHandle(STD_INPUT_HANDLE);if(hStdin == INVALID_HANDLE_VALUE)return;DuplicateHandle(GetCurrentProcess(), hStdin,GetCurrentProcess(),&hStdinDup,0,false, DUPLICATE_SAME_ACCESS);CloseHandle(hStdin);while(ok){
ok =ReadFile(hStdinDup, chBuf,sizeof(chBuf),&dwRead,NULL);// emit sig_log(QLatin1String("ok is:")+QString::number(ok));if(ok && dwRead !=0){
emit sig_receivedCommand(QString::fromUtf8(chBuf, dwRead));}}}
以上代码作用:
从一个标准输入流读取数据,并将其转换为QString对象,然后通过一个信号(sig_receivedCommand)发送出去。
bool ok = true;
- 定义一个布尔变量ok
并初始化为true
。这个变量用于检查是否成功地从标准输入读取了数据。char chBuf[4096];
- 定义一个字符数组chBuf
,大小为4096字节。这个数组用于存储从标准输入读取的数据。DWORD dwRead;
- 定义一个DWORD类型的变量dwRead
,用于存储实际读取的字节数。HANDLE hStdinDup;
- 定义一个句柄变量hStdinDup
,用于存储标准输入的副本的句柄。const HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
- 获取标准输入的句柄,并将其存储在变量hStdin
中。if (hStdin == INVALID_HANDLE_VALUE) return;
- 检查是否成功获取了标准输入的句柄。如果没有,则直接返回。DuplicateHandle(GetCurrentProcess(), hStdin, GetCurrentProcess(), &hStdinDup, 0, false, DUPLICATE_SAME_ACCESS);
- 创建一个标准输入句柄的副本,并将其存储在变量hStdinDup
中。这样做的目的是为了在关闭原始句柄后仍然能够读取数据。CloseHandle(hStdin);
- 关闭原始的标准输入句柄。现在只有副本句柄hStdinDup
是打开的。while (ok) { ... }
- 一个无限循环,持续读取标准输入的数据,直到读取失败。ok = ReadFile(hStdinDup, chBuf, sizeof(chBuf), &dwRead, NULL);
- 从标准输入的副本中读取数据到chBuf
数组中。读取的字节数存储在dwRead
中,并更新ok
变量的状态。if (ok && dwRead != 0) { ... }
- 检查是否成功读取了数据,并且实际读取的字节数不为0。如果满足这两个条件,则执行花括号中的代码。emit sig_receivedCommand(QString::fromUtf8(chBuf, dwRead));
- 将读取的数据(存储在
chBuf
中)转换为QString对象,并通过信号(sig_receivedCommand)发送出去。这里假设程序使用了Qt框架。
}
- 结束while循环。如果读取失败,循环将终止。
结果如下所示:
5. 进程间的通信之完整代码链接
完整示例、具体代码链接: https://download.csdn.net/download/MrHHHHHH/88719913?spm=1001.2014.3001.5501
版权归原作者 FreeLikeTheWind. 所有, 如有侵权,请联系我们删除。