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");