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 のインスタンスを生成し、メインウィンドウの中央ウィジェットに設定します。

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

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

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

Example に戻ると、MyScatter が中央ウィジェットとして配置された後、NumPy を使って 200 個のランダムな X・Y 座標を生成します。それぞれの座標について MyScatterappend_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技術ブログ オープンソースへ
にほんブログ村

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



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