qt QProcess

QProcess常用三个函数:

QProcess::execute():

以阻塞方式打开外部程序,只有当外部程序执行完后才继续往后执行现程序。其中,外部程序的标准输出、标准错误都是重定向到现程序的标准输出和标准错误。

QProcess::start():

以子进程的方式打开外部程序,外部进程和现进程执行互不干扰,但外部进程的父进程是现进程。

QProcess::startDetached():

以分离方式打开外部程序,外部进程和现进程执行互不干扰,外部进程的父进程是系统的init进程。

其中start()函数为普通成员函数,execute()和startDetached()为静态成员函数

函数原型:

    void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite);int execute(const QString &program, const QStringList &arguments);bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(), qint64 *pid = Q_NULLPTR);

以上三个函数都是另起一个进程来运行已经存在的外部可执行程序,QProcess类支持与新建的进程进行通信(通过信号与槽的方式)。QProcess允许将一个进程视为一个顺序I/O设备。可以像是用QTcpSocket访问网络连接一样来读写一个进程,例如可以调用read()、readLine()和getChar()等成员函数从新起进程的标准输出读取数据,可以使用write()向进程的标准输入进行写入。

下面使用自己实现的用于守护Python脚本运行的Daemon类作为举例,说明QProcess类的使用。注意,Daemon类的除 构造函数的所有函数都不是线程安全的,切勿直接在另一线程直接调用(可以尝试使用QMetaObject::invoke()函数);

daemon.h文件:

#ifndef DAEMON_H
#define DAEMON_H#include <QObject>
#include <QMap>
#include <time.h>class QStringList;
class QProcess;class Daemon : public QObject
{Q_OBJECT
public:explicit Daemon(const QStringList &, QObject *parent = 0);~Daemon();void setProStatus(bool status) { isRunning = status;}bool proStatus() { return isRunning;}signals:void restartFailed(QString);void noPythonEnv();void closeFailed(QString);void readyRead(QString, QString);void startRun(QString );void terminateRun(QString );public slots:void startAllPro();void closeAllPro();void setProPaths(const QStringList );
private slots:void output();private:QMap<QProcess *, QString> proMap;QList<QProcess *> proList;time_t * preTime;int * crashTimes;void proExitHandler(/*int exitCode, QProcess::ExitStatus exitStatus*/);bool isRunning = false;
};#endif // DAEMON_H

daemon.cpp文件:

#include "daemon.h"
#include <QStringList>
#include <QProcess>
#include <QFileInfo>
#include <QDebug>
#include <iostream>
#include <QThread>Daemon::Daemon(const QStringList & proPaths, QObject *parent) : QObject(parent)
{for(auto iterator = proPaths.begin(); iterator != proPaths.end(); iterator++){QProcess * temp = new QProcess(this);proList.push_back(temp);proMap.insert(temp, *iterator);connect(temp, &QProcess::readyRead, this, &Daemon::output);/*设置启动py文件的运行路径,对于依赖路径的py文件很重要*/temp->setWorkingDirectory(QFileInfo(*iterator).path());}preTime = new time_t[proList.count()];crashTimes = new int[proList.count()];for(int i = 0; i < proList.count(); i++){preTime[i] = time(NULL);crashTimes[i] = 0;}
}
Daemon::~Daemon()
{closeAllPro();delete[] preTime;delete[] crashTimes;
}void Daemon::proExitHandler(/*int exitCode, QProcess::ExitStatus exitStatus*/)
{QProcess * senderPro = static_cast<QProcess *>(sender());/*发出进程终止信号*/emit terminateRun(proMap[senderPro]);/*若对同一程序重启10次且相邻两次重启时间小于1秒则判断为该进程无法重启,抛出重启失败信号*/int index = proList.lastIndexOf(senderPro);if(time(NULL) - preTime[index] <= 1){crashTimes[index]++;}else{crashTimes[index] = 0;}preTime[index] = time(NULL);if(crashTimes[index] > 10/*0*/){qCritical() << proMap[senderPro] + " restart failed!";disconnect(senderPro, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Daemon::proExitHandler);emit restartFailed(proMap[senderPro]);}else{senderPro->start("python", QStringList() << "-u" << proMap[senderPro]);bool rstSuccess = senderPro->waitForStarted(30000);if(!rstSuccess){/*emit noPythonEnv();*/}else{/*重启进程成功发出进程开始运行信号*/emit startRun(proMap[senderPro]);}}
}void Daemon::startAllPro()
{if(proStatus() == true)return;for(auto iterator = proList.begin(); iterator != proList.end(); iterator++){QProcess * temp = *iterator;connect(temp, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Daemon::proExitHandler);temp->start("python", QStringList() << "-u" << proMap[temp]);bool rstSuccess = temp->waitForStarted(30000);if(!rstSuccess){emit noPythonEnv();disconnect(temp, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Daemon::proExitHandler);}else{emit startRun(proMap[temp]);}}setProStatus(true);
}void Daemon::closeAllPro()
{if(proStatus() == false)return;for(auto iterator = proList.rbegin(); iterator != proList.rend(); iterator++){QProcess * temp = *iterator;disconnect(temp, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Daemon::proExitHandler);temp->close();bool closeSuccess = temp->waitForFinished(30000);//这里永远返回false,原因待查,所以terminateRun信号在if中也发送,因为else永远不成立if(!closeSuccess){emit closeFailed(proMap[temp]);emit terminateRun(proMap[temp]);}else{emit terminateRun(proMap[temp]);}}setProStatus(false);
}void Daemon::setProPaths(const QStringList proPaths)
{for(auto iterator = proList.rbegin(); iterator != proList.rend(); iterator++){QProcess * temp = *iterator;disconnect(temp, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Daemon::proExitHandler);temp->close();bool closeSuccess = temp->waitForFinished(30000);if(!closeSuccess){emit closeFailed(proMap[temp]);}delete temp;}proMap.clear();proList.clear();delete[] preTime;delete[] crashTimes;for(auto iterator = proPaths.begin(); iterator != proPaths.end(); iterator++){QProcess * temp = new QProcess(this);proList.push_back(temp);proMap.insert(temp, *iterator);connect(temp, &QProcess::readyRead, this, &Daemon::output);temp->setWorkingDirectory(QFileInfo(*iterator).path());}preTime = new time_t[proList.count()];crashTimes = new int[proList.count()];for(int i = 0; i < proList.count(); i++){preTime[i] = time(NULL);crashTimes[i] = 0;}
}void Daemon::output()
{QProcess * proSender = static_cast<QProcess *>(sender());emit readyRead(proMap[proSender], QString(proSender->readAll()));
}

(注:Daemon类对QProcess::start()函数的调用使用 "-u"参数,是为了读取python脚本标准输出流的内容,python解释器在检测到不是从终端启动解释器就会关闭向标准输出流的输出,"-u"参数可以有效解决这个问题,但并不能完全解决,如果脚本调用的库中例如caffe,tensorflow等向标准输出中写入信息,即使加"-u"参数也不能获取输出,由于对python不是很熟悉,所以这个问题也没得到解决)

Daemon类的实现中我们只用到了QProcess类的两个信号,QProcess::finished和QProcess::readyRead,用于finished用于重启crash掉的脚本,readyRead用于读取脚本运行时输出的信息(无法输出Python脚本调用库的输出信息)。

Daemon类构造函数默认传入一个QStringList参数,代表了所有要启动的python脚本的路径,注意路径可以重复,同时QStringList对象的成员顺序也代表了程序的启动顺序,使用Daemon时可以在构造函数中传入一个空的QStringList对象,之后再调用startAllPro()函数之前调用setProPaths()函数重新设置路径。Daemon类中的QMap<QProcess *, QString> proMap成员存储了QProcess类对象指针和此对象守护的python脚本的路径,这样方便程序维护。

Daemon类对象一旦调用startAllPro()函数,就会启动传入路径的所有python脚本,当脚本退出,不论是正常还是异常退出,Daemon类对象都会重新开启此脚本,可以参考Daemon类的信号,除了closeFailed()信号没有实现,有点问题,其它正常。

restartFailed(QString),在Daemon尝试连续重启python脚本10次,相邻两次间隔小于1秒,重启全部失败的情况下发出,noPythonEnv()在首次启动python解释器失败时发出,因为即使python脚本文件有问题,python解释器依然可以启动成功,所以waitForStarted()函数返回false只可能时无python环境;Daemon::readyRead()在收到QProcess子对象发出的QProcess::readyRead()信号之后调用的output私有槽函数中发出,用于将读取信息发送到目标对象,目标对象可以存在于另一个线程;其它信号不一一赘述。

下面说明Daemon类的使用,Daemon类对象可以在主线程使用,也可以在另外的子线程使用,在子线程使用时可以参见:

https://blog.csdn.net/xiaoyink/article/details/79582429中讲述的DaemonThread类,这个类实现了在子线程中使用了Daemon类,注意Daemon类的成员函数都是非线程安全的,所以在主线程调用时使用QMetaObject::invokeMethod()函数;

MainWindow.cpp文件:

void MainWindow::startDaemon()
{for(auto iterator = mapComb.begin(); iterator != mapComb.end(); iterator++){QPushButton * temp = iterator.key();temp->setEnabled(false);}ui->openServerPB->setEnabled(false);ui->clearPathsPB->setEnabled(false);QStringList strListPaths;/*之所以没有直接遍历mapPaths是因为,以下这种方式可以保证按顺序启动进程*/for(auto iterator = listPaths.begin(); iterator != listPaths.end(); iterator++){QString temp = (mapComb[*iterator])->path->text();if(!temp.isEmpty()){strListPaths << temp;}}
for(auto iterator = strListPaths.begin(); iterator != strListPaths.end(); iterator++)
{QString temp = *iterator;std::cout <<temp.toStdString() << std::endl;
}/*采用这种方式调用是因为Daemon类成员函数非线程安全函数*/QMetaObject::invokeMethod(daemonThread->daemon, "setProPaths", Qt::QueuedConnection, Q_ARG(QStringList, strListPaths));//daemonThread->daemon->setProPaths(strListPaths);QMetaObject::invokeMethod(daemonThread->daemon, "startAllPro", Qt::QueuedConnection);
}void MainWindow::endDaemon()
{QMetaObject::invokeMethod(daemonThread->daemon, "closeAllPro", Qt::QueuedConnection);for(auto iterator = mapComb.begin(); iterator != mapComb.end(); iterator++){QPushButton * temp = iterator.key();temp->setEnabled(true);}ui->openServerPB->setEnabled(true);ui->clearPathsPB->setEnabled(true);
}

这是主界面上开始运行python脚本服务和结束运行两个按键对应的槽函数,其中daemonThread->daemon对象即使Daemon类对象。

The source code see also: https://github.com/Kylin-Wei/ccr

qt QProcess相关推荐

  1. QT QProcess 关闭程序 tasklist windows

    QT程序,windows下指定程序名关闭程序,通过tasklist.exeint existProcess(QString proName) {if (!proName.endsWith(" ...

  2. Qt QProcess执行Linux 命令行的方法

    1.QProcess 打开一个bash终端,可以执行一般的命令,主要实现步骤如下 m_proces_bash = new QProcess; m_proces_bash->start(" ...

  3. 软件中打开其他软件的方式

    打开其他软件的方法 参考链接 参考链接2 QT: QProcess process;//先执行tasklist命令,获取进程列表,进而检索是否存在待启动程序的名称即可判断是否已存在.process.s ...

  4. 检测指定进程是否存在是否运行

    小总结一下c/c++检测指定进程是否存在的方式: 方式不唯一,列举本人亲测的方式,没有过多思考,简单实现. linux: //linux判断进程是否存在FILE* fp;char szbuffer[1 ...

  5. Qt之QProcess(一)运行cmd命令

    Qt提供了QProcess类,QProcess可用于完毕启动外部程序,并与之交互通信. 一.启动外部程序的两种方式: (1)一体式:void QProcess::start(const QString ...

  6. QT输入输出(四)之 QProcess

    QProcess Qt提供了一个QProcess类用于启动外部程序并与之通信.这个类是异步工作的,而且在后台执行,这样用户界面就可以始终保持响应. 启动一个新的进程的操作非常简单,只需要将待启动的程序 ...

  7. Qt笔记-QProcess切换用户执行进程(Linux)

    如下命令: /bin/su cff -c "/bin/ping 127.0.0.1" 运行截图如下: 在Qt里面使用QProcess,不需要那个双引号,如下代码: #include ...

  8. Qt工作笔记-使用Qt中QProcess与iostream中system调用外部进程

    目录 基础概念 演示 基础概念 Qt中的QProcess类可以调用外部程序! 同时iostream有个函数system也可以调用外部程序!这个system也是Windows,Linux上通用的 演示 ...

  9. qt执行命令行失败_QT中QProcess调用命令行的痛苦经历

    在QT程序中需要将某些目录和文件压缩为一个rar的压缩包,于是想到了在QT中通过QProcess类调用命令行的rar.exe来达到效果,但是没想到QProcess类用起来很麻烦,而且达不到效果,折腾了 ...

最新文章

  1. ubuntu下安装flex和bison
  2. js之事件冒泡和事件捕获
  3. Docker安装部署ELK教程 (Elasticsearch+Kibana+Logstash+Filebeat)
  4. Centos5 install vnc
  5. System.Diagnostics.Process.Start()用法详解
  6. 打印英文年历C语言函数,C语言打印年历
  7. [OpenJudge 3066]随机序列
  8. position的用法小结
  9. UIImageView的animationImages动画
  10. centos7.0KVM虚拟化
  11. 执行计划之CONCATENATION
  12. 设计模式之————依赖注入(Dependency Injection)与控制反转(Inversion of Controller)...
  13. NetworkManagementService 解读
  14. 报文分析4、TCP协议的头结构
  15. 直接ISO启动工具ventoy
  16. 一次排查服务器挖矿病毒
  17. matlab 新建prj,操作方法:为 shapefile 创建投影元数据 (.prj) 文件
  18. 快手投放广告,快手广告优势有哪些呢?
  19. 那年的夏天——致毕业
  20. 计算机视觉论文-2021-06-10

热门文章

  1. 射击类小游戏——坦克大战(java实现)
  2. Thread类中interrupt()、interrupted()和isInterrupted()方法详解
  3. Box2d For Processing
  4. 数据预处理_数据相关性分析
  5. 计算机打印机软驱,用软驱、硬盘、打印机组合成乐器?外国人真会玩!
  6. 机械土木计算机哪个好,含金量最好的工科专业
  7. 【图像增强】基于DEHAZENET和HWD的水下去散射图像增强
  8. Leetcode刷题-312 :戳气球
  9. 智云通CRM:客户说“不要赠品,给我便宜点”,如何回应才能顺利成交?
  10. 南邮《离散数学》2018-2019学年第一学期期末考试回忆