上两篇记录了Qt的基本绘图QPainter和Graphics View绘图架构。还是在Qt4的时代,我用这个QPainter写了一个Plot类,实现了简单的二维曲线动态绘制。后来,我接触到了qwt,一款强大的第三方库Qt图表库,再后来,Qt5官方把Qt Charts和Data Visualization开放在了社区版里,使得我们可以轻松采用这两款官方插件实现二维和三维图表绘制了。接下来的几篇将总结这几个图表插件,本篇是Qt Charts。
概述
Qt Charts是Qt提供的图表模块,在Qt5.7之前只有商业版才有,但是从Qt5.7之后,社区版本也包含了Qt Charts。Qt Charts可以很方便的绘制常见的折线图、柱状图、饼图等图表。它基于Qt的Graphics View架构,核心组件是QChartView和QChart。其中QChartView正是继承于QGraphicsView类,因此它也可以作为Graphics View中的视图组件。另一个QChart则由QGraphicsWidget继承而来,继续向上追溯,发现他们都继承于QGraphicsItem,所以QChart是图形项。
要使用Qt Charts,需要在pro文件中增加Qt += charts,在代码中需要添加QtCharts头文件和命名空间。
#include <QtCharts>
using namespace QtCharts;
或用宏定义:
Qt_CHARTS_USE_NAMESPACE
在使用时可以设置不同的主题,修改颜色和属性,或是动态显示曲线。使用的基本流程如下:
//在mainwindow或是widget上新建QChartView
QChartView *chartView = new QChartView(this);
chartView->resize(400,300);
//创建QChart,进行简单设置
QChart *chart = new QChart();
chart->legend()->hide(); //隐藏图例
chart->setTitle("simple"); //设置标题
chart->createDefaultAxes(); //创建默认坐标轴
chartView->setChart(chart); //chart添加到chartView
//创建曲线
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);
//把曲线添加到图表
chart->addSeries(series);
图表类别
Qt Charts模块提供以下几类图表类型,每个图表都通过QAbstractSeries来实现具体形式。具体来说就是把series类的对象添加到QChart or ChartView对象中。当然,你可以在一个图表中混合添加多种曲线。
QLineSeries* series = new QLineSeries();
series->add(0, 6);
series->add(2, 4);
...
chartView->chart()->addSeries(series);
chartView->chart()->createDefaultAxes();
1.直线和曲线
用点连接起来的直线,曲线通过QPainterPath连接。直线实现的时候通过QLineSeries类,曲线通过QSplineSeries实现。
2.面积和散点图
面积图表把数据显示为用两条线围成的一个面积区域,散点图则把数据显示为一些列点的集合。通过QAreaSeries类实现面积图,默认情况下,X轴被认为是一个边界,另一个边界用QLineSeries实现。通过QScatterSeries类实现散点图。
3.柱状图
柱状图把数据显示为垂直或水平的柱状形状。通过QBarSet类实现柱状图中的柱形,QAbstractBarSeries类是所有柱形形状类的基类,QBarSeries用来表示其中垂直柱形的数据,QHorizontalBarSeries用来表示水平柱形的数据。QStackedBarSeries用来表示垂直的堆叠柱状图。QHorizontalStackedBarSeries则用来表示水平的堆叠状图。QPercentBarSeries用来表示垂直的百分比柱状图,而QHorizontalPercentBarSeries用来表示水平的百分比柱状图。
4.饼图
饼图是把数据显示为饼状,且分割为若干片。通过QPieSeries类实现。饼图可以设置中间为空,孔洞大小可以被设置为0.0-1.0。
5.火柴盒图和烛台图
如果你玩股票,那么你应该很熟悉烛台图,如果你不知道它,请自行百度。大概就是柱形图和曲线图的结合,通过QBoxPlotSeries和QBoxSet类来实现。
6.极坐标图
用圆形极坐标系表征数据,数据用距离和角度表示。QPolarChart是QChart的一个子类,主持直线,曲线,面积和散点图。同时还支持所有的坐标轴类型。
坐标轴(Axes)
Qt Charts支持以下坐标轴类型:
- 数值坐标轴
- 分组数值坐标轴
- 类别坐标轴
- 日期时间坐标轴
- 对数数值坐标轴
坐标轴可以被设置为刻度线,网格和投影,数据被按照刻度绘制在特定位置。所有的坐标轴类都是从QAbstractAxis基类继承而来,QValueAxis类实现了数值坐标轴,可以作为数值型数据的坐标轴;QCategoryAxis类实现了分组数值坐标轴,可以为数值范围设置文字标签;QBarCategoryAxis类类似于QCategoryAxis,实现了类别坐标轴,用字符串为作为坐标轴的刻度,用于图表的非数值坐标轴;QDateTimeAxis类实现了日期时间坐标轴,作为日期时间数据的坐标轴;QLogValueAxis类实现了对数数值坐标轴,所谓数值数据的对数坐标轴,可以设置对数的基。
图表可以定义多个坐标轴,坐标轴可以被放置于图表的上、左、右、下等区域。
图例(Legend)
图例是对图表上的显示序列的示例说明。图例的对象本身不可以被创建和删除,但是可以通过QChart控制它。当数据序列发生改变时,QChart or ChartView会自动更新图例。
图例可以被放置到图表的上、左、右、下位置,默认的,图例附着在图表视图上,也可以把它分离出来,独立成一个窗体,从而实现自由放置。
可以隐藏单个条目或者整个图例标签。图例的标签用QLegendMarker基类和其派生类管理,这些派生类主要有:QAreaLegendMarker, QBarLegendMarker, QBoxPlotLegendMarker, QCandlestickLegendMarker, and QXYLegendMarker.
交互
这里罗列一些常见的交互形式。
1.动态绘制数据
可以动态向图表添加数据,并使得图表自动滚动以显示新的数据。这就是常说的动态曲线。实际上只需要定时器的响应函数中增加坐标轴的变化即可:
Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags) :
QChart(QChart::ChartTypeCartesian, parent, wFlags),
m_series(0),
m_axis(new QValueAxis),
m_x(0),
m_y(0)
{
qsrand((uint)QTime::currentTime().msec());
QObject::connect(&m_timer, &QTimer::timeout, this, &Chart::handleTimeout);
m_timer.setInterval(200);
m_series = new QSplineSeries();
addSeries(m_series);
createDefaultAxes();
setAxisX(m_axis, m_series);
axisX()->setRange(0, 20); //初始化X轴区域
axisY()->setRange(-2.5, 2.5);//初始化y轴区域
m_timer.start();
}
Chart::~Chart()
{
}
void Chart::handleTimeout()
{
m_y = sin(m_x);
m_series->append(m_x, m_y);
if (m_x>20)
{
axisX()->setRange(0, m_x + 5); //坐标轴自适应
}
if (m_x == 100)
m_timer.stop();
m_x++;
}
2.下钻数据
对于柱状图或者饼图,当用户选择了表格中的某个项目时,一个更加详细的视图会显示出来,这个过程叫做下钻数据。
DrilldownChart *chart = new DrilldownChart();
chart->setTheme(QChart::ChartThemeLight);
chart->setAnimationOptions(QChart::AllAnimations);
chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignRight);
QPieSeries *yearSeries = new QPieSeries(&window);
yearSeries->setName("Sales by year - All");
const QStringList months = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
const QStringList names = {
"Jane", "John", "Axel", "Mary", "Susan", "Bob"
};
for (const QString &name : names) {
QPieSeries *series = new QPieSeries(&window);
series->setName("Sales by month - " + name);
for (const QString &month : months)
*series << new DrilldownSlice(qrand() % 1000, month, yearSeries);
QObject::connect(series, &QPieSeries::clicked, chart, &DrilldownChart::handleSliceClicked);
*yearSeries << new DrilldownSlice(series->sum(), name, series);
}
QObject::connect(yearSeries, &QPieSeries::clicked, chart, &DrilldownChart::handleSliceClicked);
chart->changeSeries(yearSeries);
在handleSliceClicked响应中再次调用changeSeries函数。
3.缩放和滚动
用户可以使用键盘进行缩放或滚动操作。QRubberBand可以被用来选择要被缩放的区域。
bool Chart::gestureEvent(QGestureEvent *event)
{
if (QGesture *gesture = event->gesture(Qt::PanGesture)) {
QPanGesture *pan = static_cast<QPanGesture *>(gesture);
QChart::scroll(-(pan->delta().x()), pan->delta().y());
}
if (QGesture *gesture = event->gesture(Qt::PinchGesture)) {
QPinchGesture *pinch = static_cast<QPinchGesture *>(gesture);
if (pinch->changeFlags() & QPinchGesture::ScaleFactorChanged)
QChart::zoom(pinch->scaleFactor());
}
return true;
}
4.鼠标响应
可以设计信号和槽函数来实现鼠标点击或者浮动在图表上的操作。这使得你可以为图表增加元素,比如标注:
主题
Qt Charts提供了丰富的主题,集合了特定的颜色、画笔、画刷以及字体,另外还有坐标轴、标题和图例,让图表以更加美化的方式显示出来。
- Light theme, which is the default theme
- Cerulean blue theme
- Dark theme
- Sand brown theme
- Natural color system (NCS) blue theme
- High contrast theme
- Icy blue theme
- Qt theme
这些主题也可以被在源码中定制修改。另外,要注意,更换主题后,之前的修改将会被覆盖重写,所以还是建议参照源码中的例子,自己编写代码控制主题。
例子
这里放两个例子,一个是上面这个主题的例子,它里面同时也包含了各种图表绘制方法:
chartthemes,另外一个是自定义图标例子:customchart https://download.csdn.net/download/bjtuwayne/12033059