Zhiy Student of nuclear and particle physics

QtCharts 简介

2019-08-12
zhiy

Qt Charts 模块是 Qt 提供的二维数据可视化工具。利用 QChartView 类可以方便地添加各种类型的二维数据可视化图表,其中不同类型的图表各自用一个 QAbstractSeries 类表示。

折线图与曲线图

首先在 QtCreator 中创建一个 Qt Widgets Application 工程,在 .pro 文件中添加如下语句:

QT += charts

然后在 mainwindow.cpp 文件中包含 QtChart 头文件:

#include <QtCharts/QtCharts>

接下来在 mainwindow 构造函数中创建折线图对象。

// 创建 LineSeries 对象,并添加数据
QLineSeries *series = new QLineSeries(); 
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->addSeries(series); 
chart->legend()->hide(); 
chart->createDefaultAxes(); 
chart->setTitle("Simple line chart example"); 

// 创建 QChartView 对象, 并设置渲染、主题等
QChartView *chartView = new QChartView(chart); 
chartView->setRenderHint(QPainter:: Antialiasing); 

// 最后在主窗口显示图表
QMainWindow window; 
window.setCentralWidget(chartView); 
window.resize(400, 300); 
window.show(); 

曲线图完全类似:

QSplineSeries *series = new QSplineSeries(); 
series->setName("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 *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); 
QChartView *chartView = new QChartView(chart); 
chartView->setRenderHint(QPainter:: Antialiasing); 
w.setCentralWidget(chartView); 
w.resize(400, 300); 
w.show(); 

面积图

// 首先创建两个 QLineSeries 作为上下边界
QLineSeries *series0 = new QLineSeries(); 
QLineSeries *series1 = new QLineSeries();
*series0 << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(9, 7) << QPointF(12, 6)
            << QPointF(16, 7) << QPointF(18, 5);
*series1 << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(8, 2) << QPointF(12, 3)
            << QPointF(16, 4) << QPointF(18, 3);
// 创建 QAreaSeries
QAreaSeries *series = new QAreaSeries(series0, series1);
series->setName("Batman"); // 系列名称
QPen pen(0x059605);  //指定边界样式(颜色,宽度等)
pen.setWidth(3);
series->setPen(pen);
// 指定填充样式
QLinearGradient gradient(QPointF(0,0), QPointF(1,1));
gradient.setColorAt(0.0, Qt::red);
gradient.setColorAt(1.0, Qt::blue);
gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
series->setBrush(gradient);
// 创建 QChart 指定标题、坐标轴、图例等
QChart *chart = new QChart();
chart->addSeries(series);
chart->setTitle("Simple areachart example");
chart->createDefaultAxes();
chart->axes(Qt::Horizontal).first()->setRange(0,20);
chart->axes(Qt::Vertical).first()->setRange(0,10);
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
w.setCentralWidget(chartView);
w.resize(400, 300);
w.show();

散点图

// 创建 QScatterSeries 对象
QScatterSeries *series0 = new QScatterSeries();
// 设置 series 名称, marker 形状和大小
series0->setName("scatter1");
series0->setMarkerShape(QScatterSeries::MarkerShapeCircle);
series0->setMarkerSize(15.0);

QScatterSeries *series1 = new QScatterSeries();
series1->setName("scatter2");
series1->setMarkerShape(QScatterSeries::MarkerShapeRectangle);
series1->setMarkerSize(20.0);

QScatterSeries *series2 = new QScatterSeries();
series2->setName("scatter3");
series2->setMarkerShape(QScatterSeries::MarkerShapeRectangle);
series2->setMarkerSize(30.0);

series0->append(0, 6);
series0->append(2, 4);
series0->append(3, 8);
series0->append(7, 4);
series0->append(10, 5);

*series1 << QPointF(1, 1) << QPointF(3, 3) << QPointF(7, 6) << QPointF(8, 3) << QPointF(10, 2);
*series2 << QPointF(1, 5) << QPointF(4, 6) << QPointF(6, 3) << QPointF(9, 5);

// 为 series2 创建图片 marker
QPainterPath starPath;
starPath.moveTo(28, 15);
for (int i = 1; i < 5; ++i) {
    starPath.lineTo(14 + 14 * qCos(0.8 * i * M_PI),
                    15 + 14 * qSin(0.8 * i * M_PI));
}
starPath.closeSubpath();

QImage star(30, 30, QImage::Format_ARGB32);
star.fill(Qt::transparent);

QPainter painter(&star);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QRgb(0xf6a625));
painter.setBrush(painter.pen().color());
painter.drawPath(starPath);

series2->setBrush(star);
series2->setPen(QColor(Qt::transparent));

QChart *chart = new QChart();
chart->addSeries(series0);
chart->addSeries(series1);
chart->addSeries(series2);
chart->setTitle("Simple scatterchart example");
chart->createDefaultAxes();
chart->setDropShadowEnabled(false);
chart->legend()->setMarkerShape(QLegend::MarkerShapeFromSeries);
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
w.setCentralWidget(chartView);
w.resize(400, 300);
w.show();

柱状图

竖直柱状图

// 每个 BarSet 代表柱状图中的一组数据
// 比如要展示 n 个时期的直方图, 则每个 BarSet
// 代表直方图中的一项,需要添加对应 n 个时期的 n 组数据 
QBarSet *set0 = new QBarSet("Jane");
QBarSet *set1 = new QBarSet("John");
QBarSet *set2 = new QBarSet("Axel");
QBarSet *set3 = new QBarSet("Mary");
QBarSet *set4 = new QBarSet("Samantha");

*set0 << 1 << 2 << 3 << 4 << 5 << 6;
*set1 << 5 << 0 << 0 << 4 << 0 << 7;
*set2 << 3 << 5 << 8 << 13 << 8 << 5;
*set3 << 5 << 6 << 7 << 3 << 4 << 5;
*set4 << 9 << 7 << 5 << 3 << 1 << 2;

QBarSeries *series = new QBarSeries();
series->append(set0);
series->append(set1);
series->append(set2);
series->append(set3);
series->append(set4);

QChart *chart = new QChart();
chart->addSeries(series);
chart->setTitle("Simple barchart example");
// AnimationOption 代表图片刷新时的动画
chart->setAnimationOptions(QChart::SeriesAnimations);

// 为柱状图设置文字横坐标轴和数字竖坐标轴
QStringList categories;
categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun";
QBarCategoryAxis *axisX = new QBarCategoryAxis();
axisX->append(categories);
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);

QValueAxis *axisY = new QValueAxis();
axisY->setRange(0,15);
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);

// 设置图例
chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignBottom);

QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
w.setCentralWidget(chartView);
w.resize(420, 300);
w.show();

水平柱状图

只需要将 QBarSeries 改为 QHorizontalBarSeries 即可。

堆叠柱状图

QBarSeries 改为 QStackedBarSeriesQHorizontalStackedBarSeries 即可。

饼状图

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QChartView *chartView = new QChartView();
    chartView->setRenderHint(QPainter::Antialiasing);
    QChart *chart = chartView->chart();
    chart->legend()->setVisible(false);
    chart->setTitle("Nested donuts demo");
    chart->setAnimationOptions(QChart::AllAnimations);

    qreal minSize = 0.1;
    qreal maxSize = 0.9;
    int donutCount = 5; // 5 个饼图

    for(int i = 0; i < donutCount; i++){
        QPieSeries *donut = new QPieSeries();
        int sliceCount = 3 + QRandomGenerator::global()->bounded(5);
        for(int j = 0; j < sliceCount; j++){
            qreal value = 100 + QRandomGenerator::global()->bounded(100);
            QPieSlice *slice = new QPieSlice(QString("%1").arg(value),value);
            slice->setLabelVisible(true);
            slice->setLabelColor(Qt::white);
            slice->setLabelPosition(QPieSlice::LabelInsideTangential);
            connect(slice, &QPieSlice::hovered, this, &MainWindow::explodeSlice);
            donut->append(slice);
            donut->setHoleSize(minSize+i*(maxSize-minSize)/donutCount);
            donut->setPieSize(minSize + (i + 1) * (maxSize - minSize) / donutCount);
        }
        m_donuts.append(donut);
        chartView->chart()->addSeries(donut);
    }

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->addWidget(chartView,1,1);
    setLayout(mainLayout);

    updateTimer = new QTimer(this);
    connect(updateTimer, &QTimer::timeout, this, &MainWindow::updateRotation);
    updateTimer->start(1250);

    setCentralWidget(chartView);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::updateRotation()
  {
      for (int i = 0; i < m_donuts.count(); i++) {
          QPieSeries *donut = m_donuts.at(i);
          qreal phaseShift =  -50 + QRandomGenerator::global()->bounded(100);
          donut->setPieStartAngle(donut->pieStartAngle() + phaseShift);
          donut->setPieEndAngle(donut->pieEndAngle() + phaseShift);
      }
  }

void MainWindow::explodeSlice(bool exploded)
  {
      QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
      if (exploded) {
          updateTimer->stop();
          qreal sliceStartAngle = slice->startAngle();
          qreal sliceEndAngle = slice->startAngle() + slice->angleSpan();

          QPieSeries *donut = slice->series();
          qreal seriesIndex = m_donuts.indexOf(donut);
          for (int i = seriesIndex + 1; i < m_donuts.count(); i++) {
              m_donuts.at(i)->setPieStartAngle(sliceEndAngle);
              m_donuts.at(i)->setPieEndAngle(360 + sliceStartAngle);
          }
      } else {
          for (int i = 0; i < m_donuts.count(); i++) {
              m_donuts.at(i)->setPieStartAngle(0);
              m_donuts.at(i)->setPieEndAngle(360);
          }
          updateTimer->start();
      }
      slice->setExploded(exploded);
  }

坐标轴类型

  • QValueAxis 数值坐标轴
  • QCategoryAxis 分类坐标轴
  • QBarCategoryAxis 柱状图分类坐标轴
  • QDateTimeAxis 日期坐标轴
  • QLogValueAxis 对数坐标轴

动态曲线

Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags):
    QChart(QChart::ChartTypeCartesian, parent, wFlags),
    m_series(0),
    m_axis(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);
    createDefaultAxes();
    setAxisX(m_axis, m_series);
    m_axis->setTickCount(5);
    axisX()->setRange(0, 10);
    axisY()->setRange(-5, 10);

    m_timer.start();
}

Chart::~Chart()
{

}

void Chart::handleTimeout()
{
    qreal x = plotArea().width() / m_axis->tickCount();
    qreal y = (m_axis->max() - m_axis->min()) / m_axis->tickCount();
    m_x += y;
    m_y = QRandomGenerator::global()->bounded(5) - 2.5;
    m_series->append(m_x, m_y);
    scroll(x, 0);
    if (m_x == 100)
        m_timer.stop();
}

Comments

Content