************************************************************
author: hjjdebug
date: 2024年 02月 03日 星期六 12:16:01 CST
description: QT中QProcess 的使用(完整代码)
目的: 用最简单的程序了解QT对象QProcess的使用
************************************************************
要求, 用QT编写程序,实现对test_print 程序的调用
test_print 是如下向控制台输出1-99个数字然后就退出的程序. 这个程序就自己实现一下吧。
或者看后面的附录:
./test_print
1
2
3
4
5
6
7
8
9
qt 对进程的调用可以使用QProcess 类来实现
主程序还是一样的简单,我们创造一个QObject 子对象就可以了
$ cat main.cpp
#include <QApplication>
#include <unistd.h> //for gettid()
#include "myclass.h"
QApplication* gApp;
int main(int argc, char *argv[])
{
gApp = new QApplication(argc, argv);
printf("main threadid:%d\n",gettid());
MyClass myObject;
return gApp->exec();
}
下面是 MyClass 的头文件
$ cat myclass.h
#ifndef _MYCLASS_H
#define _MYCLASS_H
#include <QObject>
#include <QProcess>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
~MyClass();
private slots:
void openProcess();
void slotRead();
void slotReadError();
void slotStateChanged(QProcess::ProcessState state);
private:
QProcess *m_process;
};
#endif // _MYCLASS_H
这个头文件继承自QObject, 并包含Q_OBJECT以实现消息收发
它包含一个QProcess 对象m_process
能够读取进程对象的stdout --> slotRead()
和进程对象的stderr --> slotReadError()
能够感知进程对象的状态改变 -->slotStateChanged
下面看CMyClass 的具体实现
$ cat myclass.cpp
#include <QDebug>
#include "myclass.h"
#include <unistd.h>
void printTime()
{
const time_t now=time(NULL);
struct tm *tm = localtime(&now);
printf("%02d:%02d:%02d ",tm->tm_hour,tm->tm_min,tm->tm_sec);
}
void printTime2()
{
printTime();
printf("\n"); //加回车才能看到向屏幕输出时间
}
MyClass::MyClass()
{
openProcess(); //构造函数中打开进程
}
MyClass::~MyClass()
{
// 析构中清理环境
disconnect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotRead()));
disconnect(m_process,SIGNAL(readyReadStandardError()),this,SLOT(slotReadError()));
m_process->close();
delete m_process;
printf("MyClass destructure!\n");
}
void MyClass::openProcess()
{ //创建一个进程, 启动它并打印其时间,状态,建立信号连接
m_process = new QProcess(this);
printf("open process begin,threadid:%d\n",gettid());
printTime();
printf("state0:%d\n",m_process->state()); // 此时状态是0, NotRunning
m_process->start("test_print");
printTime();
printf("state1:%d\n",m_process->state()); //此时状态是1, Starting,
printf("processid:%d,sub_processid:%lld\n",getpid(),m_process->pid());
#define _WANT_READ_INFO
#ifdef _WANT_READ_INFO
connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotRead()));
connect(m_process,SIGNAL(readyReadStandardError()),this,SLOT(slotReadError()));
#else
//,关闭通道读写,免得引起内存不断增长.
m_process->closeReadChannel(QProcess::StandardOutput);
m_process->closeReadChannel(QProcess::StandardError);
m_process->closeWriteChannel();
#endif
connect(m_process,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(slotStateChanged(QProcess::ProcessState)));
printTime();
printf("state2:%d\n",m_process->state()); //此时状态是1, Starting,
printf("open process end!\n");
}
void MyClass::slotStateChanged(QProcess::ProcessState state)
{
printTime2();
qDebug()<<"ProcessState changed: processid:"<<getpid();
switch(state)
{
case QProcess::NotRunning: // 0
qDebug()<<"Not Running";
break;
case QProcess::Starting: //1
qDebug()<<"Starting";
break;
case QProcess::Running: //2
qDebug()<<"Running";
break;
default:
qDebug()<<"otherState";
break;
}
}
void MyClass::slotRead()
{
QByteArray array=m_process->readAllStandardOutput();
qDebug()<<"receive:"<<array;
printf("slotRead, state:%d,pid:%d,threadid:%d\n",m_process->state(),getpid(),gettid()); //此时状态是2, Running
}
void MyClass::slotReadError()
{
QByteArray array=m_process->readAllStandardError();
qDebug()<<"err:"<<array;
printf("slotReadError, state:%d\n",m_process->state()); //此时状态是2, Running
}
下面是执行结果: 可以看到,读写子进程信息都是在主进程中进行的.
$ ./test_qprocess
main threadid:10465
open process begin,threadid:10465
12:13:31 state0:0
12:13:31 state1:1
processid:10465,sub_processid:10471
12:13:31 state2:1
open process end!
receive: "1\n"
slotRead, state:1,pid:10465,threadid:10465
12:13:31
ProcessState changed:
Running
receive: "2\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "3\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "4\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "5\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "6\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "7\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "8\n"
slotRead, state:2,pid:10465,threadid:10465
receive: "9\n"
slotRead, state:2,pid:10465,threadid:10465
12:13:40
ProcessState changed:
Not Running
补充: 再剧透一下主线程是如何调用到槽函数的. 当然它是通过gApp->exec,具体的过程我调试了一下是这样的,通过20层调用才到达了槽函数. 当然具体实现是Qt完成的,我们了解一下就可以了。
0 in MyClass::slotRead() of myclass.cpp:77
1 in MyClass::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) of moc_myclass.cpp:86
2 in void doActivate<false>(QObject*, int, void**)
3 in QProcess::readyReadStandardOutput(QProcess::QPrivateSignal)
4 in QProcessPrivate::tryReadFromChannel(QProcessPrivate::Channel*)
5 in QProcess::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)
6 in void doActivate<false>(QObject*, int, void**)
7 in QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal)
8 in QSocketNotifier::event(QEvent*)
9 in QApplicationPrivate::notify_helper(QObject*, QEvent*)
10 in QApplication::notify(QObject*, QEvent*)
11 in QCoreApplication::notifyInternal2(QObject*, QEvent*)
12 in socketNotifierSourceDispatch(_GSource*, int (*)(void*), void*)
13 in g_main_context_dispatch
14 in
15 in g_main_context_iteration
16 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)
17 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>)
18 in QCoreApplication::exec()
19 in main(int, char**) of main.cpp:11
附录: test_print 代码
$ cat main.cpp
#include <stdio.h>
#include <unistd.h>
int main()
{
for(int i=1;i<10;i++)
{
printf("%d\n",i);
fflush(stdout);
sleep(1);
}
return 0;
}
我发现要想获取子进程的打印输出, 有3个关键点
1. 利用QProcess 调用start()时候可以像所给例子一样调用,千万不要加NULL参数.否则父进程收不到信号
2.子进程必需要加fflush 刷新才能实时输出。
3. 子进程必需要加一次sleep 函数调用,哪怕只sleep(0.1)也行,否则父进程就收不到信号,
不知道是不是QT的bug, 我用的是QT5.14.0, 反正坑都绕过了,
demo程序只是我复杂程序的一个浓缩.