文章目录
一、QProcess进程通信示例
下方为默认程序启动通信示例
下方为默认程序为空,然后指定启动的应用程序通信
二、QProcess通信个人理解
- 主进程给子进程发送数据:直接通过QProcess对象的write函数写入数据(给通过start函数启动的进程,并且写入数据需要以"\n"结尾,方便子进程识别且读取数据);
- 主进程接收数据:直接关联QProcess的readyReadStandardError()、readyReadStandardOutput()信号可接收读取错误输出和数据输出;
- 子进程接收数据:本文通过QTextStream和std::string对象读取数据,且两者对象都是在线程中循环识别数据,并通过信号输出显示到主界面中;
- 子进程发送数据:子进程通过QFile打开stdout流通道,直接通过QFile的write函数写入数据即可(此处不需要以"\n"结尾主线程都可以读取数据)。
三、源码
MainWindowProcessSender
MainWindowProcessSender.h
cpp
#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
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
<?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
cpp
#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
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
<?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^/)
注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除