QT学习—属于自己的串口调试助手

简介: 本文详细介绍了如何编写一个属于自己的串口助手。


🎀 文章作者:二土电子
🐸 期待大家一起学习交流!


一、功能简介

最近尝试用QT实现了一个串口调试驻守于,作为一个刚接触QT的小白,当然是站在巨人的肩膀上完成了这个小项目。在实现过程中学习了许多内容,这里简单总结一下。

本次设计的串口调试助手只实现了一些基本功能

  • 打开/关闭串口
  • 刷新串口
  • 串口收/发
  • 定时发送
  • 清空接收窗口
  • 选择串口,配置波特率,数据位,停止位和校验位。

    二、串口助手实现

    下面首先再明确一下功能。打开串口时会提示“串口打开成功”或者“串口打开失败”。打开成功后会禁用选择串口,刷新串口,配置波特率,数据位,停止位和校验位的功能。关闭串口后恢复使用。

d5da82aebb56cdf1613bd18aa9dc09da_0239ca3375924370991a4191c6e613e3.png

由于使能和失能控件的方法比较简单,就在这介绍,不再单独介绍了。

  • 失能控件
ui->控件名->setEnabled(false);
  • 使能控件
ui->控件名->setEnabled(true);

2.1 创建UI

在开始程序编写前,先创建UI界面。创建UI时使用的控件如果和博主的不同的话,程序可能会不能直接复用,需要注意一下。本项目主要用到了这些控件

  • QLabel
    该控件是标签,用来提示,比如UI中的“串口”,“波特率”等字样,就是用的QLabel控件。
  • QComboBox
    该控件为多选下拉框。UI中可选串口号,波特率,数据为,停止位,校验位这些都是使用的该控件。双击控件可以编辑下拉框成员。
  • QPushButton
    该控件为按钮。UI中的发送,打开/关闭串口等,都是使用的该控件。
  • QLineEdit
    QLineEdit是一个单行文本编辑器,UI中定时时间输入,发送内容输入框都是使用的该控件。
  • QCheckBox
    QCheckBox为复选框控件,UI中“定时发送”和“发送新行”使用的就是该控件。该控件可以添加一个“stateChange(int)”槽函数。可以通过int变量的值来判断复选框是否被选中。如果复选框被选中,int变量的值为2,未被选中,int变量的值为0。
  • QTextBroswer
    该控件是一个文本阅读器,UI中串口接收内容的显示用到了该控件。

    2.2 扫描可用串口

    扫描可用串口的方法是
    // 搜索所有可用串口
    foreach (const QSerialPortInfo &inf0, QSerialPortInfo::availablePorts()) {
   
        serialNamePort<<inf0.portName();
    }

扫描可用串口用在两个地方,首先是用在刚打开软件时,扫描当前可用串口。扫描到可用串口后,需要将可用串口号显示到UI的控件上。

    // 将可用串口显示到serialBox
    ui->serialBox->addItems(serialNamePort);

其次用到扫描可用串口的地方就是“刷新串口”按键,后面会有介绍。

2.3 配置波特率

打开串口时,会根据UI界面波特率的下拉框中选择的值配置波特率。配置方法是直接获取下拉框中的字符,将其转换成int型变量,然后配置波特率。

        // toInt将baudrateBox中的字符串转换成整型数
        serialPort->setBaudRate(ui->baudrateBox->currentText().toInt());   // 波特率

2.4 配置数据位

配置数据位时,无法像配置波特率一样,直接获取下拉框中的字符串,转换成int型直接赋值。这里选择先将数据位下拉框内容转换成int型,然后用switch语句配置数据位

        // 用switch语句设置数据位
        switch (ui->databitBox->currentText().toInt())
        {
   
        case 5:
            serialPort->setDataBits(QSerialPort::Data5);   // 5位
            break;

        case 6:
            serialPort->setDataBits(QSerialPort::Data6);   // 6位
            break;

        case 7:
            serialPort->setDataBits(QSerialPort::Data7);   // 7位
            break;

        case 8:
            serialPort->setDataBits(QSerialPort::Data8);   // 8位
            break;

        default:
            serialPort->setDataBits(QSerialPort::Data8);   // 8位
            break;
        }

2.5 配置停止位

配置停止位也无法直接使用数字,这里使用另一个方法。获取停止位下拉框中的字符,利用if语句完成停止位的配置。

        // 设置停止位,直接用字符做判断
        if (ui->stopbitBox->currentText() == "1")
        {
   
            serialPort->setStopBits(QSerialPort::OneStop);   // 1位停止位
        }
        else if (ui->stopbitBox->currentText() == "1.5")
        {
   
            serialPort->setStopBits(QSerialPort::OneAndHalfStop);   // 1.5位停止位
        }
        else if (ui->stopbitBox->currentText() == "2")
        {
   
            serialPort->setStopBits(QSerialPort::TwoStop);   // 2位停止位
        }
        else   // 默认1位停止位
        {
   
            serialPort->setStopBits(QSerialPort::OneStop);   // 1位停止位
        }

2.6 配置校验位

配置校验位与配置停止位的方法相同,也是使用if语句。

        // 设置校验位,也用if直接判断字符串
        if (ui->checkBox->currentText() == "None")
        {
   
            serialPort->setParity(QSerialPort::NoParity);   // 无校验
        }
        else if (ui->checkBox->currentText() == "Odd")
        {
   
            serialPort->setParity(QSerialPort::OddParity);   // 奇校验
        }
        else if (ui->checkBox->currentText() == "Even")
        {
   
            serialPort->setParity(QSerialPort::EvenParity);   // 偶校验
        }
        else   // 默认无校验
        {
   
            serialPort->setParity(QSerialPort::NoParity);   // 无校验
        }

2.7 打开/关闭串口

打开串口和关闭串口使用的是同一个按钮。刚打开是,按钮为“打开串口”。打开串口成功后,将按钮上显示的字符修改为“关闭串口”。点击按钮时,根据按钮上的字符来判断是执行打开串口还是关闭串口。实现框架如下

    // 打开串口
    if (ui->openButton->text() == "打开串口")
    {
   
        // 打开串口
    }
    else   // 关闭串口
    {
   
        // 关闭串口
    }

当然,也可以使用标志位实现。

打开串口和关闭串口的函数为

    bool open(OpenMode mode) override;
    void close() override;

2.8 刷新串口

刷新串口的功能是重新扫描当前可用串口,然后将当前可用串口号显示到串口的下拉框中。扫描方法上面已经介绍过了,需要注意的是,点击刷新串口,扫描完当前可用串口后,需要将之前串口下拉框中的显示内容清除掉再重新显示。

// 刷新串口
void MainWindow::on_refrashButton_clicked()
{
   
    QStringList serialNamePort;

    // 先清除一下之前串口号中的显示内容
    ui->serialBox->clear();

    // 搜索所有可用串口
    foreach (const QSerialPortInfo &inf0, QSerialPortInfo::availablePorts()) {
   
        serialNamePort<<inf0.portName();
    }

    // 将可用串口显示到serialBox
    ui->serialBox->addItems(serialNamePort);
}

2.9 发送新行

发送新行是使用复选框实现的。在发送前会先检测是否勾选了发送新行,判断方法如下

    // 如果发送新行被勾选
    if (ui->newLineBox->isChecked())
    {
   
        // 发送新行
    }

2.10 串口发送

按下“串口发送”按钮,首先获取输入的内容,然后根据是否需要发送新行来判断是否需要在结尾增加换行。“串口发送”按钮的槽函数如下。

// 发送按钮
void MainWindow::on_sendButton_clicked()
{
   
    // 如果发送新行被勾选
    if (ui->newLineBox->isChecked())
    {
   
        // 多行文本框用sendEdit()获取内容
        // QLineExit内容用text()获取内容
        serialPort->write(ui->sendEdit->text().toLatin1() + "\r\n");      // 串口发送数据
    }
    else
    {
   
        serialPort->write(ui->sendEdit->text().toLatin1());      // 串口发送数据
    }
}

2.11 串口接收显示

上位机需要显示串口接收到的内容。有一个reayRead信号,一旦上位机接收到数据,就会想应该信号。将该信号绑定到显示接收内容的槽函数,一旦接收到数据,就开始显示。绑定方法如下

    // 将readyRead信号链接到Read_Data函数
    connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::Read_Data);

显示数据的槽函数如下。

// 读取接收到的数据并显示
void MainWindow::Read_Data()
{
   
    QByteArray buf;
    // 读取全部接收数据
    buf = serialPort->readAll();

    // 如果接收内容非空
    if(!buf.isEmpty())
    {
   
        QString str = ui->receiveBrowser->toPlainText();
        str += tr(buf);
        ui->receiveBrowser->clear();
        ui->receiveBrowser->append(str);
    }
    buf.clear();
}

2.12 清空接收窗口

// 清除窗口
void MainWindow::on_clearButton_clicked()
{
   
    ui->receiveBrowser->clear();
}

2.13 定时发送

定时发送需要用到定时器,需要先添加定时器的头文件

// 定时器头文件
#include<QTimer>

然后添加一个定时发送时间的类成员

    // 定时发送时间间隔
    QTimer *timSend;

将倒计时结束信号关联到串口发送槽函数

    // 定时发送
    timSend = new QTimer;
    timSend->setInterval(1000);   // 设置默认值1000ms
    // 定时发送与发送按键函数关联
    connect(timSend,&QTimer::timeout,this,[=]()
    {
   
         on_sendButton_clicked();
    });

定时发送功能时使用复选框实现的。关于复选框上面介绍了,可以根据一个int型变量的值来判断选中状态。定时发送勾选后需要失能定时时间编辑和发送按钮。取消勾选后,恢复定时器时间编辑和发送按钮的使用。定时发送函数如下

// 定时发送
void MainWindow::on_timeSendBox_stateChanged(int arg1)
{
   
    // 获取复选框选中状态
    // 选中值为2,未选中值为0
    if (arg1 == 0)
    {
   
        // 结束计时
        timSend->stop();

        // 恢复定时时间可编辑
        ui->timeEdit->setEnabled(true);
        // 恢复发送按钮
        ui->sendButton->setEnabled(true);
    }
    else
    {
   
        // 根据设置时间开始计时
        timSend->start(ui->timeEdit->text().toInt());

        // 禁用定时时间可编辑
        ui->timeEdit->setEnabled(false);
        // 禁用发送按钮
        ui->sendButton->setEnabled(false);
    }
}

2.14 固定窗口大小

为了防止窗口大小改变,这里选择禁用最大化按钮,固定窗口大小,防止被鼠标拖动放大。程序实现方法如下

    // 禁用最大化按钮
    setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint);
    // 禁止拖动窗口大小
    setFixedSize(673,620);

窗口大小可以在UI文件中查看,选中窗口,查看右侧属性栏。

85db35034cc9bae49d0039168e6ab095_6a2f5a905e7d4a58bf6702865226430a.png

三、总结

3.1 将信号与槽函数关联

之前一直使用的是右键控件,选择转到槽来实现的关联。这里学习到了利用“connect”关联信号与槽函数。connect基本格式如下

connect(控件名, SIGNAL(要关联的信号), this, SLOT(槽函数));

也可以像上面关联readyRead信号时那样写

connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::Read_Data);

或者像关联定时结束信号时那样写

    connect(timSend,&QTimer::timeout,this,[=]()
    {
   
         on_sendButton_clicked();
    });

3.2 优化方向

正如最开始所说,本文只是实现了简单的串口助手的功能,后续可以优化的方向还有很多。比如

  • 支持显示中文
  • 支持保存接收内容
  • 下方显示接收到的内容字符数
  • 增加时间戳等

但是针对本人平时的使用已经足够了,因此暂时并不考虑优化,后续使用过程中遇到问题再考虑。有意思的是,本人使用该串口助手访问API时,能够看到API返回来的中文。下图是利用WIFI模块访问心知天气API返回的数据。

c79689d21b06d46bc2b388d162052f82_aa38ddbbf90341b8b664ab79fb7f4f04.png

相关文章
|
7月前
|
Linux API C语言
Qt串口编程探究:理论与实践
Qt串口编程探究:理论与实践
380 1
|
7月前
|
存储 传感器 安全
【串口通信】使用C++和Qt设计和实现串口协议解析器(二)
【串口通信】使用C++和Qt设计和实现串口协议解析器
612 0
|
7月前
|
存储 开发框架 算法
【串口通信】使用C++和Qt设计和实现串口协议解析器(一)
【串口通信】使用C++和Qt设计和实现串口协议解析器
1699 0
|
7月前
|
监控 前端开发 JavaScript
Qt Quick调试之道:跟踪、输出与打印信息的全面攻略
Qt Quick调试之道:跟踪、输出与打印信息的全面攻略
369 0
|
Linux
linux系统中利用QT实现串口通信的方法
linux系统中利用QT实现串口通信的方法
355 0
|
3月前
|
定位技术 Go 开发工具
dynamic-situational-awareness-qt学习记录
本文是作者yantuguiguziPGJ关于dynamic-situational-awareness-qt学习记录的分享,介绍了在Qt学习过程中发现的qml资源丰富的代码仓库,并提供了资源路径和相关的安装、配置步骤,涉及的内容有数字地球、GIS纹理等,同时提供了相关链接和git命令来克隆代码仓库和ArcGIS Runtime SDK for Qt的安装说明。
|
5月前
|
C++
Qt中的信号与槽如何学习?(包括自定义信号)这篇文章告诉你
以现实中的事件来举例的话,例如有两把不同颜色的信号枪,分别是红色,绿色,打响不通颜色的信号枪会触发不同的槽发生,比如说打响红色这个人就跑步,绿色就走步,但是还有一个很重要的机制,那就是连接,我们需要把信号枪去跟这个人的动作连接起来。 如果上面理解没问题的话我们可以把信号和槽看成两个工具,我们最重要的是如何去把这两个工具连接起来。 它的作用可以让我们更加灵活的去使用不同窗口间的切换以及某些事件的连接。
112 0
|
5月前
|
开发者
Qt中的事件该如何学习?(附带案例)
事件是Qt中比较重要的一部分,在初期如果理解不当学习可能会比较困难,这里提一嘴当初教我的那位老师水平是真的高,让我很轻易的就理解了事件的概念。 在平时我们见到那些界面上的某些快捷键就有可能是事件做的,例如ESC关闭窗口,Enter提交或者登录这种类似的,这也是事件的强大之处。
136 0
|
7月前
QT串口助手的实现
QT串口助手的实现
146 0
|
计算机视觉 C++
《QT从基础到进阶·二十九》QT,opencv源码调试
《QT从基础到进阶·二十九》QT,opencv源码调试
98 0