使用QT写个自用的串口助手
遇到一个默认波特率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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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);// 设置默认定时时长1000msconnect(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");elsestr_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(); //ASCIIarray = 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,选中为2if(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
修改方法如下图
四、保存上述所有的文件后单机运行

上图的绿色三角符号。
过一会然间就运行起来了
如图
