Qt之进程通信-QProcess(含源码+注释)

一、QProcess进程通信示例

下方为默认程序启动通信示例
在这里插入图片描述
下方为默认程序为空,然后指定启动的应用程序通信
在这里插入图片描述

二、QProcess通信个人理解

  1. 主进程给子进程发送数据:直接通过QProcess对象的write函数写入数据(给通过start函数启动的进程,并且写入数据需要以“\n”结尾,方便子进程识别且读取数据);
  2. 主进程接收数据:直接关联QProcess的readyReadStandardError()、readyReadStandardOutput()信号可接收读取错误输出和数据输出;
  3. 子进程接收数据:本文通过QTextStream和std::string对象读取数据,且两者对象都是在线程中循环识别数据,并通过信号输出显示到主界面中;
  4. 子进程发送数据:子进程通过QFile打开stdout流通道,直接通过QFile的write函数写入数据即可(此处不需要以“\n”结尾主线程都可以读取数据)。

三、源码

MainWindowProcessSender

MainWindowProcessSender.h

#ifndef MAINWINDOWPROCESSSENDER_H
#define MAINWINDOWPROCESSSENDER_H

#include <QMainWindow>
#include <QProcess>

namespace Ui {
class MainWindowProcessSender;
}

class MainWindowProcessSender : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindowProcessSender(QWidget *parent = nullptr);
    ~MainWindowProcessSender();

private slots:
    /**
     * @brief on_btnStartProcess_clicked 启动进程信号槽
     */
    void on_btnStartProcess_clicked();

    /**
     * @brief on_readyReadStandardError 错误信息信号槽
     */
    void on_readyReadStandardError();

    /**
     * @brief on_readyReadStandardOutput 输出信息信号槽
     */
    void on_readyReadStandardOutput();

    /**
     * @brief on_btnSend_clicked 数据发送信号槽
     */
    void on_btnSend_clicked();

private:
    Ui::MainWindowProcessSender *ui;

    QProcess    m_process;  // 进程对象

    QString     m_path; // 子进程路径

};

#endif // MAINWINDOWPROCESSSENDER_H

MainWindowProcessSender.cpp

#include "MainWindowProcessSender.h"
#include "ui_MainWindowProcessSender.h"

#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>

MainWindowProcessSender::MainWindowProcessSender(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindowProcessSender)
    , m_path("")
{
    ui->setupUi(this);
    // 关联数据信号
    connect(&m_process, &QProcess::readyReadStandardError, this, &MainWindowProcessSender::on_readyReadStandardError);
    connect(&m_process, &QProcess::readyReadStandardOutput, this, &MainWindowProcessSender::on_readyReadStandardOutput);
}

MainWindowProcessSender::~MainWindowProcessSender()
{
    // 写入结束指令,使子进程读取线程停止
    m_process.write(u8"%kill%\n");
    // 结束子进程
    m_process.terminate();
    // 等待子进程结束
    m_process.waitForFinished(5000);
    delete ui;
}

void MainWindowProcessSender::on_btnStartProcess_clicked()
{
    QString path;
    if(!m_path.isEmpty()) {
        QFile file(m_path);
        if(!file.exists()) {
            QMessageBox::information(this, u8"提示", u8"启动程序不存在,请自主选择程序");
            m_path = "";
            return;
        }
        path = m_path;
    }
    else {
        path = QFileDialog::getOpenFileName(this, u8"选择启动程序", u8"./", "*.exe");
    }
    m_process.start(path);
    ui->btnStartProcess->setEnabled(false);
}

void MainWindowProcessSender::on_readyReadStandardError()
{
    // 错误信息追加
    ui->plainTextEdit->appendPlainText("Error:" + m_process.readAllStandardError());
}

void MainWindowProcessSender::on_readyReadStandardOutput()
{
    // 通信数据追加
    QByteArray data = m_process.readAllStandardOutput();
    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit(data));
}

void MainWindowProcessSender::on_btnSend_clicked()
{
    // 发送数据
    if(!m_process.isOpen()) {
        return;
    }
    // 写入数据
    m_process.write((ui->lineEdit->text()).toStdString().data());
    // 写入结束符
    m_process.write("\n");
}

MainWindowProcessSender.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindowProcessSender</class>
 <widget class="QMainWindow" name="MainWindowProcessSender">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindowProcessSender</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QLineEdit" name="lineEdit">
      <property name="text">
       <string/>
      </property>
     </widget>
    </item>
    <item row="0" column="1">
     <widget class="QPushButton" name="btnSend">
      <property name="text">
       <string>发送</string>
      </property>
     </widget>
    </item>
    <item row="0" column="2">
     <widget class="QPushButton" name="btnStartProcess">
      <property name="text">
       <string>启动通信程序</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0" colspan="3">
     <widget class="QPlainTextEdit" name="plainTextEdit"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

MainWindowProcessRecv

MainWindowProcessRecv.h

#ifndef MAINWINDOWPROCESSRECV_H
#define MAINWINDOWPROCESSRECV_H

#include <QFile>
#include <QMainWindow>
#include <QSocketNotifier>

namespace Ui {
class MainWindowProcessRecv;
}

class MainWindowProcessRecv : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindowProcessRecv(QWidget *parent = nullptr);
    ~MainWindowProcessRecv();

    void appendText(QString str);

signals:
    /**
     * @brief sigDataRead 数据输入信号
     * @param str 输入数据
     */
    void sigDataRead(QString str);

private slots:
    /**
     * @brief on_btnSend_clicked数据发送信号槽
     */
    void on_btnSend_clicked();

    /**
     * @brief on_btnStartRecv_clicked 数据接收信号槽
     */
    void on_btnStartRecv_clicked();

    /**
     * @brief on_loopReadInData 数据接收处理信号槽
     */
    void on_loopReadInData();

private:
    Ui::MainWindowProcessRecv   *ui;

    bool                        m_readFlag;         // 数据接收标记
};

#endif // MAINWINDOWPROCESSRECV_H

MainWindowProcessRecv.cpp

#include "MainWindowProcessRecv.h"
#include "ui_MainWindowProcessRecv.h"
#include <QFile>
#include <cstring>
#include <iostream>
#include <QtConcurrent/QtConcurrent>
#include <QTextStream>
#include <QMessageBox>

MainWindowProcessRecv::MainWindowProcessRecv(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindowProcessRecv)
    , m_readFlag(false) // 读取标记默认false
{
    ui->setupUi(this);
    // 连接输入流数据信号槽
    connect(this, &MainWindowProcessRecv::sigDataRead, this, [=](QString str){
        ui->editDebug->appendPlainText(str);
    });
}

MainWindowProcessRecv::~MainWindowProcessRecv()
{
    // 读取标志主动设置为false
    m_readFlag = false;
    delete ui;
}

void MainWindowProcessRecv::appendText(QString str)
{
    ui->editDebug->appendPlainText(str);
}

void MainWindowProcessRecv::on_btnSend_clicked()
{
    // 打开输出流通道
    QFile file;
    if(!file.open(stdout, QIODevice::ReadWrite)) {
        qDebug() << u8"打开失败";
        return;
    }

    // 写入数据
    file.write(ui->lineEdit->text().toLocal8Bit());
    // 关闭输出流通道
    file.close();
}

void MainWindowProcessRecv::on_loopReadInData()
{
    while(m_readFlag) {
#if 0
        // 通过文本流读取数据(因为stdin本身输入的就是流数据)
        QTextStream stream(stdin);
        stream.setCodec("utf8");    // 指定编码类型防止乱码
        QString str;
        stream.readLineInto(&str);  // 读取数据(读取一行数据,其中以\n"或"\r\n为结束标记)、
        // 数据接收信号
        emit sigDataRead(str);
#else
        // 创建数据接收数据
        std::string str;
        // 读取数据
        std::getline(std::cin, str);
        // 数据接收信号
        emit sigDataRead(QString::fromStdString(str));
#endif
        // 读取标记赋值(通过主进程发送数据识别关闭)
        m_readFlag = 0 != str.compare(u8"%kill%");
    }
}

void MainWindowProcessRecv::on_btnStartRecv_clicked()
{
    if(m_readFlag) {
        m_readFlag = false;
        ui->btnStartRecv->setText(u8"开始接收");
    }
    else {
        m_readFlag = true;
        ui->btnStartRecv->setText(u8"停止接收");
        // 以线程启动数据读取
        QtConcurrent::run(this, &MainWindowProcessRecv::on_loopReadInData);
    }
}

MainWindowProcessRecv.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindowProcessRecv</class>
 <widget class="QMainWindow" name="MainWindowProcessRecv">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindowProcessRecv</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QLineEdit" name="lineEdit">
      <property name="text">
       <string/>
      </property>
     </widget>
    </item>
    <item row="0" column="1">
     <widget class="QPushButton" name="btnSend">
      <property name="text">
       <string>发送</string>
      </property>
     </widget>
    </item>
    <item row="0" column="2">
     <widget class="QPushButton" name="btnStartRecv">
      <property name="text">
       <string>开始接收</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0" colspan="3">
     <widget class="QPlainTextEdit" name="editDebug"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

总结

QProcess启动子进程后,主进程关闭会带着子进程一起关闭,但是如果通过startDetached启动子进程写入的数据将读取不到,大概如此,网络中还包含子进程使用QSocketNotifier关联数据读取,我个人并未尝试成功,后期打算再尝试一下。

相关文章

Qt之进程通信-IPC(QLocalServer,QLocalSocket 含源码+注释)
Qt之进程通信-共享内存(含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lw向北.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值