文章目录

  • 前言
  • 准备工作
  • 安装QChart组件
  • 项目配置
  • 主要组成部分
  • QChartView
  • QChart
  • 序列
  • 坐标轴
  • 图例
  • 静态图表
  • 动态图表
  • 场景一
  • 思路
  • 效果
  • 核心实现
  • 场景二
  • 思路
  • 效果
  • 核心实现
  • 交互
  • 获取曲线在鼠标位置坐标
  • 图例控制序列隐藏/显示
  • 卡顿问题
  • 参考鸣谢

前言

这是一段做作的前言,摘自网络。
在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 Designer制作表格_Qt Designer制作表格


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

Qt Designer制作表格_Qt Designer制作表格_02


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

Qt Designer制作表格_QChart_03


Qt Designer制作表格_动态列表_04

Qt Designer制作表格_Qt Designer制作表格_05


Qt Designer制作表格_动态列表_06

项目配置

.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 的运行效果。

Qt Designer制作表格_Qt Designer制作表格_07


上述内容,建议通过官方例程配合类文档学习。

静态图表

Qt Designer制作表格_动态列表_08

源代码

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 ,再加入新点。

效果

Qt Designer制作表格_卡顿_09

核心实现

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

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

效果

Qt Designer制作表格_QChart_10

核心实现

// 最大采样数
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 Designer制作表格_Qt Designer制作表格_11


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

图例控制序列隐藏/显示

可参考官方例程 legendmarkers

Qt Designer制作表格_动态列表_12


以下为摘录核心:

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();

值得注意的是:

Qt Designer制作表格_时间轴_13

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