目录
1.界面预览
2.UI设计
2.1控件的选择
控件都很简单就不多介绍了,主要是布局时要细心 。
这是布局好的样子 (一定要细心)
3. 代码分析
第一步我们要获取到串口的端口号并打开串口
这里借助翻译软件翻译一下QSerialPort这个类的使用方法
提供了用于访问串口的函数。
您可以使用QSerialPortInfo辅助类获取有关可用串口的信息,该类允许枚举系统中的所有串口。这对于获取要使用的串口的正确名称非常有用。您可以将该辅助类的对象作为参数传递给setPort()或setPortName()方法,以指定所需的串口设备。
设置串口之后,您可以使用open()方法以只读(r/o)、只写(w/o)或读写(r/w)模式打开串口。
注意:串口始终以独占访问方式打开(即,没有其他进程或线程可以访问已经打开的串口)。
使用close()方法关闭串口并取消I/O操作。
成功打开串口后,QSerialPort会尝试确定串口的当前配置并进行初始化。您可以使用setBaudRate()、setDataBits()、setParity()、setStopBits()和setFlowControl()方法重新配置串口的所需设置。
还有一些用于处理引脚信号的属性,例如:QSerialPort::dataTerminalReady、QSerialPort::requestToSend。还可以使用pinoutSignals()方法查询当前设置的引脚信号。
一旦确定串口准备好读取或写入数据,您可以使用read()或write()方法。或者,还可以使用readLine()和readAll()便捷方法。如果没有一次读取完所有数据,剩余的数据将作为新的传入数据附加到QSerialPort的内部读取缓冲区中。您可以使用setReadBufferSize()方法限制读取缓冲区的大小。
- 根据文档发现要对串口操作还要使用QSerialPortInfo类
这里借助翻译软件翻译一下QSerialPortInfo这个类的使用方法
提供有关现有串口的信息。
使用静态函数生成一个QSerialPortInfo对象列表。列表中的每个QSerialPortInfo对象表示一个串口,可以查询其端口名称、系统位置、描述和制造商信息。QSerialPortInfo类还可以作为QSerialPort类的setPort()方法的输入参数使用。
通过文档可以知道要使用QSerialPortInfo的一个静态函数,打开Qt的帮助手册发现只有俩个静态函数,根据我初中的英语水平发现第一个静态函数可以获取可用的串口。
通过返回值的类型可以知道是一个数组,既然是数组那就遍历一下,发现QComboBox控件获取到了串口的名字。(QList不知道怎么使用直接百度)
- 下面是获取串口信息的具体实现
void Widget::on_comboBoxEnv()
{
// 清空comboBoxSerial中的所有选项
ui->comboBoxSerial->clear();
// 获取可用串口列表
QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
// 遍历可用串口列表
for(QSerialPortInfo serialPort : serialList){
// 打印当前串口的端口号名称
qDebug() << serialPort.portName();
// 在UI界面的comboBoxSerial中添加当前串口的端口号名称作为选项
ui->comboBoxSerial->addItem(serialPort.portName());
}
}
- 这一段是打开串口按键的槽函数(使用open()方法),几乎每一句都注释了就不过多的介绍了
void Widget::on_closeSerialBt_clicked()
{
if(checkSerial){
// 如果串口未打开,则执行以下操作
// 1. 选择端口号
serialPort->setPortName(ui->comboBoxSerial->currentText());
// 2. 选择波特率
serialPort->setBaudRate(ui->comboBoxBaud->currentText().toInt());
// 3. 选择数据位
serialPort->setDataBits(QSerialPort::DataBits(ui->comboBoxDate->currentText().toInt()));
// 4. 选择校验位
switch(ui->comboBoxCheck->currentIndex()){
case 0:
serialPort->setParity(QSerialPort::NoParity);
break;
case 1:
serialPort->setParity(QSerialPort::OddParity);
break;
case 2:
serialPort->setParity(QSerialPort::EvenParity);
break;
case 3:
serialPort->setParity(QSerialPort::MarkParity);
break;
case 4:
serialPort->setParity(QSerialPort::SpaceParity);
break;
default:
serialPort->setParity(QSerialPort::UnknownParity);
break;
}
// 5. 选择停止位
serialPort->setStopBits(QSerialPort::StopBits(ui->comboBoxStop->currentText().toInt()));
// 6. 流控
if(ui->comboBoxStream->currentText() == "No"){
serialPort->setFlowControl(QSerialPort::NoFlowControl);
}
// 7. 打开关闭串口
if(serialPort->open(QIODevice::ReadWrite) == true){
// 如果串口成功打开,则执行以下操作
// 启用发送按钮
ui->SendBt->setEnabled(true);
// 设置关闭串口按钮文本
ui->closeSerialBt->setText("关闭串口");
// 禁用波特率、数据位、停止位、校验位、端口号、流控制的下拉框
ui->comboBoxBaud->setEnabled(false);
ui->comboBoxDate->setEnabled(false);
ui->comboBoxStop->setEnabled(false);
ui->comboBoxCheck->setEnabled(false);
ui->comboBoxSerial->setEnabled(false);
ui->comboBoxStream->setEnabled(false);
// 启用定时发送复选框
ui->checkBtimerSend->setEnabled(true);
// 将checkSerial标志设为false,表示串口已打开
checkSerial = false;
// 设置状态栏文本为串口已打开
ui->labelSendstate->setText("serial open!");
qDebug() << "serial open!";
} else {
// 如果串口打开失败,则显示错误消息框
QMessageBox msgBox;
msgBox.setWindowTitle("TYW");
msgBox.setText("提醒:串口打开错误,请检查串口是否被占用和拔出!");
msgBox.exec();
qDebug() << "serial ERROR!";
}
} else {
// 如果串口已打开,则执行以下操作
// 关闭串口
serialPort->close();
// 禁用发送按钮
ui->SendBt->setEnabled(false);
// 设置打开串口按钮文本
ui->closeSerialBt->setText("打开串口");
// 启用波特率、数据位、停止位、校验位、端口号、流控制的下拉框
ui->comboBoxBaud->setEnabled(true);
ui->comboBoxDate->setEnabled(true);
ui->comboBoxStop->setEnabled(true);
ui->comboBoxCheck->setEnabled(true);
ui->comboBoxSerial->setEnabled(true);
ui->comboBoxStream->setEnabled(true);
// 启用接收数据输入框
ui->lineEditRevNum->setEnabled(true);
ui->lineEditSendData->setEnabled(true);
// 禁用定时发送复选框并取消选中状态
ui->checkBtimerSend->setEnabled(false);
ui->checkBtimerSend->setCheckState(Qt::Unchecked);
// 停止定时器
timer->stop();
// 将checkSerial标志设为true,表示串口已关闭
checkSerial = true;
// 设置状态栏文本为串口已关闭
ui->labelSendstate->setText("serial close!");
qDebug() << "serial close!";
}
- 通过上面的程序打开了串口,之后我们就要实现数据的发送和接收了(使用read()和write()方法)
发送添加了以HEX发送和换行发送
//数据的发送
void Widget::on_SendBt_clicked()
{
int sendCnt = 0; // 记录发送的字节数
// 获取要发送的数据
const char *sendBuf = ui->lineEditSendData->text().toLocal8Bit().constData(); // 转换为本地编码,中文可能会乱码
// 判断是否勾选以十六进制发送
if(ui->checkBHEXSend->isChecked()){
QString tmpData = ui->lineEditSendData->text();
// 将输入数据转换为本地编码的字节数组
QByteArray dataArray = tmpData.toLocal8Bit();
// 判断数据长度是否为偶数
if(dataArray.size() % 2 != 0){
ui->labelSendstate->setText("Input Error!");
return;
}
// 判断数据是否为十六进制
for(char c : dataArray){
if(!std::isxdigit(c)){
ui->labelSendstate->setText("Input Error!");
return;
}
}
// 在数据末尾添加换行符
if(ui->checkBnewLine->isChecked()) dataArray.append("\r\n");
// 将十六进制数据转换为字节数组并发送
QByteArray _senddata = QByteArray::fromHex(dataArray);
serialPort->write(_senddata);
} else {
// 非十六进制发送
if(ui->checkBnewLine->isChecked()){
// 在数据末尾添加换行符并发送
QByteArray sendDataArr = QByteArray(sendBuf, strlen(sendBuf));
sendDataArr.append("\r\n");
sendCnt = serialPort->write(sendDataArr);
} else {
// 直接发送数据
sendCnt = serialPort->write(sendBuf);
}
}
// 检查发送是否出错
if(sendCnt == -1){
ui->labelSendstate->setText("Send ERROR!");
} else {
sendCntTotal += sendCnt; // 更新发送的总字节数
ui->labelSendNum->setText("Send:" + QString::number(sendCntTotal)); // 在界面上显示发送的总字节数
qDebug() << "Send OK!" << sendBuf;
// 检查是否有新的数据发送
if(strcmp(sendBuf, sendBuftmp.toStdString().c_str()) != 0){
ui->textEdit->append(sendBuf); // 在文本框中显示发送的数据
sendBuftmp = QString::fromUtf8(sendBuf); // 更新发送的数据
}
}
}
接收添加了HEX显示和换行显示
//接受数据
void Widget::on_revReadytoRead()
{
QString revBuf = serialPort->readAll(); // 获取读取到的数据
if(ui->checkBLineWrap->isChecked()) revBuf.append("\r\n"); // 检查是否勾选了换行显示,若勾选则在数据末尾添加换行符
if(revBuf != NULL){
if(ui->checkBoHEX->isChecked()){
// 如果勾选了以十六进制显示接收的数据
QByteArray _revBuf = revBuf.toUtf8().toHex().toUpper(); // 将接收到的数据转换为十六进制形式,并转换为大写
QString revBufOdd = ui->textEditRev->toPlainText(); // 获取已经显示的十六进制数据
QByteArray revBufandrevBufOdd = _revBuf + revBufOdd.toUtf8(); // 将新接收到的数据和已显示的数据合并
ui->textEditRev->setText(QString::fromUtf8(revBufandrevBufOdd)); // 在界面上显示合并后的数据
} else {
// 如果未勾选以十六进制显示接收的数据
if(revDateTime == true){
// 如果勾选了接收时间显示
ui->textEditRev->insertPlainText("["+_datetime+"]"+" "+revBuf); // 在界面上插入带有时间戳的接收数据
} else {
// 如果未勾选接收时间显示
ui->textEditRev->insertPlainText(revBuf); // 在界面上插入接收数据
}
}
revCntTotal += revBuf.size(); // 更新接收的总字节数
ui->labelReceivedstate->setText("Revice:"+QString::number(revCntTotal)); // 在界面上显示接收的总字节数
qDebug() << "Rev OK!" << revBuf; // 在控制台输出接收的数据
}
// 跟随光标移动到文本末尾
ui->textEditRev->moveCursor(QTextCursor::End);
// 确保光标可见
ui->textEditRev->ensureCursorVisible();
// 设置焦点到接收文本框
// ui->textEditRev->setFocus();
}
- 接下来要实现定时的发送
定时发送要使用QTimer这个类
通过文档可以知道QTimer的基本使用方法 ,直接照着改就完事了
connect(timer, &QTimer::timeout, [=](){on_SendBt_clicked();});//关联定时发送
- 这里使用了Lambda 表达式
void Widget::on_checkBtimerSend_clicked(bool checked)
{
if(checked){
// 如果定时发送被勾选
ui->lineEditRevNum->setEnabled(false); // 禁用接收间隔时间输入框
ui->lineEditSendData->setEnabled(false); // 禁用发送数据输入框
timer->start(ui->lineEditRevNum->text().toInt()); // 启动定时器,间隔时间为输入框中设置的值
} else {
// 如果定时发送被取消勾选
timer->stop(); // 停止定时器
ui->lineEditRevNum->setEnabled(true); // 启用接收间隔时间输入框
ui->lineEditSendData->setEnabled(true); // 启用发送数据输入框
}
}
- 同过QTime对时间的获取,QTime类的使用还是比较简单的通过文档就可以知道他的用法
void Widget::getSysTime()
{
//获取当前系统的日期和时间
QDateTime currentime = QDateTime::currentDateTime();
//设置当前系统的日期
QDate date = currentime.date();
int year = date.year();
int month = date.month();
int day = date.day();
//设置当前系统的时间
QTime time = currentime.time();
int hour = time.hour();
int min = time.minute();
int sec = time.second();
//将设置到好的日期和时间拼接起来
_datetime = QString("%1-%2-%3 %4:%5:%6")
.arg(year, 2, 10, QChar('0'))
.arg(month, 2, 10, QChar('0'))
.arg(day, 2, 10, QChar('0'))
.arg(hour, 2, 10, QChar('0'))
.arg(min, 2, 10, QChar('0'))
.arg(sec, 2, 10, QChar('0'));
}
- HEX显示还是比较简单的就是对数据格式的转换。
void Widget::on_checkBoHEX_clicked(bool checked)
{
if(checked){
// 如果勾选了显示十六进制数据
// 获取界面上的文本数据
QString tmpData = ui->textEditRev->toPlainText();
// 将数据转换为十六进制形式
QByteArray changeHexTmp = tmpData.toUtf8();
changeHexTmp = changeHexTmp.toHex(); // 将字母、符号等转换为十六进制数
// 在界面上重新显示数据
QString lastShow;
tmpData = QString::fromUtf8(changeHexTmp);
for(int i=0; i < tmpData.size(); i+=2){
lastShow += tmpData.mid(i,2) + " "; // 格式化显示十六进制数据,每两个字符加一个空格
}
ui->textEditRev->setText(lastShow.toUpper()); // 在界面上显示转换后的十六进制数据(大写形式)
} else {
// 如果取消勾选显示十六进制数据
QString _tmpData = ui->textEditRev->toPlainText();
QByteArray _changeStrTmp = _tmpData.toUtf8();
QByteArray _changeStr = QByteArray::fromHex(_changeStrTmp); // 将十六进制数据转换为字母、符号等
ui->textEditRev->setText(QString::fromUtf8(_changeStr)); // 在界面上显示转换后的数据
}
}
剩下的功能都比较容易实现对于文件的操作可以参考一下QT学习——记事本项目-CSDN博客
文章中的类和方法用过之后下回就知道怎么用了所以直接拿来用就好了。如果不够的话还可以借助翻译软件去看一下Qt文档对它的具体介绍。
3.1 将UI控件添加到数组中
这种方法可以使得我们的代码能加简洁,因为用到了很多的QPushbutton和QLineEdit,QCheckBox的UI控件,如果每一个控件都关联一个信号与槽代码会变的很庞大,而且还要用到很多if else所以这种方式大大的简化了代码。
这个循环用于初始化按钮、行编辑框和复选框的操作。在循环中,根据循环变量拼接按钮、行编辑框和复选框的名称,然后使用findChild()函数查找对应的控件。如果找到对应的按钮控件,就给按钮设置一个属性来存储按钮的编号,并将按钮添加到按钮列表中;然后连接按钮的点击事件到槽函数on_cmdButtonclicked()。对于行编辑框和复选框,如果找到对应的控件,就分别将它们添加到对应的列表中。
for(int i = 1; i <= 9; i++){
QString btnName = QString("pushButton_%1").arg(i); // 根据循环变量拼接按钮名称
QPushButton *btn = findChild<QPushButton *>(btnName); // 查找对应的按钮控件
if(btn){
btn->setProperty("buttonId", i); // 给按钮设置属性,存储按钮的编号
button.append(btn); // 将按钮添加到按钮列表中
connect(btn, SIGNAL(clicked()), this, SLOT(on_cmdButtonclicked())); // 连接按钮的点击事件到槽函数on_cmdButtonclicked()
}
QString lineEditName = QString("lineEdit_%1").arg(i); // 根据循环变量拼接行编辑框名称
QLineEdit *line = findChild<QLineEdit *>(lineEditName); // 查找对应的行编辑框控件
if(line) _lineEdit.append(line); // 将行编辑框添加到行编辑框列表中
QString checkBoxName = QString("checkBox_%1").arg(i); // 根据循环变量拼接复选框名称
QCheckBox *check = findChild<QCheckBox *>(checkBoxName); // 查找对应的复选框控件
if(check) _checkBox.append(check); // 将复选框添加到复选框列表中
}
这是一段槽函数代码,当cmdButton按钮被点击时触发。通过sender()函数获取发送信号的对象,然后根据设置的属性获取按钮的编号。根据编号拼接出对应的行编辑框和复选框的名称,然后使用findChild()函数查找对应的控件。如果找到了行编辑框控件,将其文本设置为发送数据的文本框的文本;如果找到了复选框控件,将其选中状态设置为发送数据的十六进制复选框的选中状态。最后调用发送按钮的点击事件,实现发送数据的功能。
void Widget::on_cmdButtonclicked()
{
QPushButton *btn = qobject_cast<QPushButton *>(sender()); // 获取发送信号的对象(QPushButton QComboBox等等)
int num = btn->property("buttonId").toInt(); // 通过设置属性的方式获取按钮的编号
QString lineEditName = QString("lineEdit_%1").arg(num); // 通过字符串拼接获取对应的行编辑框的名称
QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName); // 根据名称查找对应的行编辑框控件
if(lineEdit){
if(lineEdit->text().size() <= 0){
qDebug() << "1";
return;
} else {
ui->lineEditSendData->setText(lineEdit->text()); // 将行编辑框的文本设置为发送数据的文本框的文本
}
}
QString checkBoxName = QString("checkBox_%1").arg(num); // 通过字符串拼接获取对应的复选框的名称
QCheckBox *checkBox = findChild<QCheckBox *>(checkBoxName); // 根据名称查找对应的复选框控件
if(checkBox)
ui->checkBHEXSend->setChecked(checkBox->isChecked()); // 将复选框的选中状态设置为发送数据的十六进制复选框的选中状态
on_SendBt_clicked(); // 调用发送按钮的点击事件,实现发送数据
}
4.项目的完整代码
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
mycomboBox.cpp
类名要大写
#include "mycombobox.h"
#include <QMouseEvent>
myComboBox::myComboBox(QWidget *parent):QComboBox(parent)
{
}
void myComboBox::mousePressEvent(QMouseEvent *e)
{
//发送信号
if(e->button() == Qt::LeftButton){
emit _refashl();
}
QComboBox::mousePressEvent(e);
}
mycomboBox.h
#ifndef MYCOMBOBOX_H
#define MYCOMBOBOX_H
#include <QComboBox>
#include <QWidget>
class myComboBox : public QComboBox
{
Q_OBJECT
public:
myComboBox(QWidget *parent);
protected:
void mousePressEvent(QMouseEvent *e) override;
signals:
void _refashl();
};
#endif // MYCOMBOBOX_H
Widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QSerialPortInfo>
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
#include <QDateTime>
#include <QThread>
#include <QMessageBox>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setLayout(ui->gridLayoutgloba);//实现跟随放大
serialPort = new QSerialPort(this);
timer = new QTimer(this);
DateTimeTimer = new QTimer(this);
buttonConTimer = new QTimer(this);
ui->SendBt->setEnabled(false);//未打开串口默认发送不了数据
sendCntTotal = 0;//记录发送的次数
revCntTotal = 0;//记录接收的次数
buttonIndex = 0;
checkSerial = true;
revDateTime = false;
ui->comboBoxBaud->setCurrentIndex(5);//comboBox的最开始显示的
ui->comboBoxDate->setCurrentIndex(3);
ui->checkBtimerSend->setEnabled(false);//checkBtimerSend的最开始显示成不按的
connect(ui->comboBoxSerial, &myComboBox::_refashl,this,&Widget::on_comboBoxEnv);
connect(serialPort, SIGNAL(readyRead()), this, SLOT(on_revReadytoRead()));//关联接收
connect(buttonConTimer, &QTimer::timeout, this, &Widget::on_buttonfalsh);
connect(timer, &QTimer::timeout, [=](){on_SendBt_clicked();});//关联定时发送
connect(DateTimeTimer, &QTimer::timeout, this, &Widget::on_timeReFalsh);//关联时间的刷新
DateTimeTimer->start(1000);
on_comboBoxEnv();
for(int i = 1; i<=9; i++){
QString btnName = QString("pushButton_%1").arg(i);
QPushButton *btn = findChild<QPushButton *>(btnName);
if(btn){
btn->setProperty("buttonId",i);
button.append(btn);
connect(btn,SIGNAL(clicked()),this,SLOT(on_cmdButtonclicked()));
}
QString lineEditName = QString("lineEdit_%1").arg(i);
QLineEdit *line = findChild<QLineEdit *>(lineEditName);
if(line)_lineEdit.append(line);
QString checkBoxtName = QString("checkBox_%1").arg(i);
QCheckBox *check = findChild<QCheckBox *>(checkBoxtName);
if(check)_checkBox.append(check);
}
ui->labelSendstate->setText("ready!");
}
Widget::~Widget()
{
delete ui;
}
//打开串口
void Widget::on_closeSerialBt_clicked()
{
/*
* 因为上来一定要打开串口所要让变量为true
* 但是一旦串口被拔出和占用使用QMessagebox提示用户
* 下一步要让checkSerial为false因为我们要关闭串口了
* 关闭串口的动作和打开串口的动作恰好相反
*/
//通过改变全局变量值的方式来实现串口的打开和关闭
//知道互斥的概念就很好理解了
if(checkSerial){
//1.选择端口号
serialPort->setPortName(ui->comboBoxSerial->currentText());
//2.选择波特率
serialPort->setBaudRate(ui->comboBoxBaud->currentText().toInt());
//3.选择数据位
serialPort->setDataBits(QSerialPort::DataBits(ui->comboBoxDate->currentText().toInt()));
//4.选择校验位
switch(ui->comboBoxCheck->currentIndex()){
case 0:
serialPort->setParity(QSerialPort::NoParity);
break;
case 1:
serialPort->setParity(QSerialPort::OddParity);
break;
case 2:
serialPort->setParity(QSerialPort::EvenParity);
break;
case 3:
serialPort->setParity(QSerialPort::MarkParity);
break;
case 4:
serialPort->setParity(QSerialPort::SpaceParity);
break;
default:
serialPort->setParity(QSerialPort::UnknownParity);
break;
}
//5.选择停止位
serialPort->setStopBits(QSerialPort::StopBits(ui->comboBoxStop->currentText().toInt()));
//6.流控
if(ui->comboBoxStream->currentText() == "No"){
serialPort->setFlowControl(QSerialPort::NoFlowControl);
}
//7.打开关闭串口
if(serialPort->open(QIODevice::ReadWrite) == true){
ui->SendBt->setEnabled(true);
ui->closeSerialBt->setText("关闭串口");
ui->comboBoxBaud->setEnabled(false);
ui->comboBoxDate->setEnabled(false);
ui->comboBoxStop->setEnabled(false);
ui->comboBoxCheck->setEnabled(false);
ui->comboBoxSerial->setEnabled(false);
ui->comboBoxStream->setEnabled(false);
ui->checkBtimerSend->setEnabled(true);
checkSerial = false;
ui->labelSendstate->setText("serial open!");
qDebug() << "serial open!";
}else{
QMessageBox msgBox;
msgBox.setWindowTitle("TYW");
msgBox.setText("提醒:串口打开错误,请检查串口是否被占用和拔出!");
msgBox.exec();
qDebug() << "serial ERROR!";
}
}else{
serialPort->close();
ui->SendBt->setEnabled(false);
ui->closeSerialBt->setText("打开串口");
ui->comboBoxBaud->setEnabled(true);
ui->comboBoxDate->setEnabled(true);
ui->comboBoxStop->setEnabled(true);
ui->comboBoxCheck->setEnabled(true);
ui->comboBoxSerial->setEnabled(true);
ui->comboBoxStream->setEnabled(true);
ui->lineEditRevNum->setEnabled(true);
ui->lineEditSendData->setEnabled(true);
ui->checkBtimerSend->setEnabled(false);
ui->checkBtimerSend->setCheckState(Qt::Unchecked);
timer->stop();
checkSerial = true;
ui->labelSendstate->setText("serial close!");
qDebug() << "serial close!";
}
}
void Widget::on_SendBt_clicked()
{
int sendCnt = 0;
//const char *sendBuf = ui->lineEditSendData->text().toStdString().c_str();
const char *sendBuf = ui->lineEditSendData->text().toLocal8Bit().constData();//中文会乱码
//判断一下是否勾选 以十六进制发送
if(ui->checkBHEXSend->isChecked()){
QString tmpData = ui->lineEditSendData->text();
//1.判断是否为偶数
QByteArray dataArray= tmpData.toLocal8Bit();
if(dataArray.size() % 2 != 0){
ui->labelSendstate->setText("Input Error!");
return;
}
dataArray.toHex();
//2.判断是否为16进制
for(char c : dataArray){
if(!std::isxdigit(c)){
ui->labelSendstate->setText("Input Error!");
return;
}
}
//3.发送
if(ui->checkBnewLine->isChecked()) dataArray.append("\r\n");
QByteArray _senddata = QByteArray::fromHex(dataArray);
serialPort->write(_senddata);
}else{
//非16进制发送
if(ui->checkBnewLine->isChecked()){
//write()有QByteArray没有QSting
QByteArray sendDataArr = QByteArray(sendBuf, strlen(sendBuf));
sendDataArr.append("\r\n");
sendCnt = serialPort->write(sendDataArr);
}else{
sendCnt = serialPort->write(sendBuf);
}
}
if(sendCnt == -1){
ui->labelSendstate->setText("Send ERROR!");
}else{
sendCntTotal += sendCnt;//sendCntTotal用来保存发送的字节数
//ui->labelSendNum->setNum(sendCntTotal);//显示发送的字节数
ui->labelSendNum->setText("Send:"+QString::number(sendCntTotal));
qDebug() << "Send OK!" << sendBuf;
if(strcmp(sendBuf,sendBuftmp.toStdString().c_str()) != 0){
ui->textEdit->append(sendBuf);
sendBuftmp = QString::fromUtf8(sendBuf);
}
}
}
void Widget::on_revReadytoRead()
{
QString revBuf = serialPort->readAll();//获取读到的数据
if(ui->checkBLineWrap->isChecked()) revBuf.append("\r\n");
if(revBuf != NULL){
if(ui->checkBoHEX->isChecked()){
QByteArray _revBuf = revBuf.toUtf8().toHex().toUpper();
QString revBufOdd = ui->textEditRev->toPlainText();//勾选了读出来就是HEX
QByteArray revBufandrevBufOdd = _revBuf + revBufOdd.toUtf8();
ui->textEditRev->setText(QString::fromUtf8(revBufandrevBufOdd));
}else{
if(revDateTime == true){
ui->textEditRev->insertPlainText("["+_datetime+"]"+" "+revBuf);
}else{
ui->textEditRev->insertPlainText(revBuf);
}
}
revCntTotal += revBuf.size();
//ui->labelReceivedstate->setNum(revCntTotal);
ui->labelReceivedstate->setText("Revice:"+QString::number(revCntTotal));
qDebug() << "Rev OK!" << revBuf;
}
//跟随光标移动
ui->textEditRev->moveCursor(QTextCursor::End);
//确保光标是可视的
ui->textEditRev->ensureCursorVisible();
//ui->textEditRev->setFocus();
}
void Widget::on_checkBtimerSend_clicked(bool checked)
{
if(checked){
ui->lineEditRevNum->setEnabled(false);
ui->lineEditSendData->setEnabled(false);
timer->start(ui->lineEditRevNum->text().toInt());
}else{
timer->stop();
ui->lineEditRevNum->setEnabled(true);
ui->lineEditSendData->setEnabled(true);
}
}
void Widget::on_clearRevBt_clicked()
{
ui->textEditRev->clear();
}
void Widget::on_SaveRevBt_clicked()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"D:/QT/serial.txt",
tr("Text (*.txt)"));
if(fileName != NULL){
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
out << ui->textEditRev->toPlainText();
file.close();
}
}
void Widget::on_timeReFalsh()
{
getSysTime();
ui->labeltime->setText(_datetime);
}
void Widget::getSysTime()
{
//获取当前系统的日期和时间
QDateTime currentime = QDateTime::currentDateTime();
//设置当前系统的日期
QDate date = currentime.date();
int year = date.year();
int month = date.month();
int day = date.day();
//设置当前系统的时间
QTime time = currentime.time();
int hour = time.hour();
int min = time.minute();
int sec = time.second();
//将设置到好的日期和时间拼接起来
_datetime = QString("%1-%2-%3 %4:%5:%6")
.arg(year, 2, 10, QChar('0'))
.arg(month, 2, 10, QChar('0'))
.arg(day, 2, 10, QChar('0'))
.arg(hour, 2, 10, QChar('0'))
.arg(min, 2, 10, QChar('0'))
.arg(sec, 2, 10, QChar('0'));
}
void Widget::on_checkBoRevTime_clicked(bool checked)
{
//getSysTime();
if(checked == true){
revDateTime = true;
}else{
revDateTime = false;
}
}
void Widget::on_checkBoHEX_clicked(bool checked)
{
if(checked){
//获取ui上的数据
QString tmpData = ui->textEditRev->toPlainText();
//Hex的转换
QByteArray changeHexTmp = tmpData.toUtf8();
changeHexTmp = changeHexTmp.toHex();//把字母,符合......转成十六进制数
//重新显示在UI上
QString lastShow;
tmpData = QString::fromUtf8(changeHexTmp);
for(int i=0; i < tmpData.size(); i+=2){
lastShow += tmpData.mid(i,2) + " ";
}
ui->textEditRev->setText(lastShow.toUpper());
}else{
QString _tmpData = ui->textEditRev->toPlainText();
QByteArray _changeStrTmp = _tmpData.toUtf8();
QByteArray _changeStr = QByteArray::fromHex(_changeStrTmp);//把十六进制数转成字母,符合......
ui->textEditRev->setText(QString::fromUtf8(_changeStr));
}
}
void Widget::on_hidemianBt_clicked(bool checked)
{
if(checked){
ui->hidemianBt->setText("拓展面板");
ui->groupBoxtext->hide();
}else{
ui->hidemianBt->setText("隐藏面板");
ui->groupBoxtext->show();
}
}
void Widget::on_hideHistoryBt_clicked(bool checked)
{
if(checked){
ui->hideHistoryBt->setText("显示历史");
ui->groupBoxHis->hide();
}else{
ui->hideHistoryBt->setText("隐藏历史");
ui->groupBoxHis->show();
}
}
void Widget::on_comboBoxEnv()
{
ui->comboBoxSerial->clear();
//comboBox获取serial的端口号
QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
for(QSerialPortInfo serialPort : serialList){
//打印端口号的名字
qDebug() << serialPort.portName();
//在UI界面显示端口号的名字
ui->comboBoxSerial->addItem(serialPort.portName());
}
}
void Widget::on_cmdButtonclicked()
{
QPushButton *btn = qobject_cast<QPushButton *>(sender());//sender获取发送信号的对象(QPushButton QComboBox等等)
int num = btn->property("buttonId").toInt();//巧妙的运用设置属性的方式来找到是那个按键
QString lineEditName = QString("lineEdit_%1").arg(num);//通过QString对字符串的拼接获取控件的名字
QLineEdit *lineEdit = findChild<QLineEdit *>(lineEditName);//通过名字来找到是那个QLineEdit控件
if(lineEdit){
if(lineEdit->text().size() <= 0){
qDebug() << "1";
return;
}else{
ui->lineEditSendData->setText(lineEdit->text());
}
}
QString checkBoxName = QString("checkBox_%1").arg(num);
QCheckBox *checkBox = findChild<QCheckBox *>(checkBoxName);
if(checkBox)
ui->checkBHEXSend->setChecked(checkBox->isChecked());
on_SendBt_clicked();
}
void Widget::on_checkBox_10_clicked(bool checked)
{
checked ? buttonConTimer->start(ui->spinBox->text().toInt()) : buttonConTimer->stop();
}
void Widget::on_buttonfalsh()
{
if(buttonIndex < button.size()){
QPushButton *btntmp = button[buttonIndex];
emit btntmp->clicked();
buttonIndex++;
//QThread::msleep(ui->spinBox->text().toInt());//QT中不能在UI的线程延时
}else{
buttonIndex = 0;
}
}
void Widget::on_resBt_clicked()
{
QMessageBox megBox;
megBox.setWindowTitle("提示");
megBox.setIcon(QMessageBox::Question);//设置图标
megBox.setText("重置列表不可逆,确认是否重置?");
//megBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);//这种方法设置不了中文
QPushButton *yesButton = megBox.addButton("是",QMessageBox::YesRole);
QPushButton *noButton = megBox.addButton("否",QMessageBox::NoRole);
megBox.exec();
if(megBox.clickedButton() == yesButton){
for(int i = 0; i < _lineEdit.size(); i++){
//清空QLineEdit控件
_lineEdit[i]->clear();
//清空QCheckBox控件
_checkBox[i]->setChecked(false);
}
}
}
void Widget::on_loadBt_clicked()
{
int i = 0;
QString fileName = QFileDialog::getOpenFileName(this, tr("打开文件"),
"D:/",
tr("文本文件 (*.txt)"));
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QTextStream in(&file);
while (!in.atEnd() && i <= 9) {
QString line = in.readLine();
QStringList paret = line.split(":");
if(paret.count() == 2){
_lineEdit[i]->setText(paret[1]);
}
i++;
}
}
void Widget::on_saveBt_clicked()
{
getSysTime();
QString tmpSaveData = QString("D:/") + _datetime + QString("Default");
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
tmpSaveData,
tr("Text (*.txt)"));
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
for(int i = 0; i < _lineEdit.size(); i++){
out << i << ": " << _lineEdit[i]->text() <<"\n";
}
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSerialPort>
#include <QTimer>
#include <QPushButton>
#include <QCheckBox>
#include "mycombobox.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_closeSerialBt_clicked(); // 关闭串口按钮点击事件
void on_SendBt_clicked(); // 发送按钮点击事件
void on_revReadytoRead(); // 接收准备读取事件
//void on_closeSerialBt_clicked(bool checked);
void on_checkBtimerSend_clicked(bool checked); // 定时发送复选框点击事件
void on_clearRevBt_clicked(); // 清空接收数据按钮点击事件
void on_SaveRevBt_clicked(); // 保存接收数据按钮点击事件
void on_timeReFalsh(); // 时间刷新事件
void on_checkBoRevTime_clicked(bool checked); // 接收时间复选框点击事件
void on_checkBoHEX_clicked(bool checked); // 十六进制显示复选框点击事件
void on_hidemianBt_clicked(bool checked); // 隐藏主界面按钮点击事件
void on_hideHistoryBt_clicked(bool checked); // 隐藏历史记录按钮点击事件
void on_comboBoxEnv(); // ComboBox环境事件
void on_cmdButtonclicked(); // 命令按钮点击事件
void on_checkBox_10_clicked(bool checked); // 复选框_10点击事件
void on_buttonfalsh(); // 按钮刷新事件
void on_resBt_clicked(); // 复位按钮点击事件
void on_loadBt_clicked(); // 载入按钮点击事件
void on_saveBt_clicked(); // 保存按钮点击事件
private:
Ui::Widget *ui;
QSerialPort *serialPort; // 串口对象
QString sendBuftmp; // 发送缓存
QString _datetime; // 日期时间
int sendCntTotal; // 发送计数总数
int revCntTotal; // 接收计数总数
int buttonIndex; // 按钮索引
bool checkSerial; // 串口检测
bool revDateTime; // 接收日期时间
QTimer *timer; // 定时器
QTimer *DateTimeTimer; // 日期时间定时器
QTimer *buttonConTimer; // 按钮连接定时器
QList<QPushButton *> button; // 按钮列表
QList<QLineEdit *> _lineEdit; // 行编辑框列表
QList<QCheckBox *> _checkBox; // 复选框列表
void getSysTime(); // 获取系统时间
};
#endif // WIDGET_H