上次记录了一下用Qt是如何进行串口参数设置的,这次记录一下如何实现串口的接收,也就是:
可以看到,对于接收,我实现的主要功能有将接收输出到文件,是否显示接收到的时间,
是否用十六进制显示,暂停显示以及保存数据和清除显示。
在头文件中,也就是widget.h中:
QSerialPort mSerialPort; //串口对象
QByteArray receivePrimaryData; //存放接收到的原始数据数据
bool recDisplayIsPause = false; //判断接收显示是否暂停标志位, 默认显示
QFile recFile; //接收到文件对象
bool isRecToFile = false; //是否接收到文件标志位,默认不接收到文件
定义了这几个与接收有关的成员变量。
receivePrimaryData:是接收到的原始数据,也就是发送过来的是什么,我们就不进行
任何处理,直接保存到该变量当中去。
recDisplayIsPause:该变量为true时接收暂停显示。
recFile:一个文件对象,用于将接收到的数据保存到文件当中去。
isRecToFile:该变量为true时,接收保存到文件。
对于源文件,也就是widget.cpp:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//载入用户上次设置
mLoadSet();
//添加样式表
mSetQSS();
//初始化串口设置
mQSerialPortSet();
//接收设置
mReceiveSet(); //这是与接收相关的函数
在构造函数中调用mReceiveSet();方法,所有的与接收有关的处理都是在这个方法中
实现的。
前文提到,我们已经设置好了mSerialPort;串口对象的参数。在Qt中,当串口接收到
信号的时候,会发出QSerialPort::readyRead信号,因此,我们连接该信号到槽函数,对
接收到的信号进行处理。
也就是:
//串口接收设置
void Widget::mReceiveSet()
{
//链接串口接收槽函数
connect(&mSerialPort, &QSerialPort::readyRead, this, [=](){
//当串口接收到信号时,会执行下面代码
//这里填写处理代码
});
在该Lambda函数中,主要要做的是将接收到的信号保存到原始数据也就是,
receivePrimaryData变量中,然后再将该原始数据赋值个一个临时变量,最后对这个
临时变量进行相应的处理,再显示到文本框。至于为什么要保留这个原始数据
receivePrimaryData,不直接对其进行处理,那是因为对于串口通信来说,原始数据
是很重要的,比如我们就想看看串口发送过来的数据到底是什么,并将其保存到文件中
去,那么这个原始数据就非常有用了。
以下是整个处理函数:
//链接串口接收槽函数
connect(&mSerialPort, &QSerialPort::readyRead, this, [=](){
//获取接收数据
receivePrimaryData = mSerialPort.readAll();
//qDebug() << receivePrimaryData << endl;
//创建临时数据
QByteArray receiveTemporaryData = receivePrimaryData;
//存放16进制数据
//QByteArray hexData = receivePrimaryData.toHex();
//十六进制显示
if(ui->chB_recDisplayHEX->isChecked() == true)
{
//将QByteArray转化为十六进制表示
receiveTemporaryData = receiveTemporaryData.toHex().toUpper();
//在每一个十六进制数之间插入空格
for(int i = 2; i < receiveTemporaryData.size(); i += 3)
{
receiveTemporaryData.insert(i, ' ');
}
}
//显示接收时间
if(ui->chB_recDisplayTime->isChecked() == true)
{
//获取当前时间
QDateTime currentTime = QDateTime::currentDateTime();
QString timeString = currentTime.toString("yyyy-MM-dd HH:mm:ss");
timeString = ("[" + timeString + "]");
//添加时间戳
receiveTemporaryData = timeString.append(receiveTemporaryData).toUtf8();
}
//接收显示不暂停,并且不接收到文件,则接收到接收文本框
if(recDisplayIsPause == false && isRecToFile == false)
{
ui->tE_receiveData->append(receiveTemporaryData);
}
//不暂停显示并且接收到文件
if(recDisplayIsPause == false && isRecToFile == true)
{
ui->tE_receiveData->append(receiveTemporaryData);
QTextStream out(&recFile);
out << receiveTemporaryData;
}
//暂停显示并且接收到文件
if(recDisplayIsPause == true && isRecToFile == true)
{
QTextStream out(&recFile);
out << receiveTemporaryData;
}
});
首先,我们先将接收到的信息传递给一个临时的变量:
//获取接收数据
receivePrimaryData = mSerialPort.readAll();
//qDebug() << receivePrimaryData << endl;
//创建临时数据
QByteArray receiveTemporaryData = receivePrimaryData;
之后判断用户是否勾选了十六进制接收,如果勾选了,就调用
receiveTemporaryData.toHex().toUpper();方法,将接收到的数据转换为十六进制的
字符串。
//十六进制显示
if(ui->chB_recDisplayHEX->isChecked() == true)
{
//将QByteArray转化为十六进制表示
receiveTemporaryData = receiveTemporaryData.toHex().toUpper();
//在每一个十六进制数之间插入空格
for(int i = 2; i < receiveTemporaryData.size(); i += 3)
{
receiveTemporaryData.insert(i, ' ');
}
}
结果是这样:
发送端:发送aa bb cc
没有勾选16进制显示,结果如下:
勾选十六进制显示,结果如下:
之后再判断用户是否添加接收时间。如果添加,就获取系统时间,再将信息添加到
时间后面。
//显示接收时间
if(ui->chB_recDisplayTime->isChecked() == true)
{
//获取当前时间
QDateTime currentTime = QDateTime::currentDateTime();
QString timeString = currentTime.toString("yyyy-MM-dd HH:mm:ss");
timeString = ("[" + timeString + "]");
//添加时间戳
receiveTemporaryData = timeString.append(receiveTemporaryData).toUtf8();
}
勾选添加时间信息后结果如下:
之后的代码就是用于判断是否接收显示,或者是接受到文本框,还是接收到文件。
//接收显示不暂停,并且不接收到文件,则接收到接收文本框
if(recDisplayIsPause == false && isRecToFile == false)
{
ui->tE_receiveData->append(receiveTemporaryData);
}
//不暂停显示并且接收到文件
if(recDisplayIsPause == false && isRecToFile == true)
{
ui->tE_receiveData->append(receiveTemporaryData);
QTextStream out(&recFile);
out << receiveTemporaryData;
}
//暂停显示并且接收到文件
if(recDisplayIsPause == true && isRecToFile == true)
{
QTextStream out(&recFile);
out << receiveTemporaryData;
}
});
可以看到,我们是通过一些定义的标志位来判断的。如何改变这些标志位的真假呢,
我是通过连接槽函数来实现的。
首先是否接收显示,当用户点击该控件时:
它将发出一个状态改变的信号:&QCheckBox::stateChanged,我们通过
槽函数进行状态改变。
//链接是否接收显示
connect(ui->chB_recDisplayPause, &QCheckBox::stateChanged, this, [=](int isChecked){
//如果勾选,暂停显示
if(isChecked == Qt::Checked)
{
ui->tE_receiveData->setEnabled(false);
//显示暂停
recDisplayIsPause = true;
}
else if(isChecked == Qt::Unchecked)
{
ui->tE_receiveData->setEnabled(true);
//继续显示
recDisplayIsPause = false;
}
});
当用户打勾或取消打勾时,我们改变是否接收显示状态,并且设置接收文本框的使能状态。
之后再判断用户是否接收信息到文件,如果当用户点击接收数据到文件时,弹出一个文件选择
框,让用户选择文件路径,再通过该路径对文件进行操作。
代码如下:
//链接是否接收到文件,获取文件对象
connect(ui->chB_recToFile, &QCheckBox::stateChanged, this, [=](int isChecked){
if(isChecked == Qt::Checked)
{
//修改接收方式为接收到文件
isRecToFile = true;
//创建文件路径获取对象
QFileDialog dialog;
//设置文件过滤器(txt和所有文件)
dialog.setNameFilter("Text files (*.txt);;All files (*)");
//获取文件路径
if(dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
foreach(QString fileName, fileNames){
recFile.setFileName(fileName);
//判断文件是否存在
if(!recFile.exists())
{
qDebug() << "File does not exist, Creating file...";
//创建文件(以文本模式打开)
if(!recFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << "Failed to open file for writing!" << endl;
return;
}
}
else
{
if(!recFile.open(QIODevice::Append | QIODevice::Text))
{
return;
}
}
}
}
//取消文件路径选择
else
{
//修改接收方式为不接收到文件
isRecToFile = false;
//清除勾选
ui->chB_recToFile->setCheckState(Qt::CheckState::Unchecked);
}
}
else if(isChecked == Qt::Unchecked)
{
//修改接收方式为不接收到文件
isRecToFile = false;
//关闭文件
recFile.close();
}
});
当用户勾选或取消勾选接收到文件时,会调用该槽函数,也就是:
首先,判断用户是否时勾选,如果是勾选则弹出文件选择对框,此处调用的是
dialog.exec()方法,当用户选择了文件并点击确定时会返回真值,之后改变是否接收
到文件标志位并且判断该文件是否存在,如果不存在就创建文件,如果存在就直接
打开文件。如果用户在文件选择对话框点击取消,那么dialog.exec()方法会返回假值,
此时改变文件标志位为不接收到文件,并且清除勾选。
也就是:
//获取文件路径
if(dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
foreach(QString fileName, fileNames){
recFile.setFileName(fileName);
//判断文件是否存在
if(!recFile.exists())
{
qDebug() << "File does not exist, Creating file...";
//创建文件(以文本模式打开)
if(!recFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << "Failed to open file for writing!" << endl;
return;
}
}
else
{
if(!recFile.open(QIODevice::Append | QIODevice::Text))
{
return;
}
}
}
}
//取消文件路径选择
else
{
//修改接收方式为不接收到文件
isRecToFile = false;
//清除勾选
ui->chB_recToFile->setCheckState(Qt::CheckState::Unchecked);
}
}
还有一种情况就是用户从勾选到取消勾选,那么就改变接收到文件标志位为false,
即, isRecToFile = false; 并且关闭文件。
else if(isChecked == Qt::Unchecked)
{
//修改接收方式为不接收到文件
isRecToFile = false;
//关闭文件
recFile.close();
}
之后是清除显示按钮也就是:
直接调用clear()函数即可。
//链接清除显示
connect(ui->pB_recClearDisplay, &QPushButton::clicked, this, [=](){
ui->tE_receiveData->clear();
});
最后是保存数据,也就是将接收文本框显示的数据保存到文件当中去,和接收到文件的
过程也大差不差,首先获取文件路径,再判断文件是否存在,不存在则创建,存在则直接
打开,然后读取接收文本框的数据,将该数据输入到文件中去。
//链接保存数据
connect(ui->pB_recSaveData, &QPushButton::clicked, this, [=](){
//创建获取文件路径对象
QFileDialog dialog;
//设置文件过滤器
dialog.setNameFilter("Text files (*.txt);;All files (*)");
//获取文件路径
if(dialog.exec())
{
//获取文件路径
QStringList fileNames = dialog.selectedFiles();
//创建保存数据文件对象
QFile saveFile;
//创建文件
foreach(QString fileName , fileNames)
{
saveFile.setFileName(fileName);
//判断文件是否存在,不存在创建文件
if(!saveFile.exists())
{
//创建文件(以文本模式打开)
if(!saveFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << "Failed to open file for writing!" << endl;
return;
}
}
//如果文件存在
else
{
if(!saveFile.open(QIODevice::Append | QIODevice::Text))
{
return;
}
}
}
//获取需要保存的数据
QString recEditdata = ui->tE_receiveData->toPlainText();
//保存到文件
QTextStream out(&saveFile);
out << recEditdata;
}
});
以上就是接收模块的全部代码,下面给出整个接收方法void Widget::mReceiveSet()
完整的代码。
//串口接收设置
void Widget::mReceiveSet()
{
//链接串口接收槽函数
connect(&mSerialPort, &QSerialPort::readyRead, this, [=](){
//获取接收数据
receivePrimaryData = mSerialPort.readAll();
//qDebug() << receivePrimaryData << endl;
//创建临时数据
QByteArray receiveTemporaryData = receivePrimaryData;
//存放16进制数据
//QByteArray hexData = receivePrimaryData.toHex();
//十六进制显示
if(ui->chB_recDisplayHEX->isChecked() == true)
{
//将QByteArray转化为十六进制表示
receiveTemporaryData = receiveTemporaryData.toHex().toUpper();
//在每一个十六进制数之间插入空格
for(int i = 2; i < receiveTemporaryData.size(); i += 3)
{
receiveTemporaryData.insert(i, ' ');
}
}
//显示接收时间
if(ui->chB_recDisplayTime->isChecked() == true)
{
//获取当前时间
QDateTime currentTime = QDateTime::currentDateTime();
QString timeString = currentTime.toString("yyyy-MM-dd HH:mm:ss");
timeString = ("[" + timeString + "]");
//添加时间戳
receiveTemporaryData = timeString.append(receiveTemporaryData).toUtf8();
}
//接收显示不暂停,并且不接收到文件,则接收到接收文本框
if(recDisplayIsPause == false && isRecToFile == false)
{
ui->tE_receiveData->append(receiveTemporaryData);
}
//不暂停显示并且接收到文件
if(recDisplayIsPause == false && isRecToFile == true)
{
ui->tE_receiveData->append(receiveTemporaryData);
QTextStream out(&recFile);
out << receiveTemporaryData;
}
//暂停显示并且接收到文件
if(recDisplayIsPause == true && isRecToFile == true)
{
QTextStream out(&recFile);
out << receiveTemporaryData;
}
});
//链接是否接收显示
connect(ui->chB_recDisplayPause, &QCheckBox::stateChanged, this, [=](int isChecked){
//如果勾选,暂停显示
if(isChecked == Qt::Checked)
{
ui->tE_receiveData->setEnabled(false);
//显示暂停
recDisplayIsPause = true;
}
else if(isChecked == Qt::Unchecked)
{
ui->tE_receiveData->setEnabled(true);
//继续显示
recDisplayIsPause = false;
}
});
//链接是否接收到文件,获取文件对象
connect(ui->chB_recToFile, &QCheckBox::stateChanged, this, [=](int isChecked){
if(isChecked == Qt::Checked)
{
//修改接收方式为接收到文件
isRecToFile = true;
//创建文件路径获取对象
QFileDialog dialog;
//设置文件过滤器(txt和所有文件)
dialog.setNameFilter("Text files (*.txt);;All files (*)");
//获取文件路径
if(dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
foreach(QString fileName, fileNames){
recFile.setFileName(fileName);
//判断文件是否存在
if(!recFile.exists())
{
qDebug() << "File does not exist, Creating file...";
//创建文件(以文本模式打开)
if(!recFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << "Failed to open file for writing!" << endl;
return;
}
}
else
{
if(!recFile.open(QIODevice::Append | QIODevice::Text))
{
return;
}
}
}
}
//取消文件路径选择
else
{
//修改接收方式为不接收到文件
isRecToFile = false;
//清除勾选
ui->chB_recToFile->setCheckState(Qt::CheckState::Unchecked);
}
}
else if(isChecked == Qt::Unchecked)
{
//修改接收方式为不接收到文件
isRecToFile = false;
//关闭文件
recFile.close();
}
});
//链接清除显示
connect(ui->pB_recClearDisplay, &QPushButton::clicked, this, [=](){
ui->tE_receiveData->clear();
});
//链接保存数据
connect(ui->pB_recSaveData, &QPushButton::clicked, this, [=](){
//创建获取文件路径对象
QFileDialog dialog;
//设置文件过滤器
dialog.setNameFilter("Text files (*.txt);;All files (*)");
//获取文件路径
if(dialog.exec())
{
//获取文件路径
QStringList fileNames = dialog.selectedFiles();
//创建保存数据文件对象
QFile saveFile;
//创建文件
foreach(QString fileName , fileNames)
{
saveFile.setFileName(fileName);
//判断文件是否存在,不存在创建文件
if(!saveFile.exists())
{
//创建文件(以文本模式打开)
if(!saveFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << "Failed to open file for writing!" << endl;
return;
}
}
//如果文件存在
else
{
if(!saveFile.open(QIODevice::Append | QIODevice::Text))
{
return;
}
}
}
//获取需要保存的数据
QString recEditdata = ui->tE_receiveData->toPlainText();
//保存到文件
QTextStream out(&saveFile);
out << recEditdata;
}
});
}