天天看点

QChart数据可视化应用

1 前言

1.1 定义

数据可视化的定义有很多,像百度百科的定义是:数据可视化,是关于数据视觉表现形式的科学技术研究。其中,这种数据的视觉表现形式被定义为,一种以某种概要形式抽提出来的信息,包括相应信息单位的各种属性和变量。其实就是利用运用计算机图形学、图像、人机交互等技术,将采集或模拟的数据映射为可识别的图形、图像,说白了就是使用图像形式展示数据。

1.2 分类

常用的数据可视化有柱状图、线状图、条形图、面积图、饼图、点图、仪表盘、走势图外,还有和弦图、圈饼图、金字塔、漏斗图、K线图、关系图、网络图、玫瑰图、帕累托图、数学公式图、预测曲线图、正态分布图、迷你图、行政地图、GIS地图等等。

QChart数据可视化应用

1.3 说明

可视化类型太多,此文不可能一一列出(后续有机会补充),本文使用Qt官方例子为引,描述如何使用QChart进行如下图的数据可视化:

QChart数据可视化应用

qt官方例子可依据下图方式查找(安装qt的时候需要把exaples安装)。

QChart数据可视化应用

2 正文

以下代码基本照搬qt示例代码,也就是说此文深度有限,我们以学习为主,主要搞清楚QChart的使用即达到目的,如有需要深入解析QChart源码,我们另发文章分析。

2.1 QChart的介绍

以下内容来源官网并翻译,且括号为自写内容:

QChart是一个在QGaphicScene中可以显示的QGraphicsWidget。它管理不同类型系列和其他图表相关对象(如图例和轴)的图形表示。为了仅在布局中显示图表,可以使用便利类QChartView代替QChart(QChart实例化后,将其指针传入QChartView的构造函数)。另外,通过使用QPolarChart类,可以将折线,样条曲线,面积和散布序列表示为极坐标图。

而QChart属于QCharts图表模块下的一部分,QCharts介绍:

Qt图表模块提供了一组易于使用的图表组件。它使用Qt Graphics View Framework,因此可以轻松地将图表集成到现代用户界面中。Qt图表可用作QWidgets,QGraphicsWidget或QML类型。用户可以通过选择图表主题之一轻松创建令人印象深刻的图表。

2.2 QChart的使用

1、既然属于QCharts模块,所以需要在pro文件中添加charts模块:

QT += core gui charts
           

2、上面介绍时也说了:

为了仅在布局中显示图表,可以使用便利类QChartView代替QChart(QChart实例化后,将其指针传入QChartView的构造函数)

所以想要显示QChart内容,QChartView构造时候传入QChart指针,如下所示:

QChartView *chartView;
    QChart *chart = createLineChart();
    chartView = new QChartView(chart);  //线覆盖
           

createLineChart()

为下文函数。

查看qt源代码,可了解

QChartView

继承于

QGraphicsView

类,而

QGraphicsView

又继承于

QAbstractScrollArea

,最后

QAbstractScrollArea

继承于

QFrame

,这样说明

QChartView

最终继承于基础控件类,即new后指定或不指定父对象就直接在ui中显示。

3、显示数据的传入,上面都说了QChart如何显示,下面说明其显示数据来源。

QChart

类的所有数据通过

addSeries

接口传入,参数类型为

QAbstractSeries

QAbstractSeries

类从名字上来看就知道是一个抽象类,这里使用其多态特性,作为抽象类派生各种数据子类,如:

  • 折线数据类:QLineSeries
  • 曲线数据类:QSplineSeries
  • 离散点数据类:QScatterSeries
  • 面积数据类:QAreaSeries
  • 直方图数据类:QStackedBarSeries
  • 扇型数据类:QPieSeries

以上类下文需要使用,但到底其子类还有哪些,不多说,自个按F1查看帮助文档,如下图:

QChart数据可视化应用

其中官方例子中(下文代码),所有数据来源m_dataTable,其定义为

追查其定义来源:

typedef QPair<QPointF, QString> Data;
typedef QList<Data> DataList;
typedef QList<DataList> DataTable;
           

具体数据解析显示部分自己查看下文理解。

2.3 折线图

QChart *ThemeWidget::createLineChart() const
{
    QChart *chart = new QChart();   //创建图表
    chart->setTitle("Line chart");  //设置图表名称
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable)
    {
        QLineSeries *series = new QLineSeries(chart);   //创建曲线(数据坐标类)
        for (const Data &data : list)
            series->append(data.first);     //为曲线添加数据点
        series->setName(name + QString::number(nameIndex++)); //设置曲线名字
        chart->addSeries(series);   //添加曲线
    }
    chart->createDefaultAxes(); //为坐标系添加轴,需要在所有曲线数据填入后再调用此函数
    return chart;
}
           

2.4 曲线图

注意,此处与2中唯一不同就是曲线数据类,此处使用

QSplineSeries

QChart *ThemeWidget::createSplineChart() const
{
    // spine chart
    QChart *chart = new QChart();
    chart->setTitle("Spline chart");
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable)
    {
        QSplineSeries *series = new QSplineSeries(chart);   //创建平滑曲线
        for (const Data &data : list)
            series->append(data.first);
        series->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(series);
    }
    chart->createDefaultAxes();
    return chart;
}
           

2.5 散点图

注意,此处与2\3中唯一不同就是曲线数据类,此处使用

QScatterSeries

QChart *ThemeWidget::createScatterChart() const
{
    // scatter chart
    QChart *chart = new QChart();
    chart->setTitle("Scatter chart");
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable)
    {
        QScatterSeries *series = new QScatterSeries(chart);     //离散点曲线数据类
        for (const Data &data : list)
            series->append(data.first);
        series->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(series);
    }
    chart->createDefaultAxes();
    return chart;
}
           

2.6 面积图

QChart *ThemeWidget::createAreaChart() const
{
    QChart *chart = new QChart();
    chart->setTitle("Area chart");

    // The lower series initialized to zero values
    QLineSeries *lowerSeries = 0;
    QString name("Series ");
    int nameIndex = 0;
    for (int i(0); i < m_dataTable.count(); i++)
    {
        QLineSeries *upperSeries = new QLineSeries(chart);
        for (int j(0); j < m_dataTable[i].count(); j++)
        {
            Data data = m_dataTable[i].at(j);
            if (lowerSeries)
            {
                const QVector<QPointF>& points = lowerSeries->pointsVector();
                upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
            }
            else
            {
                upperSeries->append(QPointF(j, data.first.y()));
            }
        }
        QAreaSeries *area = new QAreaSeries(upperSeries, lowerSeries);
        area->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(area);
        chart->createDefaultAxes();
        lowerSeries = upperSeries;
    }
    return chart;
}
           

2.7 条形图

QChart *ThemeWidget::createBarChart(int valueCount) const
{
    Q_UNUSED(valueCount);
    QChart *chart = new QChart();
    chart->setTitle("Bar chart");

    QStackedBarSeries *series = new QStackedBarSeries(chart);
    for (int i(0); i < m_dataTable.count(); i++)
    {
        QBarSet *set = new QBarSet("Bar set " + QString::number(i));
        for (const Data &data : m_dataTable[i])
            *set << data.first.y();
        series->append(set);
    }
    chart->addSeries(series);
    chart->createDefaultAxes();
    return chart;
}
           

2.8 扇型图(也叫饼图)

QChart *ThemeWidget::createPieChart() const
{
    QChart *chart = new QChart();
    chart->setTitle("Pie chart");

    qreal pieSize = 1.0 / m_dataTable.count();
    for (int i = 0; i < m_dataTable.count(); i++) 
    {
        QPieSeries *series = new QPieSeries(chart);
        for (const Data &data : m_dataTable[i]) 
        {
            QPieSlice *slice = series->append(data.second, data.first.y());//扇型区域名、扇型数值(所有数值比值会自动分配)
            if (data == m_dataTable[i].first()) 
            {
                slice->setLabelVisible(); //设置扇型信息可见
                slice->setExploded(); //设置该项显示突出
            }
        }
        qreal hPos = (pieSize / 2) + (i / (qreal) m_dataTable.count());
        series->setPieSize(pieSize);
        series->setHorizontalPosition(hPos);
        series->setVerticalPosition(0.5);
        chart->addSeries(series);
    }
    return chart;
}
           

此处我对示例代码做了一些修改,对每个扇型区域类添加了一个信号槽,如下代码所示:

QPieSeries *series = new QPieSeries(chart); //创建扇型
        for (const Data &data : m_dataTable[i])
        {
            QPieSlice *slice = series->append(data.second, data.first.y()); //扇型区域名、扇型数值(所有数值比值会自动分配)
            //关联其点击信号槽
            connect(slice,&QPieSlice::clicked,this,[=](){
                slice->setLabelVisible(!slice->isLabelVisible());   //设置扇型信息可见
                slice->setExploded(!slice->isExploded());       //设置该项显示突出
            });
        }
           

实现效果如下:

QChart数据可视化应用

继续阅读