共计 5244 个字符,预计需要花费 14 分钟才能阅读完成。
文章首发自公众号::nullobject。个人站点:https://www.nullobject.cn
这篇文章主要介绍 QXYSeries 类的 API 及其使用
0x00 前言
QXYSeries 作为 QAbstractSeries 的派生类之一,主要负责实现以二维点集为数据源,坐标类型为二维坐标系的图表类型,包括折线图、曲线图和散点图等。QXYSeries 封装了大量对数据源进行增删改操作的函数和信号,同时内部实现了控制数据点在坐标系上的显示形态 (点标签的格式、颜色、是否显示等) 的功能。
0x01 显示和控制点标签
Qt 图表中的点标签指的是数据点在图表上对应位置的附加信息显示(比如点的坐标或者其他信息):
QXYSeries 类中提供了 pointLabels* 开头的五个属性及对应的方法和信号来操作点标签的显示风格。
pointLabelsClipping : 获取和设置是否对点标签超出绘图区域边缘的部分进行裁剪:
bool pointLabelsClipping() const;
void setPointLabelsClipping(bool enabled = true);
效果:
可以看到,当设置 pointLabelsClipping 属性为 true(默认值)时,点标签超出绘图区域的部分会被裁剪掉。
pointLabelsClipping 属性发生改变时,会触发 pointLabelsClippingChanged(bool clipping)信号。
pointLabelsColor : 获取和设置点标签的显示颜色:
QColor pointLabelsColor() const;
void setPointLabelsColor(const QColor &color);
效果:
上图分别为将 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 属性发生改变时,会触发 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 头文件内:
增加数据
可以调用 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> 的重载方法实现对数据的更新,无形中多了一个数据转换的步骤,拉低性能:
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.