目录
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_OBJECTpublic: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()){//将新数据转化为HexQByteArray 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\\x03QString revDisplay = ui->textEdit_Rev->toPlainText();// 转换为 QByteArray \\x01\\x02\\x03QByteArray byteArray = revDisplay.toUtf8();// 转换为 QByteArray 010203byteArray = byteArray.toHex(); QString lastShow;// 转换为 QString 010203revDisplay = 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\\x03QByteArray byteArray = QByteArray::fromHex(hexDisplay.toUtf8());//QString \\x01\\x02\\x03QString 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,若有,将指针指向该checkboBoxcheckBoxs.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)