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

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

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

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

Python 3.x

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

Tkinter

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

Q&A

解決済

1回答

1973閲覧

arduinoからのセンサーデータリアルタイムプロットサブウィンドウ化

redraccoon3

総合スコア1

Matplotlib

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

Python 3.x

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

Tkinter

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

0グッド

0クリップ

投稿2021/07/13 23:17

編集2021/07/14 03:17

前提・実現したいこと

前提:python,tkinter,matplotlibの使用
実現したいこと:arduinoに刺してあるセンサーのリアルタイムプロットのサブウィンドウ化

新たにsub_window()で定義したウィンドウでarduinoから受け取ったセンサーデータのリアルタイム表示を行いたいのですが
def sub_window内で宣言されているものがやはりdef plot_data()内で必要とされているのですが、解決方法が分かりません
関数内変数を他の関数に直接橋渡しして動作できるならそれでも良いのですが
必要と要求されている変数:lines,fig,spec,ax0,ax1,canvas

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

Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\bakan\anaconda3\lib\tkinter\__init__.py", line 1705, in __call__ return self.func(*args) File "C:\Users\bakan\anaconda3\lib\tkinter\__init__.py", line 749, in callit func(*args) File "C:\Users\bakan\testes04.py", line 67, in plot_data lines.set_xdata(t) NameError: name 'lines' is not defined

該当のソースコード

python

1import tkinter as tk 2from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 3from matplotlib.figure import Figure 4import serial 5import numpy as np 6from matplotlib import gridspec 7from matplotlib.animation import FuncAnimation 8 9def sub_window(): 10 global about_window 11 # サブウインドウの作成 12 about_window = tk.Toplevel(root) 13 about_window.title("each mode") 14 about_window.configure(background = "light blue") 15 about_window.geometry("700x600") 16 17 fig = Figure() 18 spec = gridspec.GridSpec(ncols=2, nrows=1, 19 width_ratios=[1, 3]) 20 about_window.update(); 21 ax0 = fig.add_subplot(spec[0]) 22 ax1 = fig.add_subplot(spec[1]) 23 lines = ax1.plot([], [])[0] 24 ax0.set_ylim([-1.1, 1.1]) 25 ax0.axis('off') 26 ax1.set_ylim([0, 25]) 27 ax1.set_xlabel("Time[s]") 28 ax1.set_ylabel("Extension amount[mm]") 29 ax1.set_xlim([max(t)-7, max(t)+50]) 30 31 canvas = FigureCanvasTkAgg(fig,master=about_window) 32 canvas.get_tk_widget().place(x = 10,y = 10,width = 500,height = 400) 33 canvas.draw() 34 35 about_window.update(); 36 run = tk.Button(about_window, text = "Run",font = ("calbiri",12), command = lambda:plot_start()) 37 run.place(x = 100, y = 520) 38 39 about_window.update(); 40 stop = tk.Button(about_window, text = "stop",font = ("calbiri",12), command = lambda:plot_stop()) 41 stop.place(x = run.winfo_x()+run.winfo_reqwidth()+20, y = 520) 42 43 end = tk.Button(about_window, text = "exit",font = ("calbiri",12), command = quit_yn) 44 end.place(x = stop.winfo_x()+stop.winfo_reqwidth()+run.winfo_x()+run.winfo_reqwidth()+40, y = 520) 45 #anim = FuncAnimation(fig, update, frames=range(250), interval=1) 46 about_window.after(1,plot_data) 47 about_window.mainloop() 48 49def plot_data(): 50 global cond, data,t,y,tInt,lines,canvas,ser,ax1,root,about_window 51 52 if (cond == True): 53 ax1.set_xlim([max(t)-7, max(t)+0.5]) 54 55 ser.write("*".encode()) 56 data = ser.readline().strip().rsplit() 57 t = np.append(t, (float(data[0])-tInt)/10**6) 58 t = np.delete(t, 0) 59 y = np.append(y, float(data[1])*5/1023*22.2-31.7) 60 y = np.delete(y, 0) 61 sinx = np.sin(82.0 * np.pi * (max(t)) /100) 62 ax0.plot(0, sinx, marker='.', markersize=40) 63 ax0.set_ylim([-1.1, 1.1]) 64 ax0.axis('off') 65 lines.set_xdata(t) 66 lines.set_ydata(y) 67 68 canvas.draw() 69 ax0.cla() 70 71 about_window.after(1,plot_data) 72 73def plot_start(): 74 global cond 75 cond = True 76 ser.reset_input_buffer() 77 78def plot_stop(): 79 global cond 80 cond = False 81 82def update(frame): 83 global ax0 84 if 0 <=frame < 50: 85 sinx = frame/50+0.00001 86 elif 50 <= frame < 100: 87 sinx = 1 88 elif 100 <= frame <200: 89 sinx = 1 - (frame-100)/100+0.00001 90 elif 200 <= frame <250: 91 sinx = 0 92 ax0.cla() 93 ax0.set_ylim([-0.1, 1.1]) 94 ax0.axis('off') 95 ax0.plot(0,sinx,marker='.', markersize=40) 96 97def quit_yn(): 98 if tk.messagebox.askyesno("確認", "終了しますか?"): 99 root.destroy() 100 101data = np.array([]) 102cond = False 103t = np.zeros(200) 104y = np.zeros(200) 105about_window = [] 106 107ser = serial.Serial() 108ser.baudrate = 9600 109ser.port = 'COM3' 110ser.open() 111ser.reset_input_buffer() 112 113ser.write("*".encode()) 114data = ser.readline().strip().rsplit() 115tInt = float(data[0]) 116 117root = tk.Tk() 118root.title("RTP") 119root.configure(background = "light blue") 120root.geometry("200x60") 121 122end = tk.Button(root, text = "exit",font = ("calbiri",12), command = quit_yn) 123end.place(x = 10, y = 10) 124 125push_button = tk.Button(root, text = "Push",font = ("calbiri",12), command = sub_window) 126push_button.place(x = 60, y = 10) 127root.mainloop()

試したこと

メイン(関数宣言よりも下のserialポート開けと最初のウィンドウ生成のための場所)に
必要な変数をsub_windowと同じように宣言していたのですが、代わりに不必要なウィンドウが出てきてしまいました

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

問題点1: タイマーへの登録方法 (2か所)

diff

1- about_window.after(1,plot_data()) # この時点で関数を呼び出している為、再帰呼び出しになる 2+ about_window.after(1,plot_data) # タイマーに登録し、mainloopから呼び出される

問題点2: イベントループのネスト

python

1# root.mainloop() 内から呼び出される関数内でのmainloop 2 3 about_window.mainloop()

こちらはエラー原因にはなってませんが、削除して見て挙動が変わるか様子を見て下さい。
イベントループのネストにより、イベントが意図しない順番で処理されることがあるので、
mainloopは極力プログラム中で一度にすることをお勧めします。

サブウィンドウ表示が終わるまでコードの実行をブロックしたい場合は、別途
モーダルダイアログやウィンドウを閉じた時のイベント処理を調べて下さい。

投稿2021/07/14 01:12

teamikl

総合スコア8760

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

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

redraccoon3

2021/07/14 01:51

再起エラーがなくなりました。ありがとうございます。 mainloopに関しては外しても外さなくても別段挙動に変化は現れませんでした。 発生問題をシフトさせ、新たに編集、更新させていただいたためもしよろしければ見ていただけると幸いです
teamikl

2021/07/14 02:31

about_window = [] の リストオブジェクトの after メソッドを呼び出そうとしてますね。 after()は、どのウィジェットからでも呼び出せるので、 root.after で良いと思います。
redraccoon3

2021/07/14 03:07 編集

after()は前に着く●●.afterの●●のウィンドウに依存すると考えていたのですが違うのでしょうか?
teamikl

2021/07/14 03:17

通常のメソッドであれば、●●のインスタンス部分に依存しますが、 tkinter の after() は、利便性の為にどこからでも呼び出せるように なってるだけなので、問題ありません。root.after を使うのが簡単な解決策。 次点で、about_windowの初期化を事前に済ませておく。
redraccoon3

2021/07/14 03:18

なるほど、ありがとうございます また質問を更新させていただきました。 単純なのですが一番コアとなる場所でして もしよろしければご助力いただけると幸いです
teamikl

2021/07/14 03:31 編集

補足: mainloop()等と同じです。慣習的に root.mainloop() としますが tk.mainloop()でも良いし、push_button.mainloop() でも内部では同じ扱い。 メソッド内では、インスタンスに依存する情報を使っていない為、 何処から呼び出しても良いみたいな事が成立します。 一般的には、●●に依存という認識で間違いありません。 tkinter の mainloop や after での用法が特殊なケースです。 ==== タイマーで呼び出す関数内での変数については、幾つか方法があります A: グローバル変数として、利用前に予め初期化しておく B: クロージャ(関数ない関数)を使う方法 C: ジェネレータとして実装する方法 (追記) D: 関数への引数で受け渡す after(func, arg1, arg2, ...)
teamikl

2021/07/14 03:35

簡単に対応できそうな方法としては、(A) の方法で sub_window関数内で、必要な変数を global 宣言する ちなみに、root の global 宣言は不要です。 グローバル変数の参照のみの場合は問題ありません。 代入して上書き root = ... する際に global 宣言が必要になります。 例えば 「canvas」は、sub_window側で代入している為、 sub_window 側でglobal宣言が必要。利用側の plot_data 内では不要です。
redraccoon3

2021/07/14 03:45

linesの場合はこれはどちらもグローバル宣言が必要でしょうか?両方で宣言していても line 69, in plot_data lines.set_xdata(t) NameError: name 'lines' is not defined でエラーを受けてしまうのですが
redraccoon3

2021/07/14 03:47

すみません、解決しました。誠にありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問