はじめに
これまでの記事で、matplotlibのグラフにマウスをクリックするイベント、マウスを動かすイベント、要素を選択するイベントを紐づける方法について紹介してきました。
マウスクリック、マウスモーションイベント
【python】matplotlibでマウスクリックイベントを紐づける - morikomorou’s blog
ピックイベント
【python】クリックでmatplotlibのグラフの色や透明度を変える - morikomorou’s blog
今回は、新しくマウスを離すイベント(マウスリリースイベント)を使ってみます。前述のイベントと組み合わせることでドラッグして要素を動かしたりする事が出来るようになります。
目標として以下を実装していきたいと思います。
- ドラッグでmatplotlibのグラフ上の点を動かす
こんなのなんの役に立つんだ…って感じですが。
お絵描きしたり、文字通り手動で外れ値を丸め込むとかですかね 笑
マウスリリースイベント
過去記事でmatplotlibで使えるイベントについてまとめましたが、
【python】matplotlibでマウスクリックイベントを紐づける - morikomorou’s blog
今回は「マウスを離す」という'button_release_event'をグラフに紐づけます。
使い方はマウスをクリックするという'button_press_event'と同じです。
さっそくコード実装に移ります。
ドラッグでグラフ上の点を動かす
import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Rectangle def motion(event): global gco, xdata, ydata, ind if gco is None: return x = event.xdata y = event.ydata if x == None or y == None: return xdata[ind] = x ydata[ind] = y gco.set_data(xdata,ydata) plt.draw() def onpick(event): global gco, xdata, ydata, ind gco = event.artist xdata = gco.get_xdata() ydata = gco.get_ydata() ind = event.ind[0] def release(event): global gco gco = None gco = None # ピックした要素が含まれる直線(Line2Dクラス) ind = None # ピックした直線のインデックス xdata = None # ドラッグするまえの直線のxデータを入れておく ydata = None # ドラッグするまえの直線のyデータを入れておく fig, ax = plt.subplots() ax.plot(np.random.rand(10),"o-",picker=15) fig.canvas.mpl_connect('pick_event', onpick) fig.canvas.mpl_connect('motion_notify_event', motion) fig.canvas.mpl_connect('button_release_event', release) plt.show()
結果が以下です。
ポイント、解説
fig.canvas.mpl_connect('pick_event', onpick) fig.canvas.mpl_connect('motion_notify_event', motion) fig.canvas.mpl_connect('button_release_event', release)
まず、点をドラッグするためにfigureに要素ピックのイベントそしてマウスのモーションイベント、マウスのリリースイベントを紐づけます。
要素をピックしてからマウスを離すまでの間のみ、マウスのモーションイベントが有効になるようにグローバル変数を使って条件分岐しています。
これでドラッグが可能になります。
それぞれの関数の処理について順番に見ていきましょう。
onpick関数
要素をクリックした瞬間に呼ばれる関数です。
def onpick(event): global gco, xdata, ydata, ind gco = event.artist xdata = gco.get_xdata() ydata = gco.get_ydata() ind = event.ind[0]
要素がピックされたらgcoにその要素を含む直線クラスを代入します。
次にピックした要素以外を更新しないようにするため、ピックした瞬間の全x,yデータをxdata, ydataに格納しておきます。
event.ind変数にはピックした要素のインデックスのリストが入ります。
要素が重なっていたり、近いときは複数の点が同時に選択されてしまいます。
それを避けるためにevent.ind[0]として選択された要素のうち一番インデックスが若いもの1つを選択するようにしています。
motion関数
マウスを動かしている間ずっと呼ばれる関数です。
def motion(event): global gco, xdata, ydata, ind if gco is None: return x = event.xdata # データ軸内のマウスのx座標 y = event.ydata # データ軸内のマウスのy座標 if x == None or y == None: return
グローバル変数gcoを参照して、要素がピックされてなければ何もしないようにしています。
ここでドラッグ中、データ軸の外に出てしまった場合、データをNoneで上書きしてしまわないように、returnで終了します。
※データをNoneで上書きしてしまうとデータエリア上からその要素がなくなってしまうため、再度選択することができなくなります。
def motion(event): # 略 xdata[ind] = x ydata[ind] = y gco.set_data(xdata,ydata) plt.draw()
ドラッグで動かしたいのはピックした点のみなので、onpick関数実行時に取得しておいたインデックス(ind)を使ってピックした要素のx, y座標のみを更新し、グラフを再描画しています。
release関数
マウスを離した時に呼ばれる関数です。
def release(event): global gco gco = None
グローバル変数gcoを空にして、motion関数が何もしないようにします。
おわりに
今回はマウスリリースイベントを使って、グラフ要素のドラッグ移動を実装しました。
遊ぶ以外の何か良い使い道が見つかったら、記事にしたいと思います。