Qt串口助手

news2024/11/15 17:47:06

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_NAMESPACE

class CSerial : public QMainWindow
{
    Q_OBJECT

public:
    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.coloradmin.cn/o/1271228.html

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

相关文章

neo4j使用之超神之旅

1.查询整个链路中任意一段的关系类型是“department”的链路数据 MATCH path (n)-[r1 *0..7 {relation_type:once2once}]-(m) where id(n)0 and any(x in relationships(path) where type(x)department) return path效果图&#xff1a; 2.查询整个链路中最后一段的关系类型…

代币化:2024年的金融浪潮预示着什么?

自“TradFi”领袖到加密专家&#xff0c;各方预测代币化机会高达数十万亿。虽然已有引人注目的用例&#xff0c;但与未来几年可能在链上转移的大量数字化资产相比&#xff0c;这些仅是冰山一角。 代币化何时会变为洪流&#xff1f;什么阻碍了其发展&#xff1f; 今年10月&…

TA-Lib学习研究笔记——Price Transform (五)

TA-Lib学习研究笔记——Price Transform &#xff08;五&#xff09; 1.AVGPRICE Average Price 函数名&#xff1a;AVGPRICE 名称&#xff1a;平均价格函数 语法&#xff1a; real AVGPRICE(open, high, low, close) df[AVGPRICE] tlb.AVGPRICE(df[open],df[high],df[low…

7-1 哈夫曼树与哈夫曼编码

哈夫曼树与哈夫曼编码 题目描述输入格式输出格式输入样例输出样例 分数 30 作者 伍建全 单位 重庆科技学院 题目描述 哈夫曼树(Huffman Tree)又称最优二叉树&#xff0c;是一种带权路径长度最短的二叉树。所谓树的带权路径长度&#xff0c;就是树中所有的叶结点的权值乘上其到…

应用场景丨社区建筑结构健康监测系统

随着社区的快速发展&#xff0c;社区建筑的结构安全与健康问题日益受到广泛关注。考虑到社区建筑的特点&#xff0c;如人口密集、结构复杂等&#xff0c;建筑结构健康监测系统的应用显得尤为重要。 社区建筑结构健康监测系统的效果 1. 结构安全性提升&#xff1a;通过实时监测…

企业软件的分类有哪些|app小程序定制开发

企业软件的分类有哪些|app小程序定制开发 企业软件是指为了满足企业运营和管理需求而开发的软件系统。根据不同的功能和应用领域&#xff0c;企业软件可以分为以下几个分类&#xff1a; 1. 企业资源计划&#xff08;Enterprise Resource Planning&#xff0c;ERP&#xff09;软…

什么是灯塔工厂?灯塔工厂的作用?

什么是灯塔工厂&#xff1f; "灯塔工厂"概念源于德国的工业4.0战略&#xff0c;又称“工业4.0示范工厂”或“标杆工厂”&#xff0c;代表工业领域顶级的智能制造能力。2018年&#xff0c;由世界经济论坛和麦肯锡共同推出。 灯塔工厂是通过数字化、网络化和智能化手…

国产CPU计算平台选型指南

信创&#xff0c;这两年已经不是什么新鲜词了&#xff0c;随着29号文、79号文的实施落地&#xff0c;信创产品加速从党政走向八大关基行业。 八大行业中&#xff0c;金融、教育、电信、石油等企业步伐更大&#xff0c;很多企业已经从窗口业务、日常办公这类轻量应用场景&#…

网络安全应该怎么学?(0基础小白)

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…

佳易王棋牌室灯控计时计费管理系统软件教程,佳易王棋牌室内零食怎么计费

佳易王棋牌室灯控计时计费管理系统软件教程&#xff0c;佳易王棋牌室内零食怎么计费 一、佳易王棋牌桌球计时计费软件部分功能简介&#xff1a; 1、计时计费功能 &#xff1a;开台时间和所用的时长直观显示&#xff0c;每3秒即可刷新一次时间。 2、销售商品功能 &#xff1a;…

2023年【P气瓶充装】找解析及P气瓶充装复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 P气瓶充装找解析参考答案及P气瓶充装考试试题解析是安全生产模拟考试一点通题库老师及P气瓶充装操作证已考过的学员汇总&#xff0c;相对有效帮助P气瓶充装复审模拟考试学员顺利通过考试。 1、【多选题】CNG双燃料汽车…

软件测试面试经历和上岸后工作分享

哈喽、因为最近很多小伙伴私信问我的比较多&#xff0c;今天就专门说下&#xff0c;之前为甚转行和怎么选机构就不和大家细说了&#xff0c;之前的文章和视频也都有提到过。 今天主要是和大家说下自己转行后的感受和面试时候的一些经历&#xff0c;希望能给正在转行&#xff0c…

轻松搭建Nextcloud私有云盘并实现公网访问本地资源

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…

重载、重写、重定义的辨析

C重载、重写、重定义 重载、重写、重定义对比一、重载&#xff08;overload&#xff09;二、重写 / 覆盖&#xff08;override&#xff09;三、重定义 / 隐藏&#xff08;redefining&#xff09; * 为什么在虚函数中不能使用 static 关键字&#xff1f;动态绑定&#xff08;Dyn…

拼图游戏制作

2.创建用户界面 package domain; /** * ClassName: User * Author: Kox * Data: 2023/2/2 * Sketch: */ public class User { private String username; private String password; public User() { } public User(String username, String p…

Vue中的组件和插件

一、组件 组件是Vue中最核心的概念之一&#xff0c;它可以把一个页面拆分成多个独立的、可复用的部分。组件通常包含了自己的模板、样式和逻辑&#xff0c;用于封装一个特定的功能或界面。Vue的组件有单文件组件和普通组件两种类型&#xff0c;可以通过Vue.component或Vue.ext…

配电房综合监控系统

配电房综合监控系统是一种集成了实时监控、数据采集、远程控制等多功能的系统&#xff0c;用于对配电房进行全方位的监测和管理。 力安科技配电室综合监控系统依托电易云-智慧电力物联网&#xff0c;实现配电室环境监测、有害气体监测、安防监控、采暖通风、门禁、灯光、风机、…

挂耳式蓝牙耳机性价比排行榜,看完这篇知道如何选

随着科技的发展&#xff0c;蓝牙耳机早已成为我们生活的一部分&#xff0c;但市场上的选择实在太多&#xff0c;让人眼花缭乱&#xff0c;一不小心就会选错踩坑&#xff0c;消费者往往会陷入选择困境&#xff0c;为了帮助大家更好地了解挂耳式蓝牙耳机的性价比表现&#xff0c;…

云专线:安全、高速的云访问专属连接通道

随着云计算技术的急速发展&#xff0c;各行业IT体系已步入“云”时代&#xff0c;完全本地化的部署方案鲜少为企业所采用。现今企业已或多或少地将业务系统部署在公有云、私有云或者混合云上&#xff0c;以驱动企业流程重构和业务创新&#xff0c;提升管理效率&#xff0c;实现…

2023年,再不跳槽,就晚了···

以 BAT 为代表的互联网大厂&#xff0c;无论是薪资待遇、还是平台和福利&#xff0c;都一直是求职者眼中的香饽饽&#xff0c;“大厂经历” 在国内就业环境中无异于一块金子招牌。 很多想进大厂的朋友都在问&#xff0c;该怎么备战 2023年&#xff1f; 为此我专门打造了一份《…