目录
1.QSerialPort类包含了很多有关串口的API
2.实现串口的打开
2.1 方法一:通过函数实现
2.2 方法二:在ui界面右下角实现
3. 实现定时发送
3.1类的私有成员中添加定时器QTimer timer并去构造函数中初始化它
3.2帮助文档中有QTimer类相关的说明
3.3去连接该超时信号与槽函数
4.俩种方式实现时间显示
4.1通过线程实现
4.2 通过定时器实现时间显示
5. 实现发送与接收
6.实现Hex显示——实现每俩个字节加一个空格显示
7.文本框追加内容时注意事项
8.重写QComboBox组件的事件 —— 实现串口号的刷新
9.实现多文本发送
10.循环发送
第一种方法:不能通过延时函数处理,此方法为错误案例
第二种方法:定时器
第三种方法:多线程
11.Qt打包程序
1.首先切换为release,然后重新构建,并且运行
2.拷贝库文件
3.手动拷贝缺失库文件
4.整体进行压缩
1.QSerialPort类包含了很多有关串口的API
静态公有成员中提供了availablePorts() 函数去获取我当前电脑中存在的所有串口,存放在容器中元素类型是QSerialPortInfo
    // 获取当前系统中存在的串口
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    // 使用 qDebug 输出串口信息
    for (QSerialPortInfo serialinfo : serialList) {
        qDebug() << "Port Name:" << serialinfo.portName()            //端口名称
                 << ", Description:" << serialinfo.description()     //描述
                 << ", Manufacturer:" << serialinfo.manufacturer()   //制造商
                 << ", Serial Number:" << serialinfo.serialNumber(); //序列号
                 
		     ui->comboBox_01->addItem(serialinfo.portName());            //向combox组件中添加端口名称
    }
2.实现串口的打开
想要实现打开串口/关闭串口按键的点击反转,需要绑定槽函数中信号是clicked(bool checked),而不是普通的clicked()信号, 并且需要使得该按键状态是可以检测的,这里有俩种方法
2.1 方法一:通过函数实现
ui->btn_CloseOrOpenSerial->setCheckable(true);2.2 方法二:在ui界面右下角实现
3. 实现定时发送
3.1类的私有成员中添加定时器QTimer timer并去构造函数中初始化它
3.2帮助文档中有QTimer类相关的说明
- 设置定时器间隔时间 void setInterval(int time);
- Signals中包含信号检测是否超时 void timeout();
3.3去连接该超时信号与槽函数
- 在超时信号的槽函数里面进行数据的发送
4.俩种方式实现时间显示
4.1通过线程实现
curtimeupdate.h
#ifndef CURTIMEUPDATE_H
#define CURTIMEUPDATE_H
#include <QDateTime>
#include <QThread>
#include <QObject>
class curTimeUpdate : public QThread {
    Q_OBJECT
public:
    explicit curTimeUpdate(QObject *parent = nullptr): QThread(parent){};
signals:
    void updateTime(const QString &timeString);
protected:
    // 每间隔1s发送一次时间信号
    void run() override{
        while(1){
            // 获取当前时间
            QDateTime dataTime = QDateTime::currentDateTime();
            QDate date = dataTime.date();
            int year = date.year();
            int month = date.month();
            int day = date.day();
            QTime time = dataTime.time();
            int hour = time.hour();
            int minute = time.minute();
            int second = time.second();
            // 将时间拼装起来,确保月份和日期、小时、分钟、秒钟都是双位数
            QString currentTime = QString("%1-%2-%3 %4:%5:%6")
                .arg(year)
                .arg(month, 2, 10, QChar('0'))  // 确保月是两位数
                .arg(day, 2, 10, QChar('0'))    // 确保日是两位数
                .arg(hour, 2, 10, QChar('0'))   // 确保小时是两位数
                .arg(minute, 2, 10, QChar('0')) // 确保分钟是两位数
                .arg(second, 2, 10, QChar('0')); // 确保秒钟是两位数
            //发送信号
            emit updateTime(currentTime);
            sleep(1);
        }
    }
};
#endif // CURTIMEUPDATE_H
widget.cpp
// 创建线程更新右下角时间
    timeUpdater = new curTimeUpdate;
    connect(timeUpdater, &curTimeUpdate::updateTime, this, &Widget::updateTimeLabel);
    timeUpdater->start();   // 启动线程
void Widget::updateTimeLabel(const QString &timeString) {
    ui->label_nowTime->setText(timeString); // 更新标签显示时间
}
4.2 通过定时器实现时间显示
实现逻辑:定时器定时1s,每当定时器超时,会自动发出超时信号,检测到该信号时,调用槽函数实现当前时间的获取
widget.h中添加以下内容
Private:
	QTimer* getSysTimeTimer;
private slots:
	void getSysTime();
widget.cpp
  	getSysTimeTimer = new QTimer(this);
    getSysTimeTimer->setInterval(1000);                                                    //设置定时器2的间隔时间(毫秒)
    connect(getSysTimeTimer, &QTimer::timeout, this, &Widget::getSysTime);                 //定时器2超时
    getSysTimeTimer->start();                                                              //启动定时器2
// 定时器超时槽函数
void Widget::getSysTime()
{
    // 获取当前时间
    QDateTime dataTime = QDateTime::currentDateTime();
    QDate date = dataTime.date();
    QTime time = dataTime.time();
    // 提取年、月、日、时、分、秒
    int year   = date.year();
    int month  = date.month();
    int day    = date.day();
    int hour   = time.hour();
    int minute = time.minute();
    int second = time.second();
    // 将时间拼装起来,确保月份和日期、小时、分钟、秒钟都是双位数
    QString currentTime = QString("%1-%2-%3 %4:%5:%6")
        .arg(year)
        .arg(month, 2, 10, QChar('0'))  // 确保月是两位数
        .arg(day, 2, 10, QChar('0'))    // 确保日是两位数
        .arg(hour, 2, 10, QChar('0'))   // 确保小时是两位数
        .arg(minute, 2, 10, QChar('0')) // 确保分钟是两位数
        .arg(second, 2, 10, QChar('0')); // 确保秒钟是两位数
    // 更新标签显示时间
    ui->label_nowTime->setText(currentTime);
}
5. 实现发送与接收

// 发送按键槽函数
void Widget::on_pushButton_19_clicked()
{
    int WriteCnt = 0;
    QString sendData = ui->lineEdit_4->text();
    //Hex发送是否勾选,此处为勾选
    if(ui->checkBox_15->isChecked()){
        QByteArray tmpArray = ui->lineEdit_4->text().toLocal8Bit();
        //检查字节数是否是偶数
        if(tmpArray.size() % 2 != 0){
            ui->label_5->setText("input error!");
            return;
        }
        //检查是否符合16进制表达
        for(char c:tmpArray){
            if(!std::isxdigit(c)){
                ui->label_5->setText("input error!");
                return;
            }
        }
        //检查是否添加新行
        if(ui->checkBox_14->isChecked()){
            tmpArray = tmpArray.append("\\r\\n");
        }
        //转化为16进制发送
        QByteArray tmp = QByteArray::fromHex(tmpArray);
        WriteCnt = serialPort->write(tmp);
    }else{
        QByteArray data = sendData.toLocal8Bit();
        //检查是否添加新行
        if(ui->checkBox_14->isChecked()){
            data = data.append("\\r\\n");
        }
        WriteCnt = serialPort->write(data);
    }
    if(WriteCnt == -1){
        ui->label_5->setText("Send false!");
    }else{
        ui->label_5->setText("Send Ok!");
        ui->label_4->setText("Send: " + QString::number(WriteCntTotal));
        if(0 != strcmp(sendBak.toLocal8Bit().constData(), sendData.toLocal8Bit().constData())){
            ui->textEdit_Record->append(sendData);
            sendBak = sendData;
        }
    }
}
// 接受数据槽函数
void Widget::on_SerialData_reched()
{
    QString revData = serialPort->readAll();                        // 读取所有可用的数据
    qDebug() << "接受到的新数据为:" << revData;
    if(revData != NULL){
        //a.是否勾选自动换行
        if(ui->checkBox_6->isChecked()) revData.append("\\r\\n");
        //b.更新接受长度
        RevCntTotal += revData.size();                              // 统计接受到的字节数
        ui->label_7->setText("Rev OK!");
        ui->label_3->setText("Rev: " + QString::number(RevCntTotal));
        //c.检查hex显示是否勾选
        if(ui->checkBox_5->isChecked()){
            //将新数据转化为Hex
            QByteArray tmp = revData.toUtf8();
            qDebug() << "tmp:" <<tmp;
            QByteArray tmpHexString = revData.toUtf8().toHex().toUpper();
            qDebug() << "转化后的新数据为:" << tmpHexString;
            //获取旧数据
            QString tmpStringHex = ui->textEdit_Rev->toPlainText(); //因为勾选了,读出来的就是hex
            //拼接旧数据与新数据
            tmpHexString = tmpStringHex.toUtf8() + tmpHexString;
            //重新显示在控件上
            ui->textEdit_Rev->setText(tmpHexString);
        }else{
            //接受时间是否勾选,此处为未勾选
            if(ui->checkBox_4->checkState()== Qt::Unchecked){
                ui->textEdit_Rev->insertPlainText(revData);
            }else{
                getSysTime();
                ui->textEdit_Rev->insertPlainText("【"+currentTime+"】 "+revData);
            }
        }
    }
}
6.实现Hex显示——实现每俩个字节加一个空格显示
在 QString 类中,mid() 函数用于提取字符串的子字符串。

QString mid(int position, int length);
//position: 指定从哪个位置开始提取子字符串
//length: 指定要提取的字符数。如果这个参数省略,则默认提取到字符串的末尾。
// Hex显示槽函数
void Widget::on_checkBox_5_clicked(bool checked)
{
    if (checked) {
        // 获取原始文本 QString  \\x01\\x02\\x03
        QString revDisplay = ui->textEdit_Rev->toPlainText();
        // 转换为      QByteArray \\x01\\x02\\x03
        QByteArray byteArray = revDisplay.toUtf8();
        // 转换为      QByteArray  010203
        byteArray = byteArray.toHex();             
        QString lastShow;
        // 转换为      QString     010203
        revDisplay = QString::fromUtf8(byteArray); 
        for(int i=0; i<revDisplay.size();i+=2){
            lastShow += revDisplay.mid(i,2) + " ";
        }
        // 显示添加空格后的16进制内容
        ui->textEdit_Rev->setText(lastShow);
    } else {
        // 获取 Hex 文本
        QString hexDisplay = ui->textEdit_Rev->toPlainText();
        // 将 Hex 转换回原始字符串
        //QByteArray \\x01\\x02\\x03
        QByteArray byteArray = QByteArray::fromHex(hexDisplay.toUtf8());
        //QString  \\x01\\x02\\x03
        QString normalDisplay = QString::fromUtf8(byteArray);
        // 设置文本为正常字符串
        ui->textEdit_Rev->setText(normalDisplay);
    }
}
7.文本框追加内容时注意事项
我们使用的函数是insertPlainText()而不是append()。因为append()函数添加为内容后会自动换行。
8.重写QComboBox组件的事件 —— 实现串口号的刷新
- 当鼠标按下QComboBox组件时,发送reflesh()刷新信号。
- 在widget.cpp中连接该reflesh()信号与槽函数,槽函数中实现串口号的检测刷新。
mycombobox.h
#ifndef MYCOMBOBOX_H
#define MYCOMBOBOX_H
#include <QComboBox>
class myCombobox : public QComboBox
{
    Q_OBJECT
public:
    myCombobox(QWidget *parent);
protected:
    void mousePressEvent(QMouseEvent *e) override;  //鼠标按下事件
signals:
    void reflesh();  //刷新信号
};
#endif // MYCOMBOBOX_H
mycombobox.cpp
#include "mycombobox.h"
#include <QMouseEvent>
myCombobox::myCombobox(QWidget *parent):QComboBox(parent)
{}
void myCombobox::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton){
        emit reflesh();
    }
    QComboBox::mousePressEvent(e);
}
9.实现多文本发送
构造函数中先生成按键的名称,然后查找系统中是否有该名字的按键,有的话指针指向它,接着给该按键绑定槽函数
for(int i = 1; i <= 8; i++){
        QString btnName = QString("pushButton_%1").arg(i);                        //生成按键名字
        QPushButton* btn = findChild<QPushButton *>(btnName);                     //查找组件中是否有该名字的按键,若有,将指针指向该按键
        if(btn){
            btn->setProperty("buttonId",i);                                       //给该按键添加属性
            buttons.append(btn);                                                  //将该按键添加到容器中
            connect(btn,SIGNAL(clicked()),this,SLOT(on_command_button_clicked()));//给该按键连接信号与槽函数
        }
        QString lineEditName = QString("lineEdit_%1").arg(i);                     //生成输入框名字
        QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName);               //查找组件中是否有该名字的输入框,若有,将指针指向该输入框
        lineEdits.append(lineEdit);                                               //将该输入框添加到容器中
        QString checkBoxName = QString("checkBox_%1").arg(i);                     //生成checkboBox名字
        QCheckBox *checkBox = findChild<QCheckBox *>(checkBoxName);               //查找组件中是否有该名字的checkboBox,若有,将指针指向该checkboBox
        checkBoxs.append(checkBox);                                               //将该checkboBox添加到容器中
    }
槽函数中先通过sender()函数获取到发送信号的对象,接着获取按键的属性,接着根据属性生成对应的名称
void Widget::on_command_button_clicked() {
    // 通过 sender() 函数获取发出信号的对象,在通过 qobject_cast<> 方法进行类型转换
    QPushButton *btn = qobject_cast<QPushButton *>(sender());
    if (btn) {
        // 获取按键的属性 buttonId 的值,并转换为整数
        int num = btn->property("buttonId").toInt();
        // 根据 buttonId 构造与之对应的 QLineEdit 对象的名称
        QString lineEditName = QString("lineEdit_%1").arg(num);
        QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName);
        if (lineEdit) {
            if (lineEdit->text().isEmpty()) {
                return; // 如果文本为空,直接返回,不发送
            }
            ui->lineEdit_sendData->setText(lineEdit->text());
        }
        // 根据 buttonId 构造与之对应的 QCheckBox 对象的名称
        QString hexName = QString("checkBox_%1").arg(num);
        QCheckBox *checkBox = findChild<QCheckBox *>(hexName);
        ui->checkBox_sendHex->setChecked(checkBox ? checkBox->isChecked() : false); // 确保 checkBox 存在后再获取状态
        // 只有在 lineEdit 有内容的情况下发送数据
        on_pushButton_send_clicked();
    }
}
10.循环发送
第一种方法:不能通过延时函数处理,此方法为错误案例
//循环发送()
void Widget::on_checkBox_cirSend_clicked(bool checked)
{
    if(checked){
        for(QPushButton *btn : buttons){
            //当前按键发送点金信号
            emit btn->clicked();
            QThread::msleep(ui->spinBox->text().toInt());
        }
    }
}
第二种方法:定时器
实现逻辑:当勾选循环发送时候,调用槽函数:去设置定时器的间隔时间,并且启动定时器。当定时器超时时候,执行槽函数去发送信号。
void Widget::on_checkBox_cirSend_clicked(bool checked)
{
    if(checked){
        timer3->setInterval(ui->spinBox->text().toInt());
        timer3->start(); // 启动定时器
    }else{
        timer3->stop();
    }
}
void Widget::buttons_handler()
{
    if(btnIndex < 8){
        QPushButton *btn = buttons[btnIndex];
        emit btn->click();
        btnIndex++;
    }else{
        btnIndex = 0;
    }
}
第三种方法:多线程
缺点,没办法与ui组件关联,无法通过ui组件获取定时时间。
11.Qt打包程序
11.1.首先切换为release,然后重新构建,并且运行
11.2.拷贝库文件
首先进入C盘
C:进入你程序的目录下
 cd C:\\Users\\mi\\Desktop\\Qt Project\\build-untitled-Desktop_Qt_5_12_9_MinGW_64_bit-Release\\release执行命令,拷贝库到该目录下
D:\\Linux\\Qt\\5.12.9\\mingw73_32\\bin\\windeployqt.exe untitled.exe
11.3.手动拷贝缺失库文件
11.4.整体进行压缩即可
12.整体代码如下
tunnek/QT- (github.com)



















