使用QT写个自用的串口助手

news2024/12/23 19:30:03

遇到一个默认波特率1.5M的终端设备,看了下手上常用的串口助手竟然没有这个选项,所以干脆自己用QT手撕一个。

开发环境:QT 5.12.0 mingw64

一、创建工程

1、新建创建QMainWindow工程,基类可以选择QMainWindow也可以选择Qwiget,这个网上参考很多,自己搜哈。

2、工程我命名为UART,UART.pro是工程文件。

二、设计UI

1、双击打开mainwindow.ui就是界面文件

2、弹出界面设计文件

3、界面中使用的组件都标在下图了

按照上图标的组件按途中摆放修改成我们需要的名字,然后使用布局工具对齐就行了,布局菜单如下图

3、把Plain text edit喝Text edit的背景设置成黑色

选中Plain text edit

找到QWidget中的palette选项打开

Base选项修改成黑色,这是背景颜色选项,把Text选项修改成绿色,这里Text一定要修改颜色,默认是黑色,背景修改成黑色后正常也看不出字。

最后点击确定,下面的发送框也是一样的设置。

4、编辑Combo box组件里面的选项

双击端口、波特率、数据位、校验位后面的空白部分弹出这个串口

分别填上下列内容

4、修改各个组件的变量,如下图,变量和代码中的变量是一一对应的,务必修改的一致,当然,也可以自定义

修改方法也很简单

选中要修改的组件,然后在objectname栏里修改成你想要的名字即可

三、编写代码

1、打开UART.pro文件,在core gui后面添加serialport,如下图

2、打开mainwindow.h粘贴代码如下

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>
#include <QString>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <QPainter>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QSerialPort *serialPort;//定义串口指针

private slots:

    /*手动连接槽函数*/
    void manual_serialPortReadyRead();

    /*以下为mainwindow.ui文件中点击“转到槽”自动生成的函数*/
    void on_Button_openserial_clicked();

    void on_Button_tx_clicked();

    void on_Button_clearrecive_clicked();

    void on_Button_cleartx_clicked();

    void on_check_autosend_stateChanged(int arg1);

    void on_Button_checkserial_clicked();

private:
    Ui::MainWindow *ui;

    // 发送、接收字节计数
    long sendNum, recvNum;
    QLabel *lblSendNum;
    QLabel *lblRecvNum;
    QLabel *lblPortState;
    void setNumOnLabel(QLabel *lbl, QString strS, long num);

    // 定时发送-定时器
    QTimer *timSend;
    //QTimer *timCheckPort;
};
#endif // MAINWINDOW_H

3、打开mainwindow.cpp粘贴代码如下

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QSerialPortInfo"
#include <QSerialPort>
#include <QMessageBox>
#include <QDateTime>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QStringList serialNamePort;

    serialPort = new QSerialPort(this);
    connect(serialPort,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/

    /*找出当前连接的串口并显示到serailCb*/
    //foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    //{
        //serialNamePort<<info.portName();// 自动扫描当前可用串口,返回值追加到字符数组中
    //}
    //ui->serailCb->addItems(serialNamePort);// 可用串口号,显示到串口选择下拉框中
    ui->serialCB->clear();
    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->serialCB->addItem(info.portName());
    }

    // 发送、接收计数清零
    sendNum = 0;
    recvNum = 0;
    // 状态栏
    QStatusBar *sBar = statusBar();
    // 状态栏的收、发计数标签
    lblSendNum = new QLabel(this);
    lblRecvNum = new QLabel(this);
    lblPortState = new QLabel(this);
    lblPortState->setText("Connected");
    //设置串口状态标签为绿色 表示已连接状态
    lblPortState->setStyleSheet("color:red");

    // 设置标签最小大小
    lblSendNum->setMinimumSize(100, 20);
    lblRecvNum->setMinimumSize(100, 20);
    lblPortState->setMinimumSize(550, 20);
    setNumOnLabel(lblSendNum, "S: ", sendNum);
    setNumOnLabel(lblRecvNum, "R: ", recvNum);
    // 从右往左依次添加
    sBar->addPermanentWidget(lblPortState);
    sBar->addPermanentWidget(lblSendNum);
    sBar->addPermanentWidget(lblRecvNum);

    // 定时发送-定时器
    timSend = new QTimer;
    timSend->setInterval(1000);// 设置默认定时时长1000ms
    connect(timSend, &QTimer::timeout, this, [=](){
        on_Button_tx_clicked();
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

//检测通讯端口槽函数
void MainWindow::on_Button_checkserial_clicked()
{
    ui->serialCB->clear();
    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->serialCB->addItem(info.portName());
    }
}

/*手动实现接收数据函数*/
void MainWindow::manual_serialPortReadyRead()
{
    QByteArray recBuf = serialPort->readAll();;
    QString str_rev;

    // 接收字节计数
    recvNum += recBuf.size();
    // 状态栏显示计数值
    setNumOnLabel(lblRecvNum, "R: ", recvNum);

    if(ui->check_hex_recive->checkState() == false){
        if(ui->check_timestamp->checkState() == Qt::Checked){
            QDateTime nowtime = QDateTime::currentDateTime();
            str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
            str_rev += QString(recBuf).append("\r\n");
        }
        else{
            // 在当前位置插入文本,不会发生换行。如果没有移动光标到文件结尾,会导致文件超出当前界面显示范围,界面也不会向下滚动。
            //ui->recvEdit->appendPlainText(buf);

            if(ui->check_changeline->checkState() == Qt::Checked){
                str_rev = QString(recBuf).append("\r\n");
            }
            else
            {
                str_rev = QString(recBuf);
            }
        }
    }else{

        // 16进制显示,并转换为大写
        QString str1 = recBuf.toHex().toUpper();//.data();
        // 添加空格
        QString str2;
        for(int i = 0; i<str1.length (); i+=2)
        {
            str2 += str1.mid (i,2);
            str2 += " ";
        }
        if(ui->check_timestamp->checkState() == Qt::Checked)
        {
            QDateTime nowtime = QDateTime::currentDateTime();
            str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
            str_rev += str2.append("\r\n");
        }
        else
        {
            if(ui->check_changeline->checkState() == Qt::Checked)
                str_rev += str2.append("\r\n");
            else
                str_rev = str2;

        }
    }
    ui->recive_Edit->insertPlainText(str_rev);
    ui->recive_Edit->moveCursor(QTextCursor::End);

}

/*打开串口*/
void MainWindow::on_Button_openserial_clicked()
{
    /*串口初始化*/
    QSerialPort::BaudRate baudRate;
    QSerialPort::DataBits dataBits;
    QSerialPort::StopBits stopBits;
    QSerialPort::Parity checkBits;

    // 获取串口波特率
    // baudRate = ui->baundrateCb->currentText().toInt();直接字符串转换为 int 的方法

    if(ui->baudrate_CB->currentText()=="1200")
        baudRate=QSerialPort::Baud1200;
    else if(ui->baudrate_CB->currentText()=="2400")
        baudRate=QSerialPort::Baud2400;
    else if(ui->baudrate_CB->currentText()=="4800")
        baudRate=QSerialPort::Baud4800;
    else if(ui->baudrate_CB->currentText()=="9600")
        baudRate=QSerialPort::Baud9600;
    else if(ui->baudrate_CB->currentText()=="19200")
        baudRate=QSerialPort::Baud19200;
    else if(ui->baudrate_CB->currentText()=="38400")
        baudRate=QSerialPort::Baud38400;
    else if(ui->baudrate_CB->currentText()=="57600")
        baudRate=QSerialPort::Baud57600;
    else if(ui->baudrate_CB->currentText()=="115200")
        baudRate=QSerialPort::Baud115200;
    else if(ui->baudrate_CB->currentText()=="128000")
        baudRate=QSerialPort::Baud128000;
    else if(ui->baudrate_CB->currentText()=="230400")
        baudRate=QSerialPort::Baud230400;
    else if(ui->baudrate_CB->currentText()=="256000")
        baudRate=QSerialPort::Baud256000;
    else if(ui->baudrate_CB->currentText()=="460800")
        baudRate=QSerialPort::Baud460800;
    else if(ui->baudrate_CB->currentText()=="921600")
        baudRate=QSerialPort::Baud921600;
    else if(ui->baudrate_CB->currentText()=="1500000")
        baudRate=QSerialPort::Baud1500000;
    else if(ui->baudrate_CB->currentText()=="3000000")
        baudRate=QSerialPort::Baud3000000;

    // 获取串口数据位
    if(ui->databit_CB->currentText()=="8")
        dataBits=QSerialPort::Data5;
    else if(ui->databit_CB->currentText()=="7")
        dataBits=QSerialPort::Data6;
    else if(ui->databit_CB->currentText()=="6")
        dataBits=QSerialPort::Data7;
    else if(ui->databit_CB->currentText()=="5")
        dataBits=QSerialPort::Data8;

    // 获取串口停止位
    if(ui->stopbit_CB->currentText()=="1")
        stopBits=QSerialPort::OneStop;
    else if(ui->stopbit_CB->currentText()=="1.5")
        stopBits=QSerialPort::OneAndHalfStop;
    else if(ui->stopbit_CB->currentText()=="2")
        stopBits=QSerialPort::TwoStop;

    // 获取串口奇偶校验位
    if(ui->checkbit_CB->currentText() == "None"){
        checkBits = QSerialPort::NoParity;
    }else if(ui->checkbit_CB->currentText() == "Odd"){
        checkBits = QSerialPort::OddParity;
    }else if(ui->checkbit_CB->currentText() == "Even"){
        checkBits = QSerialPort::EvenParity;
    }

    // 初始化串口属性,设置 端口号、波特率、数据位、停止位、奇偶校验位数
    serialPort->setPortName(ui->serialCB->currentText());
    serialPort->setBaudRate(baudRate);
    serialPort->setDataBits(dataBits);
    serialPort->setStopBits(stopBits);
    serialPort->setParity(checkBits);

    // 根据初始化好的串口属性,打开串口
    // 如果打开成功,反转打开按钮显示和功能。打开失败,无变化,并且弹出错误对话框。
    if(ui->Button_openserial->text() == "打开串口"){
        if(serialPort->open(QIODevice::ReadWrite) == true){
            //QMessageBox::
            ui->Button_openserial->setText("关闭串口");
            // 让端口号下拉框不可选,避免误操作(选择功能不可用,控件背景为灰色)
            ui->serialCB->setEnabled(false);
        }else{
            QMessageBox::critical(this, "错误提示", "串口打开失败!!!\r\n该串口可能被占用\r\n请选择正确的串口");
        }
        //statusBar 状态栏显示端口状态
        QString sm = "%1 OPENED, %2, 8, NONE, 1";
        QString status = sm.arg(serialPort->portName()).arg(serialPort->baudRate());
        lblPortState->setText(status);
        lblPortState->setStyleSheet("color:green");
    }else{
        serialPort->close();
        ui->Button_openserial->setText("打开串口");
        // 端口号下拉框恢复可选,避免误操作
        ui->serialCB->setEnabled(true);
        //statusBar 状态栏显示端口状态
        QString sm = "%1 CLOSED";
        QString status = sm.arg(serialPort->portName());
        lblPortState->setText(status);
        lblPortState->setStyleSheet("color:red");
    }

}

/*发送数据*/
void MainWindow::on_Button_tx_clicked()
{
    QByteArray array;

    //Hex复选框
    if(ui->check_hexsend->checkState() == Qt::Checked){
        //array = QString2Hex(data);  //HEX 16进制
        array = QByteArray::fromHex(ui->send_Edit->toPlainText().toUtf8()).data();
    }else{
        //array = data.toLatin1();    //ASCII
        array = ui->send_Edit->toPlainText().toLocal8Bit().data();
    }

    if(ui->check_sendnewline->checkState() == Qt::Checked){
        array.append("\r\n");
    }
    // 如发送成功,会返回发送的字节长度。失败,返回-1。
    int a = serialPort->write(array);
    // 发送字节计数并显示
    if(a > 0)
    {
        // 发送字节计数
        sendNum += a;
        // 状态栏显示计数值
        setNumOnLabel(lblSendNum, "S: ", sendNum);
    }
}
// 状态栏标签显示计数值
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
{
    // 标签显示
    QString strN;
    strN.sprintf("%ld", num);
    QString str = strS + strN;
    lbl->setText(str);
}
/*清空*/
void MainWindow::on_Button_clearrecive_clicked()
{
    ui->recive_Edit->clear();
    // 清除发送、接收字节计数
    sendNum = 0;
    recvNum = 0;
    // 状态栏显示计数值
    setNumOnLabel(lblSendNum, "S: ", sendNum);
    setNumOnLabel(lblRecvNum, "R: ", recvNum);
}

void MainWindow::on_Button_cleartx_clicked()
{
    ui->recive_Edit->clear();
    // 清除发送字节计数
    sendNum = 0;
    // 状态栏显示计数值
    setNumOnLabel(lblSendNum, "S: ", sendNum);
}
// 定时发送开关 选择复选框
void MainWindow::on_check_autosend_stateChanged(int arg1)
{
    // 获取复选框状态,未选为0,选中为2
    if(arg1 == 0){
        timSend->stop();
        // 时间输入框恢复可选
        ui->spin_sendtime->setEnabled(true);
    }else{
        // 对输入的值做限幅,小于10ms会弹出对话框提示
        if(ui->spin_sendtime->text().toInt() >= 10){
            timSend->start(ui->spin_sendtime->text().toInt());// 设置定时时长,重新计数
            // 让时间输入框不可选,避免误操作(输入功能不可用,控件背景为灰色)
            ui->spin_sendtime->setEnabled(false);
        }else{
            ui->check_autosend->setCheckState(Qt::Unchecked);
            QMessageBox::critical(this, "错误提示", "定时发送的最小间隔为 10ms\r\n请确保输入的值 >=10");
        }
    }
}

4、修改下串口的名字

要不然打开串口显示的是Qmainwindow之类的,看起来很low,这里我们修改成串口助手V1.0

修改方法如下图

四、保存上述所有的文件后单机运行

上图的绿色三角符号。

过一会然间就运行起来了

如图

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

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

相关文章

ATF(TF-A)安全通告TF-V11——恶意的SDEI SMC可能导致越界内存读取(CVE-2023-49100)

目录 一、ATF(TF-A)安全通告TFV-11 (CVE-2023-49100) 二、透过事务看本质SDEI是干啥的呢&#xff1f; 三、CVE-2023-49100 1、GICv2 systems 2、GICv3 systems 四、漏洞修复 一、ATF(TF-A)安全通告TFV-11 (CVE-2023-49100) Title 恶意的SDEI SMC可能导致越界内存读取&am…

SwiftUI 打造酷炫流光边框 + 微光滑动闪烁的 3D 透视滚动卡片墙

功能需求 有时候我们希望自己的 App 能向用户展示与众不同、富有创造力的酷炫视觉效果: 如上图所示,我们制作了一款流光边框 + 微光滑动闪烁的 3D 透视卡片滚动效果。这是怎么做到的呢? 在本篇博文中,您将学到以下内容 功能需求1. 3D 透视滚动2. 灵动边框流光效果3. 背景…

np.bincount函数的用法

官网写的非常清晰了&#xff0c; 返回数组的数量比x中的最大值大1&#xff0c;它给出了每个索引值在x中出现的次数。下面&#xff0c;我举个例子让大家更好的理解一下&#xff1a; np.bincount(np.array([0, 1, 1, 3, 2, 1, 7])) array([1, 3, 1, 1, 0, 0, 0, 1])最大值是7&a…

5.3 内容管理模块 - 课程发布、任务调度、页面静态化、熔断降级

内容管理模块 - 课程发布 - 任务调度、熔断降级、页面静态化 文章目录 内容管理模块 - 课程发布 - 任务调度、熔断降级、页面静态化一、课程发布 - 任务调度1.1 添加Maven依赖1.2 XxlJobConfig配置文件1.3 消息处理抽象类 MessageProcessAbstract1.4 课程发布任务类 CoursePubl…

webpack常用配置

1.webpack概念 ​ 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项目中所需的每一个模块组合成一个或多个 …

go语言(十六)----tag

package mainimport ("fmt""reflect" )type resume struct {Name string info:"name" doc:"我的名字"Sex string info:"sex" }func findTag(str interface{}) {t : reflect.TypeOf(str).Elem()for i : 0;i < t.NumField…

Go后端开发 -- 即时通信系统

Go后端开发 – 即时通信系统 文章目录 Go后端开发 -- 即时通信系统一、即时通信系统1.整体框架介绍2.基础server构建3.用户上线及广播功能4.用户消息广播机制5.用户业务封装6.用户在线查询7.修改用户名8.超时强踢9.私聊功能10.完整代码 二、客户端实现1.建立连接2.命令行解析3.…

阿里云幻兽帕鲁服务器租用价格表,免费?

幻兽帕鲁异常火爆自建幻兽帕鲁服务器不卡又稳定&#xff0c;继腾讯云推出幻兽帕鲁自建服务器教程和4核16G幻兽帕鲁专用特价游戏服务器后&#xff0c;阿里云坐不住了&#xff0c;直接推出特价4核32G和4核16G的palworld专属游戏机&#xff0c;另外还可以申请免费3个月的4核8G无影…

C语言或C++通过IShellLinkA创建或解析lnk快捷方式(使用char字符数组)

本例程用到的COM接口有IShellLinkA和IPersistFile。 请注意因为函数参数的类型不为BSTR&#xff0c;所以这两个接口可直接传char *或wchar_t *字符串&#xff0c;不需要提前转化为BSTR类型。 C语言的写法&#xff1a; /* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展…

智慧之光:ChatGPT 引领工作效率新纪元

随着科技的不断发展&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐融入我们的日常生活和工作中。其中&#xff0c;ChatGPT 作为一种先进的 AI 技术&#xff0c;正逐步改变我们的工作方式&#xff0c;提升我们的工作效率。本文灸哥将介绍如何利用ChatGPT提升工作效率&…

intellij idea怎么设置中文

CtrlAltS快捷键打开Settings界面选择Plugins在搜索部分搜索chinese&#xff0c;选择下方的Chinese&#xff08;simplified&#xff09;Language下载最后重启软件即可

数据结构:完全二叉树(递归实现)

如果完全二叉树的深度为h&#xff0c;那么除了第h层外&#xff0c;其他层的节点个数都是满的&#xff0c;第h层的节点都靠左排列。 完全二叉树的编号方法是从上到下&#xff0c;从左到右&#xff0c;根节点为1号节点&#xff0c;设完全二叉树的节点数为sum&#xff0c;某节点编…

leetcode-hot100双指针专题

第一题&#xff1a;移动零 题目链接 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 解题思路 我们创建两个指针i,j&#xff0c;第一次遍历的时候指针j用来记录当前面有多少非0元素。即遍历的时候每遇到一个非0元素就将其往数组左边挪&#xff0c;第一次遍历完后&…

解决国内 github.com 打不开的准确方法

** 下载watt toolkit&#xff0c; 选择‘github’&#xff0c;点击‘一键加速’&#xff0c;很简单方便 **

【阿里云服务器数据迁移】 同一个账号 不同区域服务器

前言 假如说一台云服务器要过期了,现在新买了一台,有的人会烦恼又要将重新在新的服务器上装环境,部署上线旧服务器上的网站项目, 但是不必烦恼,本文将介绍如何快速将就旧的服务器上的数据迁移到新的服务器上. 包括所有的环境和网站项目噢 ! 步骤 (1) 创建旧服务器自定义镜像…

Llama2中文大模型——牛刀小试

文章目录 Llama2中文大模型——牛刀小试前言更新库导包加载模型对话问答-1对话问答-2对话问答-3对话问答-4对话问答-5 Llama2中文大模型——牛刀小试 前言 Meta开源的Llama从第一版开始&#xff0c;效果就很不错&#xff0c;有很多开源LLM都是基于它训练的&#xff0c;例如Vic…

GPT-5不叫GPT-5?下一代模型会有哪些新功能?

OpenAI首席执行官奥特曼在上周三达沃斯论坛接受媒体采访时表示&#xff0c;他现在的首要任务就是推出下一代大模型&#xff0c;这款模型不一定会命名GPT-5。虽然GPT-5的商标早已经注册。 如果GPT-4目前解决了人类任务的10%&#xff0c;GPT-5应该是15%或者20%。 OpenAI从去年开…

登录kafka报错:Could notstart Jolokia agent: java.net.BindException: Address in use

在执行kafka命令增加或删除topic的时候&#xff0c;发现总是报错。 Address in use&#xff1f;端口还是ip被占用了。只能禁用了&#xff0c;再执行命令。 禁用代码&#xff1a; 然后就可以拉~ 总体步骤&#xff1a; 1.先进入k8s的kafka pod 2.进入kafka的bin目录 3.两个…

PPO学习

openai用tf实现的真的看不懂&#xff0c;大佬的世界… PPO的详细细节 1. 奖励模型和策略的价值头将 query 和 response 的连接作为输入 奖励模型和策略的价值头 不 仅仅查看响应。相反&#xff0c;它将 query 和 response 连接在一起&#xff0c;作为 query_response def ge…

openEuler操作系统的安装及免密远程连接(超详细版)

一、下载地址 注意&#xff1a;可以先注册华为账号&#xff0c;注册后可享1倍加速 mirrors.huaweicloud.com/openeuler/openEuler-22.03-LTS-SP3/ISO/x86_64/ 二、创建虚拟机步骤 ①选择自定义 ② 根据自己的VMware选择版本 ③选择稍后安装操作系统 ④没有openEuler可以选择…