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

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

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

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

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

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

Q&A

解決済

2回答

7153閲覧

<Python3>tkinter上にmatplotlibのグラフを表示し、canvas内でマウス操作をできるようにしたい

A_7

総合スコア4

Matplotlib

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

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

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

0グッド

0クリップ

投稿2020/10/20 07:31

編集2020/10/20 07:50

前提・実現したいこと

 読み込んだcsvファイルのグラフをmatplotlibを使用してtkinter上に表示させ、その表示させたcanvas上において、右クリックで二点間の範囲を選択し、真ん中クリックでキャンセル、左ダブルクリックで決定して最小二乗法の線を引くプログラムを作成したいと考えております。

発生している問題・エラーメッセージ

 読み込んだcsvファイルをcanvasに表示させることはできましたが、canvas内でマウス操作をすることができなくて悩んでおります。

該当のソースコード

Python

1from tkinter import * 2from matplotlib import pyplot as plt 3from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 4import numpy as np 5from scipy import optimize 6import pylab 7import turtle 8import tkinter as tk 9 10x_data = [] 11y_data = [] 12state = 0 13f = open('xrr_data.csv', 'r') 14 15datalist = f.readlines() 16for data in datalist: 17 if state == 1: 18 x_data.append(float(data.split(',')[0])) 19 y_data.append(float(data.split(',')[1])) 20 if "Angle,Intensity" in data: 21 state =1 22 23f.close() 24xdata = np.array(x_data) 25ydata = np.array(y_data) 26 27def drawlsm(lind,rind): 28 29 global xdata2 30 global ydata2 31 32 xdata2 = xdata[lind:rind] 33 ydata2 = ydata[lind:rind] 34 35 parameter0 = [0.,0.] 36 result = optimize.leastsq(fit_func,parameter0,args=(xdata2,ydata2)) 37 print(result) 38 a_fit=result[0][0] 39 b_fit=result[0][1] 40 print(a_fit,b_fit) 41 plt.plot(xdata,a_fit*xdata+b_fit,'k-', label='fitted line', linewidth=3, alpha=0.3) 42 43 44 45#Least squares method with scipy.optimize 46def fit_func(parameter,x,y): 47 a = parameter[0] 48 b = parameter[1] 49 residual = y-(a*x+b) 50 return residual 51 52def oncmask(event): 53 54 global stat 55 global leftind, rightind 56 57 ind=np.searchsorted(xdata,event.xdata) 58 plt.title("You clicked index="+str(ind)) 59 if event.button==3 and stat==1: 60 leftind=ind 61 ax.plot([xdata[ind]],[ydata[ind]],".",color="red") 62 stat=2 63 elif event.button==3 and stat==2: 64 rightind=ind 65 ax.plot(xdata[leftind:rightind],ydata[leftind:rightind],color="red") 66 stat=3 67 print (leftind, rightind) 68 elif event.button==1 and event.dblclick==1 and stat==3: 69 plt.title("Approved") 70 mask[leftind:rightind]=False 71 drawlsm(leftind,rightind) 72 stat=1 73 elif event.button==2 and stat==3: 74 plt.title("Canceled") 75 ax.plot(xdata[leftind:rightind],ydata[leftind:rightind],color="blue") 76 ax.plot([xdata[leftind]],[ydata[leftind]],".",color="green") 77 stat=1 78 fig.canvas.draw() 79 80mask=np.ones(len(xdata),dtype=bool) 81 82stat = 1 83fig=plt.figure() 84ax=fig.add_subplot(111) 85ax.plot(xdata,ydata) 86 87cid = fig.canvas.mpl_connect('button_press_event', oncmask) 88 89# Windowの設定 90root = Tk() 91root.title("Plot window") 92root.geometry() 93 94 95# Frameの設定 96frame_1 = Frame(root, bd=4, relief=GROOVE) 97frame_2 = Frame(root, bd=4, relief=GROOVE) 98 99# widgetの設定 100canvas = FigureCanvasTkAgg(fig, frame_2) 101 102# widgetの配置 103frame_1.grid(row=0, column=0, sticky=W + E) 104frame_2.grid(row=1, column=0) 105canvas.get_tk_widget().grid(row=0, column=0) 106 107root.mainloop()

試したこと

以下のメソッドで
fig.canvas.mpl_connect('button_press_event', oncmask)
tkinterのウィンドウにグラフを乗せることはできたのですが、
そのグラフにマウスクリックを行っても何も反応がありませんでした。

補足情報(FW/ツールのバージョンなど)

python初心者で至らない点が多々あると思いますが、アドバイス頂けると嬉しいです。
よろしくお願いします。

プログラムを動かした際の画像を添付します。
現在は通常のmatplotlibで出力した際にでてくるグラフ(画面右)にクリックなどの操作を行い、それがtkinter側のグラフ(画面左)に反映されるという形になっております。
![イメージ説明]

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

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

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

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

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

guest

回答2

0

後から思いついたのだけど、こちらの方が変更点少なくて良いかもしれません。

ポイントは、FigureCanvasTkAggの後に fig.canvas.mpl_connect
と root.mainloop() の代わりに canvas.start_event_loop()

python

1 2canvas = FigureCanvasTkAgg(fig, frame_2) 3 4# 略 5 6cid = fig.canvas.mpl_connect('button_press_event', oncmask) 7 8canvas.start_event_loop() 9 10# root.mainloop() 11

詳細

  • 最初のコードは FigureCanvasTkAgg 以前に button_press_event を登録していました。
  • FigureCanvasTkAgg により fig.canvas がtkinter 対応のものに置き換わります。
  • tkinter 対応の canvas に button_press_event を登録
  • ここでの canvas.start_event_loop は、FigureCanvasTkAgg により tk に対応した

 tkinter と mpl_connect で登録したイベント両方に対応するイベントループ

こちらのアプローチでは逆で、
matplotlib 側のイベントループ内で、tkinter のイベント処理を行うために
上記の説明で言う flush_events() 相当のモノ
(tkinterではupdate_idletasks()) を呼び出しています。

何方が良いかについては、状況次第ですが
上記で説明したoncmaskを呼び出す方法としては、こちらが適切でした。


イベントループが混在すると面倒な点についての解説

python

1canvas.start_event_loop() 2# root.mainloop()

NOTE: canvas.start_event_loop() を使う場合、

root.mainloop() は使えません (イベントループが競合) し不要になります。
FigureCanvasTkAgg 後の canvas.start_event_loop は
tkinter のイベントにも対応したイベントループになってます。

XXX: 但し、tkinter の幾つかのメソッドは想定通りに機能しない場合があります

例えば、root.quit() は tkinter のイベントループ(mainloop)を終了するものなので
通常のtkinterのプログラムでは、プログラムの終了として扱えますが、
tkinter のイベントループ(mainloop) を使っていないと機能しません。

canvas.start_event_loop()を使う場合、
matplotlib 側の作法(stop_event_loop)でイベントループを終了します。

この様に、どちらの方法を取るかによって参考に出来る情報が変わってきます。
イベントループの仕組みを知った上で扱うのであれば問題は少ないと思いますが、
そうでない場合、何処で問題が起きているのか解り難くなるので、注意点として書き残しておきます。

イベントループイベント処理ループ終了
tkintermainloopupdate_idletasksquit
FigureCanvasstart_event_loopflush_eventsstop_event_loop

投稿2020/10/20 14:59

編集2020/10/21 02:33
teamikl

総合スコア8664

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

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

A_7

2020/10/21 07:21

再び回答して頂き、ありがとうございます。 なるほど。そのようなやり方があるのですね。とても勉強になりました。 イベントループの解説も、今まで曖昧な理解でしたがこの解説を読んで理解できました。 本当にありがとうございました。
guest

0

ベストアンサー

tkinter で使う場合は、tkinter の作法で
マウス操作などのイベント等の処理を行った方が良いです。

python

1# バックエンドのキャンバスが変更される前に保存 2button_press_event = fig.canvas.button_press_event 3flush_events = fig.canvas.flush_events 4 5 6# 略 7 8canvas = FigureCanvasTkAgg(fig, frame_2) 9 10# 略 11 12def tk_canvas_click(event, isDouble=False): 13 # NOTE: ここで得られる event は mpl_connect のものとは異なります 14 15 # mpl_connect で登録された関数の呼び出し&実行 16 button_press_event(event.x, event.y, event.num, isDouble) 17 flush_events() 18 19from functools import partial 20tk_canvas = canvas.get_tk_widget() 21tk_canvas.bind("<Double-2>", partial(tk_canvas_click, isDouble=True)) 22tk_canvas.bind("<Button-1>", tk_canvas_click) 23tk_canvas.bind("<Button-2>", tk_canvas_click) 24tk_canvas.bind("<Button-3>", tk_canvas_click) 25 26# 元のコードに合わせて、一つの関数にしてますが、 27# ボタンに応じて別々の関数を登録でも良いです。 28 29# 注意点: ひとつの関数に纏める場合、 30# eventがダブルクリックの情報を持たない為 31# 関数登録時にダブルクリックかどうかのフラグを設定していますが、 32# ボタン毎に個別の関数にするなら不要です。
  • button_press_event でボタンをクリックされたときのイベントを作ります
  • flush_events で pyplot側のイベントを処理します

※ 上記のコードはoncmaskが呼ばれるのを確認したのみです、
マウスの座標が正しいか等、実際のデータはテストしてません。

※ ダブルクリック・イベントの扱いには注意。ソース中に注釈


詳細

GUIライブラリは通常、イベントループを持ち
各種イベントの処理や描画等を継続的に行っています。

  • tkinter の場合は mainloop
  • matplotlib.pyplot の場合は plt.show / fig.canvas.start_event_loop

ループなわけですから、どちらかのループが実行されている間は
もう片方のループは実行されません。
イベントループが実行されない → イベントが処理されない、
となり、これがcanvas内でマウス操作が出来なかった原因です。

上記のコードで提示した flush_events() を呼ぶ方法は、
他方のイベントループの内部でされる処理を1回だけ呼んだ形です。
tkinter のマウスイベントを pyplot へ伝搬しています。

参考: start_event_loop の実装 短いコードなので、
ループ内で flush_events が呼ばれているのだけ確認してください。

イベントループを混在させると問題が起こった時に面倒なことになるので
お勧めの対策という訳ではありませんが、既に実装されたコード資産がある場合
元のコードを呼び出す工夫のひとつとして紹介します。

他にも別々のスレッド/プロセスにして埋め込む方法等ありますが、
よりプログラムは複雑化します。

通常は、oncmask関数の実装を tkinter 側に移植するのが無難な方法です。
0からコードを書く場合は、tkinter->pyplotへのイベントの変換等は冗長な処理です。

投稿2020/10/20 10:02

teamikl

総合スコア8664

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

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

A_7

2020/10/20 12:54

回答して頂き、ありがとうございます。初心者の私でも理解できるようにとても丁寧な説明をして下さり、非常にわかりやすかったです。無事にtkinter上でマウス操作を行うことができました。 本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問