質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4542閲覧

matplotlibでズーム状態でグラフを変化させたい

yamatan

総合スコア8

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/09/23 08:07

編集2021/09/23 12:43

実現したいこと

現在tkinter内のmatplotlibのグラフを動的に変化させることをしています。そこで、グラフ内のある領域をズームした後、ズームした状態でグラフの点の色などを変化させたいです。

問題

現在matplotlibで作成したグラフのツールバーのZoom to rectangleのボタンを押すことでズームすることができていますが、ズームした状態でグラフをcanvas.draw()で再描画すると、ズーム前のグラフ全体を描画した状態に戻ってしまいます。本来はズームを維持した状態で、グラフを変化、再描画させたいです。

試してみたこと

試したと言うより、ズーム状態でのグラフ変更の実現のために試そうと思ってできなかったことです。
matplotlib.backend_toolsのbackend_tools.pyのスクリプトでズームに関わるToolZoomというクラスを見つけました。ToolZoom内の関数にはzoom_mode変数やその他_switch_on_zoom_mode()や_mouse_move()などのイベント関数らしきもの、実際のズーム処理と思われる_set_view_from_bbox()関数などがありますが、それらの関数内でprint()で文字を入れてみても、実際のグラフでズーム時にターミナルに表示されません。

同じようにToolZoomクラスの__init__()コンストラクタでもprint()の文字は出力されないようなので、そもそもこのクラスを呼び出していないように思えます。

私は意図しない挙動を起こす心配があるので、backend_toolsの変数などを外部から変更するなどはできればしたくありません。あくまでも現在ズームモードかだけを読み取れれば、グラフのxlim, ylimなどを変更した再描画でズームすることができると思っています。なので、ツールバーのどのボタンが押されたのか、あるいはズームボタンが押されているのかを確認する方法があるのか知りたいです。、

ぜひ、アドバイスをお願いします。

プログラム

Python

1from tkinter.constants import X 2import matplotlib.pyplot as plt 3from matplotlib.collections import PathCollection 4import numpy as np 5import tkinter as tk 6from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk) 7 8 9def windowDestroy(): 10 root.quit() 11 root.destroy() 12 13def click(event): 14 global select_obj_ind 15 artist = event.artist 16 if isinstance(event.artist, PathCollection): 17 mouse_xy = np.array([event.mouseevent.xdata, event.mouseevent.ydata]) 18 data_xy = np.array(artist.get_offsets().data)[event.ind] 19 object_ind = event.ind[np.argmax(np.square(data_xy-mouse_xy).sum(axis=1))] # 最も近いオブジェクトを選択 20 select_obj_ind = object_ind 21 updateCanvas() 22 23def updateCanvas(): 24 ax.cla() 25 ax.set_xlabel("x") 26 ax.set_ylabel("y") 27 edgecolors = ['none'] * N 28 if select_obj_ind != -1: 29 edgecolors[select_obj_ind] = 'yellow' 30 ax.scatter(x, y, edgecolor=edgecolors, picker=True, pickradius=2.5) 31 canvas.draw() 32 33seed = 0 34select_obj_ind = -1 35print(f'initial seed:{seed}') 36np.random.seed(seed=seed) 37N = 100 38x = np.random.randn(N) 39y = np.random.randn(N) 40 41fig = plt.figure() 42ax = fig.add_subplot(111) 43edgecolors = ['none'] * N 44ax.set_xlabel("x") 45ax.set_ylabel("y") 46ax.scatter(x, y, edgecolor=edgecolors, picker=True, pickradius=2.5) 47 48root = tk.Tk() 49root.protocol('WM_DELETE_WINDOW', windowDestroy) 50 51frame = tk.Frame(root) 52frame.pack(side=tk.TOP, anchor=tk.CENTER, expand=True, fill=tk.BOTH) 53 54canvas = FigureCanvasTkAgg(fig, master=frame) 55canvas.draw() 56canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) 57canvas.mpl_connect('pick_event', click) 58 59toolbar = NavigationToolbar2Tk(canvas, frame) 60toolbar.update() 61 62root.mainloop() 63

追記・修正

問題のプログラムを再現したコードを載せました。
2次元座標上にランダムに配置した点を散布図として表示し、グラフ内の点をクリックすると、その点のエッジカラーが黄色に変化します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2021/09/23 09:25

問題を再現できる最少のコードを提示してください。 データ更新の度に、プロットを初期化してませんか? データのみの変更であれば zoom 状態が変化することはないはずです。
yamatan

2021/09/23 12:49

>>問題を再現できる最少のコードを提示してください。 失礼しました。 問題のプログラムを再現したコードを追加しました。 >>データ更新の度に、プロットを初期化してませんか? はい、ax.cla()でグラフのaxisを削除してから、新しくプロットし直しています。 既にプロットされた特定の点の情報を取得したり、変更する関数などあるのでしょうか。
teamikl

2021/09/23 13:34

コードを拝見しました。 > 既にプロットされた特定の点の情報を取得したり、変更する関数などあるのでしょうか。 削除・新規作成としてるのが原因のようですね。 作成済みの情報を後から変更する方法があるので、 対応手順を回答に投稿します。
guest

回答1

0

ベストアンサー

既にプロットされた特定の点の情報を取得したり、変更する関数などあるのでしょうか。

ax.scatter(...) で得られる、PathCollection オブジェクトに
set_edgecolor メソッドがあります。

対応手順としては、


(1) 最初の ax.scatter の戻り値を、グローバル変数に格納。
updateCanvas() 関数内から参照できるようにします。

python

1paths = ax.scatter(x, y, edgecolor=edgecolors, picker=True, pickradius=2.5)

(2) updateCanvas関数内: ax.cla() を削除。zoom 情報がリセットされた原因。
x/y のラベル変更も不要になります。


(3) updateCanvas関数内: 輪郭線情報の更新

diff

1- ax.scatter(x, y, edgecolor=edgecolors, picker=True, pickradius=2.5) 2+ paths.set_edgecolor(edgecolors) 3 4

訂正: zoom情報がリセットされる原因は、cla() と scatter の再生成の為。
作成済みのオブジェクトの情報を変更することで、zoom情報が失われるのを回避します。

投稿2021/09/23 13:45

編集2021/09/23 15:43
teamikl

総合スコア8817

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yamatan

2021/09/24 06:48

ありがとうございます。 ズーム状態を維持したままエッジカラーを変更できました。 公式のドキュメント https://matplotlib.org/stable/api/collections_api.html を参考に、set_offsets()で点の削除や追加もできることがわかりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問