Qt串口助手

QT5 串口助手

​ 由于C++课程作业的需要,用QT5写了个简陋的串口助手。只作为一个简单的案例以供参考,默认读者具有C++基础和了解简单的Qt操作。

功能展示

【用QT写了个简单的串口助手】

准备工作

Qt自带有<QSerialPort> 库, 可以方便地配置和调用计算机的串口资源

将QSerialPort编译到项目中

<QSerialPort> 默认是不能再Qt项目中使用的,需要在.pro文件中 添加一行QT += serialport , 才能使用<QSerialPort>

这点在文档中也有写出

在这里插入图片描述
在.pro文件中添加QT += serialport

在这里插入图片描述

界面设计

在这里插入图片描述

  • 左上文本框显示接收和发送到的信息
  • 右上区域选择串口和对串口进行配置
  • 右边中间打开串口和保存或者清楚接收区的信息,十六进制显示将会使下次接收到的信息以十六进制显示
  • 下边是发送文本区
  • 最下边是定时发送功能,开启后会一直发送信息,能够设置发送的周期

界面初始化

将界面信息和所有配置项加载, 需要代码实现加载的主要有Combox中所有文本, 以及串口的初始配置

加载串口名

/**
* @brief 扫描所有可用的串口,并将其添加到Box中
*/
void CSerial::Scan_Serial(void)
{ports = QSerialPortInfo::availablePorts();foreach(QSerialPortInfo info, ports){qDebug() << "串口名称" << info.portName();qDebug() << "设备描述:" << info.description();ui->comboBox->addItem(QString(info.portName() + info.description()));}
}
  • QSerialPortInfo类,实现了一些静态方法用于获取串口的信息

  • 通过QSerialPortInfo::availablePorts()获取所有可用的串口(以及他们的信息)

  • 然后遍历ports, 将串口名称添加到comboBox中(请忽略qDebug()调试代码)

加载可用波特率、停止位、数据位和检验位配置

与加载串口名类似, 只不过加载的内容是QSerialPort类中定义好的配置, 在注释中也列举出了部分支持的配置(更多支持的配置请查询QSerialPort的文档)

/**
* @brief 将支持的波特率添加到Box中
* 1200 2400 4800 9600 19200 38400 57600 115200
*/
void CSerial::Play_BoudRate(void)
{ui->BoudRateBox->addItem("1200");ui->BoudRateBox->addItem("2400");ui->BoudRateBox->addItem("4800");ui->BoudRateBox->addItem("9600");ui->BoudRateBox->addItem("19200");ui->BoudRateBox->addItem("38400");ui->BoudRateBox->addItem("57600");ui->BoudRateBox->addItem("115200");
}
/**
* @brief 将停止位添加到Box中
* OneStop 1      TowStop 2     OneAndHalfStop 3
*/
void CSerial::Play_StopBits(void)
{ui->StopBitsBox->addItem("1");ui->StopBitsBox->addItem("2");ui->StopBitsBox->addItem("1.5");
}
/**
* @brief 将数据位添加到Box中
* Data5 5     Data6 6       Data7 7       Data8  8
*/
void CSerial::Play_DataBits(void)
{ui->DataBitsBox->addItem("5");ui->DataBitsBox->addItem("6");ui->DataBitsBox->addItem("7");ui->DataBitsBox->addItem("8");
}
/**
* @brief 将校验添加到Box中
* NoParity 0        EvenParity 2         OddParity   3
*/
void CSerial::Play_Parity(void)
{ui->ParityBox->addItem("No Parity");ui->ParityBox->addItem("Even");ui->ParityBox->addItem("Odd");
}

将文本加载完后, 需要根据文本内容对端口进行初始化, 不然每次使用都需要用户一个个配置(防止用户对未配置的端口进行操作)

/**
* @brief 根据Box中的内容初始化端口
*/
void CSerial::Port_Init(void)
{int index;OCFlag = 0;HexFlag = 0;TimerFlag = 0;currentPort.setPortName(ports[ui->comboBox->currentIndex()].portName());currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());currentPort.setStopBits(QSerialPort::StopBits(ui->StopBitsBox->currentIndex()+1));currentPort.setDataBits(QSerialPort::DataBits(ui->DataBitsBox->currentIndex()+5));index = ui->ParityBox->currentIndex();index = index == 0 ? index : (index+1);currentPort.setParity(QSerialPort::Parity(index));currentPort.setFlowControl(QSerialPort::NoFlowControl);qDebug() << currentPort.portName() << Qt::endl;qDebug() << currentPort.baudRate() << Qt::endl;qDebug() << currentPort.stopBits() << Qt::endl;qDebug() << currentPort.dataBits() << Qt::endl;qDebug() << currentPort.parity() << Qt::endl;
}

串口配置

初始化完界面后,实现串口配置的功能

串口配置,有选择串口号配置波特率配置停止位, 配置数据位配置校验位

选择串口号

// 选择端口connect(ui->comboBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << ui->comboBox->currentText() << Qt::endl;currentPort.setPortName(ports[index].portName());qDebug() << currentPort.portName();});
  • connect()在Qt中将信号与槽进行连接, QOverload<int>::of(&QComboBox::activated) , 这里的槽函数是个匿名函数
// 槽函数
[=](int index){qDebug() << index << ui->comboBox->currentText() << Qt::endl;currentPort.setPortName(ports[index].portName());qDebug() << currentPort.portName();}

连接好信号(Signal)与槽(Slots)后, 简单讲解下实现的机制

  • 在用户对ComboBox进行操作时或者ComboBox本身的属性发生改变时, 如点击或者ComboBox的现在的文本内容发生改变, ComboBox对象会产生一个信号(这里是activated, 鼠标点击内容后触发)
  • 这是文档中ComboBox对象能够产生的信号

在这里插入图片描述

  • 点进去后会有说明和例子

在这里插入图片描述

  • connect() 连接好信号与槽后, 会在收到信号后直接执行槽函数, 槽函数可以是普通函数或者匿名函数, 这里主要是调用了匿名函数,匿名函数内再调用currentPort.setPortName(ports[index].portName())将现在端口的名字设置为鼠标点中的内容

  • 学过单片机的,可以简单将信号与槽理解为中断信号中断服务函数

  • 除了一些C++的基本语法Qt的重点就是信号与槽

配置波特率

与上面类似,直接放代码

// 选择波特率connect(ui->BoudRateBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());qDebug() << currentPort.baudRate() << Qt::endl;});

配置停止位

 // 选择停止位connect(ui->StopBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setStopBits(QSerialPort::StopBits(index+1));qDebug() << currentPort.stopBits() << Qt::endl;});

配置数据位

// 选择数据位connect(ui->DataBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setDataBits(QSerialPort::DataBits(index+5));qDebug() << currentPort.dataBits() << Qt::endl;});

配置校验位

// 选择校验connect(ui->ParityBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;index = index == 0 ? index : (index+1);currentPort.setParity(QSerialPort::Parity(index));qDebug() << currentPort.parity() << Qt::endl;});

基础功能实现

打开和关闭串口

配置完串口后,就可以打开和关闭串口了

OCFlag==0时, 调用open打开串口, 并将按钮的Icon切换, 若打开串口失败则调用QMessageBox::warning弹出提示框;当OCFlag==1时,调用close()关闭串口, 并将按钮的Icon切换.

/**
* @brief 打开和关闭串口
*/
void CSerial::OpenAndClose_Port(void)
{if(OCFlag == 0){if(currentPort.open(QIODevice::ReadWrite)){qDebug() << "串口已打开" << Qt::endl;ui->OpenSerial->setIcon(QIcon(":/Image/off.png"));ui->OpenSerial->setText("关闭串口");OCFlag = 1;}else{QMessageBox::warning(this, "Error", "Cannot Open Port:" + currentPort.errorString());}}else{currentPort.close();ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));qDebug() << "串口已关闭" << Qt::endl;ui->OpenSerial->setText("打开串口");OCFlag = 0;}
}

槽函数写完,将其与一个按钮连接

// 打开关闭串口connect(ui->OpenSerial, &QPushButton::clicked, this, &CSerial::OpenAndClose_Port);
  • 第一个参数是信号发出的对象,第二个参数是需要连接的信号,第三个参数是调用槽函数的对象,第四个参数是槽函数

  • OpenSerial是一个QpushButton类的对象,俗称"按钮", clicked为鼠标点击信号, this表示对象为该对象(一个Cserial类的对象), 槽函数是OpenAndClose_Port(打开或者关闭串口)

  • 执行完上面这行代码后, OpenSerial(按钮)被按下后发出一个信号,被监测到后调用,检测器通知this,然后this再调用OpenAndClose_Port 执行槽函数

  • 而若用匿名函数,则不需要槽函数的发起对象,因为在一个匿名函数中,可以调用不同对象的不同的函数, 所以指定槽函数发起对象失去意义

发送数据

打开串口后,就能够发送数据了

OCFlag==1时(串口打开时),调用write()发送数据;若bytesWritten==-1,则数据发送失败,用QMessageBox打印错误信息, 并将发送的信息显示在接受区

OCFlag==0时(串口关闭时),调用QMessageBox提示用户打开串口

/**
* @brief 发送数据
*/
void CSerial::Send_Data(void)
{if(OCFlag){QString SendString = ui->SendText->toPlainText();qDebug() << SendString << Qt::endl;QByteArray dataToSend = SendString.toLocal8Bit();// 发送数据qint64 bytesWritten =currentPort.write(dataToSend);if(bytesWritten == -1){QMessageBox::warning(this, "Error", "数据发送失败!");}else{QString SendedData = QString::fromStdString(dataToSend.toStdString());ui->textEdit->append("Tx:"+SendedData);}}else{QMessageBox::warning(this, "Error", "请先打开串口!");}
}

槽函数写完,将其与一个按钮连接

// 发送数据connect(ui->SendData, &QPushButton::clicked, this, &CSerial::Send_Data);

接收数据

打开串口后,也可以接收到其他装置发送来的信息

接收到数据后,将数据显示到串口中, 若HexFlag==1,则以十六进制显示

/**
* @brief 接收数据
*/
void CSerial::Receive_Data(void)
{if(OCFlag){QByteArray data = currentPort.readAll();QString text = QString::fromStdString(data.toStdString());qDebug() << text << Qt::endl;if(HexFlag == 0){ui->textEdit->append("Rx:" + text);}else{ui->textEdit->append("Rx:" + data.toHex());}}
}

槽函数写完后,将其与信号连接, 该信号由串口产生; readRead在串口读取完数据后产生

// 接收数据connect(&currentPort, &QSerialPort::readyRead, this, &CSerial::Receive_Data);

保存接收区内容

收到数据后,可以将数据以.txt文件的形式保存到指定路径

通过QFileDialog::getSaveFileName()获取保存路径, 它的最后一个参数将保存文件类型限制为.txt

通过QFile对象打开文件,若打开失败则通过QMessageBox弹出错误信息窗口

通过textEdit对象的toPlainText()方法获取接收区数据并转换为纯文本

通过QFile对象write()方法将文本以utf-8的格式写入文件中

通过close()方法关闭文件,并弹出保存成功的提示窗口

/**
* @brief 保存接受区内容
*/
void CSerial::Save_RxText(void)
{// 获取文件保存路径QString fileName = QFileDialog::getSaveFileName(this, "Save", "/text", tr("Text (*.txt)"));qDebug() << fileName << Qt::endl;QFile file(fileName);if(!file.open(QIODevice::WriteOnly | QFile::Text)){QMessageBox::warning(this, "Warning", "Cannot Save file: " + file.errorString());return;}setWindowTitle(fileName);QTextStream out(&file);QString text = ui->textEdit->toPlainText();file.write(text.toUtf8());file.close();QMessageBox::information(this, "提示", "保存成功!");
}

清除接收区和发送区内容

清除内容,其实没必要封装起来

/**
* @brief 清除发送区内容
*/
void CSerial::Clear_SendText(void)
{ui->SendText->clear();
}
/**
* @brief 清除接收区内容
*/
void CSerial::Clear_RxText(void)
{ui->textEdit->clear();
}
// 清除发送区文本内容connect(ui->ClearData, &QPushButton::clicked, this, &CSerial::Clear_SendText);
// 清除接收区文本内容connect(ui->Clear_Rx, &QPushButton::clicked, this, &CSerial::Clear_RxText);

改变十六进制标志

改变十六进制标志,封装起来思路清晰一点吧

/**
* @brief 改变十六进制标志
*/
void CSerial::Change_HexFlag(void)
{qDebug() << ui->checkBox->checkState() << Qt::endl;HexFlag = ui->checkBox->checkState();qDebug() << HexFlag << Qt::endl;
}
// 改变十六进制接收标志connect(ui->checkBox, &QCheckBox::stateChanged, this, &CSerial::Change_HexFlag);

定时发送

  • 定时发送相关的函数有三个

  • 当定时发送的按钮被触动后,会触发槽函数,在槽函数中先检测标志位,再设置定时器发送周期,最后根据定时发送标志位和串口关闭标志位判断是开启定时器还是关闭定时器

  • 定时器槽函数中调用Send_Data()发送数据

  • 下面简单介绍下QTimer定时器,主要就是用来定时,start(Time)开启定时器并设置周期,单位是毫秒,定时时间到发出timeout信号,用connect将信号与槽连接后,达到每过Time触发一次槽函数

/**
* @brief 改变定时发送标志
*/
void CSerial::Change_TimerFlag(void)
{qDebug() << ui->TimerCheck->checkState() << Qt::endl;TimerFlag = ui->TimerCheck->checkState();qDebug() << TimerFlag << Qt::endl;
}/**
* @brief 设置定时器周期
*/
void CSerial::Set_TimerTime(void)
{bool Flag;int temp;temp = ui->TimerEdit->text().toInt(&Flag);if(Flag){Timer_Time = temp;qDebug() << "T = " << Timer_Time << " ms" << Qt::endl;}else{QMessageBox::warning(this, "Error", "请输入合法的数字");}
}/**
* @brief 定时发送
*/
void CSerial::Send_InTime(void)
{this->Change_TimerFlag();  // 改变标志位this->Set_TimerTime();   // 设置发送周期if(TimerFlag && OCFlag){Timer->start(Timer_Time);  // 启动定时器}else{Timer->stop();   // 关闭定时器if(TimerFlag) QMessageBox::warning(this, "Error", "请先打开串口!");}
}
// 定时发送数据connect(ui->TimerCheck, &QCheckBox::stateChanged, this, &CSerial::Send_InTime);Timer = new QTimer(this);connect(Timer, &QTimer::timeout, [=](){this->Send_Data();});

整体程序

cserial.h

#ifndef CSERIAL_H
#define CSERIAL_H#include <QMainWindow>
#include <QSerialPort>
#include <QList>
#include <QSerialPortInfo>
#include <QDebug>
#include <QComboBox>
#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class CSerial; }
QT_END_NAMESPACEclass CSerial : public QMainWindow
{Q_OBJECTpublic:CSerial(QWidget *parent = nullptr);~CSerial();
private slots:   // 定义的槽函数void Scan_Serial(void);void Play_BoudRate(void);void Play_StopBits(void);void Play_DataBits(void);void Play_Parity(void);void Port_Init(void);void OpenAndClose_Port(void);void Send_Data(void);void Receive_Data(void);void Clear_SendText(void);void Save_RxText(void);void Clear_RxText(void);void Change_HexFlag(void);void Change_TimerFlag(void);void Set_TimerTime(void);void Send_InTime(void);
private:int OCFlag;  // 打开关闭标志位int HexFlag;  // 十六进制显示标志位int TimerFlag;  // 定时发送标志位int Timer_Time;  // 定时发送周期QTimer * Timer;  // 定时器Ui::CSerial *ui;QSerialPort currentPort;  // 当前端口QList<QSerialPortInfo> ports;  // 所有可以端口信息
};
#endif // CSERIAL_H

cserial.cpp

#include "cserial.h"
#include "ui_cserial.h"CSerial::CSerial(QWidget *parent): QMainWindow(parent), ui(new Ui::CSerial)
{ui->setupUi(this);ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));ui->Save_Rx->setIcon(QIcon(":/Image/save.png"));ui->ClearData->setIcon(QIcon(":/Image/clear.png"));ui->Clear_Rx->setIcon(QIcon(":/Image/clear.png"));ui->SendData->setIcon(QIcon(":/Image/send.png"));this->setWindowIcon(QIcon(":/Image/serial.png"));this->Scan_Serial();this->Play_BoudRate();this->Play_StopBits();this->Play_DataBits();this->Play_Parity();this->Port_Init();// 选择端口connect(ui->comboBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << ui->comboBox->currentText() << Qt::endl;currentPort.setPortName(ports[index].portName());qDebug() << currentPort.portName();});// 选择波特率connect(ui->BoudRateBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());qDebug() << currentPort.baudRate() << Qt::endl;});// 选择停止位connect(ui->StopBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setStopBits(QSerialPort::StopBits(index+1));qDebug() << currentPort.stopBits() << Qt::endl;});// 选择数据位connect(ui->DataBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;currentPort.setDataBits(QSerialPort::DataBits(index+5));qDebug() << currentPort.dataBits() << Qt::endl;});// 选择校验connect(ui->ParityBox, QOverload<int>::of(&QComboBox::activated), [=](int index){qDebug() << index << Qt::endl;index = index == 0 ? index : (index+1);currentPort.setParity(QSerialPort::Parity(index));qDebug() << currentPort.parity() << Qt::endl;});// 打开关闭串口connect(ui->OpenSerial, &QPushButton::clicked, this, &CSerial::OpenAndClose_Port);// 发送数据connect(ui->SendData, &QPushButton::clicked, this, &CSerial::Send_Data);// 接收数据connect(&currentPort, &QSerialPort::readyRead, this, &CSerial::Receive_Data);// 清除发送区文本内容connect(ui->ClearData, &QPushButton::clicked, this, &CSerial::Clear_SendText);// 保存接受区文本内容connect(ui->Save_Rx, &QPushButton::clicked, this, &CSerial::Save_RxText);// 清除接收区文本内容connect(ui->Clear_Rx, &QPushButton::clicked, this, &CSerial::Clear_RxText);// 改变十六进制接收标志connect(ui->checkBox, &QCheckBox::stateChanged, this, &CSerial::Change_HexFlag);// 定时发送数据connect(ui->TimerCheck, &QCheckBox::stateChanged, this, &CSerial::Send_InTime);Timer = new QTimer(this);connect(Timer, &QTimer::timeout, [=](){this->Send_Data();});
}CSerial::~CSerial()
{currentPort.close();  // 关闭串口delete ui;
}/**
* @brief 扫描所有可用的串口,并将其添加到Box中
*/
void CSerial::Scan_Serial(void)
{ports = QSerialPortInfo::availablePorts();foreach(QSerialPortInfo info, ports){qDebug() << "串口名称" << info.portName();qDebug() << "设备ID:" << info.description();ui->comboBox->addItem(QString(info.portName() + info.description()));}
}/**
* @brief 将支持的波特率添加到Box中
* 1200 2400 4800 9600 19200 38400 57600 115200
*/
void CSerial::Play_BoudRate(void)
{ui->BoudRateBox->addItem("1200");ui->BoudRateBox->addItem("2400");ui->BoudRateBox->addItem("4800");ui->BoudRateBox->addItem("9600");ui->BoudRateBox->addItem("19200");ui->BoudRateBox->addItem("38400");ui->BoudRateBox->addItem("57600");ui->BoudRateBox->addItem("115200");
}/**
* @brief 将停止位添加到Box中
* OneStop 1      TowStop 2     OneAndHalfStop 3
*/
void CSerial::Play_StopBits(void)
{ui->StopBitsBox->addItem("1");ui->StopBitsBox->addItem("2");ui->StopBitsBox->addItem("1.5");
}/**
* @brief 将数据位添加到Box中
* Data5 5     Data6 6       Data7 7       Data8  8
*/
void CSerial::Play_DataBits(void)
{ui->DataBitsBox->addItem("5");ui->DataBitsBox->addItem("6");ui->DataBitsBox->addItem("7");ui->DataBitsBox->addItem("8");
}/**
* @brief 将校验添加到Box中
* NoParity 0        EvenParity 2         OddParity   3
*/
void CSerial::Play_Parity(void)
{ui->ParityBox->addItem("No Parity");ui->ParityBox->addItem("Even");ui->ParityBox->addItem("Odd");
}/**
* @brief 根据Box中的内容初始化端口
*/
void CSerial::Port_Init(void)
{int index;OCFlag = 0;HexFlag = 0;TimerFlag = 0;currentPort.setPortName(ports[ui->comboBox->currentIndex()].portName());currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());currentPort.setStopBits(QSerialPort::StopBits(ui->StopBitsBox->currentIndex()+1));currentPort.setDataBits(QSerialPort::DataBits(ui->DataBitsBox->currentIndex()+5));index = ui->ParityBox->currentIndex();index = index == 0 ? index : (index+1);currentPort.setParity(QSerialPort::Parity(index));currentPort.setFlowControl(QSerialPort::NoFlowControl);qDebug() << currentPort.portName() << Qt::endl;qDebug() << currentPort.baudRate() << Qt::endl;qDebug() << currentPort.stopBits() << Qt::endl;qDebug() << currentPort.dataBits() << Qt::endl;qDebug() << currentPort.parity() << Qt::endl;
}/**
* @brief 打开和关闭串口
*/
void CSerial::OpenAndClose_Port(void)
{if(OCFlag == 0){if(currentPort.open(QIODevice::ReadWrite)){qDebug() << "串口已打开" << Qt::endl;ui->OpenSerial->setIcon(QIcon(":/Image/off.png"));ui->OpenSerial->setText("关闭串口");OCFlag = 1;}else{QMessageBox::warning(this, "Error", "Cannot Open Port:" + currentPort.errorString());}}else{currentPort.close();ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));qDebug() << "串口已关闭" << Qt::endl;ui->OpenSerial->setText("打开串口");OCFlag = 0;}
}/**
* @brief 发送数据
*/
void CSerial::Send_Data(void)
{if(OCFlag){QString SendString = ui->SendText->toPlainText();qDebug() << SendString << Qt::endl;QByteArray dataToSend = SendString.toLocal8Bit();// 发送数据qint64 bytesWritten =currentPort.write(dataToSend);if(bytesWritten == -1){QMessageBox::warning(this, "Error", "数据发送失败!");}else{QString SendedData = QString::fromStdString(dataToSend.toStdString());ui->textEdit->append("Tx:"+SendedData);}}else{QMessageBox::warning(this, "Error", "请先打开串口!");}
}/**
* @brief 接收数据
*/
void CSerial::Receive_Data(void)
{if(OCFlag){QByteArray data = currentPort.readAll();QString text = QString::fromStdString(data.toStdString());qDebug() << text << Qt::endl;if(HexFlag == 0){ui->textEdit->append("Rx:" + text);}else{ui->textEdit->append("Rx:" + data.toHex());}}
}/**
* @brief 清除发送区内容
*/
void CSerial::Clear_SendText(void)
{ui->SendText->clear();
}/**
* @brief 保存接受区内容
*/
void CSerial::Save_RxText(void)
{// 获取文件保存路径QString fileName = QFileDialog::getSaveFileName(this, "Save", "/text", tr("Text (*.txt)"));qDebug() << fileName << Qt::endl;QFile file(fileName);if(!file.open(QIODevice::WriteOnly | QFile::Text)){QMessageBox::warning(this, "Warning", "Cannot Save file: " + file.errorString());return;}setWindowTitle(fileName);QTextStream out(&file);QString text = ui->textEdit->toPlainText();file.write(text.toUtf8());file.close();QMessageBox::information(this, "提示", "保存成功!");
}/**
* @brief 清除接收区内容
*/
void CSerial::Clear_RxText(void)
{ui->textEdit->clear();
}/**
* @brief 改变十六进制标志
*/
void CSerial::Change_HexFlag(void)
{qDebug() << ui->checkBox->checkState() << Qt::endl;HexFlag = ui->checkBox->checkState();qDebug() << HexFlag << Qt::endl;
}/**
* @brief 改变定时发送标志
*/
void CSerial::Change_TimerFlag(void)
{qDebug() << ui->TimerCheck->checkState() << Qt::endl;TimerFlag = ui->TimerCheck->checkState();qDebug() << TimerFlag << Qt::endl;
}/**
* @brief 设置定时器周期
*/
void CSerial::Set_TimerTime(void)
{bool Flag;int temp;temp = ui->TimerEdit->text().toInt(&Flag);if(Flag){Timer_Time = temp;qDebug() << "T = " << Timer_Time << " ms" << Qt::endl;}else{QMessageBox::warning(this, "Error", "请输入合法的数字");}
}/**
* @brief 定时发送
*/
void CSerial::Send_InTime(void)
{this->Change_TimerFlag();this->Set_TimerTime();if(TimerFlag && OCFlag){Timer->start(Timer_Time);}else{Timer->stop();if(TimerFlag) QMessageBox::warning(this, "Error", "请先打开串口!");}
}

main.cpp

#include "cserial.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);CSerial w;w.show();return a.exec();
}

源码提取

链接:https://pan.baidu.com/s/1a5b75bhMe2OeSB2egvImEw?pwd=nlxl
提取码:nlxl
–来自百度网盘超级会员V4的分享

参考资料

Qt Assistant(Qt助手)官方的文档还是好用的
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/185951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lv11 嵌入式开发 轮询与中断13

1 CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需要一段时间后再次询问&#xff0c;周而复始 中断 CPU执行程序时若硬件需要其服务&#xff0c;对应的硬件给CPU发送中断信号&#xff0c;CPU接收到中…

python爬取robomaster论坛数据,作为后端数据

一. 内容简介 python爬取robomaster论坛数据&#xff0c;作为后端数据 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 三.主要流程 3.1 接口分析 # 接口分析 # 全部数据 # https://bbs.robomaster.com/forum.php?modforumdisplay&fid63 2…

Ubuntu systemd-analyze命令(系统启动性能分析工具:分析系统启动时间,找出可能导致启动缓慢的原因)

文章目录 Ubuntu systemd-analyze命令剖析目录简介systemd与systemd-analyze工作原理 安装和使用命令参数详解用例与示例显示启动时间&#xff08;systemd-analyze time&#xff09;列出启动过程中各个服务的启动时间&#xff08;systemd-analyze blame&#xff09;显示系统启动…

使用opencv实现图像滤波

1 图像滤波介绍 滤波是信号和图像处理中的基本任务之一&#xff0c;其旨在有选择地提取图像的某些特征&#xff0c;可以用于在给定应用程序的上下文中传达重要信息&#xff0c;例如&#xff0c;去除图像中的噪声、提取所需的视觉特征、图像重采样等。 1.1 图像滤波理论 图像…

厦门城市建设与建筑结构健康监测系统的重要性与作用

厦门&#xff0c;这座美丽的海滨城市&#xff0c;随着经济的快速发展和城市化的不断推进&#xff0c;城市建设已成为人们关注的焦点。其中&#xff0c;建筑结构健康监测系统对于保障城市建设和建筑的安全具有举足轻重的地位。 WITBEE万宾针对建筑的动态平衡&#xff0c;温湿度&…

shell编程系列(7)-使用wc进行文本统计

文章目录 前言wc命令的使用wc命令的参数说明&#xff1a;统计字数统计行数打印文本行号 结语 前言 统计功能也是我们在shell编程中经常碰到的一个需求&#xff0c;wc命令可以适用于任何需要统计的数据&#xff0c;不只是统计文本&#xff0c;配合ls命令我们可以统计文件的个数…

java gc垃圾回收机制(垃圾收集 Garbage Collection)

一文带你深入JAVA GC&#xff08;垃圾回收机制&#xff09;面试讲解&#xff01;&#xff01;&#xff01; - 知乎 gc常用算法 gc常用算法有&#xff1a;标记-清除算法&#xff0c;标记-压缩算法&#xff0c;复制算法&#xff0c;分代收集算法。 复制算法&#xff08;Java中…

java--子类构造器的特点

1.子类构造器的特点 子类的全部构造器&#xff0c;都会先调用父类的构造器&#xff0c;再执行自己。 2.子类构造器是如何实现调用父类构造器的 ①默认情况下&#xff0c;子类全部构造器的第一行代码都是super()(写不写都有)&#xff0c;它会调用父类的无参数构造器。 ②如果…

【攻防世界-misc】来自银河的信号

1.下载并打开文件&#xff0c;是个音频软件 2.由于打开音频出现的声音类似于无线波&#xff0c;因此需要用RX-SSTV工具打开&#xff0c; RX-SSTV代表“接收图像慢扫描电视”的意思。慢扫描电视是一种通过无线电进行图像传输的技术&#xff0c;通常用于业余无线电领域。RX-SST…

分治法之归并排序

思路: 将待排序数组分成两个子数组&#xff0c;计算中间位置mid。对左半部分进行递归排序&#xff0c;得到一个有序的子数组。对右半部分进行递归排序&#xff0c;得到另一个有序的子数组。合并两个有序的子数组&#xff0c;得到一个完整的有序数组。 示例图: 代码: #include&…

JAVA 算法面试总结

1、二分查找 二分查找又叫折半查找&#xff0c;要求待查找的序列有序。每次取中间位置的值与待查关键字比较&#xff0c;如果中间位置 的值比待查关键字大&#xff0c;则在前半部分循环这个查找的过程&#xff0c;如果中间位置的值比待查关键字小&#xff0c; 则在后半部分循环…

【ONE·Linux || 网络基础(四)】

总言 主要内容&#xff1a;传输层UDP、TCP协议基本介绍。UDP报文格式、TCP报文格式、三次握手四次挥手、TCP可靠性策略说明。 文章目录 总言8、UDP协议&#xff08;传输层一&#xff09;8.1、传输层预备知识8.1.1、端口号8.1.2、一些指令&#xff08;netstat、pidof、xargs&am…

热烈欢迎省工信厅毛郑建处长莅临公司调研指导工作

2023年11月28日&#xff0c;河南省工信厅信息化和软件服务业处毛郑建处长莅临郑州埃文计算机科技有限公司&#xff08;以下简称“埃文科技”&#xff09;调研考察工作。河南省工业信息安全产业发展联盟理事长任传军陪同调研。 首先&#xff0c;埃文科技董事长王永向毛处长介绍埃…

优维全新低碳产品亮相SBE23 Asia-Pacific绿色建筑促进碳中和论坛

2023年11月23日—24日&#xff0c;由深圳市人民政府主办&#xff0c;深圳市住房和建设局、深圳市发展与改革委员会、深圳市龙岗区人民政府承办&#xff0c;深圳市绿色建筑协会作为执行单位的“2023年可持续建筑环境亚太地区会议&#xff08;SBE23 Asia-Pacific&#xff09;”在…

Ubuntu Server 20.04.6安装Anaconda3

下载安装包 去下面的网页找到自己想要安装的对应版本的链接&#xff1a; https://repo.anaconda.com/archive/ 我安装的版本链接如下&#xff1a; https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh 复制这个链接后使用如下命令下载&#xff1a; wget …

一篇文章带你掌握MongoDB

文章目录 1. 前言2. MongoDB简介3. MongoDB与关系型数据库的对比4. MongoDB的安装5. Compass的使用6. MongoDB的常用语句7. 总结 1. 前言 本文旨在帮助大家快速了解MongoDB,快速了解和掌握MongoDB的干货内容. 2. MongoDB简介 MongoDB是一种NoSQL数据库&#xff0c;采用了文档…

arthas使用

官方文档 Github: https://github.com/alibaba/arthas 文档: https://arthas.aliyun.com/doc/ Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断…

C语言从入门到实战——常用字符函数和字符串函数的了解和模拟实现

常用字符函数和字符串函数的了解和模拟实现 前言1. 字符分类函数2. 字符转换函数3. strlen的使用和模拟实现4. strcpy的使用和模拟实现5. strcat的使用和模拟实现6. strcmp的使用和模拟实现7. strncpy函数的使用8. strncat函数的使用9. strncmp函数的使用10. strstr的使用和模拟…

MatchPyramid实现文本匹配

引言 今天利用MatchPyramid实现文本匹配。 原论文解析→点此←。 MatchPyramid 核心思想是计算两段文本间的匹配矩阵&#xff0c;把它当成一个图形利用多层卷积网络提取不同层级的交互模式。 匹配矩阵是通过计算两段输入文本基本单元(比如字或词)之间相似度得到的&#xf…

43.0BaseDao抽取dao公共父类

43.1. 回顾 1. 把数据库表中查询的结果封装到一个实体类中。 命名规则:类名和表名一致 类中属性和表的字段对应。 表中的一条记录对应实体的一个对象 多条记录→集合 43.2. 正文 目录 43.1. 回顾 43.2. 正文 43.3. 抽取dao公共父类。 43.4. 引入数据源 43.3. 抽取dao公共…