Qt - QTChart绘制图表

前言

这是一段做作的前言,摘自网络。
在Qt5.7之前,Qt Charts 一直是商业版才有,所以在此之前的Qt开发人员若想绘制图表需采用第三方库,常见的是Qt Charts是Qt提供的图表模块,在Qt5.7之前只有商业版才有,但是从Qt5.7之后,社区版本也包含了Qt Charts。Qt Charts可以很方便的绘制常见的折线图、柱状图、饼图等图表。它基于Qt的Graphics View架构,核心组件是QChartView和QChart。其中QChartView正是继承于QGraphicsView类,因此它也可以作为Graphics View中的视图组件。另一个QChart则由QGraphicsWidget继承而来,继续向上追溯,发现他们都继承于QGraphicsItem,所以QChart是图形项。

准备工作

安装QChart组件

Qt 安装时,默认情况下不安装 QCharts组件 ,所以如果需要使用 QCharts组件 ,我们需要先更新一下 Qt组件

首先在我们的 Qt安装路径 下找到 MaintenanceTool

在这里插入图片描述
若在下一步出现 “要继续此操作,至少需要一个有效且已启用的储存库。”;则需要在设置中设置存储库。

Qt 存储库(戳我)
在这里插入图片描述
ps:我采用的是清华镜像,只需点击HTTP 进入镜像地址并移动至图示目录下,复制如上图所示的 HTTP地址(点击此处偷懒) 作为存储库,随后添加:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

项目配置

.pro文件中添加

QT    += charts

.h文件中添加

QT_CHARTS_USE_NAMESPACE

QCharts必须的头文件

#include <QChartView>
#include <QChart>

主要组成部分

Qt Charts 模块是一组易于使用的图标组件,基于 Qt 的 GrapHics View 架构,核心是 QChartViewQChart

QChartView

QChartViewQChart 的视图组件,用于显示。在 QtCreator 中使用 QChartView 可以放置一个 QWidget,然后升级为 QChartView

QChartView 的内容很少,建议直接过一遍文档 QChartView

QChartView继承关系如下:

QWidget
QFrame
QGraphicsObject
QGraphicsView
QChartView

QChart

QChart 管理不同类型的序列和其他与图表相关的对象,例如坐标轴及图例。

QChart 继承关系如下:

QObject
QGraphicsObject
QGraphicsItem
QGraphicsWidget
QGraphicsItem
QChart

序列

常见的序列如下:

  • QBarSeries (柱状图)

  • QHorizontalBarSeries (水平柱状图)

  • QHorizontalPercentBarSeries (水平百分比柱状图)

  • QHorizontalStackedBarSeries (水平层叠图)

  • QPercentBarSeries 百分比柱状图)QStackedBarSeries (层叠图/堆叠的条形图)

  • QAreaSeries (面积图)

  • QBoxPlotSeries (形图/盒须图)

  • QPieSeries (饼图)

  • QXYSeries (线性图、曲线图、散点图的基类)

  • QLineSeries (折线图)

  • QSplineSeries (曲线图)

  • QScatterSeries (散点图)

序列继承关系如下:

QAbstractSeries
QBoxPlotSeries
QAreaSeries
QAbstractBarSeries
QCandlestickSeries
QPieSeries
QXYSeries
QBarSeries
QHorizontalBarSeries
QHorizontalPercentBarSeries
QHorizontalStackedBarSeries
QPercentBarSeries
QStackedBarSeries
QLineSeries
QScatterSeries
QSplineSeries

本文仅介绍最常用的 QLineSeries

坐标轴

坐标轴封装了刻度,标签,网格线,标题等属性。

坐标轴有以下几种:

  • QBarCategoryAxis
    类别坐标轴,用字符串作为坐标轴的可读,用于图表的非数值坐标轴
  • QDateTimeAxis
    时间坐标轴,用作时间数据的坐标轴
  • QLogValueAxis
    对数数值坐标轴,作为数值类数据的对数坐标轴
  • QValueAxis
    数值坐标轴,用作数值型数据的坐标轴
  • QCategoryAxis
    分组数值坐标轴,可以为数值范围设置标签

坐标轴继承关系如下:

AbstractAxis
QBarCategoryAxis
QDateTimeAxis
QLogValueAxis
QValueAxis
QCategoryAxis

图例

图例是对图表上序列的补充说明。我们可以设置序列颜色及其文字说明、并控制序列显示的位置。

此外,图例中还有一个 QLegendMarker 类,可为每个序列的图例生成一个类似QChrckedBox 的组件;单击序列的标记,可以控制序列是否显示。

下面是官方例程 chartthemes 的运行效果。

在这里插入图片描述
上述内容,建议通过官方例程配合类文档学习。

静态图表

在这里插入图片描述

源代码

    chart = new QChart();

    mAxY = new QValueAxis();
    mAxX = new QValueAxis();
    mLineSeries = new QLineSeries();

    //y轴范围
    mAxY->setRange(0, 10);
    // Y轴分等份
    mAxY->setTickCount(11);
    mAxX->setRange(0,10);
    mAxX->setTickCount(11);

    // 将系列添加到图表
    chart->addSeries(mLineSeries);
    chart->setTheme(QtCharts::QChart::ChartThemeBrownSand);

    mAxX->setTitleText(QString(tr("ImageNumber")));
    mAxY->setTitleText(QString(tr("ReadRate(%)")));
    chart->addAxis(mAxY, Qt::AlignLeft);
    chart->addAxis(mAxX, Qt::AlignBottom);

    mLineSeries->attachAxis(mAxY);
    mLineSeries->attachAxis(mAxX);

    //隐藏背景
    chart->setBackgroundVisible(false);
    //设置外边界全部为0
    chart->setContentsMargins(0, 0, 0, 0);
    //设置内边界全部为0
    chart->setMargins(QMargins(0, 0, 0, 0));
    //设置背景区域无圆角
    chart->setBackgroundRoundness(0);

    //突出曲线上的点
    mLineSeries->setPointsVisible(true);

    //图例
    QLegend *mlegend = chart->legend();
    mLineSeries->setName("testname");
    mLineSeries->setColor(QColor(255,0,0));
    //在底部显示
    mlegend->setAlignment(Qt::AlignBottom);
    mlegend->show();

    // 将图表绑定到视图 wiget 为 QChartView
    ui->widget->setChart(chart);

    for(int i = 0 ;i < 10;i++){
        mLineSeries->append(i+1, i);
    }

动态图表

场景一

显示最近的 n 个数据。

思路

对于数值坐标轴,我们仅需要在加入数据时判断数据点是否超过 x轴 最大值。
若超过则取得旧的pointslist,并将其所有点的 x轴 数值 -1 ,再加入新点。

效果

在这里插入图片描述

核心实现

    QList<QPointF> oldPoints = mLineSeries->points();
    int pointCount =oldPoints.size();

    if(pointCount > mAxX->max()){

        QList<QPointF> newPoints;

        for(int i = 0; i < pointCount; i++){

            QPointF point;
            point.setX(oldPoints.at(i).x()-1);
            point.setY(oldPoints.at(i).y());

            newPoints.append(point);
        }
        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        QPointF point;
        point.setX(oldPoints.at(pointCount -1).x());
        point.setY(qrand()%10);
        newPoints.append(point);
        mLineSeries->clear();
        mLineSeries->append(newPoints);
        mLineSeries->setPointsVisible(true);
    }
    else{
        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        QPointF point;
        point.setX(oldPoints.at(pointCount -1).x() +1);
        point.setY(qrand()%10);
        mLineSeries->append(point);
    }

场景二

显示最近一段时间的数据。

思路

这里需要增加两个概念

  • 定时间隔 TimerBreaks
  • 最大采样数 MaxSize

首次添加时设置时间轴,每次添加数据点都先判断当前数据点是否超过最大采样数,若超过则删除线中第一个点,再添加数据。

效果

在这里插入图片描述

核心实现

// 最大采样数
const int MaxSize = 100;
// 定时时间间隔-ms
const int TimerBreaks = 10;

void MainWindow::slot_timeout()
{
    static int cnt = 0;
    static QDateTime BeginTime, EndTime;

    if (cnt == 0)
    {
        BeginTime = QDateTime::currentDateTime();
        EndTime = BeginTime.addMSecs(MaxSize*(TimerBreaks + 1));
        mAxDateX->setMin(BeginTime);
        mAxDateX->setMax(EndTime);
    }

    qint64 x1 = QDateTime::currentMSecsSinceEpoch();

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    qreal y =qrand()%10;

    cnt = cnt > (MaxSize - 1) ? 1 : (++cnt);

    if (mLineSeries->count() > (MaxSize-1))
    {
        //删除第一个元素
        mLineSeries->removePoints(0, 1);
        EndTime = QDateTime::currentDateTime();
        BeginTime = EndTime.addMSecs((qint64)MaxSize*(-1)*TimerBreaks);

        mAxDateX->setMin(BeginTime);
        mAxDateX->setMax(EndTime);
    }
    
    mLineSeries->append(x1, y);

}

交互

获取曲线在鼠标位置坐标

QXYSeries 中可以发现该信号:

  • void clicked(const QPointF &point)
    鼠标点击序列时,触发该信号

  • void hovered(const QPointF &point, bool state)
    鼠标在序列上时触发该信号

有了这两个信号,就可以很容易的实现以下功能:

在这里插入图片描述
以上效果来自 Qt 官方例程 - Callout,核心是通过这两个信号实现控件的动态创建、隐藏和显示。

图例控制序列隐藏/显示

可参考官方例程 legendmarkers

在这里插入图片描述
以下为摘录核心:


const auto markers = m_chart->legend()->markers();
for (QLegendMarker *marker : markers) {
// Disconnect possible existing connection to avoid multiple connections
 QObject::disconnect(marker, &QLegendMarker::clicked, this ,&MainWidget::handleMarkerClicked);
QObject::connect(marker, &QLegendMarker::clicked, this, &MainWidget::handleMarkerClicked);

//点击事件
void MainWidget::handleMarkerClicked()
{
    QLegendMarker* marker = qobject_cast<QLegendMarker*> (sender());
    //断言
    Q_ASSERT(marker);
    switch (marker->type())
    {
        case QLegendMarker::LegendMarkerTypeXY:
        {
        //控序列隐藏/显示
        // Toggle visibility of series
        marker->series()->setVisible(!marker->series()->isVisible());

        // Turn legend marker back to visible, since hiding series also hides the marker
        // and we don't want it to happen now.
        marker->setVisible(true);
        
        //修改图例
        // Dim the marker, if series is not visible
        qreal alpha = 1.0;

        if (!marker->series()->isVisible())
            alpha = 0.5;

        QColor color;
        QBrush brush = marker->labelBrush();
        color = brush.color();
        color.setAlphaF(alpha);
        brush.setColor(color);
        marker->setLabelBrush(brush);

        brush = marker->brush();
        color = brush.color();
        color.setAlphaF(alpha);
        brush.setColor(color);
        marker->setBrush(brush);

        QPen pen = marker->pen();
        color = pen.color();
        color.setAlphaF(alpha);
        pen.setColor(color);
        marker->setPen(pen);
        break;
        }
    default:
        {
        qDebug() << "Unknown marker type";
        break;
        }
    }
}

卡顿问题

在使用图表时,经常会遇到卡顿的问题。

目前总结的解决卡顿的方法主要有:

  • 去掉动画、样式、标签形状等
m_chart->setAnimationOptions(QChart::NoAnimation);
  • 开启 OPenGL 加速
line->setUseOpenGL();

值得注意的是:

在这里插入图片描述

  • QAreaSeries 面积和散点图无法使用。
  • 加速系列不支持系列动画。
  • 加速序列不支持点标签。
  • 对于加速系列,笔样式和标记形状被忽略。仅支持实线和普通散点。散点可以是圆形或矩形,这取决于底层图形硬件和驱动程序。
  • 极坐标图不支持加速序列。
  • 避免短时间内逐点添加,大数据刷新采用 append(const QList<QPointF> &points)
 line->append(newPoints);

参考鸣谢

Qt Charts Examples

QChart时间坐标轴实时更新数据

  • 43
    点赞
  • 304
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值