2026-09-04

QQuickWidget でトレンドチャート 〜 PySide6

PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。

QChart を含む QtCharts モジュールは、Qt 6.10 / PySide6 6.10 以降、公式に非推奨となりました[1] [2]。

従来 QChart で作成していたプロットを(Matplotlib や PyQtGraph を利用せずに)新しいやり方で作成できたので、前回に引き続き、備忘録的にサンプルを紹介します。

今回は、折れ線グラフです。時間の経過に伴うデータの変化や傾向(トレンド)を表すことを意識して、トレンドチャートと呼んでいます。

下記の環境で動作確認をしています。

Fedora Linux 44
KDE Plasma Desktop x86_64
Python 3.14.7
PySide6 6.11.2
NumPy 2.5.2

トレンドチャートのサンプル

実行例

まずは、実行例を示します。乱数を生成してつなげてプロットしただけのサンプルです。

qt_quickwidget_trend.py の実行例

Python のコード

ひとつのファイルにまとめたコードを示しました。

import sys

import numpy as np
from PySide6.QtCore import Qt
from PySide6.QtGraphs import (
    QGraphsTheme,
    QLineSeries,
    QValueAxis,
)
from PySide6.QtQuickWidgets import QQuickWidget
from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QStyle,
)


class MyTrend(QQuickWidget):
    def __init__(self):
        super().__init__()
        self.setResizeMode(
            QQuickWidget.ResizeMode.SizeRootObjectToView
        )
        self.setFixedSize(600, 300)

        # --------------------------------
        # Theme
        # --------------------------------
        self.theme = theme = QGraphsTheme()
        grid = theme.grid()
        grid.setMainWidth(1.0)
        grid.setSubWidth(0.5)
        theme.setGrid(grid)

        # --------------------------------
        # Axes
        # --------------------------------
        self.axis_x = axis_x = QValueAxis()
        axis_x.setRange(0, 100)

        self.axis_y = axis_y = QValueAxis()
        axis_y.setRange(0, 1)
        axis_y.setTitleText("Value")

        # --------------------------------
        # Line series
        # --------------------------------
        self.series = series = QLineSeries()
        series.setAxisX(axis_x)
        series.setAxisY(axis_y)

        # 線の太さと色
        series.setWidth(1.0)
        series.setColor(Qt.GlobalColor.green)

        # GraphsView に渡すプロパティ
        self.setInitialProperties({
            "theme": theme,
            "axisX": axis_x,
            "axisY": axis_y,
            "seriesList": [series],
        })

        # QtGraphs の GraphsView をロード
        self.loadFromModule(
            "QtGraphs",
            "GraphsView",
        )

    def append_data(self, x: float, y: float):
        self.series.append(x, y)


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

        icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMenuButton
        )
        self.setWindowIcon(icon)
        self.setWindowTitle("Trend Chart")

        # --------------------------------
        # QQuickWidget
        # --------------------------------
        trend = MyTrend()
        self.setCentralWidget(trend)

        arr_y = np.random.random(100)
        for i, y in enumerate(arr_y):
            trend.append_data(i, y)


def main():
    app = QApplication(sys.argv)
    win = Example()
    win.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

プログラムの流れ

このプログラムでは、QQuickWidget を継承した MyTrend クラスを作成し、Qt Graphs の QLineSeries を使って折れ線グラフを表示しています。MyTrend の初期化時に表示サイズを 600×300 に固定し、グラフのテーマ、グリッド線、X軸とY軸、そして折れ線グラフ用の系列を順番に設定します。

テーマには QGraphsTheme を使用し、グリッド線についてはメインの線を 1 ピクセル、サブの線を 0.5 ピクセルに設定しています。X軸とY軸には QValueAxis を使用し、X軸を 0 ~ 100、Y軸を 0 ~ 1 の範囲に設定しています。また、Y軸には Valueというタイトルを設定しています。

グラフのデータを保持する QLineSeries を作成し、先ほど作成した X軸 と Y軸 を割り当てます。折れ線の太さを 1 ピクセル、色を緑色に設定した後、テーマ、軸、系列を GraphsView に渡すための初期プロパティとして設定します。その後、QtGraphs モジュールの GraphsView をロードすることで、グラフが表示される状態になります。

MyTrend には append_data() メソッドを用意しており、X座標 と Y座標を受け取って QLineSeries に追加します。これによって、グラフそのものを管理する MyTrend と、そこへデータを供給する側を分離しています。


Example クラスでは MyTrend をメインウィンドウの中央ウィジェットとして配置し、NumPy の random.random() を使って 100個 のランダムな値を生成します。生成した値を enumerate() で順番に取り出し、インデックスを X座標、ランダムな値を Y座標 として append_data() に渡します。

その結果、0 ~ 99 の X座標 に対して 0 ~ 1 の範囲でランダムに生成された 100個 のデータが QLineSeries に追加され、Qt Graphs によって折れ線グラフとして描画されます。

最後に main() で QApplication を生成し、メインウィンドウを表示してイベントループを開始します。

参考サイト

  1. Qt Charts C++ Classes | Qt Charts
  2. Obsolete Members for QChart | Qt Charts
  3. PySide6.QtGraphs.QGraphsTheme - Qt for Python
  4. PySide6.QtGraphs.QLineSeries - Qt for Python
  5. PySide6.QtGraphs.QValueAxis - Qt for Python
  6. PySide6.QtQuickWidgets.QQuickWidget - Qt for Python

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2026-09-03

QQuickWidget で散布図 〜 PySide6

PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。

QChart を含む QtCharts モジュールは、Qt 6.10 / PySide6 6.10 以降、公式に非推奨となりました[1] [2]。

従来 QChart で作成していたプロットを(Matplotlib や PyQtGraph を利用せずに)新しいやり方で作成できたので、備忘録的にサンプルを紹介します。現時点では自由に使いこなせるほど理解ができているとは言えないので、より良い方法が判れば、その都度、投稿していく予定です。

下記の環境で動作確認をしています。

Fedora Linux 44
KDE Plasma Desktop x86_64
Python 3.14.7
PySide6 6.11.2
NumPy 2.5.2

散布図のサンプル

実行例

まずは、実行例を示します。乱数を生成して散布図にプロットしただけのサンプルです。

qt_quickwidget_scatter.py の実行例

Python のコード

ひとつのファイルにまとめたコードを示しました。

import sys

import numpy as np
from PySide6.QtCore import QUrl
from PySide6.QtGraphs import (
    QGraphsTheme,
    QScatterSeries,
    QValueAxis,
)
from PySide6.QtQml import QQmlComponent, QQmlEngine
from PySide6.QtQuickWidgets import QQuickWidget
from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QStyle,
)


class MyScatter(QQuickWidget):
    def __init__(self):
        super().__init__()
        self.setResizeMode(
            QQuickWidget.ResizeMode.SizeRootObjectToView
        )
        self.setFixedSize(600, 600)

        # --------------------------------
        # Theme
        # --------------------------------
        self.theme = theme = QGraphsTheme()
        grid = theme.grid()
        grid.setMainWidth(1.0)
        grid.setSubWidth(0.5)
        theme.setGrid(grid)

        # --------------------------------
        # Axes
        # --------------------------------
        self.axis_x = axis_x = QValueAxis()
        axis_x.setRange(0, 1)

        self.axis_y = axis_y = QValueAxis()
        axis_y.setRange(0, 1)

        # --------------------------------
        # Scatter series
        # --------------------------------
        self.series = series = QScatterSeries()
        series.setAxisX(axis_x)
        series.setAxisY(axis_y)

        # ----------------------
        # QMLで描画ポイントを修飾
        # ----------------------
        qml = """
        import QtQuick

        Rectangle {
            width: 5
            height: 5
            radius: width / 2
        }
        """
        self.engine = QQmlEngine()
        self.point_delegate = QQmlComponent(self.engine)
        self.point_delegate.setData(qml.encode(), QUrl())

        if self.point_delegate.isError():
            print(self.point_delegate.errors())
        else:
            series.setPointDelegate(self.point_delegate)

        # GraphsView に渡すプロパティ
        self.setInitialProperties({
            "theme": theme,
            "axisX": axis_x,
            "axisY": axis_y,
            "seriesList": [series],
        })

        # QtGraphs の GraphsView をロード
        self.loadFromModule(
            "QtGraphs",
            "GraphsView",
        )

    def append_data(self, x: float, y: float):
        self.series.append(x, y)


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

        icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMenuButton
        )
        self.setWindowIcon(icon)
        self.setWindowTitle("Scatter Chart")

        # --------------------------------
        # QQuickWidget
        # --------------------------------
        scatter = MyScatter()
        self.setCentralWidget(scatter)

        arr_xy = np.random.random(size=(200, 2))
        for x, y in arr_xy:
            scatter.append_data(x, y)


def main():
    app = QApplication(sys.argv)
    win = Example()
    win.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

プログラムの流れ

まず main() が呼び出され、Qt アプリケーションの実行環境が作られます。その後、Example クラスのウィンドウが生成されます。

Example の初期化では、まずメインウィンドウのアイコンとタイトルを設定します。続いて MyScatter のインスタンスを生成し、メインウィンドウの中央ウィジェットに設定します。

MyScatter は QQuickWidget を継承しています。MyScatter の初期化では、

  1. 最初にウィジェットのリサイズ方法とサイズを設定します。
  2. 次に、グラフ全体の外観を管理する QGraphsTheme を生成します。このテーマからグリッド線の設定を取得し、主グリッド線と補助グリッド線の線幅を変更してから、テーマへ戻します。
  3. 続いて X 軸と Y 軸をそれぞれ QValueAxis として生成し、両方とも 0 から 1 の範囲に設定します。
  4. その後、散布図のデータを保持する QScatterSeries を生成します。ここで X 軸と Y 軸をこのシリーズに関連付けます。
  5. 次に、散布図の各データポイントをどのように描画するかを設定します。今回は QML を外部ファイルにせず、Python の文字列として Rectangle を定義しています。この Rectangle は幅と高さを 5 にし、角を丸めることで円形のマーカーになります。
  6. この QML を実際に利用するために QQmlEngine と QQmlComponent を生成し、QML の文字列を QQmlComponent に読み込ませます。QML にエラーがなければ、そのコンポーネントを QScatterSeries の pointDelegate に設定します。
  7. グラフを構成するオブジェクトの準備が終わったら、テーマ、X 軸、Y 軸、散布図シリーズを GraphsView の初期プロパティとして登録します。
  8. 最後に loadFromModule() を呼び出し、Qt Graphs の GraphsView をロードします。この順序によって、先ほど準備したテーマ、軸、シリーズが GraphsView に初期状態として渡されます。

ここまでで MyScatter の準備が完了します。

Example に戻ると、MyScatter が中央ウィジェットとして配置された後、NumPy を使って 200 個のランダムな X・Y 座標を生成します。それぞれの座標について MyScatter の append_data() を呼び出し、QScatterSeries にデータポイントを追加します。

データが追加されると、Qt Graphs がそれぞれのポイントを描画します。その際、先ほど設定した pointDelegate が使われるため、通常の散布点ではなく、QML の Rectangle によって定義された直径 5 程度の円として表示されます。

最後に Example のウィンドウを表示し、Qt のイベントループを開始します。以降は Qt が画面の描画やイベント処理を担当し、生成された 200 個のデータポイントが散布図として表示されます。

参考サイト

  1. Qt Charts C++ Classes | Qt Charts
  2. Obsolete Members for QChart | Qt Charts
  3. PySide6.QtGraphs.QGraphsTheme - Qt for Python
  4. PySide6.QtGraphs.QScatterSeries - Qt for Python
  5. PySide6.QtGraphs.QValueAxis - Qt for Python
  6. PySide6.QtQml.QQmlComponent - Qt for Python
  7. PySide6.QtQml.QQmlEngine - Qt for Python
  8. PySide6.QtQuickWidgets.QQuickWidget - Qt for Python

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加