2024-06-27

CentOS Stream 10 情報 (2)

CentOS Stream は、継続的に提供される Red Hat® Enterprise Linux (RHEL) のディストリビューション・アップストリームを、オープンソース・コミュニティのメンバーが Red Hat の開発者と連携して開発、テスト、貢献することができる、Linux® ディストリビューションです。

Red Hat は Red Hat Enterprise Linux ソースコードを CentOS Stream 開発プラットフォームで開発してから、新しい Red Hat Enterprise Linux バージョンをリリースします。Red Hat Enterprise Linux 9 は、CentOS Stream 内で構築された最初のメジャーリリースです。

参考サイト [1] より引用

来年は Red Hat Enterprise Linux 10 がリリースされると予想されますが、その開発版である CentOS Stream の開発が始まっています。まだ正式な CentOS Stream 10 のリリース前ですが、開発版の iso ファイルがダウンロード可能になっています。

CentOS Linux 7 の EOL が、今月末の 6 月 30 日になっていますので、7 月から CentOS Project のホームページが新しくなり、ひょっとすると CentOS Stream 10 も一般公開されるかもしれません。まあ、どうなるか判りませんが…。

とりあえず、現時点での CentOS Stream 10 の開発版の iso ファイルをダウンロードして、仮想環境 (GNOME Boxes) にインストールしてみました。

CentOS Stream 10(プレリリース版)の入手先
CentOS-Stream-10-20240621.0-x86_64-dvd1.iso をインストールしたデスクトップ画面

CentOS Stream のロゴが、従来の CentOS のロゴとはちがうデザインに変わりました。

現時点で、CentOS Stream 10 (CentOS-Stream-10-20240621.0-x86_64-dvd1.iso) の構成は以下のようになっています。まだ、さほど詳しくは調べていません。🙇🏻

Linux kernel
kernel-6.8.0-0.rc3.20240209git1f719a2f3fa6.31.el10.x86_64
GNOME
gnome-shell-46.2-3.el10.x86_64

最終的に RHEL 10 のカーネルのバージョンが 6.8.0 になるのかが気になります。ひきつづき、ときどき確認します。

参考サイト

  1. CentOS Stream とは | Red Hat
  2. Red Hat Enterprise Linux 10 での Wayland および Xorg サーバーに関する方針 [2023-11-27]
  3. Red Hat Evaluating x86-64-v3 Requirement For RHEL 10 - Phoronix [2024-01-03]
  4. Red Hat、RHELからLibreOfficeパッケージを削除へ、今後はFlatpakで対応 | gihyo.jp [2023-06-08]
  5. bitWalk's: CentOS Stream 10 情報 (1) [2024-05-26]

 

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

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



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
# Reference:
# https://stackoverflow.com/questions/48474699/marker-size-alpha-scaling-with-window-size-zoom-in-plot-scatter
import sys
from math import sin
from typing import Any
from matplotlib.axes import Axes
from matplotlib.backends.backend_qtagg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.collections import PathCollection
from matplotlib.figure import Figure
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QMainWindow, QApplication
class MyChart(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.fig.canvas.mpl_connect('resize_event', self.onResize)
super().__init__(self.fig)
self.fig_w = self.fig.get_figwidth()
self.fig_h = self.fig.get_figheight()
self.ax = self.fig.add_subplot(111)
self.ax.callbacks.connect('xlim_changed', self.onLimitChanged)
self.ax.callbacks.connect('ylim_changed', self.onLimitChanged)
self.xlim = self.ax.get_xlim()
self.ylim = self.ax.get_ylim()
self.fig_factor = None
self.path = None
self.sizes = None
def getZoomFactor(self,
limx: tuple[float, float],
limy: tuple[float, float]) -> float:
return min(
(self.xlim[1] - self.xlim[0]) / (limx[1] - limx[0]),
(self.ylim[1] - self.ylim[0]) / (limy[1] - limy[0])
)
def onLimitChanged(self, ax: Axes):
lx = ax.get_xlim()
ly = ax.get_ylim()
zfactor = self.getZoomFactor(lx, ly)
try:
self.path.set_sizes(
[s * zfactor * self.fig_factor for s in self.sizes]
)
except KeyError:
pass
def onResize(self, event: Any):
w = self.fig.get_figwidth()
h = self.fig.get_figheight()
self.fig_factor = min(w / self.fig_w, h / self.fig_h)
def setPath(self, path: PathCollection):
self.path = path
self.sizes = self.path.get_sizes()
def draw_chart(chart: MyChart):
list_x = list()
list_y = list()
for i in range(100):
x = i / 10.
y = sin(x)
list_x.append(x)
list_y.append(y)
path = chart.ax.scatter(list_x, list_y, s=20, c='blue')
chart.setPath(path)
class Example(QMainWindow):
def __init__(self):
super().__init__()
chart = MyChart()
chart.ax.grid()
self.setCentralWidget(chart)
navbar = NavigationToolbar(chart, self)
self.addToolBar(Qt.ToolBarArea.BottomToolBarArea, navbar)
draw_chart(chart)
def main():
app = QApplication()
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

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

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
#!/usr/bin/env python
import sys
from math import sin, pi
from PySide6.QtCharts import (
QChart,
QChartView,
QLineSeries,
QValueAxis,
)
from PySide6.QtCore import QTimer, Qt
from PySide6.QtGui import QPainter, QPen
from PySide6.QtWidgets import QApplication, QMainWindow
def get_red_pen() -> QPen:
pen = QPen(Qt.GlobalColor.red)
pen.setWidth(1)
return pen
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.x = 0
self.series = QLineSeries()
self.series.setPen(get_red_pen())
self.series.setPointsVisible(True)
self.series.setMarkerSize(2.0)
self.series.append(self.x, 0)
self.axis_x = QValueAxis()
self.axis_x.setTitleText('X')
self.axis_x.setTickCount(5)
self.axis_x.setRange(0, 0)
self.axis_y = QValueAxis()
self.axis_y.setTitleText('Y')
self.axis_y.setRange(-1, 1)
chart = QChart()
chart.setTitle("Sample")
chart.addSeries(self.series)
chart.legend().hide()
chart.addAxis(self.axis_x, Qt.AlignmentFlag.AlignBottom)
self.series.attachAxis(self.axis_x)
chart.addAxis(self.axis_y, Qt.AlignmentFlag.AlignLeft)
self.series.attachAxis(self.axis_y)
view = QChartView(chart)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(view)
self.resize(600, 400)
self.timer = QTimer()
self.timer.timeout.connect(self.update_data)
self.timer.setInterval(50)
self.timer.start()
def update_data(self):
self.x += 0.02 * pi
self.series.append(self.x, sin(self.x))
x_max = round(self.x + 0.5)
if self.axis_x.max() < x_max:
self.axis_x.setRange(0, self.axis_x.max() + 4)
if self.x > 10 * pi:
self.timer.stop()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

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

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
import datetime
import math
import sys
import pandas as pd
from PySide6.QtCore import Qt, QTimer
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
)
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class RTChart(FigureCanvas):
def __init__(self):
self.fig = Figure()
super().__init__(self.fig)
self.ax = ax = self.fig.add_subplot(111)
ax.set(title='Sample')
ax.set_ylabel('Y')
ax.grid(True)
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Real Time Sample')
self.resize(600, 400)
self.t0 = None
self.y0 = None
self.count = 0
self.canvas = canvas = RTChart()
self.setCentralWidget(canvas)
toolbar = NavigationToolbar(canvas, self)
self.addToolBar(Qt.ToolBarArea.BottomToolBarArea, toolbar)
self.timer = timer = QTimer(self)
timer.timeout.connect(self.update_data)
timer.start(50)
def update_data(self):
if self.count > 1000:
self.timer.stop()
print('completed!')
return
t1 = datetime.datetime.now()
y1 = math.sin(t1.timestamp())
if self.t0 is not None:
self.canvas.ax.plot(
[self.t0, t1], [self.y0, y1], color='C1',
linewidth=1, marker='o', markersize=2)
self.canvas.fig.canvas.draw()
self.t0 = t1
self.y0 = y1
self.count += 1
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

このサンプルを実行した例を示しました。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技術ブログ オープンソースへ
にほんブログ村

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