开源 C++ QT Widget 开发(三)图表--波形显示器

文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 C++ QT Widget 开发(一)工程文件结构-优快云博客

开源 C++ QT Widget 开发(二)基本控件应用-优快云博客

开源 C++ QT Widget 开发(三)图表--波形显示器-优快云博客

开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-优快云博客

 开源 C++ QT Widget 开发(五)通讯--串口调试-优快云博客

开源 C++ QT Widget 开发(六)通讯--TCP调试-优快云博客

开源 C++ QT Widget 开发(七)线程--多线程及通讯-优快云博客

开源 C++ QT Widget 开发(八)网络--Http文件下载-优快云博客

开源 C++ QT Widget 开发(九)图表--仪表盘-优快云博客

开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-优快云博客

开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-优快云博客

开源 C++ QT Widget 开发(十二)图表--环境监测表盘-优快云博客

开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)

开源 C++ QT Widget 开发(十四)多媒体--录音机

开源 C++ QT Widget 开发(十五)多媒体--音频播放


推荐链接:

开源 java android app 开发(一)开发环境的搭建-优快云博客

开源 java android app 开发(二)工程文件结构-优快云博客

开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客

开源 java android app 开发(四)GUI界面重要组件-优快云博客

开源 java android app 开发(五)文件和数据库存储-优快云博客

开源 java android app 开发(六)多媒体使用-优快云博客

开源 java android app 开发(七)通讯之Tcp和Http-优快云博客

开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客

开源 java android app 开发(九)后台之线程和服务-优快云博客

开源 java android app 开发(十)广播机制-优快云博客

开源 java android app 开发(十一)调试、发布-优快云博客

开源 java android app 开发(十二)封库.aar-优快云博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-优快云博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-优快云博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-优快云博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-优快云博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-优快云博客

本章主要内容:使用 Qt Charts 模块,通过定时器模拟数据源,动态更新图表,实现了一个简单的波形显示器。

一、基本介绍

该程序创建了一个主窗口,包含一个可以显示两条实时曲线的图表(MyWave)和三个控制按钮(开始、停止、清除)。通过定时器模拟数据源,动态更新图表,实现了一个简单的波形显示器。

需要修改的文件有4个,如下图

三个按钮为代码中生成,不需要特别注意。mywave需要做出单独的文件创建调用,这里需要拉入Widget控件,然后右键进行提升,为mywave。

拉入Widget控件

右键进行提升

设为mywave

二、源码分析

2.1  头文件:mainwindow.h
目的:定义主窗口类 MainWindow。

关键成员:

MyWave *wave;: 指向自定义波形显示部件的指针,是应用程序的核心。

QTimer *timer;: 定时器,用于周期性地触发数据更新,模拟实时数据流。

代码如下图:

2.2  头文件:mywave.h
目的:定义自定义波形显示部件类 MyWave,继承自 QWidget。

核心组件:

QChart *m_chart;: 图表对象,是所有系列和坐标轴的容器。

QChartView *m_chartView;: 用于显示图表的视图部件。

QSplineSeries *m_series1, *m_series2;: 两条平滑曲线系列,用于存储和绘制数据点。

QValueAxis *m_axisX, *m_axisY;: X轴和Y轴,用于定义图表的坐标范围。

数据管理:

QList<qreal> m_data1, m_data2;: 两个链表,用于在内存中存储接收到的所有数据(用于回放或缩放等功能,但当前实现主要用于管理显示点数)。

int m_maxDisplayPoints = 200;: 控制图表上最大显示的数据点数,实现“滑动窗口”效果。

qreal m_xCounter = 0;: 一个不断递增的计数器,作为每个新数据点的X坐标。

公有接口 (API):

提供了一系列 set... 方法(如 setYRange, setLine1Color),这使得在 MainWindow 中配置波形显示的外观和行为非常方便,体现了良好的可定制性。

appendData(): 最重要的方法,用于向图表追加新的数据点。

clearData(): 清除所有数据。

代码如下图:

3. 源文件:mainwindow.cpp
构造函数:

初始化UI:

创建 MyWave 实例 wave 并对其进行详细配置(大小、范围、标题、颜色等)。

创建按钮,并设置了统一的样式表 (QSS) 使其美观。

使用 QVBoxLayout 和 QHBoxLayout 进行布局管理。

初始化定时器:

创建 QTimer 实例 timer。

连接定时器的 timeout() 信号到一个 Lambda 表达式,该表达式生成正弦和余弦数据(qSin, qCos)并调用 wave->appendData()。phase1 和 phase2 是静态变量,用于保持相位连续,使波形平滑变化。

代码如下图:

4. 源文件:mywave.cpp

核心方法 appendData():

将新数据 yValue1 和 yValue2 分别添加到 m_data1 和 m_data2 列表的末尾。

递增X轴计数器 m_xCounter。

滑动窗口逻辑:检查数据列表大小是否超过 m_maxDisplayPoints。如果超过,则移除列表最先加入的数据(removeFirst()),保持列表长度固定,从而实现图表只显示最新的一段数据。

调用 updateChart() 刷新显示。

核心方法 updateChart():

性能关键:这是代码中最高效的部分。它没有使用 clear() 再逐个 append(),而是先将要显示的所有点准备在 QVector<QPointF> 中,然后一次性调用 QSplineSeries::replace()。这极大地减少了绘图开销,非常适合实时数据更新。

计算当前显示的X轴起始位置 (startX)。

遍历数据列表,为每个Y值创建对应的点(X坐标为 startX + i)。

用准备好的点集替换整个系列的数据。

根据 startX 和 m_maxDisplayPoints 调整X轴的范围,使视图跟随数据滑动。

resizeEvent():

确保当 MyWave 部件大小改变时,内部的 m_chartView 也随之改变,充满整个客户区。

代码如下图:

三、所有源码

3.1  mainwindow.h文件源码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTimer>

class MyWave;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    MyWave *wave;
    QTimer *timer;
};

#endif // MAINWINDOW_H

3.2  mywave.h文件源码

#ifndef MYWAVE_H
#define MYWAVE_H

#include <QWidget>
#include <QtCharts>

QT_CHARTS_USE_NAMESPACE

class MyWave : public QWidget
{
    Q_OBJECT

public:
    explicit MyWave(QWidget *parent = nullptr);

    // 设置方法
    void setSize(const QSize &size);
    void setXRange(qreal min, qreal max);
    void setYRange(qreal min, qreal max);
    void setAxisTitle(const QString &xTitle, const QString &yTitle);
    void setBackgroundColor(const QColor &color);
    void setLine1Color(const QColor &color);
    void setLine2Color(const QColor &color);
    void setMaxDisplayPoints(int count);
    void appendData(qreal yValue1, qreal yValue2);
    void clearData();

protected:
    void resizeEvent(QResizeEvent *event) override;

private:
    void updateChart();

    QChart *m_chart;
    QChartView *m_chartView;
    QSplineSeries *m_series1;
    QSplineSeries *m_series2;
    QValueAxis *m_axisX;
    QValueAxis *m_axisY;

    QList<qreal> m_data1;
    QList<qreal> m_data2;
    int m_maxDisplayPoints = 200;
    qreal m_xCounter = 0;
};

#endif // MYWAVE_H

3.3  mainwindow.cpp文件源码

#include "mainwindow.h"
#include "mywave.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    // 创建波形控件
    wave = new MyWave(this);
    wave->setSize(QSize(600, 300));
    wave->setXRange(0, 100);
    wave->setYRange(-10, 10);
    wave->setAxisTitle("时间", "数值");
    wave->setLine1Color(Qt::blue);
    wave->setLine2Color(Qt::red);
    wave->setMaxDisplayPoints(100);

    // 创建按钮控件
    QPushButton *startButton = new QPushButton("开始", this);
    QPushButton *stopButton = new QPushButton("停止", this);
    QPushButton *clearButton = new QPushButton("清除", this);

    // 设置按钮样式
    QString buttonStyle = "QPushButton {"
                         "  padding: 8px 16px;"
                         "  font-size: 14px;"
                         "  min-width: 80px;"
                         "  border-radius: 4px;"
                         "  background-color: #f0f0f0;"
                         "}"
                         "QPushButton:hover {"
                         "  background-color: #e0e0e0;"
                         "}";
    startButton->setStyleSheet(buttonStyle);
    stopButton->setStyleSheet(buttonStyle);
    clearButton->setStyleSheet(buttonStyle);

    // 创建水平布局放置按钮
    QHBoxLayout *buttonLayout = new QHBoxLayout();
    buttonLayout->addWidget(startButton);
    buttonLayout->addWidget(stopButton);
    buttonLayout->addWidget(clearButton);
    buttonLayout->addStretch();
    buttonLayout->setSpacing(10);

    // 创建垂直主布局
    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout(buttonLayout);
    mainLayout->addWidget(wave);
    mainLayout->setContentsMargins(20, 20, 20, 20);
    mainLayout->setSpacing(15);

    // 设置中央部件
    QWidget *centralWidget = new QWidget(this);
    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);
    this->setFixedSize(1200, 800);

    // 定时器用于模拟数据
    timer = new QTimer(this);
    bool isRunning = false;

    // 按钮信号连接
    connect(startButton, &QPushButton::clicked, [=, &isRunning]() {
        if (!isRunning) {
            timer->start(100);
            isRunning = true;
        }
    });

    connect(stopButton, &QPushButton::clicked, [=, &isRunning]() {
        timer->stop();
        isRunning = false;
    });

    connect(clearButton, &QPushButton::clicked, [this]() {
        wave->clearData();
    });

    connect(timer, &QTimer::timeout, [this]() {
        static qreal phase1 = 0;
        static qreal phase2 = 0;
        wave->appendData(
            qSin(phase1) * 10,
            qCos(phase2) * 8
        );
        phase1 += 0.1;
        phase2 += 0.15;
    });
}

MainWindow::~MainWindow()
{
    delete timer;
    delete wave;
}

3.4  mywave.cpp文件源码

#include "mywave.h"

MyWave::MyWave(QWidget *parent) : QWidget(parent)
{
    // 初始化图表
    m_chart = new QChart();

    // 初始化两条曲线
    m_series1 = new QSplineSeries();
    m_series2 = new QSplineSeries();
    m_chart->addSeries(m_series1);
    m_chart->addSeries(m_series2);

    // 初始化坐标轴
    m_axisX = new QValueAxis();
    m_axisY = new QValueAxis();
    setXRange(0, 200);
    setYRange(-10, 10);

    // 添加坐标轴
    m_chart->addAxis(m_axisX, Qt::AlignBottom);
    m_chart->addAxis(m_axisY, Qt::AlignLeft);
    m_series1->attachAxis(m_axisX);
    m_series1->attachAxis(m_axisY);
    m_series2->attachAxis(m_axisX);
    m_series2->attachAxis(m_axisY);

    // 默认样式
    setBackgroundColor(Qt::white);
    setLine1Color(Qt::blue);
    setLine2Color(Qt::red);
    m_chart->legend()->setVisible(true);
    m_series1->setName("曲线1");
    m_series2->setName("曲线2");
    m_chart->legend()->setAlignment(Qt::AlignBottom);

    // 初始化视图
    m_chartView = new QChartView(m_chart, this);
    m_chartView->setRenderHint(QPainter::Antialiasing);
    setSize(QSize(800, 400));
}

void MyWave::setSize(const QSize &size)
{
    this->resize(size);
    m_chartView->resize(size);
}

void MyWave::setXRange(qreal min, qreal max)
{
    m_axisX->setRange(min, max);
    m_axisX->setTickCount(10);
}

void MyWave::setYRange(qreal min, qreal max)
{
    m_axisY->setRange(min, max);
    m_axisY->setTickCount(10);
}

void MyWave::setAxisTitle(const QString &xTitle, const QString &yTitle)
{
    m_axisX->setTitleText(xTitle);
    m_axisY->setTitleText(yTitle);
}

void MyWave::setBackgroundColor(const QColor &color)
{
    m_chart->setBackgroundBrush(QBrush(color));
    m_chart->setBackgroundPen(QPen(color));
}

void MyWave::setLine1Color(const QColor &color)
{
    m_series1->setPen(QPen(color, 2));
}

void MyWave::setLine2Color(const QColor &color)
{
    m_series2->setPen(QPen(color, 2));
}

void MyWave::setMaxDisplayPoints(int count)
{
    m_maxDisplayPoints = count;
    setXRange(0, count);
}

void MyWave::appendData(qreal yValue1, qreal yValue2)
{
    m_data1.append(yValue1);
    m_data2.append(yValue2);
    m_xCounter++;

    if (m_data1.size() > m_maxDisplayPoints) {
        m_data1.removeFirst();
        m_data2.removeFirst();
    }

    updateChart();
}

void MyWave::clearData()
{
    m_data1.clear();
    m_data2.clear();
    m_xCounter = 0;
    m_series1->clear();
    m_series2->clear();
    updateChart();
}

void MyWave::updateChart()
{
    QVector<QPointF> points1, points2;
    qreal startX = qMax(0.0, m_xCounter - m_maxDisplayPoints);

    for (int i = 0; i < m_data1.size(); ++i) {
        points1.append(QPointF(startX + i, m_data1.at(i)));
        points2.append(QPointF(startX + i, m_data2.at(i)));
    }

    m_series1->replace(points1);
    m_series2->replace(points2);
    m_axisX->setRange(startX, startX + m_maxDisplayPoints);
}

void MyWave::resizeEvent(QResizeEvent *event)
{
    m_chartView->resize(event->size());
    QWidget::resizeEvent(event);
}

四、演示效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值