资讯详情

【Qt系列】绘制实时曲线展示超声波传感器的数据【串口助手定制版】

一、显示效果

在这里插入图片描述

二、绪论

1.教程背景

由于该项目涉及超声波传感器,如果您想制作一个上位机来显示超声波传感器读取的距离信息,许多在线教程都是关于普通串口助手显示文本数据的,因此串口读取和曲线显示在一起。串口助手删除了许多功能,只保留了读取功能,没有发送功能(不使用),因此实现相对简单。

2.工程架构

chart源文件主要用于设置图标,serial源文件主要用于串口设置。

3.serial头文件

#ifndef SERIAL_H #define SERIAL_H  #include <QMainWindow> #include "ui_serial.h" #include <QSerialPort> #include <QSerialPortInfo> #include <QString> #include <QDateTime> #include <QTimer> #include <qmath.h> #include <QDebug> #include <QChartView> #include <QSplineSeries> #include <QValueAxis> #include <QtCharts> using namespace QtCharts;   QT_BEGIN_NAMESPACE namespace Ui { 
         class serial; } QT_END_NAMESPACE  class serial : public QMainWindow { 
             Q_OBJECT  public:     serial(QWidget *parent = nullptr);
    ~serial();

private slots:  //自定义槽函数
    //检测通讯端口槽函数
    void btn_serial_check(bool);
    //打开选择端口槽函数
    void btn_open_port(bool);
    //关闭选择端口槽函数
    void btn_close_port(bool);
    //接收数据槽函数
    void receive_data(void);
    //清空接收槽函数
    void btn_clear_rev(bool);
    //更新曲线
    void updatechart();

private:
    Ui::serial *ui;
    void ChartInit();  //图表初始化函数
    void system_init();//用户系统初始化
    QSerialPort global_port;//串口全局类声明
    
    QChart *m_chart ;
    QChartView  *m_chartView;//画布
    QValueAxis *axisX;       //坐标轴
    QValueAxis *axisY;
    QSplineSeries *series ; //平滑曲线的集合
    QTimer *m_timer;

};
#endif // SERIAL_H

三、UI界面

三、曲线的产生

1.Qt Charts组件的使用

在serial.pro加上charts,按Ctril+S进行保存,可能会报错。解决方案:解决思路链接。 在确认.pro文件中添加了相应语句并保存之后,需要在程序头文件中添加

#include <QtCharts>
using namespace QtCharts;

2.chart源文件

#include "serial.h"

void serial::ChartInit()
{ 
        
    m_chart = new QChart();
    m_chartView = new QChartView(m_chart,this);//画布
    m_chart = m_chartView->chart();//画笔
    m_chartView->setRubberBand(QChartView::NoRubberBand);  //矩形缩放
    m_chartView->setRenderHint(QPainter::Antialiasing); //设置抗锯齿

    //设置x轴
    axisX = new QValueAxis;
    axisY = new QValueAxis;
    axisX->setRange(0, 10);         //坐标系区间
    axisY->setRange(-1, 1);
    axisX->setLabelFormat("%d");    //坐标轴下标的数字类型
    axisY->setLabelFormat("%0.2f");
    axisX->setGridLineVisible(true);//是否显示网格
    axisY->setGridLineVisible(true);
    axisX->setTickCount(10+1);       //设置坐标轴区间格数
    axisY->setTickCount(10+1);
    axisX->setMinorTickCount(5);    //小刻度
    axisX->setTitleText("x");       //x轴名称
    axisY->setTitleText("距离cm");

    //设置图表
    m_chart->addAxis(axisX, Qt::AlignBottom);  //将x坐标轴加到chart上,居下
    m_chart->addAxis(axisY, Qt::AlignLeft);    //将y坐标轴加到chart上,居左
    m_chart->setTitle("y = sin(x)");   //设置图表标题
    m_chart->legend()->hide();  //设置图例不可见
    m_chart->legend()->setLayoutDirection(Qt::LeftToRight);
    m_chart->legend()->setAlignment(Qt::AlignLeft);

    //设置曲线
    series = new QSplineSeries(this);   //平滑曲线的集合
    series->setName(QString("y=sinx")); //设置曲线对应的名字,用于图例显示
    m_chart->addSeries(series);         //将曲线增入chart中
    series->attachAxis(axisX);          //将曲线与轴对应起来
    series->attachAxis(axisY);

    //将图表添加进控件
    ui->verticalLayout_2->addWidget(m_chartView);

    //以定时器推动
    m_timer = new QTimer(this);
    m_timer->start(100);
    connect(m_timer,SIGNAL(timeout()),this,SLOT(updatechart()));
}


void serial::updatechart()
{ 
        
    axisX->setRange(0,points*0.1);
    series->append(points*0.1,sin(points*0.1));
    points++;
}

  • QSplineSeries: 用于创建有由一系列数据组成的曲线.类似的还有QPieSeries(饼图数据). QLineSeries(折线数据)。
  • QChart: 图表界面,用来管理图表内容,颜色,大小等。
  • QChartView: 负责显示QChart。

通过对应的函数设置坐标轴参数、图标参数、曲线参数。 通过上述代码将图表添加到控件,这个控件对应的位置如下图箭头所示,就是个layout控件。 到了这一步还没开始通过串口读取数据,所以自己用定时器先产生数据模拟串口数据,通过updatechart函数不断更新数据。 通过setRange函数不断拓宽x轴范围,通过appedf函数给曲线增加新的点。

3. 初步实现效果

下一步完成串口的设置,并将串口读取的数据显示出来。

四、串口设置

1.准备工作

在pro文件添加serialport。 添加对应头文件,在上面可以复制。 对象初始化函数包含一个串口初始化函数,一个图标初始化函数。

serial::serial(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::serial)
{ 
        
    ui->setupUi(this);

    //串口初始化
    system_init();

    //图表初始化
    ChartInit();

}

2.用户系统初始化函数

//用户系统初始化
void serial::system_init()
{ 
        
    //清空端口所有选项
    ui->cmd_port_name->clear();

    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    { 
        
        //将可用串口添加到端口显示框
        ui->cmd_port_name->addItem(info.portName());
    }

    //设置串口状态标签为红色 表示未连接状态
    ui->lab_status->setStyleSheet("color:red");

    //check port 检测通讯端口
    connect(ui->btn_serial,&QPushButton::clicked,this,&serial::btn_serial_check);
    //open port 打开选择端口
    connect(ui->btn_open,&QPushButton::clicked,this,&serial::btn_open_port);

    //receive data 串口数据接收完触发对应槽函数
    connect(&global_port,&QSerialPort::readyRead,this,&serial::receive_data);

    //clear recevie 清除接收文本框的内容
    connect(ui->btn_clear,&QPushButton::clicked,this,&serial::btn_clear_rev);

}

在标准C++中,并没有foreach关键字。但是在QT中,可以使用这一个关键字,其主要原因是QT自己增加了这一个关键字,就像slots和signals、emit等一样。增加的foreach关键字在编译时会进行预处理。 其用法为:foreach (varItem , Items) // foreach(variable ,container)

:,以QList数据类型返回系统可用的串口清单。 :是组合框QComboBox类的函数,将函数带着的文本增加至组合框内的选项中。 :设置指定样式。

利用connect函数,将【检测通讯端口】【打开】【清除】按键的clicked信号与对应的槽函数连接在一起。

3.检测通讯端口槽函数

和系统初始化函数里面的操作是一样的,就是更新端口。

void serial::btn_serial_check(bool)
{
    ui->cmd_port_name->clear();
    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        //将可用串口添加到端口显示框
        ui->cmd_port_name->addItem(info.portName());
    }
}

4.打开选择端口槽函数

void serial::btn_open_port(bool)
{
    //port name 设置端口
    global_port.setPortName(ui->cmd_port_name->currentText());
    //baud rate 设置波特率为115200
   global_port.setBaudRate(QSerialPort::Baud115200);
    //parity 设置无校验位
    global_port.setParity(QSerialPort::NoParity);
    //data bits 设置数据位8位
    global_port.setDataBits(QSerialPort::Data8);
    //stop bits 设置停止位1位
    global_port.setStopBits(QSerialPort::OneStop);

    //port open 打开选择端口
    bool ret = global_port.open(QIODevice::ReadWrite);

    if(ret){
        //打开成功更新状态
        ui->lab_status->setText("Connected");
        //设置串口状态标签为绿色 表示已连接状态
        ui->lab_status->setStyleSheet("color:green");
    }
}

cmd_port_name是选择端口号那个组合框,currentText表示当前端口号的名称。

因为应用场景固定,所以直接给定了串口的格式要求,而通过组合框不需要选择。通过QSerialPort类的函数进行波特率、校验位、数据位、停止位的设定。

:通过固定模式打开串口,成功返回true,否则返回false。模式必须是 QIODevice::ReadOnly, QIODevice::WriteOnly, or QIODevice::ReadWrite.,其他模式不支持。这里我们选择是读写模式。

最后对返回值ret进行判断,连接成功则将状态栏文本改为“Connected”,颜色也改为绿色。

5.关闭选择端口槽函数

void serial::btn_close_port(bool)
{

    global_port.close();

    //关闭端口后显示状态
    ui->lab_status->setText("DisConnected");
    ui->lab_status->setStyleSheet("color:red");
}

colse函数和open函数是对应的,一个打开,一个关闭。

6.数据更新函数和清除文本函数

//接收数据槽函数,显示对应曲线
float max=0;
void serial::receive_data(void)
{ 
        
    QByteArray array = global_port.readAll();
    QString str_rev;


    //更新曲线显示
    if(array.toFloat()>max) //寻找Y轴最大值
        max=array.toFloat();
    axisY->setRange(0,max+2);
    axisX->setRange(0,points);
    series->append(points,array.toFloat());
    points++;
    //qDebug() << array.toFloat();


    //文本框显示
    if(ui->chk_rev_time->checkState() == Qt::Checked) //是否显示时间
    { 
        
        //获取当前系统时间
        QDateTime nowtime = QDateTime::currentDateTime();
        //时间转换为字符串格式
        str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
        //加上接收数据 转换为16进制并空格分开 接收数据换行
        str_rev += QString(array).append("\r\n");
    }
    else
    { 
        
        str_rev += QString(array).append("\r\n");
    }
    ui->pte_rcv->insertPlainText(str_rev);  //将字符串添加到显示文本框

}

//清空接收文本框槽函数
void serial::btn_clear_rev(bool)
{ 
        
    ui->pte_rcv->clear();
}

通过QByteArray数据类型承接串口数据,然后通过toFloat函数转换为float数据类型的数值,通过append函数添加到曲线当中去。

文本方面通过insertPlainText函数添加字符串,通过clear函数删除字符串。

pte_ecv控件就是那个多行编辑框控件,如下图。

7.其他修正

测试用的定时器可以注释或删除,更新函数updatechart也可以删除。 将设置坐标轴范围的代码注释,因为这是y=sinx曲线的范围,对应超声波显示已经不适用了。 通过单片机printf函数随机发送float数据类型数据。

QT中foreach的使用 Qt绘制实时曲线图形简单例程 - Qt实时绘图

标签: ss系列传感器头2m线长传感器qt32干湿球传感器2m槽型传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台