本文介绍了如何使用Qt的QProcess 进行程序开发,包括启动进程间通信、设置环境变量、通用方法;方便在日常开发中使用;
1.使用Qt
进行程序开发,可以通过QProcess
类用于启动外部程序并与其进行通信.;
进程A(例如主程序)创建了一个QProcess B,这个B就称为A的子进程,而A称为B的父进程。
1.1. 运行进程
可以使用 Start or Open
要启动进程,需要运行的程序的名称和命令行参数作为参数传递给start()。参数以QStringList形式提供。
start()方法原型:
void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite)
void start(const QString &command, OpenMode mode = ReadWrite)
也可以使用setProgram()
和setArguments()
设置要运行的程序,然后调用start()
或open()
。
以下是setProgram()
、setArguments()
、open()
函数原型:
bool open(OpenMode mode = ReadWrite) Q_DECL_OVERRIDE;
QString program() const;
void setProgram(const QString &program);
QStringList arguments() const;
void setArguments(const QStringList & arguments);
1.2 环境运行设置
因为有一些程序会有依赖,所以需要设备运行环境:
通过调用setProcessEnvironment()
为进程设置环境变量。
要设置工作目录,请调用setWorkingDirectory()
。默认情况下,进程在调用进程的当前工作目录中运行
原型:
void QProcess::setProcessEnvironment(const QProcessEnvironment &environment)
样例:
QProcess process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("PATH", "E:\\Program"); // Add an environment variable
process.setProcessEnvironment(env);
process.start("app.exe");
1.3. 进程间通信
1.3.1关于父进程读写子进程的数据,主要是用到
子进程接收到了父进程数据,两个信号会发射出来:
void readyReadStandardError()
void readyReadStandardOutput()
父进程通过上面这两个信号,判断读取子进程发来的消息;
QByteArray QProcess::readAllStandardError()
QByteArray QProcess::readAllStandardOutput()
QProcess::write()发出信息
1.3.2 关于子进程读写父进程的数据,主要是用到
Windows中:需要开启一个线程来管理stdin的文件变化,这个需要使用Windows API函数
linux中:使用QSocketNotifier 监听 stdin文件,当改文件有变化是,读取信息
ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);// get hstdinDup handle data
这个是阻塞函数,类似控制台程序中的cin >> data,或者gets()。
因此需要开启一个线程来管理stdin的文件变化(函数中有涉及到while阻塞,一直检测标准输入通道stdin是否有可读取的信息,因此另开线程),在pro文件中添加:QT +=concurrent。实例代码如下:
QFuture<void> fu=QtConcurrent::run(this,&Widget::readStdin);// open a thread
void Widget::readStdin()
{
bool ok=true;
char chbuf[1024];
DWORD dwRead;
HANDLE hStdinDup;//HANDLE
const HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);//GetStdHandle
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));
}
}
}
linux平台:QFile来读取标准输入
QProcess子进程通过QFile来读取标准输入来接收父进程信息。通过QFile绑定QSocketNotifier来接收标准输入的实时信号,因为QSocketNotifier的activated信号可以通过标准输入是否有消息实时触发。
QProcess子进程通过QFile绑定标准输出stdout来发送消息给父进程。
注意:子进程读取信息不能通过QFile的readline等接口读取父进程信息,因为QFile他会读取标准输入的所有信息,并且不到长度就没有返回
m_file.open(stdin, QFile::ReadOnly); //stdin=0
if (m_file.isOpen())
{
m_clientsocket = new QSocketNotifier(m_file.handle(), QSocketNotifier::Read,this);
connect(m_clientsocket,SIGNAL(activated(int)),this,SLOT(slotreaddata(int)));
}void MainWindow::slotreaddata(int fd)
{
if(fd != m_file.handle() )
return; char buf[128] = {0};
read(fd,buf,sizeof(buf));
m_readedit->append(QString::fromUtf8("rev msg:%1").arg(buf));
}
2. 程序样例:
主进程
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
if(m_pProcess)
{
m_pProcess->close();
}
delete ui;
}
void MainWindow::on_btn_invokingClient_clicked()
{
if(!m_pProcess)
{
m_pProcess = new QProcess(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("G:/workspace/Qt_process-invoke-client/build-client-Desktop_Qt_5_12_4_MSVC2017_64bit-Debug/debug/client.exe");
}
void MainWindow::on_btn_sendValueToClient_clicked()
{
if(!m_pProcess)
return;
// 可输中文
m_pProcess->write(ui->lineEdit->text().toUtf8());
}
子进程:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFile>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("client");
QFuture<void> f1 = QtConcurrent::run(this, &MainWindow::readstdin);
connect(this,&MainWindow::sig_receivedCommand, this, [&](QString text){
ui->textBrowser->append("[读取标准输入 ] "+ text);
});
connect(this, &MainWindow::sig_log, this, &MainWindow::slot_print);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btn_sendOutputToInvoker_clicked()
{
QString text = ui->lineEdit->text();
if(text.isEmpty()) return;
#if 0
QFile fileout;
if(fileout.open(stdout,QIODevice::WriteOnly)){
fileout.write(text.toStdString().c_str());
}else{
printErr("open fail");
}
fileout.close();
#else
std::cout << text.toStdString() << std::endl;
#endif
}
void MainWindow::printErr(const QString &errText)
{
QFile fileerr;
if(fileerr.open(stderr,QIODevice::WriteOnly)){
fileerr.write(errText.toStdString().c_str());
}
fileerr.close();
}
void MainWindow::on_btn_sendErrToInvoker_clicked()
{
QString text = ui->lineEdit->text();
if(text.isEmpty()) return;
#if 1
QFile fileerr;
if(fileerr.open(stderr,QIODevice::WriteOnly)){
fileerr.write(text.toStdString().c_str());
}
fileerr.close();
#else
std::cerr << text.toStdString() << std::endl;
#endif
}
void MainWindow::slot_print(const QString &text)
{
ui->textBrowser->append("[读取标准输入 ] "+ text);
}
void MainWindow::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));
}
}
}