2024-06-15

Matplotlib のズーム機能とマーカーサイズ ~ PySide6

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

データを視覚化したいときには、まず JupyterLab 上で Matplotlib を利用してあれこれチャートを作ります。そして、再利用するニーズがあるチャートについては、PySide6 に埋め込んで簡単な GUI アプリにするのが常です。

GUI 化するときには Matplotlib の NavigatorToolBar を利用して、主にズーム機能を利用しています。

NavigatorToolBar に表示されている虫眼鏡アイコンをクリックして、プロット上の矩形領域をマウスでドラッグして指定すると、簡単に拡大(ズームイン)できるので重宝しているのですが、不満が一つあります。

それは、拡大してもデータ点(マーカー)のサイズが変わらないことです。

同じようなことを不満に思う人はいるだろうなと思って調べたところ、Stack Overflow に、しかもずいぶん前にポストされていました(参考サイト [1])。当然ながら、PySide6 にチャートを埋め込む内容ではないので、参考サイトの回答にあったサンプルをベースにして、PySide6 にチャートを埋め込むサンプルを作ってみました。

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

RHEL 9.4 x86_64
Python 3.12.1
PySide6 6.7.1
matplotlib 3.9.0

このサンプルでは、単純化するために、プロットする path はひとつ、xy 軸は一対のみで、散布図 (scatter) を対象として、データ点の間は線を結ばず、ズーム機能ではマーカーのサイズのみを考慮するものとしています。

qt_matplotlib_zoom.py

サンプルの実行例を示しました。

qt_matplotlib_zoom.py の実行例 (1)
qt_matplotlib_zoom.py の実行例 (2)
qt_matplotlib_zoom.py の実行例 (3)

時系列データを扱う場合などで、データ点の間を線で結ぶ場合でも、ズームインした際にマーカーのサイズさえ大きくなれば、線の太さはそのままで個人的には十分だと思っています。それでも、一つのチャートで複数のデータパスを扱うことが多いので、このサンプルで紹介した MyChart クラスを実用的に使えるように機能追加していく必要があります。

参考サイト

  1. pandas - Marker size/alpha scaling with window size/zoom in plot/scatter - Stack Overflow [2018-01-27]

 

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

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



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

2024-06-13

動的に QChart を利用する ~ PySide6

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

本ブログで、Matplotlib のチャートを PySide6 の GUI に埋め込んで、QTimer でイベントを発生させて動的にチャートに正弦曲線を描画するサンプルを示しました [1]

このぐらいのチャートであれば、PySide6 の QChart でも実現できるだろうと考え、同じようなサンプルを作ってみました。

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

RHEL 9.4 x86_64
Python 3.12.1
PySide6 6.7.1

今回もタイマーで 50 ミリ秒毎にイベントを発生させて正弦曲線 (Sin) を描画していますが、時刻の扱いが不慣れだったので、単純に 0.02π 刻みでプロットして、10π になったらタイマーを止めています。またチャートの y 軸のレンジは固定し、x 軸はある程度の頻度で更新するようにしました、

qtcharts_linechart_realtime.py

このサンプルを実行した例を示しました。

qtcharts_linechart_realtime.py の実行例

Matplotlib でチャートを描画する作法とは異なるものの、QLineSeries にデータ点を追加するだけでチャートを更新でき、必要があればデータ全体を簡単に取り出せます。PySide6 だけで完結させたければ、これでもよいのかもしれません。個人的には、圧倒的に Matplotlib を利用する頻度が高いので、QChart によるプロットは慣れが必要です。

参考サイト

  1. bitWalk's: 動的に Matplotlib のチャートを利用する ~ PySide6 [2024-06-10]
  2. QChart - Qt for Python
  3. QChartView - Qt for Python
  4. QLineSeries - Qt for Python
  5. QValueAxis - Qt for Python

 

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

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



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

2024-06-10

動的に Matplotlib のチャートを利用する ~ PySide6

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

Matplotlib のチャートを PySide6 に GUI に埋め込んで利用しています。リアルタイムに…、と言っても更新頻度は数秒に一回程度なのですが、データを取得して Matplotlib のチャートに反映させるときは、表示されているチャートを一旦消してから書き直しています。この程度の更新頻度であれば、チャートを書き直しても問題ありませんが、もっと更新頻度が高いときに、チャートを書き直さずにチャート上に新しいデータ点を追加する方法を確認しておきたくなりました。

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

RHEL 9.4 x86_64
Python 3.12.1
PySide6 6.7.1
matplotlib 3.9.0

次のサンプルは、タイマーで 50 ミリ秒毎にイベントを発生させて、タイムスタンプとその値で計算した正弦曲線 (Sin) を描画します(最大 1,000 点)。点と点を描画するだけでなく、点と点を直線で結びたかったので、前の点の座標を保持しておき、表示上は擬似的な正弦曲線に見えるようにしています。

qt_matplotlib_realtime.py

このサンプルを実行した例を示しました。x 軸の時刻表示については改善の余地があります。🙇🏻

qt_matplotlib_realtime.py の実行例
completed!

このサンプルでは、タイマーのイベントが発生する度にカウンタの値を増やし、1000 回になったところでタイマーを止めています。

参考サイト

  1. matplotlib.pyplot.plot — Matplotlib documentation
  2. Embedding in Qt — Matplotlib documentation
  3. QTimer - Qt for Python

 

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

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



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

2024-06-09

棒グラフの色付け

Matplotlib は、Python と NumPy のためのプロットライブラリです。Tkinter、wxPython、Qt、GTK のような汎用 GUI ツールキットを使ったアプリケーションにプロットを埋め込むためのオブジェクト指向 API を提供しています。

Wikipedia より引用、翻訳

棒グラフは、縦軸にデータ量をとり、棒の高さでデータの大小を表したグラフです。Matplotlib を利用すると簡単に棒グラフを作成できます。

正負の値を棒グラフにするときがあります。そんなとき、正負を色分けしたくなります。毎回、どうやって色付けをするんだっけと調べ直してしまうので、備忘録にまとめました。

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

RHEL 9.4 x86_64
Python 3.12.1
jupyterlab 4.2.1
matplotlib 3.9.0

ここでは、JupyterLab で棒グラフを作成します。

まず、ライブラリのインポートと、サンプルデータを定義します。

import numpy as np
import matplotlib.pyplot as plt

left = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
height = np.array([1.2, -1.1, -2.2, 0.7, 3.1, -2.3, -3.1, 2.3, 1.7, -2.6])

棒グラフをプロットするには次のようにします。

plt.bar(left, height)
# plt.savefig('bar_001.png')
plt.show()

次のような棒グラフが表示されます。棒グラフの表示方法はとても簡単ですが、少々もの足りません。

棒グラフで、正負の値別に色を変えることにします。あとグリッド線を表示するなど少々見やすくしています。

colors = ['C0' if h > 0 else 'C1' for h in height]

plt.bar(left, height, color=colors)
plt.axhline(y = 0, lw=0.75, color='black')
plt.grid(axis='y')
# plt.savefig('bar_002.png')
plt.show()

次のように棒グラフが表示されます。

参考サイト

  1. matplotlib.pyplot.bar — Matplotlib documentation

 

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

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



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

2024-06-05

Python のリストをソートする時の key の使い方

Python のリストをソートするとき、リストの要素が数値とか文字列であれば、単純に sorted()list.sort() メソッドを使いますが、要素がオブジェクトになるとそのままではソートができません。そんな時に key パラメータを使うとソートできるようになります。

いつも調べ直してしまうので、備忘録にまとめました。

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

RHEL 9.4 x86_64
Python 3.12.1
jupyterlab 4.2.1

JupyterLab 上で確認しています。

まず、サンプル用に文字列を保持するだけのクラスを用意します。

class MyObject:
    def __init__(self, x: str):
        self.x = x

ソートを確認するためにリスト obj_listMyObject のインスタンスを3つ要素に追加します。

obj_list = list()
obj_list.append(MyObject('C'))
obj_list.append(MyObject('A'))
obj_list.append(MyObject('B'))
[obj.x for obj in obj_list]

リスト obj_list の要素のオブジェクト(MyObject のインスタンス)が保持している文字列を順番に取り出してリストにすると以下のようになります。

['C', 'A', 'B']

リストの要素であるオブジェクトが保持している文字列をキーにしてソートするには、要素(この場合 MyObject のインスタンス)を引数にして、保持している文字列(obj.x)を返す関数を用意して、key パラメータにその関数を指定します。簡単な関数ですので、今回はラムダ式で無名関数にしています。

sorted() では次のようにします。

obj_list_2 = sorted(obj_list, key=lambda obj: obj.x)
[obj.x for obj in obj_list_2]
['A', 'B', 'C']

list.sort() メソッドで元のリストをソートする場合は次のようにします。

obj_list.sort(key=lambda obj: obj.x)
[obj.x for obj in obj_list]
['A', 'B', 'C']

参考サイト

  1. Sorting Techniques — Python ドキュメント
  2. Pythonでリストをソートするsortとsortedの違い | note.nkmk.me
  3. Pythonのlambda(ラムダ式、無名関数)の使い方 | note.nkmk.me

 

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

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



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

2024-06-01

PySide6 と watchdog

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

Watchdog は、ファイルシステムのイベントを監視するための Python API とシェルユーティリティです [1]

Python で、特定のディレクトリ内にアップロードされるファイルを監視できるようなライブラリがないか探したところ、参考サイト [3] で watchdog が紹介されていました。早速、紹介されていたサンプルを PySide6 の GUI アプリに組み込んで動作確認をしていたところ、ときどき Segmentation Fault でコアダンプを吐いてアプリが終了してしまいます。

コアダンプを解析して原因を探るのが正しいやり方なのでしょうが、イベント発生の相性が PySide6 (Qt6) のシグナル/スロットのやり方と合わないのかもしれないと勝手にあたりをつけました。QObject でラップして、むりくり Qt 風のシグナル/スロットのやり方にしたところ、コアダンプを吐くことなく動作するようになったので、単純なサンプルにまとめました。

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

RHEL 9.4 x86_64
Python 3.12.1
PySide6 6.7.1
watchdog 4.0.1
qt_dogwatch.py

このサンプルでは、ローカルアカウントに ~/tmp ディレクトリを作成します。このディレクトリ内を監視して、変化があった際にはシグナルを発生させ、参考サイト [3] で紹介されているサンプルの EventHandler が出力する文字列と同等の内容をスロットへ渡します。サンプルの GUI アプリは受け取った文字列を QPlainTextEdit のインスタンス上に表示します。

なお、本サンプルは GUI アプリなので、イベントループ内で動作しています。そのため、watchdog 用に監視ループを生成する必要はありません。

サンプルの実行例を以下に示します。

[bitwalk@rhel9 tmp]$ touch test.txt
[bitwalk@rhel9 tmp]$ 
qt_dogwatch.py の実行例

実用的には FileSystemEventHandler クラスの on_any_event メソッドを使うと情報量が多すぎるかもしれません。例えば、新たにファイルが生成された時だけを監視したいのであれば on_created メソッドを利用するなどして、監視する情報を絞ることができます。API の詳細は、参考サイト [2] にあります。

ひょっとしたら Qt にもディレクトリを監視するクラスが用意されているかもしれないと調べてみたら QFileSystemWatcher というクラスがありました [4]。使い勝手が watchdog と違います。こちらのサンプルの紹介はまたの機会にします。

参考サイト

  1. gorakhargosh/watchdog: Python library and shell utilities to monitor filesystem events.
  2. Watchdog — watchdog documentation
  3. 【一番短い】Pythonでフォルダ監視するサンプル | 初心者DIYプログラミング入門 [2023-03-01]
  4. QFileSystemWatcher - Qt for Python

 

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

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



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