Qt绘制线型图与样条图

线型图和样条图

本文参考自 Qt 官方帮助文档,主要介绍线型图、样条图以及它们的动态情况。

1. 折线图

1.1 介绍

折线图(Line Chart)表示由直线连接的一系列数据点。使用类型为 QLineSeries 类或 LineSeries QML类型。

折线图的基本结构包括:

  • 横轴(X轴): 通常表示时间或类别。
  • 纵轴(Y轴): 表示变量的值。可以是数值,也可以是百分比或其他度量。
  • 数据点: 在图表上表示具体的数据值的点。
  • 折线: 将数据点连接起来的线,形成变化趋势。

1.2 示例

本示例是使用 Qt 帮助文档的示例

/* 创建 QLineSeries 实例 */
QLineSeries *series = new QLineSeries();    

/* 添加数据点到 series */
// 方式一:使用 append 接口
series->append(0, 6);
series->append(2, 4);
series->append(3, 8);
series->append(7, 4);
series->append(10, 5);
// 方式二:采用 << 操作符
*series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18,3) << QPointF(20, 2);


/* 创建一个 QChart 实例,以能够在图标上显示数据 */
QChart *chart = new QChart();
chart->legend()->hide();    // 隐藏了图表的图例,这样图表将不显示数据序列标识
chart->addSeries(series);   // 添加数据序列到图表中
chart->createDefaultAxes(); // 创建默认的坐标轴
chart->setTitle("Simple line chart example");   // 设置标题

/* 用 QChart 作为参数创建一个 QChartView 对象。这样就不需要再创建 QGraphicsView 场景了。 */
QChartView *chartView = new QChartView(chart);
chartView->setRendeHint(QPainter::Antialiasing);    // 设置抗锯齿

/* 显示图表 */
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(400, 300);
window.show();

运行效果如下:

1.3 使用场景

折线图的适用场景:

  • 显示趋势和波动: 显示随时间变化的数据趋势,例如股票价格、气温变化等。
  • 比较多个数据系列: 折线图可以同时展示多个数据系列的趋势对比,帮助用户直观地看出不同数据之间的关系。。

2. 样条图

2.1 介绍

在样条图中,数据点通过样条连接。样条是使用 QPainterPath 绘制的。
样条图是通过使用继承 QLineSeriesQSplineSeries 类(或继承 LineSeriesSplineSeries 类型)来实现的。

2.2 示例

/* 要创建样条图,需要将数据放入 QSplineseries 中。QSplineSeries 会自动计算绘制样条所需的样条段控制点 */
QSpineSeries *series = new QSplineSeries();
series->setname("spline");  // 为该数据序列设置名称为 spline


/* 为数据序列添加数据点 */
series->append(0, 6);
series->append(2, 4);
series->append(3, 8);
series->append(7, 4);
series->append(10, 5);
*series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) << QPointF(20, 2);


/* 创建 QChart 对象并将数据序列添加到其中 */
QChart *chart = new QChart();
chart->legend()->hide();
chart->addSeries(series);
chart->setTitle("Simple spline chart example");
chart->createDefaultAxes();
chart->axes(Qt::Vertical).first()->setRange(0, 10); // 限制 y 轴上的值的范围

/* 创建QChartView对象 */
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);

/* 显示 */
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(400, 300);
window.show();

运行结果如下:

2.3 使用场景

样条图的适用场景:

  • 平滑曲线展示:样条图适合展示数据之间的曲线变化趋势,通过光滑的曲线能够更清晰地展现数据的变化规律,尤其适合对趋势进行分析和预测。
  • 插值处理:当数据点之间缺少连续性时,样条图可以通过插值算法实现数据点之间的平滑过渡,呈现出连续而光滑的曲线,用于更准确地观察数据的变化趋势。

综合来说,折线图适合展示数据的整体趋势和比较多个数据系列的变化,而样条图则更适合展示数据的平滑曲线变化趋势和对趋势进行分析。


3. 动态图

此例依然是 Qt 官方帮助文档的例子:

chart.h

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Charts module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef CHART_H
#define CHART_H

#include <QtCharts/QChart>
#include <QtCore/QTimer>

QT_CHARTS_BEGIN_NAMESPACE
class QSplineSeries;
class QValueAxis;
QT_CHARTS_END_NAMESPACE

QT_CHARTS_USE_NAMESPACE

//![1]
class Chart: public QChart
{
    Q_OBJECT
public:
    Chart(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
    virtual ~Chart();

public slots:
    void handleTimeout();

private:
    QTimer m_timer;
    QSplineSeries *m_series;
    QStringList m_titles;
    QValueAxis *m_axisX;    // 坐标轴类
    QValueAxis *m_axisY;
    qreal m_step;
    qreal m_x;
    qreal m_y;
};
//![1]

#endif /* CHART_H */

chart.cpp

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Charts module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "chart.h"
#include <QtCharts/QAbstractAxis>
#include <QtCharts/QSplineSeries>
#include <QtCharts/QValueAxis>
#include <QtCore/QRandomGenerator>
#include <QtCore/QDebug>

Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags):
    QChart(QChart::ChartTypeCartesian, parent, wFlags),
    m_series(0),
    m_axisX(new QValueAxis()),
    m_axisY(new QValueAxis()),
    m_step(0),
    m_x(5),
    m_y(1)
{
    QObject::connect(&m_timer, &QTimer::timeout, this, &Chart::handleTimeout);
    m_timer.setInterval(1000);

    m_series = new QSplineSeries(this);
    QPen green(Qt::red);
    green.setWidth(3);
    m_series->setPen(green);
    m_series->append(m_x, m_y);

    addSeries(m_series);

    addAxis(m_axisX,Qt::AlignBottom);
    addAxis(m_axisY,Qt::AlignLeft);
    m_series->attachAxis(m_axisX);
    m_series->attachAxis(m_axisY);
    m_axisX->setTickCount(5);
    m_axisX->setRange(0, 10);
    m_axisY->setRange(-5, 10);

    m_timer.start();
}

Chart::~Chart()
{

}

void Chart::handleTimeout()
{
    /*
     * plotArea().width(): 获取绘图区域的宽度,随着拉动显示窗口大小变化而变化
     * m_axisX->tickCount(): 获取x轴的刻度数量
     * m_axisX->max(): 表示x轴的最大值
     * m_axisX->min(): 表示x轴的最小值
     */
    qreal x = plotArea().width() / m_axisX->tickCount();
    qDebug() << "ployArea().width(): " << plotArea().width() << "maxisX->tickCount()" << m_axisX->tickCount() << endl;
    qreal y = (m_axisX->max() - m_axisX->min()) / m_axisX->tickCount();
    qDebug() << "m_axisX->max(): " << m_axisX->max() << "m_axisx->min(): " << m_axisX->min() << endl;
    qDebug() << "x = " << x << ", y = " << y << endl;
    m_x += y;   // 样条图 每次增加的点的步距x为固定的2
    m_y = QRandomGenerator::global()->bounded(5) - 2.5; //  限制样条图的y始终在
    m_series->append(m_x, m_y);
    scroll(x, 0);
    if (m_x == 100)
        m_timer.stop();
}

main.cpp

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Charts module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "chart.h"
#include <QtCharts/QChartView>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>

QT_CHARTS_USE_NAMESPACE

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QMainWindow window;
    Chart *chart = new Chart;
    chart->setTitle("Dynamic spline chart");
    chart->legend()->hide();
    chart->setAnimationOptions(QChart::AllAnimations);
    QChartView chartView(chart);
    chartView.setRenderHint(QPainter::Antialiasing);
    window.setCentralWidget(&chartView);
    window.resize(400, 300);
    window.show();
    return a.exec();
}

运行后可看到持续在动态延伸的样条图。
使其能够持续动态延伸变化的关键代码为handleTimeout()接口:

代码块一

qreal x = plotArea().width() / m_axisX->tickCount();

scroll(x, 0);

获取绘图区域的宽度 除以 x轴的刻度数量;保证当调整窗口大小时,获得的值都是为一刻度(总共五刻度)宽度。

再通过 scroll(x, 0); 将整个图向右滑动五分之一(一刻度)的距离。

代码块二

qreal y = (m_axisX->max() - m_axisX->min()) / m_axisX;

m_x += y;
m_y = QRandomGenerator::global()->bounded(5) - 2.5;

m_series->append(m_x, m_y);

m_axisX 在初始化时的 max()、min() 为 0, 10; (10 - 0) / 5 = 2
m_x += 2;
在 scroll(x, 0); 后,max() 变为 12、min() 变为 2
依次循环

代码块一负责整个图向右延伸。
代码块二负责新增更新数据序列的点。

疑问点
通过加打印,确定是在scroll(x, 0); 后,m_axisX->max()m_axisX->min()会同时增加 2 ,所以才能使得更新序列的点都相较于前一个点横向距离为2。
但为何 scroll(x, 0); 能修改 x 轴的最大最小值不理解,官方帮助文档也没查阅到有写明能修改。希望有大神知道的话不吝赐教~

此外,作者在尝试使用Qt creator的debug模式进行调试该槽函数时,在m_y = QRandomGenerator::global()->bounded(5) - 2.5;m_series->append(m_x, m_y);均会出现段错误,但运行时却能够正常运行。有大神了解的话也请恳请指教一下~

posted @ 2024-03-20 00:13  功夫在诗外  阅读(1142)  评论(0)    收藏  举报