【PySide6】QChart笔记(一)—— 用QDateTimeAxis作为x轴绘制多条折线图

一、QDateTimeAxis简介

1. 官方描述

https://doc.qt.io/qtforpython-6/PySide6/QtCharts/QDateTimeAxis.html

QDateTimeAxis可以用作带有刻度线、网格线以及阴影的轴。可以通过设置适当的日期时间格式来配置标签。QDateTimeAxis有效的时间范围为4714 BCE(公元前4714)到287396 CE(公元287396)。对于其他有关于QDateTime的限制,请参考QDateTime的官方文档。

1.1 属性

属性 描述
format 从QDateTime对象创建轴标签时使用的字符串
max 轴的最大值
min 轴的最小值
tickCount 轴的刻度线数量

1.2 信号

信号 描述
formatChanged 属性format值改变时触发
maxChanged 属性max值改变时触发
minChanged 属性min值改变时触发
rangeChanged 属性max、min值改变时触发
tickCountChanged 属性tickCount值改变时触发

1.3 使用方法

QDateTimeAxis可以与所有QXYSeries(QScatterSeries, QLineSeries, QSplineSeries)搭配使用。使用时,通过调用toMSecsSinceEpoch()方法向series中加点。

2. 官方用例

https://doc.qt.io/qtforpython-6/overviews/qtcharts-datetimeaxis-example.html

【官方警告】本节包含从C++自动转换为Python的片段,其中可能包含错误

【译注:事实上示例代码确实有问题 😆 】。

2.1 创建折线图

为了创建折线图,我们需要使用QLineSeries对象:

Python 复制代码
series = QLineSeries()

在图表中,我们将展示太阳黑子的数量随时间变化的情况,该数据从文本文件中读入。数据来源于Space Weather Prediction Center。

在下面的代码片段中,请注意如何使用toMSecsSinceEpoch()方法将QDateTime对象转换为可以传递给QLineSeries.append()方法的数字类型【译注:相当于整数型或浮点型时间戳】。

Python 复制代码
# 获取数据,存入series
# data from http://www.swpc.noaa.gov/ftpdir/weekly/RecentIndices.txt
# http://www.swpc.noaa.gov/ftpdir/weekly/README
# http://www.weather.gov/disclaimer
sunSpots = QFile(":sun_spots")
if not sunSpots.open(QIODevice.ReadOnly | QIODevice.Text):
    m_loadError = "Failed to load '%1' file.".arg(sunSpots.fileName())
    return False

stream = QTextStream(sunSpots)
while not stream.atEnd():
    line = stream.readLine()
    if line.startsWith("#") or line.startsWith(":"):
        continue
    values = line.split(' ', Qt.SkipEmptyParts)
    momentInTime = QDateTime()
    momentInTime.setDate(QDate(values[0].toInt(), values[1].toInt() , 15))
    # 下面这句为官方给出的语句,会报错 OverflowError: Python int too large to convert to C long
    # 请参考 "三、问题与总结" 中的内容
    # series.append(momentInTime.toMSecsSinceEpoch(), values[2].toDouble())

    # 解决方式如下
    series.append(numpy.int64(momentInTime.toMSecsSinceEpoch()), values[2].toDouble())

sunSpots.close()

为了在图表上显示数据,我们需要QChart实例。我们向其中添加series,隐藏图例(legend),创建默认轴,并设置图表的标题。

Python 复制代码
chart = QChart()
chart.addSeries(series)
chart.legend().hide()
chart.setTitle("Sunspots count (by Space Weather Prediction Center)")

由于我们使用的是QLineSeries对象,调用createDefaultAxes()方法将创建QValueAxis对象作为X轴和Y轴。要使用QDateTimeAxis,我们需要将它手动设置到图表中【意思是,不能图省事,直接调用 createDefaultAxes() 方法了。其中 createDefaultAxes() 方法会根据 QChart 对象已绑定的 QAbstractSeries 类型自动重新创建合适的坐标轴】。

首先,创建QDateTimeAxis的实例,然后设置要显示的刻度数。太阳黑子数量的含义是某月的平均值,因此,轴标签中无需包含时间(time)和日号(day)的信息,可以通过设置自定义标签格式来实现只展示年月。

更多自定义标签格式请参考QDateTime.toString()方法文档来了解可用的格式选项。

【译注:以下代码中,MMM表示缩写的本地化月份名称(e.g. 'Jan' to 'Dec')。可在 QDateTime.toString() 方法文档中查阅】

Python 复制代码
axisX = QDateTimeAxis()
axisX.setTickCount(10)
axisX.setFormat("MMM yyyy")
axisX.setTitleText("Date")
chart.addAxis(axisX, Qt.AlignBottom)
series.attachAxis(axisX)
axisY = QValueAxis()
axisY.setLabelFormat("%i")
axisY.setTitleText("Sunspots count")
chart.addAxis(axisY, Qt.AlignLeft)
series.attachAxis(axisY)

然后我们创建一个QChartView对象,将chart作为参数。这样我们就不需要自己创建QGraphicsView场景了。我们还设置了抗锯齿功能,让渲染后的线条看起来更漂亮。

Python 复制代码
createDefaultChartView(chart)

现在可以展示图表了。

二、实践

1. 用例说明

在同一个QChart中显示两条折线,其中x轴为QDateTimeAxis类型。

2. 代码实现

Python 复制代码
from PySide6.QtCharts import QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis
from PySide6.QtGui import QPainter
from PySide6.QtCore import Qt, QDateTime
from PySide6.QtWidgets import QApplication, QMainWindow
import numpy as np

app = QApplication([])
window = QMainWindow()
chart = QChart()
chart.setTitle("Two Lines Chart")

# 准备数据
axisX_date = [QDateTime.currentDateTime().addDays(i) for i in range(5)]
axisY_value1 = [10 - 2 * i for i in range(5)]
axisY_value2 = [5 + i * (-1) ** i for i in range(5)]

series1 = QLineSeries()
for i in range(5):
    series1.append(np.int64(axisX_date[i].toMSecsSinceEpoch()), axisY_value1[i])

series2 = QLineSeries()
for i in range(5):
    series2.append(np.int64(axisX_date[i].toMSecsSinceEpoch()), axisY_value2[i])

# 将series添加到chart中
chart.addSeries(series1)
chart.addSeries(series2)

# 创建x轴
axisX = QDateTimeAxis()
axisX.setFormat("yyyy/MM/dd")
axisX.setTitleText("Date")
axisX.setTickCount(5)
axisX.setRange(QDateTime.currentDateTime(), QDateTime.currentDateTime().addDays(4))
# 将x轴与chart和series绑定
chart.addAxis(axisX, Qt.AlignBottom)
series1.attachAxis(axisX)
series2.attachAxis(axisX)

# 创建y轴
axisY = QValueAxis()
axisY.setTitleText("Value")
axisY.setRange(0, 10)
# 将y轴与chart和series绑定
chart.addAxis(axisY, Qt.AlignLeft)
series1.attachAxis(axisY)
series2.attachAxis(axisY)

# 显示图表
chartView = QChartView(chart)
chartView.setRenderHint(QPainter.Antialiasing)
window.setCentralWidget(chartView)
window.show()
app.exec()

三、问题与总结

1. OverflowError: Python int too large to convert to C long

问题描述

根据官方文档的指引,将QDateTime对象加入QChart图表的series时,需要用toMSecsSinceEpoch()方法转换为数值型,C++中完全没有问题,但在Python中却会出现该错误。

解决方法

通过numpy.int64()将调用toMSecsSinceEpoch()后过大的值转换为numpy的Int64类型,然后再传给QLineSeries.append()方法即可。即调用:

Python 复制代码
numpy.int64(QDateTime().toMSecsSinceEpoch())

2. 更新series后,图像或坐标轴缺失

问题描述

修改series中的点集后,重新绘制QChart时,出现仅显示折线而不显示坐标轴、或仅显示坐标轴而不显示折线图的情况。

解决方法

其原因是,绘制图像时,各对象的创建、绑定顺序不正确,同时坐标轴也需要重新创建。应当遵循如下顺序:

  • 清除 QXYseries 对象中的旧数据,即调用QXYseries.clear()
  • 将数据写入 QXYseries 对象, 即调用QXYseries.append()
  • 【仅需一次】将 QXYseries 对象与 QChart 对象绑定,即调用QChart.addSeries()
  • 删除旧坐标轴 QAbstractAxis 对象,并重新创建
  • 将新 QAbstractAxis 对象与 QChart 对象绑定,即调用QChart.addAxis()
  • 将新 QAbstractAxis 对象与 QXYseries 对象绑定,即调用QXYseries.attachAxis()

对于一般的坐标轴类型,可直接调用 QChart.createDefaultAxes() 方法,相当于直接完成了后面三步。而对于 QDateTimeAxis 类型的坐标轴,需要自行实现类似的方法。一个例子:

Python 复制代码
    def createDateTimeAxis(self, x_range, y_range):
        """
        Describe: 更新series后需要重新创建坐标轴标轴,这样才能展示出新的series;
                  又因为x轴类型为QDateTimeAxis,无法直接调用QChart.createDefaultAxes()方法创建坐标轴,
                  因此需要自行实现该方法

        Args:
            x_range: tuple[QDateTime]
                x轴范围
            y_range: tuple[float]
                y轴范围
        """
        # 先删除旧坐标轴
        self.removeAxis(self._axisX)
        self.removeAxis(self._axisY)
        # 创建x轴
        self._axisX = QDateTimeAxis()
        self._axisX.setRange(x_range[0], x_range[1])
        self._axisX.setFormat("yyyy/MM")
        # 创建y轴
        self._axisY = QValueAxis()
        self._axisY.setRange(y_range[0], y_range[1])
        # 将新坐标轴与QChart和series绑定
        self.addAxis(self._axisX, Qt.AlignBottom)
        self.addAxis(self._axisY, Qt.AlignLeft)
        for series in self.list_series_line:
            series.attachAxis(self._axisX)
            series.attachAxis(self._axisY)

3. 同一QChart显示多条折线时,其中一条显示不完全

问题描述

同一QChart显示多条折线时,y轴的范围限定在第一条折线series点集的数值范围中,导致另一条显示不完全。

解决方法

每次更新series后,在自定义的createDateTimeAxis()方法中,通过形参y_range,传入合适的范围。

相关推荐
爱打球的白师傅1 小时前
python机器学习工程化demo(包含训练模型,预测数据,模型列表,模型详情,删除模型)支持线性回归、逻辑回归、决策树、SVC、随机森林等模型
人工智能·python·深度学习·机器学习·flask·逻辑回归·线性回归
MediaTea2 小时前
Python 第三方库:TensorFlow(深度学习框架)
开发语言·人工智能·python·深度学习·tensorflow
Joker-Tong2 小时前
大模型数据洞察能力方法调研
人工智能·python·agent
B站计算机毕业设计之家2 小时前
基于Python+Django+双协同过滤豆瓣电影推荐系统 协同过滤推荐算法 爬虫 大数据毕业设计(源码+文档)✅
大数据·爬虫·python·机器学习·数据分析·django·推荐算法
逻极2 小时前
Webhook 全解析:事件驱动时代的实时集成核心技术
python·web
程序员三藏2 小时前
一文了解UI自动化测试
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
极客代码2 小时前
第七篇:深度学习SLAM——端到端的革命--从深度特征到神经辐射场的建图新范式
人工智能·python·深度学习·计算机视觉·slam·回环检测·地图构建
larance3 小时前
python中的鸭子类型
开发语言·python
陈辛chenxin3 小时前
【大数据技术04】数据可视化
大数据·python·信息可视化
大邳草民3 小时前
深入理解 Python 的属性化方法
开发语言·笔记·python