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

文章目录

  • 一、QProcess进程通信示例
  • 二、QProcess通信个人理解
  • 三、源码
    • MainWindowProcessSender
      • MainWindowProcessSender.h
      • MainWindowProcessSender.cpp
      • MainWindowProcessSender.ui
    • MainWindowProcessRecv
      • MainWindowProcessRecv.h
      • MainWindowProcessRecv.cpp
      • MainWindowProcessRecv.ui
  • 总结
  • 相关文章

一、QProcess进程通信示例

下方为默认程序启动通信示例
Qt之进程通信-QProcess(含源码+注释)_第1张图片
下方为默认程序为空,然后指定启动的应用程序通信
Qt之进程通信-QProcess(含源码+注释)_第2张图片

二、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 
#include 

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 
#include 
#include 

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


<ui version="4.0">
 <class>MainWindowProcessSenderclass>
 <widget class="QMainWindow" name="MainWindowProcessSender">
  <property name="geometry">
   <rect>
    <x>0x>
    <y>0y>
    <width>400width>
    <height>300height>
   rect>
  property>
  <property name="windowTitle">
   <string>MainWindowProcessSenderstring>
  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>0x>
     <y>0y>
     <width>400width>
     <height>23height>
    rect>
   property>
  widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarAreaenum>
   attribute>
   <attribute name="toolBarBreak">
    <bool>falsebool>
   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 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 

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


<ui version="4.0">
 <class>MainWindowProcessRecvclass>
 <widget class="QMainWindow" name="MainWindowProcessRecv">
  <property name="geometry">
   <rect>
    <x>0x>
    <y>0y>
    <width>400width>
    <height>300height>
   rect>
  property>
  <property name="windowTitle">
   <string>MainWindowProcessRecvstring>
  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>0x>
     <y>0y>
     <width>400width>
     <height>23height>
    rect>
   property>
  widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarAreaenum>
   attribute>
   <attribute name="toolBarBreak">
    <bool>falsebool>
   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^/)

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

你可能感兴趣的:(qt,命令模式,开发语言)