QT串口助手:识别串口号,发送,接收,十六进制

1 摘要

本文主要讲述如何使用QT从零开始实现一个串口助手的基本功能,功能如标题所示,文末附有源码供大家参考。文中若有纰漏,烦请读者斧正。

2 环境

  • QT 5.14.1
  • Window 11

3 功能

  • 串口打开/关闭
    • 启动软件时识别串口号
    • 打开按键随串口打开状态而改变文字
    • 打开失败弹窗提示
  • 串口发送/接收
    • 字符和十六进制
  • 清除发送/接收

4 创建工程

  1. 下载QT
  2. 安装QT
  3. 创建工程

在这里插入图片描述

5 界面布局

5.1 用到的控件

  • Buttons
    • Push Button:即最常见的按键:打开串口、发送、清除接收、清除发送,都是用了这个控件
    • Check Box:即用于打钩的小方框,16进制显示和16进制接收用到了此控件
  • Input Widgets
    • Combo Box:点击有下拉菜单的控件,串口、波特率、停止位、数据位、奇偶校验的选择用到此控件
    • Text Edit:输入文本框,串口的发送内容的编辑用到此控件,串口接收内容的显示也用到此控件(此时该控件为只读)
  • Display Widgets
    • Label:串口、波特率、停止位、数据位、奇偶校验,这些文字的显示用到此控件
  • Layouts
    • Vertical Layout:垂直布局,在此布局框内的空间按垂直等间距排列
    • Horizontal Layout:水平布局,在此布局框内的控件按水平等间距排列
    • Form Layout:在此布局框内的控件按垂直两列等间距排列

5.2 摆放控件

将控件从左侧拖拽到窗口编辑处
在这里插入图片描述

5.3 控件编辑及布局

  • 修改Label/Push Button/Check Box控件的文本内容(双击控件即可修改)
  • 在Combo Box中添加菜单内容(双击控件即可添加)
    • 串口:不加,后面通过串口号识别,在代码里添加
    • 波特率:本文只添加9600/19200/38400/57600/115200
    • 停止位:1/1.5/2
    • 数据位:5/6/7/8
    • 校验位:无/奇校验/偶校验
  • 控件布局:调整控件位置,通过布局菜单对选中的控件进行布局
  • 修改控件名:按控件的实际用途修改控件名

控件布局:在这里插入图片描述

修改控件名:
在这里插入图片描述

6 添加库及头文件

本文的串口助手基于QT自带的QSerialPort类实现,故需要添加该类相关的宏和头文件,除此之外,本文用到的头文件也在此一并添加。

添加宏serialport:
在这里插入图片描述

添加头文件:
在这里插入图片描述
其中QSerialPort即QT自带的串口类,QSerialPortInfo用于获取串口号,QMessageBox用于实现弹窗提示,QDebug用于输出调试信息。

7 打开串口

7.1 槽函数

打开串口这个动作是在按下“打开串口”这个按键后进行的,因此必须建立按键跟动作之间的联系,在QT中,这种联系是通过“信号和槽”这样的机制来实现的,简单来说,“信号”就是按下按键这个事件,可以理解为一个标志,“槽”是指对这个事件所做的响应,可以理解为一个函数。

从控件转到槽函数(此处转到“按下时”的槽函数,即此函数是在按键按下时被调用):
在这里插入图片描述

选择clicked()后,QT将在cpp文件中生成槽函数(显然,函数里的内容是要程序员手写的,不是QT生成的):
在这里插入图片描述

槽函数所调用的子函数applySerialPortConfig(此函数实现获取combobox中的输入并设置到串口):
在这里插入图片描述

槽函数所调用的子函数setEnableSerialPortConfig(此函数实现combobox的屏蔽与打开):
在这里插入图片描述

“打开按键”的槽函数主要做这几件事情:

  1. 根据当前串口的打开状态,选择是要打开串口还是关闭串口
    • 通过自定义成员变量mIsOpen实现
  2. 若要打开串口,则从combobox中获取配置并打开串口
    • 先通过QComboBox的currentText方法获取当前输入,再把输入通过QSerialPort类的setXXX方法进行设置,再调open方法
  3. 若要关闭串口,则调用关闭串口函数
    • 调QSerialPort类的close方法
  4. 串口打开失败时,弹窗提示
    • 调QMessageBox类的warning方法
  5. 串口打开状态改变后,修改按键的文本内容
    • 调QPushButton的setText方法
  6. 串口打开状态改变后,修改combobox的激活状态
    • 调QComboBox的setText方法

注意:相关成员变量需先在头文件中定义好

7.2 串口列表的获取

前文中并没有在combobox中写死串口列表,是为了动态获取串口列表。本文只在软件打开的时候获取串口列表(更完善的做法是在点击combobox后更新,后面有时间会实现这个功能),只需在构造函数中添加以下代码。
在这里插入图片描述

foreach是QT中的一个关键字,其作用是对第二个参数中的对象进行遍历,把遍历过程中的每个对象依次赋给第一个参数,并执行花括号中的内容。在这里,就是把可获取的串口列表availablePorts()中的串口,逐个将其串口号添加到combobox中。

8 串口发送/接收

8.1 字符的收发

对于发送来说,其实现过程如下:

  1. 给发送按键创建槽函数
  2. 在槽函数中获取发送文本框中的数据
  3. 对获取到的数据进类型和格式的转换(如需)
  4. 发送数据到串口

代码实现:

void MainWindow::on_pushButtonSend_clicked()
{if(mIsOpen == true) {//mSerialPort.write(ui->textEditSend->toPlainText().toStdString().c_str());   //ENTER键:0A(即\n)mSerialPort.write(ui->textEditSend->toPlainText().replace("\n", "\r\n").toStdString().c_str()); //ENTER键:0D 0A(即\r\n)}
}

对于接收来说,由于不存在接收按键,其实现跟发送有些许不同,但本质还是一样的,都是QT中的信号和槽的机制:

  1. 通过connect方法,连接接收完成信号readyRead和自定义槽函数on_serialPort_readyRead(函数名字自定义)
  2. 在槽函数中读串口
  3. 对读到的串口数据进行类型和格式的转换(如需)
  4. 把数据显示在接收文本框中

代码实现:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//此处省略中间内容....//连接接收完成信号readyRead和自定义槽函数on_serialPort_readyReadconnect(&mSerialPort, SIGNAL(readyRead()), this, SLOT(on_serialPort_readyRead()));
}void MainWindow::on_serialPort_readyRead()
{if(mIsOpen == true) {QByteArray rxData = mSerialPort.readAll();ui->textEditReceive->insertPlainText(rxData);   //用append会多一个换行ui->textEditReceive->moveCursor(QTextCursor::End);  //调整光标位置到最新接收的尾部,避免看不到最新接收的数据}
}

8.2 十六进制收发

在实现了上面字符的收发基础上,再实现十六进制的收发,其实不难,无非多了显示方式的判断,以及相应格式和类型的转换罢了。这些格式和类型的转换通常都有现成的轮子,不需要再造轮子,这样节省了大量时间。

首先是checkbox控件的槽函数实现,无论是接收还是发送的16进制checkbox,其实现的都是以下两件事情:

  1. 根据当前checkbox状态,对文本框的数据进行字符和十六进制数之间的格式转换
  2. 记录当前checkbox状态(用于接收和发送函数的格式转换)

16进制接收显示checkbox的stateChanged槽函数:

void MainWindow::on_checkBoxHexDisplay_stateChanged(int arg1)
{if(arg1 == Qt::Checked) {QString *strHex = new QString;*strHex = ui->textEditReceive->toPlainText().replace("\n", "\r\n"); //QT中ENTER键为:\n(即0A),将其替换为Windows中的\r\n(即0D 0A)ui->textEditReceive->clear();ui->textEditReceive->insertPlainText(strHex->toUtf8().toHex(' ').append(' '));  //QString转QByteArray,QByteArray中的字符转16进制并追加空格,toHex在每个16进制数后加空格,append在最后加空格ui->textEditReceive->moveCursor(QTextCursor::End);delete strHex;mHexDisplay = true;} else {QString *strChar = new QString;*strChar = ui->textEditReceive->toPlainText().remove(QRegExp("\\s"));   //删除空格,空格的正则表达式为\sui->textEditReceive->clear();ui->textEditReceive->insertPlainText(QByteArray::fromHex(strChar->toLatin1())); //toLatin1:按照ASCII编码把String转成ByteArray,fromHex:对ByteArray做16进制解码ui->textEditReceive->moveCursor(QTextCursor::End);delete strChar;mHexDisplay = false;}
}

16进制发送显示checkbox的stateChanged槽函数:

void MainWindow::on_checkBoxHexSend_stateChanged(int arg1)
{if(arg1 == Qt::Checked) {QString *strHex = new QString;*strHex = ui->textEditSend->toPlainText().replace("\n", "\r\n");ui->textEditSend->clear();ui->textEditSend->insertPlainText(strHex->toUtf8().toHex(' ').append(' '));ui->textEditSend->moveCursor(QTextCursor::End);delete strHex;mHexSend = true;} else {QString *strChar = new QString;*strChar = ui->textEditSend->toPlainText().remove(QRegExp("\\s"));ui->textEditSend->clear();ui->textEditSend->insertPlainText(QByteArray::fromHex(strChar->toLatin1()));ui->textEditSend->moveCursor(QTextCursor::End);delete strChar;mHexSend = false;}
}

而收发槽函数中也要相应加入格式转换的逻辑。

接收槽函数:

void MainWindow::on_serialPort_readyRead()
{if(mIsOpen == true) {QByteArray rxData = mSerialPort.readAll();if(ui->checkBoxHexDisplay->isChecked()) {ui->textEditReceive->insertPlainText(rxData.toHex(' ').append(' '));  //把ByteArray按16进制编码,toHex在每个16进制数后加空格,append在最后加空格} else {ui->textEditReceive->insertPlainText(rxData);}ui->textEditReceive->moveCursor(QTextCursor::End);  //调整光标位置到最新接收的尾部,避免看不到最新接收的数据}
}

发送槽函数:

void MainWindow::on_pushButtonSend_clicked()
{if(mIsOpen == true) {if(ui->checkBoxHexSend->isChecked()) {QByteArray* arrayTxData = new QByteArray;*arrayTxData = ui->textEditSend->toPlainText().remove(QRegExp("\\s")).toUtf8();mSerialPort.write(QByteArray::fromHex(*arrayTxData));delete arrayTxData;} else {//mSerialPort.write(ui->textEditSend->toPlainText().replace("\n", "\r\n").toStdString().c_str());mSerialPort.write(ui->textEditSend->toPlainText().replace("\n", "\r\n").toUtf8()); //QT中ENTER键为:\n(即0A),将其替换为Windows中的\r\n(即0D 0A)}}
}

也许有读者会对上述代码中那一连串的成员函数感到疑惑,不知其为何意,想要知道这些函数的作用,最好的办法是查阅QT的帮助文档。

比如要查fromHex这个函数的作用(对于QT中的类的搜索也是同理,查阅帮助文档和手册是学习QT乃至许多技术的必备技能):
在这里插入图片描述

9 清除发送/接收

这个非常简单,只需在槽函数中调用QTextEdit控件的clear方法即可。
在这里插入图片描述

10 后记

QT中的各种控件类都是经过层层继承而来,调用某个控件类中的方法,不一定是定义在该类里面,而是定义在其父类中。这种套娃模式极好地用代码描述了真实世界,是面向对象的精髓之一。

如果要实现串口列表的实时更新,习惯了面向过程开发的朋友可能第一反应是用定时器去周期更新,而在面向对象的世界中有一个方法是把控件的方法给改写,在其中加入获取串口列表的逻辑,这是两种开发思想差异的一个体现。

本文中的串口助手其实还是有很多不完善之处,比如还缺少以下功能:

  • 16进制发送模式下,发送框的非法字符检测
  • 发送接收字节数统计
  • 自动发送
  • 保存上一次的串口配置

后续有时间将慢慢补上。

11 源码

懒得传git,先放某度云上
链接:Serial
提取码:hjq5

12 参考

  • QT帮助文档
  • QT中的foreach关键字
  • QComboBox点击时自动更新列表(自动刷新QSerialPort)
  • QT弹窗
  • QTextEdit追加纯文本(无额外的换行)
  • QString、QByteArray、ASCII码、16进制等类型转换和编码转换
  • QT 十六进制字符串与原数据字符串互转
  • QT QString去除空格
  • QT 正则表达式


SZ
2023.9.3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/65285.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习之 Jupyter Notebook 使用

🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! &…

CocosCreator3.8研究笔记(四)CocosCreator 脚本说明及使用(上)

在Cocos Creator中,脚本代码文件分为模块和插件两种方式: 模块一般就是项目的脚本,包含项目中创建的代码、引擎模块、第三方模块。 插件脚本,是指从 Cocos Creator 属性检查器中导入的插件,一般是引入第三方引入库文件…

vulnhub Seattle-0.0.3

环境:vuluhub Seattle-0.0.3 1.catelogue处任意文件下载(目录穿越) http://192.168.85.139/download.php?item../../../../../../etc/passwd 有个admin目录,可以下载里面的文件进行读取 2.cltohes详情页面处(参数prod)存在sql报错注入 http://192.16…

【C++刷题】动态规划

文章目录 前言一、斐波那契系列1.第 N 个泰波那契数2.三步问题3.使用最小花费爬楼梯4.解码方法5.不同路径6.下降路径最小和7.地下城游戏 二、多种状态系列1.按摩师2.打家劫舍II3.删除并获得点数4.粉刷房子5.买卖股票的最佳时机6.买卖股票的最佳时机III 三、子数组和子串系列1.最…

【iOS】Masonry的基本使用

文章目录 前言一、使用Masonry的原因二、约束的常识三、Masonry的简单使用四、Masonry的用例总结 前言 暑假安装了cocoapods,简单使用其调用了SVGKit,但是没有学习Masonry,特此总结博客记录Masonry的学习 一、使用Masonry的原因 Masonry是一…

最新ChatGPT程序源码+AI系统+详细图文部署教程/支持GPT4.0/支持Midjourney绘画/Prompt知识库

一、AI系统 如何搭建部署人工智能源码、AI创作系统、ChatGPT系统呢?小编这里写一个详细图文教程吧!SparkAi使用Nestjs和Vue3框架技术,持续集成AI能力到AIGC系统! 1.1 程序核心功能 程序已支持ChatGPT3.5/GPT-4提问、AI绘画、Mi…

详解 SpringMVC 的 @RequestMapping 注解

文章目录 1、RequestMapping注解的功能2、RequestMapping注解的位置3、RequestMapping注解的value属性4、RequestMapping注解的method属性5、RequestMapping注解的params属性(了解)6、RequestMapping注解的headers属性(了解)7、Sp…

6. vue-element-admin 二次开发避坑指南

vue-element-admin 二次开发避坑指南 1.1 前言1.1.1 切换标签时未保存页面的操作内容1.1.2 markdown 样式乱码1.1.3 修改默认尺寸1.1.4 当后端服务器宕机情况下页面加载层一直转圈无法停止,只能关闭页面1.1.5 隐藏齿轮 1.1 前言 上一篇博文,我们分享了vu…

从零构建深度学习推理框架-11 Resnet

op和layer结构 在runtime_ir.cpp中,我们上一节只构建了input和output,对于中间layer的具体实现一直没有完成: for (const auto& kOperator : this->operators_) {if (kOperator->type "pnnx.Input") {this->input_o…

ssm+vue“魅力”繁峙宣传网站源码和论文

ssmvue“魅力”繁峙宣传网站源码和论文102 开发工具:idea 数据库mysql5.7 数据库链接工具:navcat,小海豚等 技术:ssm 摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身…

C语言:截断+整型提升+算数转换练习

详情关于整型提升、算数转换与截断见文章: 《C语言:整型提升》 《C语言:算数转换》 一、代码一 int main() { char a -1; signed char b -1; unsigned char c -1; printf("%d %d %d", a, b, c); return 0; } 求…

使用安全复制命令scp在Windows系统和Linux系统之间相互传输文件

现在已经有很多远程控制服务器的第三方软件平台,比如FinalShell,MobaXterm等,半可视化界面,使用起来非常方便和友好,两个系统之间传输文件直接拖就行,当然也可以使用命令方式在两个系统之间相互传递。 目录…

C++面试题(丝)-计算机网络部分(1)

目录 1计算机网络 53 简述epoll和select的区别,epoll为什么高效? 54 说说多路IO复用技术有哪些,区别是什么? 55 简述socket中select,epoll的使用场景和区别,epoll水平触发与边缘触发的区别?…

使用 ElasticSearch 作为知识库,存储向量及相似性搜索

一、ElasticSearch 向量存储及相似性搜索 在当今大数据时代,快速有效地搜索和分析海量数据成为了许多企业和组织的重要需求。Elasticsearch 作为一款功能强大的分布式搜索和分析引擎,为我们提供了一种优秀的解决方案。除了传统的文本搜索,El…

【两周学会FPGA】从0到1学习紫光同创FPGA开发|盘古PGL22G开发板学习之数码管动态显示(五)

本原创教程由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处 适用于板卡型号: 紫光同创PGL22G开发平台(盘古22K) 一:盘古22K开发板(紫光同创PGL22G开发…

http和https的区别?

什么是 HTTP? HTTP是一种互联网数据传输协议,用于在网络服务器和客户端之间进行数据传输。作为万维网的基础,HTTP协议允许网络浏览器向网络服务器发送请求,服务器则会返回响应。HTTP协议基于文本,因此传输的数据是人类…

41.岛屿数量(第四期模拟笔试)(BFS练习题)

题目: 给定一个 m 行 n 列的二维地图,初始化每个单元格都是海洋,二维地图外也全是海洋。 操作 addLand 会将单元格(col, row)变为陆地。 定义一系列相连的被海洋包围的陆地为岛屿, 横向相邻或者纵向相连的…

微信小程序请求接口返回的二维码(图片),本地工具和真机测试都能显示,上线之后不显示问题

请求后端接口返回的图片&#xff1a; 页面展示&#xff1a; 代码实现&#xff1a; :show-menu-by-longpress"true" 是长按保存图片 base64Code 是转为base64的地址 <image class"code" :src"base64Code" alt"" :show-menu-by-long…

Three.js实现模型,模型材质可拖拽效果 DragControls

Three.js提供了一个拖拽的API DragControls 用于实现模型材质拖拽效果 DragControls&#xff1a;是一个用于在Three.js中实现拖拽控制的辅助类。它简化了在Three.js中实现拖拽物体的过程。 DragControls的构造函数接受三个参数&#xff1a; objects&#xff1a;一个包含需要…

C# textBox 右键菜单 contextMenuStrip

需求&#xff1a; 想在上图空白处可以右键弹出菜单&#xff0c;该怎么做呢&#xff1f; 1.首先&#xff0c;拖出一个 ContextMenuStrip。 随便放哪里都行&#xff0c;如下: 2.在textBox里关联这个“右键控件”即可&#xff0c;如下&#xff1a; 最终效果如下&#xff1a; 以上…