QtCharts之QXYSeries类

这篇文章主要介绍QXYSeries类的API及其使用

0x00 前言

QXYSeries作为QAbstractSeries的派生类之一,主要负责实现以二维点集为数据源,坐标类型为二维坐标系的图表类型,包括折线图曲线图散点图等。QXYSeries封装了大量对数据源进行增删改操作的函数和信号,同时内部实现了控制数据点在坐标系上的显示形态(点标签的格式、颜色、是否显示等)的功能。

0x01 显示和控制点标签

Qt图表中的点标签指的是数据点在图表上对应位置的附加信息显示(比如点的坐标或者其他信息):

QtCharts点标签

QXYSeries类中提供了**pointLabels***开头的五个属性及对应的方法和信号来操作点标签的显示风格。

  • pointLabelsClipping : 获取和设置是否对点标签超出绘图区域边缘的部分进行裁剪:
bool pointLabelsClipping() const;
void setPointLabelsClipping(bool enabled = true);

pointsLabelsClipping属性效果

可以看到,当设置pointLabelsClipping属性为true(默认值)时,点标签超出绘图区域的部分会被裁剪掉。

pointLabelsClipping属性发生改变时,会触发pointLabelsClippingChanged(bool clipping)信号。

  • pointLabelsColor : 获取和设置点标签的显示颜色
QColor pointLabelsColor() const;
void setPointLabelsColor(const QColor &color);

pointLabelsColor属性效果

上图分别为将pointLabelsColor属性设置为黑色和绿色的效果。看到这您有可能会有疑问:咦怎么只改变了坐标的颜色,而折线上点的颜色没有变?注意了,这里的点标签是指图表上对数据点加以说明的信息部分,不包括点本身的显示。

pointLabelsColor属性发生改变时,会触发pointLabelsColorChanged(const QColor &color)信号。

  • pointLabelsFont : 获取和设置点标签的显示字体
QFont pointLabelsFont() const;
void setPointLabelsFont(const QFont &font);

该属性通过一个QFont设置来实现:

// 获取pointLabels默认字体
QFont font = series->pointLabelsFont();
// 加粗
font.setBold(true);
// 斜体
font.setItalic(true);
// 设置字体大小
font.setPointSize(12);
// 设置字体为Arial类型
font.setFamily(QStringLiteral("Arial"));
// 更新pointLabels字体
series->setPointLabelsFont(font);

pointLabelsFont设置

pointLabelsFont属性发生改变时,会触发pointLabelsFontChanged(const QFont &font)信号。

  • pointLabelsFormat : 获取和设置点标签的显示格式
QString pointLabelsFormat() const;
void setPointLabelsFormat(const QString &format);

QXYSeries类提供了两个占位符来设置点标签显示真实坐标数据:

@xPoint X轴坐标
@yPoint Y轴坐标

例如,我们需要以:(15,23)这样的格式来显示点标签,可以这样设置,即可实现上文中点标签的显示格式效果:

// 设置点标签格式
series->setPointLabelsFormat(QStringLiteral("(@xPoint,@yPoint)"));

pointLabelFormat属性默认被设置为没有括号的坐标格式:@xPoint, @yPoint

pointLabelsFormat属性发生改变时,会触发pointLabelsFormatChanged(const QString &format)信号。

  • pointLabelsVisible : 获取和设置显示/隐藏点标签:
bool pointLabelsVisible() const;
void setPointLabelsVisible(bool visible=true);

该属性默认被设置为false。

// 隐藏点标签
series->setPointLabelsVisible(false);

隐藏点标签

pointLabelsVisible属性发生改变时,会触发pointLabelsVisibilityChanged(bool visible)信号。

  • pointsVisible : 获取和设置显示/隐藏点在图表上的标注
bool pointsVisible() const;
void setPointsVisible(bool visible=true);

pointsLabelVisible不同,pointsLabelVisible设置的是点标签的显示,而pointsVisible设置的是点本身在图表上的位置标注

该属性默认被设置为false。对比显示和隐藏点标注效果:

显示和隐藏点标注

0x02 对数据源进行增删改

QXYSeries类中封装了一系列的重载方法用于操作图表的数据源进行增删改。QXYSeries内部用一个模板类型为QPointF的QVector来持有数据源,其声明在qxyseries_p.h头文件内:

QVector<QPointF>声明

  • 增加数据

可以调用append的对应重载方法,或者是重载操作符<<添加单个点或者整个点集到数据源的尾部:

void append(qreal x, qreal y);
void append(const QPointF &point);
void append(const QList<QPointF> &points);
QXYSeries& operator<<(const QPointF &point);
QXYSeries& operator<<(const QList<QPointF> &points);

也可以调用insert方法将数据点插入到数据源的指定位置:

void insert(int index, const QPointF &point);

对应地,不管以何种方式,成功添加数据后都会触发pointAdded(int index)信号,index为被添加的数据的下标。需要注意:每添加一个点就会触发一次pointAdded信号,即批量添加数据时会触发对应次数的pointAdded。

  • 删除数据

QXYSeries提供了remove重载方法和removePoints方法来移除目标数据:

// 删除指定坐标的点
void remove(qreal x, qreal y);
void remove(const QPointF &point);
// 删除指定索引下标的点
void remove(int index);
// 从下标index位置开始,删除count数量的点
void removePoints(int index, int count);

增加数据类似,成功删除数据后会触发对应次数的pointRemoved(int index)信号,index为被删除的数据的下标。

  • 修改数据源

修改数据源通过replace系列的重载方法实现:

// 替换具体坐标的点
void replace(qreal oldX, qreal oldY, qreal newX, qreal newY);
void replace(const QPointF &oldPoint, const QPointF &newPoint);
// 替换指定索引下标的点
void replace(int index, qreal newX, qreal newY);
void replace(int index, const QPointF &newPoint);
// 替换整个数据源
void replace(QList<QPointF> points);
void replace(QVector<QPointF> points);

当只替换数据源的局部数据点(调用前四种重载方法)时,会触发pointReplaced(int index)信号,index为被替换的点的下标;而当替换了整个数据源数据(调用后两种方法)时,则会触发pointsReplaced()信号。

需要注意:调用replace后两种重载替换整个数据源时,传QList还是传QVector在性能上是有所区别的:直接传入QList结构的数据集远远比逐个点替换或者先清除所有点再添加这两种方式的效率要高。而直接传入QVector结构的数据集又比前者效率要高。因此:需要批量更新数据点时,应当优先选择void replace(QVector<QPointF> points);这个重载方法。究其原因:本小节一开始就介绍了,QXYSeries内部持有数据源的数据结构为QVector<QPointF>,而参数为QList<QPointF>的重载方法的内部实现也是将QList转换为QVector,再调用参数为QVector<QPointF> 的重载方法实现对数据的更新,无形中多了一个数据转换的步骤,拉低性能:

replace重载方法实现

Tips:QList和QVector两个容器本身的效率上也是有区别的,Qt官方推荐优先使用QVector。详细可以参考QVector的官方文档介绍。

  • 获取数据源

QXYseries提供了返回类型分别为QList<QPointF>QVector<QPointF>的方法获取数据源:

QList<QPointF> points() const;
QVector<QPointF> pointsVector() const;

与更新replace方法批量更新数据源的重载类似,points()方法获取数据源的方法,内部实现也是获取原始数据源再将其转为QList类型返回;pointsVector()方法则直接返回原始的QVector数据源。

0x03 QXYSeries中的鼠标操作

QXYSeries共提供了个鼠标事件响应信号,分别是:单击clicked、双击doubleClicked、鼠标键按下pressed,鼠标键松开released、光标移到图表线上或从图表移开hovered:

series->connect(series,&QLineSeries::clicked,[](const QPointF& point){
  qDebug() << "point clicked:" << point;
});
series->connect(series,&QLineSeries::doubleClicked,[](const QPointF& point){
  qDebug() << "point double clicked:" << point;
});
series->connect(series,&QLineSeries::hovered,[](const QPointF& point,bool state){
  qDebug() << "point hovered:" << point << " state:" << (state?"光标移动到图表上":"光标从图表上移开");
});
series->connect(series,&QLineSeries::pressed,[](const QPointF& point){
  qDebug() << "point pressed:" << point;

});
series->connect(series,&QLineSeries::released,[](const QPointF& point){
  qDebug() << "point released:" << point;
});

运行程序,执行光标移动到图表序列上->左键双击->光标从图表序列上方移开,结果如下:

point hovered: QPointF(4.58291,8.71473)  state: moved to series
point pressed: QPointF(4.58291,8.71473)
point released: QPointF(4.58291,8.71473)
point clicked: QPointF(4.58291,8.71473)
point double clicked: QPointF(4.58291,8.71473)
point pressed: QPointF(4.58291,8.71473)
point released: QPointF(4.58291,8.71473)
point clicked: QPointF(4.58291,8.71473)
point hovered: QPointF(4.71022,8.18182)  state: moved from series

0x04 The End.

Last modification:September 12th, 2019 at 05:30 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment