Qt制作基础功能的串口调试助手

 完整工程文件详见uart: Qt制作串口调试助手

使用Qt5.12制作串口调试助手,实现功能如下图展示

相关设计逻辑及代码展示

  • 串口调试助手首先是要对串口进行相关设置
private:
    Ui::Widget *ui;// ui对象指针,用于访问界面元素
    QSerialPort mSerialPort;

    QString mPortName;     // 端口号
    QString mPortBaudrate; // 波特率
    QString mPortParity;   // 校验位
    QString mPortDatabits; // 数据位
    QString mPortStopbits; // 停止位
bool Widget::getSerialPortConfig()
{
    //获取串口配置
    mPortName     = ui->Box_COM->currentText();
    mPortBaudrate = ui->Box_baud->currentText();
    mPortParity   = ui->Box_parity->currentText();
    mPortDatabits = ui->Box_databits->currentText();
    mPortStopbits = ui->Box_stopbits->currentText();

    /*设置串口配置*/
    mSerialPort.setPortName(mPortName);            // 端口号
    mSerialPort.setBaudRate(mPortBaudrate.toInt());// 波特率
    // 校验位
    if("Even"==mPortParity)
    {
      mSerialPort.setParity(QSerialPort::EvenParity);
    }else if("Odd"==mPortParity)
    {
      mSerialPort.setParity(QSerialPort::OddParity);
    }else
    {
      mSerialPort.setParity(QSerialPort::NoParity);
    }
    // 数据位
    if("5"==mPortDatabits)
    {
        mSerialPort.setDataBits(QSerialPort::Data5);
    }else if("6"==mPortDatabits)
    {
        mSerialPort.setDataBits(QSerialPort::Data6);
    }else if("7"==mPortDatabits)
    {
        mSerialPort.setDataBits(QSerialPort::Data7);
    }else
    {
        mSerialPort.setDataBits(QSerialPort::Data8);
    }
    // 停止位
    if("2"==mPortStopbits)
    {
        mSerialPort.setStopBits(QSerialPort::TwoStop);
    }else if("1.5"==mPortStopbits)
    {
        mSerialPort.setStopBits(QSerialPort::OneAndHalfStop);
    }else
    {
        mSerialPort.setStopBits(QSerialPort::OneStop);
    }

    return mSerialPort.open(QSerialPort::ReadWrite);;
}

void Widget::on_Button_uart_clicked()
{
    if(this->ui->Button_uart->text() == "打开串口")
    {
        if(true == getSerialPortConfig())
        {
            qDebug() << "串口打开成功!";
            rec_flag = 1;

            this->ui->Button_uart->setText("关闭串口");
            /*串口开启禁止修改配置*/
            this->ui->Box_COM->setEnabled(false);
            this->ui->Box_baud->setEnabled(false);
            this->ui->Box_parity->setEnabled(false);
            this->ui->Box_databits->setEnabled(false);
            this->ui->Box_stopbits->setEnabled(false);
            /*相关功能使能*/
            this->ui->Button_tranmit->setEnabled(true);
            this->ui->Button_stop->setEnabled(true);
            this->ui->checkBox_timeout->setEnabled(true);// 定时发送复选框使能能
            this->ui->checkBox_lines->setEnabled(true);  // 多行循环发送复选框使能
            this->ui->Button_line1->setEnabled(true);    // 单条数据发送功能使能
            this->ui->Button_line2->setEnabled(true);    // 单条数据发送功能使能
            this->ui->Button_line3->setEnabled(true);    // 单条数据发送功能使能
        }else
        {
            //QMessageBox::critical(NULL, "提示", "串口打开失败");
            QMessageBox::information(this,"提示","串口打开失败,请检查配置");
        }
    }
    else
    {
        mSerialPort.close();
        qDebug() << "串口关闭!";       
        rec_flag = 0;

        this->ui->Button_uart->setText("打开串口");
        /*串口关闭可修改配置*/
        this->ui->Box_COM->setEnabled(true);
        this->ui->Box_baud->setEnabled(true);
        this->ui->Box_parity->setEnabled(true);
        this->ui->Box_databits->setEnabled(true);
        this->ui->Box_stopbits->setEnabled(true);
        /*相关功能禁能*/
        this->ui->Button_tranmit->setEnabled(false);
        this->ui->Button_stop->setEnabled(false);
        this->ui->checkBox_timeout->setEnabled(false);// 定时发送复选框禁能
        this->ui->checkBox_lines->setEnabled(false);  // 多行循环发送复选框禁能
        this->ui->Button_line1->setEnabled(false);    // 单条数据发送功能禁能
        this->ui->Button_line2->setEnabled(false);    // 单条数据发送功能禁能
        this->ui->Button_line3->setEnabled(false);    // 单条数据发送功能禁能
    }
}

获取相关设置,并对应编写打开/关闭串口按键槽函数

其中获取端口号需要自动识别端口号(初始化函数中执行)

/*识别可用串口号*/
QList<QSerialPortInfo> serialportsInfo = QSerialPortInfo::availablePorts();
int count = serialportsInfo.count();
for (int i = 0; i<count; i++)
{
    ui->Box_COM->addItem(serialportsInfo.at(i).portName());
}
  • 发送与接受功能实现
/*接收数据设置*/
//connect(&mSerialPort, SIGNAL(readyRead()), this, SLOT(SerialPort_readyRead()));    // 老版qt语言
connect(&mSerialPort, &QSerialPort::readyRead, this, &Widget::SerialPort_readyRead); // 触发接收

初始化函数中执行接收中断链接到接收显示函数

相关功能标记位(在对应复选框状态变化槽函数中根据复选状态赋值标记位)

bool rec_flag;      // 接收标记位
bool wrapline;      // 自动换行标记位
bool demoHex;       // 16进制显示标记位
bool tranHex;       // 16进制发送
bool showTime;      // 时间戳标记位
bool TX_RX_flag;    // TX、RX标识标记位
bool timeout;       // 定时发送标记位
bool MutiState[10]; // 多行循环发送选中状态
int LastSend = 0;   // 多行循环发送起始位置

qint64 totalBytesReceived;  // 接收总字节数
qint64 totalBytesTranmit;   // 发送总字节数
void Widget::on_checkBox_wrapline_stateChanged(int arg1)
{
    if (arg1 == Qt::Checked)
    {
        // 复选框被选中
        wrapline = 1;
    } else if (arg1 == Qt::Unchecked)
    {
        // 复选框被取消选中
        wrapline = 0;
    }
}

接收显示函数 

void Widget::SerialPort_readyRead()
{
    if (rec_flag == 1)//接收显示标记位
        {
            QByteArray rx_buf = mSerialPort.readAll();         
            QString rx_buf_tmp;

            /*更新接收到的总字节数*/
            totalBytesReceived += rx_buf.size();
            ui->label_recbitsNum->setText(QString::number(totalBytesReceived));

            if (showTime)
            {
                wrapline=1;
                timestamp();
            }

            QTextCharFormat timestampFormat;
            timestampFormat.setForeground(Qt::black);  // 设置黑色文本颜色
            ui->textEdit_receive->setCurrentCharFormat(timestampFormat);

            if(TX_RX_flag)
            {
                ui->textEdit_receive->insertPlainText("RX: ");
            }

            if (demoHex)
            {
               const char *data = rx_buf.constData();
               for (int i = 0; i < rx_buf.size(); ++i)
               {
                   rx_buf_tmp += QString("%1 ").arg(static_cast<quint8>(data[i]), 2, 16, QLatin1Char('0'));
               }

               if (wrapline)
               {
                   rx_buf_tmp += "\n";
               }

               ui->textEdit_receive->insertPlainText(rx_buf_tmp);

            }else
            {
                QString rx_buf_tmp = QString::fromLocal8Bit(rx_buf);  // 转换为中文格式

                if (wrapline)
                {
                    rx_buf_tmp += "\n";
                }

                ui->textEdit_receive->insertPlainText(rx_buf_tmp);
            }

            /*led指示灯状态切换*/
            static int led_counter = 0;
            if (led_counter == 0)
            {
                setLED(ui->label_led, 0, 14);
                led_counter = 1;
            } else
            {
                setLED(ui->label_led, 2, 14);
                led_counter = 0;
            }


            scrollToBottom(); // 调用自动下拉函数

            //rx_buf_tmp.clear();
            rx_buf.clear();
        }
}

发送按键槽函数执行发送显示函数

void Widget::on_Button_tranmit_clicked()
{
    // 选择发送textEdit_transmit的内容
    sendData("textEdit_transmit");
}

void Widget::sendData(const QString &inputType)
{
    QString tx_buf;

    if (inputType == "textEdit_transmit")
    {
        tx_buf = ui->textEdit_transmit->toPlainText();
        if (tx_buf.isEmpty())
        {
            QMessageBox::information(this,"Error","数据内容为空,发送失败");
        }
    }
    else if (inputType == "lineEdit_line1")
    {
        tx_buf = ui->lineEdit_line1->text();
        if (tx_buf.isEmpty())
        {
            QMessageBox::information(this,"Error","数据内容为空,发送失败");
            this->ui->checkBox_timeout->setChecked(false);// 定时发送复选框设置为未选中状态
            this->ui->checkBox_lines->setChecked(false);  // 多行循环发送复选框设置为未选中状态
        }
    }
    else if (inputType == "lineEdit_line2")
    {
        tx_buf = ui->lineEdit_line2->text();
        if (tx_buf.isEmpty())
        {
            QMessageBox::information(this,"Error","数据内容为空,发送失败");
            this->ui->checkBox_timeout->setChecked(false);// 定时发送复选框设置为未选中状态
            this->ui->checkBox_lines->setChecked(false);  // 多行循环发送复选框设置为未选中状态
        }
    }
    else if (inputType == "lineEdit_line3")
    {
        tx_buf = ui->lineEdit_line3->text();
        if (tx_buf.isEmpty())
        {
            QMessageBox::information(this,"Error","数据内容为空,发送失败");
            this->ui->checkBox_timeout->setChecked(false);// 定时发送复选框设置为未选中状态
            this->ui->checkBox_lines->setChecked(false);  // 多行循环发送复选框设置为未选中状态
        }
    }
    //mSerialPort.write(ui->textEdit_transmit->toPlainText().toStdString().c_str());

    if (showTime)
    {
        timestamp();
    }

    QTextCharFormat timestampFormat;
    timestampFormat.setForeground(Qt::blue);  // 设置黑色文本颜色
    ui->textEdit_receive->setCurrentCharFormat(timestampFormat);

    if(TX_RX_flag && !tx_buf.isEmpty())
    {
        ui->textEdit_receive->insertPlainText("TX: ");
    }

    if (tranHex)
    {
        QStringList hexList = tx_buf.split(" ", QString::SkipEmptyParts); // 以空格分割输入的十六进制数据
        QByteArray tx_buf_tmp;

        foreach (QString hex, hexList)
        {
             bool ok;
             uchar byte = hex.toUShort(&ok, 16); // 将十六进制字符串转换为字节

             if (ok)
             {
                  tx_buf_tmp.append(byte); // 将字节添加到字节数组中
             }
         }

        // 更新发送总字节数
        totalBytesTranmit += tx_buf_tmp.size();
        ui->label_tranbitsNum->setText(QString::number(totalBytesTranmit));

        mSerialPort.write(tx_buf_tmp); // 通过串口发送数据

        tx_buf_tmp.clear();// 清空临时数据
        //qDebug() << "发送成功!";
        //qDebug() << "发送的十六进制数据:" << tx_buf_tmp;
    }else
    {
        // 更新发送总字节数
        totalBytesTranmit += tx_buf.size();
        ui->label_tranbitsNum->setText(QString::number(totalBytesTranmit));

        QByteArray tx_buf_tmp = tx_buf.toLocal8Bit(); // 转换为标准字符,支持中文
        mSerialPort.write(tx_buf_tmp); // 把数据通过串口发送出去

        tx_buf_tmp.clear();
        //qDebug() << "发送成功!";
     }

     ui->textEdit_receive->insertPlainText(tx_buf+"\r\n");
     scrollToBottom(); // 调用自动下拉函数

     tx_buf.clear();
}

其中时间戳显示函数

void Widget::timestamp()
{
    QDateTime nowTime = QDateTime::fromMSecsSinceEpoch(QDateTime::currentMSecsSinceEpoch());// 获取当前系统时间(精度为1ms)
    QString timeStr = "[" + nowTime.toString("yyyy-MM-dd hh:mm:ss.zzz") + "] "+ "\n";       // 时间转换为字符串格式
    // 创建时间戳的文本格式
    QTextCharFormat timestampFormat;
    timestampFormat.setForeground(Qt::red);  // 设置红色文本颜色
    ui->textEdit_receive->setCurrentCharFormat(timestampFormat);
    ui->textEdit_receive->insertPlainText(timeStr);
}

指示灯显示函数

void Widget::setLED(QLabel *label, int color, int size)
{
     // 将label中的文字清空
     label->setText("");
     // 设置矩形大小
     // 如果ui界面设置的label大小比最小宽度和高度小,矩形将被设置为最小宽度和最小高度;
     // 如果ui界面设置的label大小比最小宽度和高度大,矩形将被设置为最大宽度和最大高度;
     QString min_width = QString("min-width: %1px;").arg(size);              // 最小宽度:size
     QString min_height = QString("min-height: %1px;").arg(size);            // 最小高度:size
     QString max_width = QString("max-width: %1px;").arg(size);              // 最小宽度:size
     QString max_height = QString("max-height: %1px;").arg(size);            // 最小高度:size
     // 设置边界形状及边框
     QString border_radius = QString("border-radius: %1px;").arg(size/2);    // 边框是圆角,半径为size/2
     QString border = QString("border:1px solid black;");                    // 边框为1px黑色
     // 设置背景颜色
     QString background = "background-color:";
     switch (color) {
     case 0:
         // 灰色
         background += "rgb(240,240,240)";
         break;
     case 1:
         // 红色
         background += "rgb(255,0,0)";
         break;
     case 2:
         // 绿色
         background += "rgb(0,255,0)";
         break;
     case 3:
         // 黄色
         background += "rgb(255,165,0)";
         break;
     default:
         break;
     }

     const QString SheetStyle = min_width + min_height + max_width + max_height + border_radius + border + background;
     label->setStyleSheet(SheetStyle);
}

自动下拉函数(使显示框永远显示最新行)

void Widget::scrollToBottom()
{
    QScrollBar* scrollBar = ui->textEdit_receive->verticalScrollBar();
    scrollBar->setValue(scrollBar->maximum());
}
  • 定时发送功能

在初始化函数中设置好相关参数,在定时发送复选状态变化槽函数中开启/关闭定时器从而实现定时功能

/*定时发送设置*/
timer = new QTimer(this);   // 创建定时器对象
// 连接定时器的 timeout() 信号到槽函数
connect(timer, &QTimer::timeout, this, [=]() {
    sendData("textEdit_transmit");
});
ui->lineEdit_timeout->setText("1000");//设置默认值
void Widget::on_checkBox_timeout_stateChanged(int arg1)
{
    if (arg1 == Qt::Checked)
    {
        int interval = ui->lineEdit_timeout->text().toInt();    // 获取定时器的时间间隔(以毫秒为单位)
        timer->setInterval(interval);                           // 设置定时器的时间间隔
        timer->start();                                         // 定时器开启
        this->ui->lineEdit_timeout->setEnabled(false);
    } else if (arg1 == Qt::Unchecked)
    {
        timer->stop();
        this->ui->lineEdit_timeout->setEnabled(true);
    }
}
  • 多行发送功能

发送单行数据时根据选择不同的发送行,执行发送不同区域的数据

void Widget::on_Button_line1_clicked()
{
    sendData("lineEdit_line1");
}


void Widget::on_Button_line2_clicked()
{
    sendData("lineEdit_line2");
}


void Widget::on_Button_line3_clicked()
{
    sendData("lineEdit_line3");
}

选择多行执行周期循环发送,以设置的时间周期,单次发送一行数据,轮流发送选择的数据行

 控制思路与定时发送功能类似,初始化函数中获取相关设置,由复选框状态变化槽函数控制定时器开关

/*多行循环发送周期设置*/
timer1 = new QTimer(this);
connect(timer1, &QTimer::timeout, this, &Widget::multilines_control);
ui->lineEdit_lines_time->setText("100"); // 设置周期默认值
void Widget::multilines_control()
{
    int EditLineNo=0;
    if(LastSend >=Get_checkBoxline_State())  LastSend = 0;
    for(int i = LastSend;i < 3;i++)
    {
        if(MutiState[i] == true)
        {
           EditLineNo = i+1;
           LastSend = i+1;
           break;
        }
    }
    multilines_send(EditLineNo);  // 根据状态,发送指定行的数据
}


void Widget::multilines_send(int EditLineNo)
{
    switch (EditLineNo) {
        case 1:
            on_Button_line1_clicked();
            break;
        case 2:
            on_Button_line2_clicked();
            break;
        case 3:
            on_Button_line3_clicked();
            break;
        default:
            break;
    }
}


int Widget::Get_checkBoxline_State()
{
    int temp;
    if(ui->checkBox_line1->isChecked() == true)
       {
           MutiState[0]= true;
       }
       else
       {
           MutiState[0]= false;
       }
       if(ui->checkBox_line2->isChecked() == true)
       {
           MutiState[1]= true;
       }
       else
       {
           MutiState[1]= false;
       }
       if(ui->checkBox_line3->isChecked() == true)
       {
           MutiState[2]= true;
       }
       else
       {
           MutiState[2]= false;
       }

       for(int i = 2;i>0;i--)
       {
           if( MutiState[i]==true)
           {
                  temp = i+1;
                  break;
            }else
                  temp = 0;
       }
       return temp;
}


void Widget::on_checkBox_lines_stateChanged(int arg1)
{
    if (arg1 == Qt::Checked)
    {
        int interval = ui->lineEdit_lines_time->text().toInt();    // 获取定时器的时间间隔(以毫秒为单位)
        timer1->setInterval(interval);                             // 设置定时器的时间间隔
        timer1->start();                                           // 定时器开启
        this->ui->lineEdit_lines_time->setEnabled(false);
    } else if (arg1 == Qt::Unchecked)
    {
        timer1->stop();
        this->ui->lineEdit_lines_time->setEnabled(true);
    }
}

基础功能大致如上,其余为一些细节美化

.pro文件中执行增加图标(提前将.ico文件放置工程文件夹中)

# 添加图标
RC_ICONS = icon.ico

背景颜色设置(根据rgb颜色可进行修改)

/*背景颜色设置*/
QPalette pal;
pal.setColor(QPalette::Background, QColor(249, 239, 249));
// 应用QPalette到部件
this->setAutoFillBackground(true);
this->setPalette(pal);

设置程序名

this->setWindowTitle("Jing 串口助手1.0");