はじめに
この実験では、Matplotlib イベントハンドリングを使ってキャンバス上のオブジェクトとやり取りし、変更するクロス GUI アプリケーションの例を通じて案内します。マウスでマーカーをドラッグしてプロット上のパスを編集し、その可視性をトグルする方法を学びます。
VM のヒント
VM の起動が完了したら、左上隅をクリックして ノートブック タブに切り替えて、Jupyter Notebook を使った練習にアクセスします。
時々、Jupyter Notebook が読み込み終了するまで数秒待つ必要がある場合があります。Jupyter Notebook の制限により、操作の検証を自動化することはできません。
学習中に問題に直面した場合は、Labby にお問い合わせください。セッション後にフィードバックを提供してください。そうすれば、迅速に問題を解決します。
ライブラリのインポート
このステップでは、実験に必要なライブラリをインポートします。グラフを作成し、イベントを処理するために Matplotlib を使用します。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backend_bases import MouseButton
from matplotlib.patches import PathPatch
from matplotlib.path import Path
グラフの作成
このステップでは、提供されたパスデータを使って、緑色のパスと黄色のエッジを持つグラフを作成します。その後、パスを表す PathPatch オブジェクトをグラフに追加します。
fig, ax = plt.subplots()
pathdata = [
(Path.MOVETO, (1.58, -2.57)),
(Path.CURVE4, (0.35, -1.1)),
(Path.CURVE4, (-1.75, 2.0)),
(Path.CURVE4, (0.375, 2.0)),
(Path.LINETO, (0.85, 1.15)),
(Path.CURVE4, (2.2, 3.2)),
(Path.CURVE4, (3, 0.05)),
(Path.CURVE4, (2.0, -0.5)),
(Path.CLOSEPOLY, (1.58, -2.57)),
]
codes, verts = zip(*pathdata)
path = Path(verts, codes)
patch = PathPatch(
path, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
PathInteractor クラスの作成
このステップでは、PathInteractor クラスを作成します。このクラスは、パスオブジェクトのイベントコールバックを処理します。このクラスを使うと、グラフ上のマーカーをドラッグすることで、パスを対話的に編集できます。
class PathInteractor:
"""
パスエディタ。
't' キーを押すと、頂点マーカーをオンオフ切り替えできます。頂点マーカーがオンのときは、マウスでドラッグできます。
"""
showverts = True
epsilon = 5 ## 頂点にヒットとみなす最大ピクセル距離
def __init__(self, pathpatch):
self.ax = pathpatch.axes
canvas = self.ax.figure.canvas
self.pathpatch = pathpatch
self.pathpatch.set_animated(True)
x, y = zip(*self.pathpatch.get_path().vertices)
self.line, = ax.plot(
x, y, marker='o', markerfacecolor='r', animated=True)
self._ind = None ## アクティブな頂点
canvas.mpl_connect('draw_event', self.on_draw)
canvas.mpl_connect('button_press_event', self.on_button_press)
canvas.mpl_connect('key_press_event', self.on_key_press)
canvas.mpl_connect('button_release_event', self.on_button_release)
canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
self.canvas = canvas
def get_ind_under_point(self, event):
"""
イベント位置に最も近い点のインデックスを返します。イベント位置から ``self.epsilon`` 以内の点がない場合は *None* を返します。
"""
xy = self.pathpatch.get_path().vertices
xyt = self.pathpatch.get_transform().transform(xy) ## 表示座標に変換
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
ind = d.argmin()
return ind if d[ind] < self.epsilon else None
def on_draw(self, event):
"""描画時のコールバック。"""
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self.pathpatch)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
def on_button_press(self, event):
"""マウスボタン押下時のコールバック。"""
if (event.inaxes is None
or event.button!= MouseButton.LEFT
or not self.showverts):
return
self._ind = self.get_ind_under_point(event)
def on_button_release(self, event):
"""マウスボタン離脱時のコールバック。"""
if (event.button!= MouseButton.LEFT
or not self.showverts):
return
self._ind = None
def on_key_press(self, event):
"""キー押下時のコールバック。"""
if not event.inaxes:
return
if event.key == 't':
self.showverts = not self.showverts
self.line.set_visible(self.showverts)
if not self.showverts:
self._ind = None
self.canvas.draw()
def on_mouse_move(self, event):
"""マウス移動時のコールバック。"""
if (self._ind is None
or event.inaxes is None
or event.button!= MouseButton.LEFT
or not self.showverts):
return
vertices = self.pathpatch.get_path().vertices
vertices[self._ind] = event.xdata, event.ydata
self.line.set_data(zip(*vertices))
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.pathpatch)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
パスインタラクタの作成
このステップでは、先ほど作成した PathPatch オブジェクトを渡して、PathInteractor クラスのインスタンスを作成します。
interactor = PathInteractor(patch)
グラフのプロパティを設定する
このステップでは、グラフのタイトルと軸の範囲を設定します。
ax.set_title('drag vertices to update path')
ax.set_xlim(-3, 4)
ax.set_ylim(-3, 4)
グラフを表示する
このステップでは、グラフを画面に表示します。
plt.show()
まとめ
この実験では、グラフ上のマーカーをドラッグすることでパスを編集できる対話型グラフを作成する方法を学びました。Matplotlib ライブラリを使ってグラフを作成し、イベントを処理し、イベントコールバックを処理するためのカスタムクラスを作成しました。これらの手順に従えば、Matplotlib を使って独自の対話型グラフを作成できます。