参考up主:社长_嵌入式
Day1 界面的完成
一:学到的重点
1 this->setLayout(ui->gridLayoutGlobal);
使ui界面里面的内容全部有伸缩性
2 QList <QSerialPortInfo> serial=QSerialPortInfo::availablePorts();
得到串口列表存储到QList列表里面存放QSerialInfo类型
//设置串口名称
//得到串口列表存储到QList列表里面存放QSerialInfo类型
QList <QSerialPortInfo> serial=QSerialPortInfo::availablePorts();
for(QSerialPortInfo serial_info :serial){
qDebug()<<serial_info.portName();
//设置名称
ui->comboBox_6->addItem(serial_info.portName());
}
3 QString byte=ui->textEditRec->toPlainText();
QString byte = ui->textEditRec->toPlainText();
这行代码的作用是获取 ui->textEditRec
(一个 QTextEdit
类型的控件)中显示的纯文本内容,并将其存储到一个 QString
类型的变量 byte
中。
得到textEidt里面的字节
QString text = ui->textEditRec->toPlainText();
QByteArray byteArray = text.toLocal8Bit();
int byteCount = byteArray.size();
4 Strcmp函数
strcmp
是 C 语言中的一个标准库函数,用于比较两个字符串的大小关系,其函数原型为int strcmp(const char *s1, const char *s2);
,具体作用及返回值情况如下:
- 如果
str1
和str2
相等,函数返回 0。例如,strcmp("hello", "hello")
的返回值为 0。 - 如果
str1
大于str2
,函数返回一个大于 0 的整数。这里的 “大于” 是指在字典序(按照字符的 ASCII 码值顺序)中str1
排在str2
后面。比如strcmp("hello", "apple")
,因为 'h' 的 ASCII 码值大于 'a' 的 ASCII 码值,所以返回值大于 0。 - 如果
str1
小于str2
,函数返回一个小于 0 的整数。例如strcmp("apple", "banana")
,由于 'a' 的 ASCII 码值小于 'b' 的 ASCII 码值,所以返回值小于 0。
4 QStringList historyList
historyList.contains(cStr)判断列表里面是否含有cStr字符串 含有返回一 可以利用该发送解决历史记录去重问题
// 检查历史记录中是否已经存在该字符串
if (!historyList.contains(sendStr)) {
historyList.append(sendStr);
ui->textEditRecord->append(sendStr);
}
5 除了ui的方式第二种找到ui界面上的控件的名称
QString btnName=QString("pushButton_%1").arg(i);
QPushButton * btn =findChild<QPushButton*>(btnName);
6 btn->setProperty("buttonId",i);
给btn设置序号
7 int num=btn->property("buttonId").toInt();
得到点击的第几个按钮从而获取对应的第几个LineEdit
二:完成界面设计:
改善界面并把得到的串口显示到界面上
//设置串口名称
//得到串口列表存储到QList列表里面存放QSerialInfo类型
QList <QSerialPortInfo> serial=QSerialPortInfo::availablePorts();
for(QSerialPortInfo serial_info :serial){
qDebug()<<serial_info.portName();
//设置名称
ui->comboBox_6->addItem(serial_info.portName());
}
三 配置串口
1 配置串口的参数
// 配置串口的通用函数
void Widget::configureSerialPort(QSerialPort *port)
{
// 配置波特率
port->setBaudRate(ui->comboBox_5->currentText().toInt());
// 配置数据位
port->setDataBits(QSerialPort::DataBits(ui->comboBox->currentText().toInt()));
// 配置校验位
switch (ui->comboBox_2->currentIndex()) {
case 0:
port->setParity(QSerialPort::NoParity);
break;
case 1:
port->setParity(QSerialPort::EvenParity);
break;
case 2:
port->setParity(QSerialPort::MarkParity);
break;
case 3:
port->setParity(QSerialPort::OddParity);
break;
case 4:
port->setParity(QSerialPort::SpaceParity);
break;
case 5:
port->setParity(QSerialPort::UnknownParity);
break;
}
// 配置停止位
QString stopBitsText = ui->comboBox_3->currentText();
if (stopBitsText == "One") {
port->setStopBits(QSerialPort::OneStop);
} else if (stopBitsText == "OneAndHalf") {
port->setStopBits(QSerialPort::OneAndHalfStop);
} else if (stopBitsText == "Two") {
port->setStopBits(QSerialPort::TwoStop);
} else {
port->setStopBits(QSerialPort::OneStop); // 默认值
}
// 流控
QString flowControl = ui->comboBox_4->currentText();
if (flowControl == "No") {
port->setFlowControl(QSerialPort::NoFlowControl);
} else if (flowControl == "Soft") {
port->setFlowControl(QSerialPort::SoftwareControl);
} else if (flowControl == "Hard") {
port->setFlowControl(QSerialPort::HardwareControl);
}
}
2 完善打开串口的功能
这里需要创建俩个串口一个发送一个接收 一个串口不行
// 打开串口按钮
void Widget::on_pushButton_12_clicked()
{
// 配置串口1
serialPort1->setPortName(ui->comboBox_6->currentText());
configureSerialPort(serialPort1);
// 配置串口2,假设你手动选择另一个串口
// 这里简单假设你选择的是列表中的下一个串口,如果列表只有一个串口会有问题,实际使用时需要处理这种情况
int nextIndex = (ui->comboBox_6->currentIndex() + 1) % ui->comboBox_6->count();
serialPort2->setPortName(ui->comboBox_6->itemText(nextIndex));
configureSerialPort(serialPort2);
// 打开串口1
if(serialPort1->open(QIODevice::ReadWrite))
{
serialPort1->setDataTerminalReady(true);
qDebug() << "Serial port 1 opened successfully and DTR set to true";
}
else
{
qDebug() << "Failed to open serial port 1:" << serialPort1->errorString();
}
// 打开串口2
if(serialPort2->open(QIODevice::ReadWrite))
{
serialPort2->setDataTerminalReady(true);
qDebug() << "Serial port 2 opened successfully and DTR set to true";
}
else
{
qDebug() << "Failed to open serial port 2:" << serialPort2->errorString();
}
}
3 完善发送的功能
当点击发送的时候进行发送数据 qint64 bytesWritten1 = serialPort1->write(cStr);写入的时候自动发送
&QSerialPort::readyRead信号
// 发送按钮
void Widget::on_pushButton_sent_clicked()
{
if (!serialPort1->isOpen() || !serialPort2->isOpen()) {
qDebug() << "串口未全部打开";
return;
}
std::string str = ui->lineEdit_9->text().toStdString();
const char* cStr = str.c_str();
qDebug() << cStr;
// 向串口1发送数据
qint64 bytesWritten1 = serialPort1->write(cStr);
if (bytesWritten1 == -1) {
qDebug() << "写入串口1失败";
} else {
qDebug() << "成功向串口1写入" << bytesWritten1 << "字节";
}
// 向串口2发送数据
qint64 bytesWritten2 = serialPort2->write(cStr);
// if (bytesWritten2 == -1) {
// qDebug() << "写入串口2失败";
// } else {
// qDebug() << "成功向串口2写入" << bytesWritten2 << "字节";
// }
}
4 进行连接
qint64 bytesWritten1 = serialPort1->write(cStr);写入的时候自动发送&QSerialPort::readyRead信号
connect(serialPort1, &QSerialPort::readyRead, this, &Widget::on_receive1);
on_receive1函数
void Widget::on_receive1()
{
qDebug() << "Entering on_receive1 function";
if (serialPort1->bytesAvailable() > 0) {
qDebug() << "Bytes available on serialPort1:" << serialPort1->bytesAvailable();
QString str = serialPort1->readAll();
qDebug() << "Received data on serialPort1:" << str;
// 追加数据到 textEditRec 中
ui->textEditRec->append(str);
} else {
qDebug() << "No data available on serialPort1";
}
}
5 发送的结果
6 历史记录去重
7 界面优化
实现打开串口都时候串口配置不可以选择 关闭串口的时候才可以选择
打开串口
rec_serial是一个标志位默认为假 第一次打开按钮的时候进入if语句设置rec_serial为true第二次点击按钮进入else语句中
关闭串口
7 定时器实现定时发送
头文件中定义定时器
//定义定时器来完成定时发送
QTimer *timer;
widget构造函数中初始化定时器
//初始化定时器
timer=new QTimer(this);
connect(timer,&QTimer::timeout,[=](){
//开始发送
on_pushButton_sent_clicked();
});
给定时发送按钮设置信号和槽点击为true 在点击为false
void Widget::on_checkBox_13_clicked(bool checked)
{
if(checked){
//开启定时器
timer->start(ui->lineEdit_10->text().toInt());
//发送,发送内容按钮设置为假
ui->pushButton_sent->setEnabled(false);
ui->lineEdit_9->setEnabled(false);
}
else{
//关闭定时器
timer->stop();
//发送,发送内容按钮设置为真
ui->pushButton_sent->setEnabled(true);
ui->lineEdit_9->setEnabled(true);
}
}
8 保存接收
void Widget::on_pushButton_14_clicked()
{
QString FileName=QFileDialog::getSaveFileName(this,"Save File","C:\\Users\\50192\\Desktop","Text (*.txt)");
if(FileName!=nullptr){
//打开文件
QFile File(FileName);
if(!File.open(QIODevice::WriteOnly|QIODevice::Text)){
return;
}
else{
QTextStream out(&File);
//写入数据到文件中
out<<ui->textEditRec->toPlainText();
}
}
}
9 显示时间功能
在状态栏下显示时间
//状态栏设置当前时间
QTimer *myTimer=new QTimer(this);
//一秒刷新一下正好对应时间
myTimer->start(1000);
connect(myTimer,&QTimer::timeout,[=](){
QDateTime currentTime=QDateTime::currentDateTime();
time=currentTime.toString("yyyy-MM-dd hh:mm:ss");
ui->label_15->setText(time);
});
点击显示时间接收方接收数据显示时间
//文本框显示时间
void Widget::on_checkBox_10_clicked(bool checked)
{
flag=checked;
}
// 追加数据到 textEditRec 中
//flag为真显示时间
if(flag){
ui->textEditRec->append(time+" "+str);
}
//flag为假不显示时间
else {
ui->textEditRec->append(str);
}
10 hex显示
//hex(16进制)显示
void Widget::on_checkBox_11_clicked(bool checked)
{
if(checked){
//读取textEdit的内容
QString tmp=ui->textEditRec->toPlainText();
//把QString转化为字节
QByteArray qtemp=tmp.toUtf8();
//字节转化为hex
qtemp=qtemp.toHex();
//把hex转化为QString
ui->textEditRec->setText(QString::fromUtf8(qtemp));
}
else{
//读取textEdit的内容
QString tmp=ui->textEditRec->toPlainText();
//把QString转换为字节
QByteArray qtemp=tmp.toUtf8();
//fromHex() 是 QByteArray 类的一个静态函数,其作用是将十六进制编码的字符串转换为对应的二进制数据
qtemp=QByteArray::fromHex(qtemp);
ui->textEditRec->setText(QString::fromUtf8(qtemp));
}
}
11 改善hex显示当定时发送的时候转换为hex
void Widget::on_receive1()
{
if (serialPort1->bytesAvailable() > 0) {
QString str = serialPort1->readAll();
if(ui->checkBox_11->isChecked()){
//读取以前的内容
QString tep=ui->textEditRec->toPlainText();
//读取新的内容转换为16进制
QByteArray byt=str.toUtf8().toHex();
byt=tep.toUtf8()+byt;
ui->textEditRec->setText(QString::fromUtf8(byt));
}
else{
// 追加数据到 textEditRec 中
//flag为真显示时间
if(flag){
ui->textEditRec->append(time+" "+str);
}
//flag为假不显示时间
else {
ui->textEditRec->append(str);
}
rec_by=rec_by+str.size();
ui->label_13->setText("Receive:"+QString::number(rec_by));
}
}
else {
qDebug() << "No data available on serialPort1";
}
}
12 发送的时候点击hex发送
hex_flag是判断是否加入历史记录里面
//判断hex是否选中
if(ui->checkBox_15->isChecked()){
hex_flag=false;
QString tmp=ui->lineEdit_9->text();
QByteArray tmpArray=tmp.toUtf8();
//判断是否为偶数
if(tmpArray.size()%2!=0){
hex_flag=true;
ui->label_12->setText("Send: Error");
return;
}
//判断是否为16进制
for(char c : tmpArray){
//std::isdigit(c)判断是否符合16进制
if(!std::isdigit(c)){
hex_flag=true;
ui->label_12->setText("Send: Error");
return;
}
}
//转换为16进制
tmpArray=QByteArray::fromHex(tmpArray);
bytesWritten1 = serialPort1->write(tmpArray);
}
13 优化hex显示
转化的数据要有间隔 并要大写
tmp.mid(i,2)+“ ” 从第i个开始读取2个并在后面加个空格
BUG:hex发送的数据在点击hex显示转化为ASCII码了
14 换行功能的实现
// insertPlainText 发送的时候不进行换行
ui->textEditRec->insertPlainText(time + " " + str);
void Widget::on_receive1()
{
if (serialPort1->bytesAvailable() > 0) {
QByteArray data = serialPort1->readAll();
// 自动换行
if(ui->checkBox_12->isChecked()){
data.append("\r\n");
}
if( rn_flag){
data.append("\r\n");
}
//hex显示 每俩个字符之间加俩个空格
if(ui->checkBox_11->isChecked()){
// 读取以前的内容
QString prevText = ui->textEditRec->toPlainText();
// 读取新的内容转换为16进制
QByteArray hexData = data.toHex().toUpper();
// 格式化十六进制数据,每两个字符加一个空格
QString formattedHex;
for (int i = 0; i < hexData.size(); i += 2) {
formattedHex += hexData.mid(i, 2) + " ";
}
// 拼接旧内容和新的十六进制内容
QString newText = prevText + formattedHex;
ui->textEditRec->setText(newText);
}
//一般显示的时候
else {
QString str = QString::fromUtf8(data);
// flag为真显示时间
if(flag){
// insertPlainText 发送的时候不进行换行
ui->textEditRec->insertPlainText(time + " " + str);
}
// flag为假不显示时间
else {
ui->textEditRec->insertPlainText(str);
}
rec_by = rec_by + str.size();
ui->label_13->setText("Receive:" + QString::number(rec_by));
}
}
else {
qDebug() << "No data available on serialPort1";
}
}
15 隐藏面板,历史
才有伸缩性
才会出现俩种点击状态
//隐藏面板
void Widget::on_pushButton_16_clicked(bool checked)
{
if(checked){
ui->pushButton_16->setText("打开面板");
ui->groupBoxText->hide();
}
else{
ui->pushButton_16->setText("隐藏面板");
ui->groupBoxText->show();
}
}
//隐藏历史
void Widget::on_pushButton_18_clicked(bool checked)
{
if(checked){
ui->pushButton_18->setText("打开历史");
ui->groupBoxrecord->hide();
}
else{
ui->pushButton_18->setText("隐藏历史");
ui->groupBoxrecord->show();
}
}
16 自定义控件实现串口的刷新
完善文件
完善代码
交给父类处理很重要 如果没有将会丢了原有的功能
连接
完成槽函数
17 实现多文本功能(多个按钮共同实现功能)
//完成多文本功能
//创建list列表存储QPushButton*的数据
QList <QPushButton *> buttons;
for(int i=1;i<=9;i++){
QString btnName=QString("pushButton_%1").arg(i);
//除了ui的方式第二种找到ui界面上的控件的名称
QPushButton * btn =findChild<QPushButton*>(btnName);
if(btn){
//给btn设置序号
btn->setProperty("buttonId",i);
//添加到QList列表中
buttons.append(btn);
//连接信号和槽
connect(btn,&QPushButton::clicked,this,&Widget::on_Many_Text);
}
}
void Widget::on_Many_Text()
{
//找到那个控件发送了信号
QPushButton*btn=qobject_cast<QPushButton*>(sender());
if(btn){
//得到点击的第几个按钮从而获取对应的第几个LineEdit
int num=btn->property("buttonId").toInt();
QString lineName=QString("lineEdit_%1").arg(num);
//找到ui界面上的控件
QLineEdit *lineEdit=findChild<QLineEdit*>(lineName);
if(lineEdit){
//多文本写入的内容发送时候对应的LineEdit内容
ui->lineEdit_9->setText(lineEdit->text());
}
//得到点击的第几个按钮从而获取对应的第几个checkBoxName
QString checkBoxName=QString("checkBox_%1").arg(num);
//找到ui界面对应的控件
QCheckBox *checkBox=findChild<QCheckBox*>(checkBoxName);
if(checkBox){
//如果checkBox->isChecked()true则hex发送也为true
ui->checkBox_15->setChecked(checkBox->isChecked());
}
//调用发送按钮
on_pushButton_sent_clicked();
}
}
18 初步实现循环发送
有缺点循环的时候不能点击其他事件页面会卡
//初步循环发送
void Widget::on_checkBox_9_clicked(bool checked)
{
if(checked){
for(int i=0;i<this->buttons.size()-1;i++){
QPushButton *btn=buttons[i];
//发送点击信号
emit btn->clicked();
//延迟发送
QThread::msleep(ui->spinBox->value());
}
}
}
19 定时器实现循环发送
//初始化循环定时器
cycle_timer=new QTimer(this);
//连接
connect(cycle_timer,&QTimer::timeout,this,&Widget::button_handler);
//循环发送函数
void Widget::button_handler()
{
//不能用for循环 如果for循环一次性输出全部
if(cycle_index<buttons.size()){
QPushButton *btn=buttons[cycle_index];
//发送点击信号
emit btn->clicked();
cycle_index=cycle_index+1;
}
else{
cycle_index=0;
}
}
//初步循环发送
void Widget::on_checkBox_9_clicked(bool checked)
{
if(checked){
//启动定时器
cycle_timer->start(ui->spinBox->value());
}
else{
//关闭定时器
cycle_timer->stop();
}
}
20 多线程实现循环发送
21 重置
//重置按钮实现
void Widget::on_pushButton_re_clicked()
{
QMessageBox mesBox;
//设置窗口名
mesBox.setWindowTitle("提示 ");
//设置图标
mesBox.setIcon(QMessageBox::Question);
//设置提示的内容
mesBox.setText("重置列表不可逆,确认是否重置? ");
//设置按钮 yes和no
QPushButton *yesBtn=mesBox.addButton("Y是Y",QMessageBox::YesRole);
QPushButton *noBtn=mesBox.addButton("N否N",QMessageBox::NoRole);
//显示窗口进行阻塞
mesBox.exec();
if(mesBox.clickedButton()==yesBtn){
for(int i=0;i<lineEdits.size();i++){
//清空数据
lineEdits[i]->clear();
//取消打勾
checkBoxs[i]->setChecked(false);
}
}
if(mesBox.clickedButton()==noBtn)
{
}
}
21 多文本保存和载入
//保存按钮
void Widget::on_pushButton_save_clicked()
{
QString FileName=QFileDialog::getSaveFileName(this,"Save File","C:\\Users\\50192\\Desktop","Text(*.txt)");
QFile File(FileName);
if(!File.open(QIODevice::WriteOnly|QIODevice::Text)){
return;
}
else{
QTextStream out(&File);
for(int i=0;i<8;i++){
out<<checkBoxs[i]->isChecked()<<","<<lineEdits[i]->text()<<"\n";
}
}
}
//载入按钮
void Widget::on_pushButton_zai_clicked()
{
int i=0;
QString FileName=QFileDialog::getOpenFileName(this,"Open File","C:\\Users\\50192\\Desktop","Text(*.txt)");
QFile file(FileName);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
return;
}
else{
QTextStream in(&file);
while(!in.atEnd()&&i<8){
QString line=in.readLine();
QStringList strList=line.split(",");
if(strList.count()==2){
checkBoxs[i]->setChecked(strList.at(0).toInt());
lineEdits[i]->setText(strList.at(1));
i++;
}
}
}
}