Python で GUI アプリを作成するときに Qt の Python 用バインディングである PySide (Qt for Python) を使用することが多くなりました。散布図などのチャート作成には、もっぱら matplotlib を使っていますが、他の選択肢も検討しようと、QtCharts というチャート作成用ライブラリの使い方をまとめました。
当初、PySide 用の QtCharts のサンプルが見つからず、C++ 用のサンプル [1] を PySide 用に書き直していましたが、よく探してみると PySide 用サンプルもありました [2]。ここでは、勉強がてら C++ 用のサンプルを書き直したものを紹介していきます。
本記事では、下記の OS 環境を使用しています。
![]() |
Fedora 34 Workstation | x86_64 |
- Python 3.9.6 | ||
- PySide6 6.1.2 (venv) | ||
- IDE: PyCharm 2021.1.3 (Community Edition) |
InteractScatterChart(やりとりできる散布図)
ScatterChart(散布図)をインタラクティブに変化させるサンプルです。
qtcharts_interactscatterchart.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# coding: utf-8 | |
# Reference | |
# https://doc.qt.io/qt-6/qtcharts-scatterinteractions-example.html | |
import math | |
import sys | |
from PySide6.QtCharts import ( | |
QChart, | |
QChartView, | |
QScatterSeries, | |
) | |
from PySide6.QtCore import ( | |
QPointF, | |
Qt, | |
) | |
from PySide6.QtGui import QPainter | |
from PySide6.QtWidgets import ( | |
QApplication, | |
QMainWindow, | |
) | |
class InteractScatterChart(QChartView): | |
def __init__(self): | |
super().__init__() | |
chart = self.init_ui() | |
self.setChart(chart) | |
self.setRenderHint(QPainter.Antialiasing) | |
def init_ui(self): | |
series_a = QScatterSeries() | |
series_a.setName('scatter1') | |
series_a.setColor('cyan') | |
for i in range(8): | |
x = 0.5 * (i + 1) | |
for j in range(8): | |
y = 0.5 * (j + 1) | |
series_a << QPointF(x, y) | |
series_b = QScatterSeries() | |
series_b.setName('scatter2') | |
series_b.setColor('magenta') | |
series_a.clicked.connect(lambda point: self.handleClickedPoint(point, series_a, series_b)) | |
series_b.clicked.connect(lambda point: self.handleClickedPoint(point, series_b, series_a)) | |
chart = QChart() | |
chart.legend().hide() | |
chart.addSeries(series_a) | |
chart.addSeries(series_b) | |
chart.setTitle('Click to interact with scatter points') | |
chart.createDefaultAxes() | |
chart.axes(Qt.Horizontal)[0].setRange(0, 4.5) | |
chart.axes(Qt.Vertical)[0].setRange(0, 4.5) | |
return chart | |
def handleClickedPoint(self, clickedPoint: QPointF, series_old: QScatterSeries, series_new: QScatterSeries): | |
# Find the closest point from series 1 | |
INT_MAX = 100000 | |
closest = QPointF(INT_MAX, INT_MAX) | |
distance = float(INT_MAX) | |
points = series_old.points() | |
for currentPoint in points: | |
currentDistance = math.sqrt((currentPoint.x() - clickedPoint.x()) | |
* (currentPoint.x() - clickedPoint.x()) | |
+ (currentPoint.y() - clickedPoint.y()) | |
* (currentPoint.y() - clickedPoint.y())) | |
if currentDistance < distance: | |
distance = currentDistance | |
closest = currentPoint | |
# Remove the closes point from series 1 and append it to series 2 | |
series_old.remove(closest) | |
series_new.append(closest) | |
class Example(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
scatter = InteractScatterChart() | |
self.setCentralWidget(scatter) | |
self.resize(500, 300) | |
self.setWindowTitle('InteractScatterChart') | |
def main(): | |
app = QApplication(sys.argv) | |
ex = Example() | |
ex.show() | |
sys.exit(app.exec()) | |
if __name__ == '__main__': | |
main() |
実行例を下記に示しました。マウスでプロット点をクリックすると水色から赤紫色(あるいはその逆)に変化します。
qtcharts_interactscatterchart.py の実行例
データ点クリック時のイベントで何か処理をするには、データ列(このサンプルの場合、series_a と series_b )にバインディングを指定します。
series_a.clicked.connect(lambda point: self.handleClickedPoint(point, series_a, series_b)) series_b.clicked.connect(lambda point: self.handleClickedPoint(point, series_b, series_a))
def handleClickedPoint(self, clickedPoint: QPointF, series_old: QScatterSeries, series_new: QScatterSeries): ... ... ...
データ点をクリックするイベントから簡単に処理へバインディングできるのは、データ解析用のアプリを作る場合に魅力的な機能ですが、大量のデータを扱ったときにリソースをどれだけ消費することになるかが気になるところです。別途、調べてみる予定です。
参考サイト

にほんブログ村
0 件のコメント:
コメントを投稿