目录
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)