【PySide6】QChart筆記(二)—— QBarSeries的使用

林風冰翼發表於2023-11-10

一、QBarSeries簡介

1. 官方描述

https://doc.qt.io/qtforpython-6/PySide6/QtCharts/QBarSeries.html
【譯註:官方文件內容過於簡潔,表明完全僅繼承了QAbstractBarSeries,且沒有擴充套件任何屬性、方法和訊號。因此,直接參考QAbstractBarSeries的文件:】
https://doc.qt.io/qtforpython-6/PySide6/QtCharts/QAbstractBarSeries.html

在條形圖中,條形被定義為每個類別(category,x軸劃分的一個區間)包含一個資料值的條形集合(bar set)。條形的位置由類別指定,其高度由資料值指定。包含多個條形集合的條形序列(bar series)將屬於同一類別的條形組合在一起。條形圖的樣式由所選的 QAbstractBarSeries 子類 ( QBarSeries, QStackedBarSeries, QPercentBarSeries, QHorizontalStackedBarSeries, QHorizontalPercentBarSeries, QHorizontalBarSeries ) 來決定。

1.1 屬性

屬性 描述
barWidth 序列中條形的寬度
count 序列中條形集合的數量
labelsAngle 數值標籤的角度(單位為度)。數值標籤是標註在條形上,表示該條形的值的標籤
labelsFormat 數值標籤的顯示格式。透過 setLabelsFormat(str) 方法實現,入參字串中透過'@value'表示引用數值。一個例子:setLabelsFormat('@value km')
labelsPosition 數值標籤的位置
labelsPrecision 數值標籤中顯示的最大有效位數
labelsVisible 數值標籤的可見性。預設為False

1.2 訊號

訊號 描述
hovered(status, index, barset) 滑鼠懸停到條形,或離開條形時觸發,傳送三個資料。滑鼠懸停到條形時,status為True,離開時為False;index的值為條形在條形組(bar set)中的編號;barset的值為對應的QBarSet物件。可以透過 QBarSet.label() 獲取該條形組的名稱,還可以透過 QBarSet.at(index) 獲取該條形的數值

2. 官方用例

https://doc.qt.io/qtforpython-6/examples/example_charts_barchart.html
該用例繪製了每組包含5種資料的條形圖。

image

2.1 建立條形圖

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

"""PySide6 port of the linechart example from Qt v6.x"""

import sys

from PySide6.QtCharts import (QBarCategoryAxis, QBarSeries, QBarSet, QChart,
                              QChartView, QValueAxis)
from PySide6.QtCore import Qt
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import QApplication, QMainWindow


class TestChart(QMainWindow):
    def __init__(self):
        super().__init__()

        # 建立QBarSet。每組條形有5種資料,因此需要建立5個
        self.set_0 = QBarSet("Jane")
        self.set_1 = QBarSet("John")
        self.set_2 = QBarSet("Axel")
        self.set_3 = QBarSet("Mary")
        self.set_4 = QBarSet("Samantha")

        # 向各QBarSet新增資料
        self.set_0.append([1, 2, 3, 4, 5, 6])
        self.set_1.append([5, 0, 0, 4, 0, 7])
        self.set_2.append([3, 5, 8, 13, 8, 5])
        self.set_3.append([5, 6, 7, 3, 4, 5])
        self.set_4.append([9, 7, 5, 3, 1, 2])

        # 建立QBarSeries,並將QBarSet加入其中
        self.series = QBarSeries()
        self.series.append(self.set_0)
        self.series.append(self.set_1)
        self.series.append(self.set_2)
        self.series.append(self.set_3)
        self.series.append(self.set_4)

        # 建立QCahrt,並與QBarSeries繫結
        self.chart = QChart()
        self.chart.addSeries(self.series)
        self.chart.setTitle("Simple barchart example")
        self.chart.setAnimationOptions(QChart.SeriesAnimations)

        # 不同於QXYSeries,條形圖的x軸標籤直接與QBarCategoryAxis繫結
        self.categories = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
        self.axis_x = QBarCategoryAxis()
        self.axis_x.append(self.categories)
        # 將x軸先後與QChart、QBarSeries繫結
        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.series.attachAxis(self.axis_x)

        self.axis_y = QValueAxis()
        self.axis_y.setRange(0, 15)
        # 將y軸先後與QChart、QBarSeries繫結
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)
        self.series.attachAxis(self.axis_y)

        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignBottom)

        self._chart_view = QChartView(self.chart)
        self._chart_view.setRenderHint(QPainter.Antialiasing)

        self.setCentralWidget(self._chart_view)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = TestChart()
    window.show()
    window.resize(420, 300)
    sys.exit(app.exec())

二、實踐

1. 用例說明

用每日收支資料繪製條形圖。

image

2. 程式碼實現

from PySide6.QtCharts import QChart, QChartView, QBarSet, QBarSeries, QBarCategoryAxis
from PySide6.QtGui import QPainter
from PySide6.QtCore import Qt, QDateTime
from PySide6.QtWidgets import QApplication, QMainWindow

app = QApplication([])
window = QMainWindow()
chart = QChart()
chart.setTitle('收支統計')

# 準備資料
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)]

# 建立條形圖集合set【每個set都是條形圖中一種待展示的資料類別】
bar_set_expense = QBarSet('支出')
bar_set_income = QBarSet('收入')
# 將值傳入條形圖集合set【透過列表的形式,存入某種資料類別的一組數值】
for i in range(5):
    bar_set_expense.append(axisY_value1[i])
    bar_set_income.append(axisY_value2[i])

# 建立條形圖序列series【series只與QChart有關,而與待展示資料的種類無關,因此只需建立一個】
bar_series = QBarSeries()
# 將資料集合存入series
bar_series.append(bar_set_expense)
bar_series.append(bar_set_income)

# 將series加入chart
chart.addSeries(bar_series)

# 嘗試另一種座標建立方法:先建立預設座標系,再建立自定義座標軸,最後僅替換對應的座標軸
chart.createDefaultAxes()

# 建立條形圖座標軸
axis_x = QBarCategoryAxis()
# 必須是字串列表,以便得到預期格式
axis_x.append([date.toString("yyyy/MM/dd") for date in axisX_date])
# 替換原x軸
chart.removeAxis(chart.axes(Qt.Orientation.Horizontal)[0])
chart.addAxis(axis_x, Qt.AlignBottom)
bar_series.attachAxis(axis_x)

# 【注意】也許你會在網上看到如下方法的使用。具體情況參考“三、問題與總結”
# 未來將不支援的方法:chart.removeAxis(chart.axisX())
# 未來將不支援的方法:chart.setAxisX(axis_x, bar_series)

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

三、問題與總結

1. DeprecationWarning: Function: 'QChart.axisX(QAbstractSeries * series) const' is marked as deprecated, please check the documentation for more information

問題描述

當呼叫QChart.axisX()Qchart.setAxisX()等方法時,得到該警告。原因是這些方法被標記為“不支援的方法”,應當透過其他方法實現。

解決方法

對於QChart.axisX()

替換為:

# defination
def Qchart.axes(self, orientation: Orientation,
                series: QAbstractSeries | None) -> list[QAbstractAxis]

# example: 獲取第一個水平座標軸
axis_x = chart.axes(Qt.Orientation.Horizontal)[0]

對於QChart.setAxisX()

沒有對應的方法可以替代,但可以更改為以下幾個步驟:

# defination
chart.removeAxis(chart.axes(Qt.Orientation.Horizontal)[0])
chart.addAxis(axis_x, Qt.AlignBottom)
series.attachAxis(axis_x)

2. 更新條形圖的正確程式碼順序

問題描述

當需要展示多種意義的資料時,不同於QXYSeries及其派生類序列,條形圖QChart只需與一個QBarSeries繫結。在這個唯一的QBarSeries中,多種意義的資料透過多個QBarSet表示。

當更新條形圖時,只需更新QBarSeries內的各個QBarSet(與其說是更新,更像是丟棄重造)。因此,實現該過程的程式碼會與QXYSeries類的圖表有所差異(主要在於資料傳入的部分)。

解決方法

  • 清除 QBarSeries 物件中的舊資料,即呼叫QBarSeries.clear()
  • 對於每種資料,建立 QBarSet 物件,並將資料寫入 QBarSet 物件, 即呼叫QBarSet.append()
  • 將每個 QBarSet 物件加入 QBarSeries 物件, 即呼叫QBarSeries.append()
  • 【僅需一次】將 QBarSeries 物件與 QChart 物件繫結,即呼叫QChart.addSeries()
  • 刪除舊座標軸 QAbstractAxis 物件,並重新建立。其中,x軸為 QBarCategoryAxis 物件
  • 將新 QAbstractAxis 物件與 QChart 物件繫結,即呼叫QChart.addAxis()
  • 將新 QAbstractAxis 物件與 QBarSeries 物件繫結,即呼叫QBarSeries.attachAxis()

相關文章