QProcess

QProcess可以用于启动外部程序,具有两种方式。Qt 使用 QProcess 类完成进程间交互。

简介

运⾏过程要启动进程需要将要运⾏的程序的名称和命令⾏参数作为参数传递给start()函数。设置参数的⽅式有两种:

  • ⽅式⼀,将参数保存在QStringList对象中;
  • ⽅式⼆,调⽤setProgram()函数和setArguments()函数进⾏设置。

调⽤start()函数或open()函数启动进程。在程序启动后,QProcess进⼊运⾏状态并发出started()信号。

	QObject *parent;
    ...
    QString program = "./path/to/Qt/examples/widgets/analogclock";
    QStringList arguments;
    arguments << "-style" << "fusion";

    QProcess *myProcess = new QProcess(parent);
    myProcess->start(program, arguments);

当进程退出时,QProcess重新进⼊NotRunning状态(初始状态),并发出finish()信号。finish()信号将进程的退出代码和退出状态作为参数提供,开发⼈员还可以调⽤exitCode()函数获得最后完成的进程的退出代码,并调⽤exitStatus()函数获得其退出状态。如果在任何时间点发⽣错误,QProcess将发出errorOccurred()信号。开发⼈员还可以调⽤error()函数来查找最后发⽣的错误类型,并调⽤state()函数来查找当前进程状态。

通道通信

进程具有两个预定义的输出通道:

  • 标准输出通道(stdout)提供常规控制台输出。
  • 标准错误通道(stderr)通常提供由进程打印的错误。
    这些通道代表两个单独的数据流,可以通过调⽤setReadChannel()函数在它们之间切换。当前读取通道上有可⽤数据时,QProcess发出readyRead()信号。当有新的标准输出数据可⽤时,它也会发出readyReadStandardOutput()信号,⽽当有新的标准错误数据可⽤时,它会发出readyReadStandardError()信号。⽆需调⽤read()函数,readLine()函数或getChar()函数,⽽是可以通过调⽤readAllStandardOutput()函数或readAllStandardError()函数显式地从两个通道之⼀读取所有数据。

进程的输出通道与QProcess的读取通道相对应,⽽进程的输⼊通道与QProcess的写⼊通道相对应。这是因为我们使⽤QProcess读取的内容是进程的输出,⽽我们编写的内容则成为进程的输⼊。
QProcess可以合并两个输出通道,因此运⾏过程中的标准输出和标准错误数据都使⽤标准输出通道。在启动激活此功能的过程之前,请使⽤MergedChannels调⽤setProcessChannelMode()函数。还可以选择通过传递ForwardedChannels作为参数,,将正在运⾏的进程的输出转发到调⽤主进程。也可以只转发其中⼀个输出通道——通常⼀个将使⽤ForwardedErrorChannel,ForwardedOutputChannel也存在。

示例:查看CUP ID命令

QProcess p;
p.start("dmidecode -t 4 | grep ID |sort -u |awk -F': ' '{print $2}'");

以上的方式是无法执行的。可以将整个命令作为sh的参数传入 或 使用QProcess::setStandardOutputProcess(QProcess *destination)即将一个进程的标准输出流传入目标进程的标准输入流:

//将整个命令作为sh的参数传入
QProcess p;
p.start("sh", QStringList() << "-c" << "dmidecode -t 4 | grep ID |sort -u |awk -F': ' '{print $2}'");
p.waitForFinished();
QString str = p.readAllStandardOutput();
qDebug() << str;

//使用setStandardOutputProcess()
QProcess process1;                                
QProcess process2;                                
QProcess process3;                                
QProcess process4;                                
                                                  
process1.setStandardOutputProcess(&process2);     
process2.setStandardOutputProcess(&process3);     
process3.setStandardOutputProcess(&process4);     
                                                  
process1.start("sudo fdisk -l");                  
process2.start("grep ID");                        
process3.start("sort -u");                        
process4.start("awk", {"-F", ": ", "{print $2}"});
                                                  
process4.waitForFinished(); //等待最后一个命令执行完成               
QString str = process4.readAllStandardOutput();     
qDebug() << str;                                    

同步进程API

QProcess提供了⼀组可以在没有事件循环的情况下使⽤的函数(通过挂起调⽤线程直到发出某些信号):

  • waitForStarted()函数——阻塞直到进程开始。
  • waitForReadyRead()函数——阻塞直到有新数据可在当前读取通道上读取为⽌。
  • waitForBytesWritten()函数——阻塞直到将⼀个有效载荷数据写⼊该进程为⽌。
  • waitForFinished()函数——阻塞直到过程完成。

从主线程(调⽤QApplication::exec()函数的线程)调⽤这些函数可能会导致⽤户界⾯卡住。

	QProcess gzip;
    gzip.start("gzip", QStringList() << "-c");
    if (!gzip.waitForStarted())
        return false;

    gzip.write("Qt rocks!");
    gzip.closeWriteChannel();

    if (!gzip.waitForFinished())
        return false;

    QByteArray result = gzip.readAll();

启动

合并式:QProcess::start 方法。

void	start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)
void	start(QIODevice::OpenMode mode = ReadWrite)
  • program:启动外部应用程序的路径。不包含空格,包含空格需要在arguments中传入
  • arguments:传入待启动外部程序的参数,即:int main(int argc, char *argv[])这里的参数。

如果在启动前已经用 setProgram() 和 setArguments() 指定了程序和参数,那么直接调用第二种 start() 函数即可启动;如果没有指定,那么调用第一种 start() 时需要将程序和参数传入参数列表中。
此函数是以子进程的 方式打开外部程序的,外部进程与主程序互不干扰,但外部进程的父进程就是主程序。
外部程序启动后,将随主程序的退出而退出。 貌似要使用QProcess的close方法才可以关闭启动的外部程序。

void MainWindow::start()
{
    QProcess* caller = new QProcess(this);//创建对象,指定父类指针
    caller->start("G:\\file\\Bar\\x64\\Debug\\Bar.exe");
    //或者可以用这种找文件的符号来写
    //如果用“\”来作为文件路径分隔符则会找不到文件。
    //caller->start("G:/file/Bar/x64/Debug/Bar.exe");
    qDebug()<<"this here"<<endl;

}

如果使用“\”作为文件地址分隔符,则是不不能识别出这是文件地址,正确的的方式是:
(1)使用“\”,如:G:\file\Bar\x64\Debug\Bar.exe
(2)使用“/”, 如:G:/file/Bar/x64/Debug/Bar.exe

合并式:QProcess::execute()

此函数是以阻塞的方式打开外部程序,只有外部程序执行完成后,主程序才会继续执行。外部程序的标准输出、标准错误都是重定向到主程序的标准输出和标准错误的。

分离式:QProcess::startDetached 方法。

此函数是以分离的方式打开外部程序的,外部程序与主程序互不干扰,外部进程的父进程是系统的init进程。
外部程序启动后,当主程序退出时并不退出,而是继续运行。

退出

void kill()
void terminate()

kill(),作用是杀死当前进程使之退出。这个函数也是调用平台相关的 API,如在 Windows 上调用 TerminateProcess,而在 Unix 和 macOS 上是将 SIGKILL 信号发送到进程中。
terminate(),区别于 kill() 这种暴力的退出不同,它在退出进程的时候是有机会提示用户输入任何为保存的文件等。在 Windows 上是发送 WM_CLOSE 到进程的顶级窗口,然后发到进程本身的主线程,而 在 Unix 和 macOS 上是发送 SIGTERM 信号。注意在 Windows 上,没有事件循环或者不处理 WM_CLOSE 消息的控制台程序只能调用 kill() 来终止。
示例:根据进程名称先找到进程PID,再根据PID杀死进程

函数说明
QProcess(QObject *parent = nullptr)
virtual ~QProcess()
QStringList arguments() const(准备阶段)获取待启动外部程序的参数
void closeReadChannel(QProcess::ProcessChannel channel)(运行阶段)
void closeWriteChannel()(运行阶段)
QProcess::CreateProcessArgumentModifier createProcessArgumentsModifier() const
QProcess::ProcessError error() const(运行阶段)
int exitCode() const(退出阶段)
QProcess::ExitStatus exitStatus() const(退出阶段)
QProcess::InputChannelMode inputChannelMode() const(运行阶段)
QString nativeArguments() const(准备阶段)获取Windows专属参数
QProcess::ProcessChannelMode processChannelMode() const(准备阶段)获取stdout、stderr
QProcessEnvironment processEnvironment() const(准备阶段)获取进程环境
qint64 processId() const(运行阶段)获取进程ID
QString program() const(准备阶段)获取启动外部应用程序的路径
QByteArray readAllStandardError()(运行阶段)
QByteArray readAllStandardOutput()(运行阶段)
QProcess::ProcessChannel readChannel() const(运行阶段)
void setArguments(const QStringList &arguments)(准备阶段)设置待启动外部程序的参数
void setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier modifier)
void setInputChannelMode(QProcess::InputChannelMode mode)(运行阶段)
void setNativeArguments(const QString &arguments)(准备阶段)设置Windows专属参数
void setProcessChannelMode(QProcess::ProcessChannelMode mode)(准备阶段)设置stdout、stderr
void setProcessEnvironment(const QProcessEnvironment &environment)(准备阶段)设置进程环境
void setProgram(const QString &program)(准备阶段)设置启动外部应用程序的路径
void setReadChannel(QProcess::ProcessChannel channel)(运行阶段)
void setStandardErrorFile(const QString &fileName, QIODevice::OpenMode mode = Truncate)(运行阶段)重定向到文件
void setStandardInputFile(const QString &fileName)(运行阶段)重定向到文件
void setStandardOutputFile(const QString &fileName, QIODevice::OpenMode mode = Truncate)(运行阶段)重定向到文件
void *setStandardOutputProcess(QProcess destination)(运行阶段)
void setWorkingDirectory(const QString &dir)(准备阶段)设置工作目录
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)(启动阶段)
void start(QIODevice::OpenMode mode = ReadWrite)(启动阶段)以异步方式的执行shell命令,命令输出的数据存储于缓冲区,可以通过readAllStandardOutput()捕获
bool *startDetached(qint64 pid = nullptr)(启动阶段)以分离的方式执行shell命令,调用进程退出,则分离的进程将继续运行,而不受影响。
QProcess::ProcessState state() const(启动阶段)获取状态
bool waitForFinished(int msecs = 30000)(运行阶段)阻塞直到过程完成。
bool waitForStarted(int msecs = 30000)(运行阶段)阻塞直到进程开始
virtual bool waitForBytesWritten(int msecs = 30000) override阻塞直到将⼀个有效载荷数据写⼊该进程为⽌。
virtual bool waitForReadyRead(int msecs = 30000) override阻塞直到有新数据可在当前读取通道上读取为⽌。
QString workingDirectory() const(准备阶段)获取工作目录
int execute(const QString &program, const QStringList &arguments)(启动阶段)以堵塞方式的执行shell命令,当命令执行完成后,调用进程才会继续执行。命令输出的任何数据都将转发给调用进程输出(因此无法捕获)。
信号说明
void errorOccurred(QProcess::ProcessError error)
void finished(int exitCode, QProcess::ExitStatus exitStatus)(退出阶段)
void readyReadStandardError()(运行阶段)
void readyReadStandardOutput()(运行阶段)
void started()
void stateChanged(QProcess::ProcessState newState)(启动阶段)

示例

窗口只有一个按钮,当你点击按钮之后,程序会调用 Windows 的记事本。这里我们使用的是p->start("notepad.exe");

//mainwindow.h
#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  

#include <QtGui>  

class MainWindow : public QMainWindow  
{  
    Q_OBJECT  

public:  
    MainWindow(QWidget *parent = 0);  
    ~MainWindow();  

private slots:  
    void openProcess();  

private:  
    QProcess *p;  
};  

#endif // MAINWINDOW_H 
//mainwindow.cpp
#include "mainwindow.h"  

MainWindow::MainWindow(QWidget *parent)  
    : QMainWindow(parent)  
{  
    p = new QProcess(this);  
    QPushButton *bt = new QPushButton("execute notepad", this);  
    connect(bt, SIGNAL(clicked()), this, SLOT(openProcess()));  
}  

MainWindow::~MainWindow()  
{  

}  

void MainWindow::openProcess()  
{  
    p->start("notepad.exe");  
} 

QProcess::start() 接受两个参数,第一个是要执行的命令或者程序,这里就是 notepad.exe;第二个是一个 QStringList 类型的数据,也就是需要传递给这个程序的运行参数。注意,这个程序是需要能够由系统找到的,一般是完全路径。但是这里为什么只有 notepad.exe 呢?因为这个程序实际是放置在 Windows 系统文件夹下,是已经添加到了系统路径之中,因此不需要再添加本身的路径。

示例二

//mainwindow.h
#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  

#include <QtGui>  

class MainWindow : public QMainWindow  
{  
    Q_OBJECT  

public:  
    MainWindow(QWidget *parent = 0);  
    ~MainWindow();  

private slots:  
    void openProcess();  
    void readResult(int exitCode);  

private:  
    QProcess *p;  
};  

#endif // MAINWINDOW_H 
//mainwindow.cpp
#include "mainwindow.h"  

MainWindow::MainWindow(QWidget *parent)  
    : QMainWindow(parent)  
{  
    p = new QProcess(this);  
    QPushButton *bt = new QPushButton("execute notepad", this);  
    connect(bt, SIGNAL(clicked()), this, SLOT(openProcess()));  
}  

MainWindow::~MainWindow()  
{  

}  

void MainWindow::openProcess()  
{  
    p->start("cmd.exe", QStringList() << "/c" << "dir");  
    connect(p, SIGNAL(finished(int)), this, SLOT(readResult(int)));  
}  

void MainWindow::readResult(int exitCode)  
{  
    if(exitCode == 0) {  
        QTextCodec* gbkCodec = QTextCodec::codecForName("GBK");  
        QString result = gbkCodec->toUnicode(p->readAll());  
        QMessageBox::information(this, "dir", result);  
    }  
} 

按钮点击的 slot 中,我们通过 QProcess::start() 函数运行了指令cmd.exe /c dir这里是说,打开系统的 cmd 程序,然后运行 dir 指令。
在 slot 中,我们检查退出代码是否是0,一般而言,如果退出代码为0,说明是正常退出。然后把结果显示在 QMessageBox 中。
QProcess::readAll() 函数可以读出程序输出内容。我们使用这个函数将所有的输出获取之后,由于它的返回结果是 QByteArray 类型,所以再转换成 QString 显示出来。

  • 23
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值