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

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

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

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

Q&A

解決済

2回答

1261閲覧

tkinterを使った覚えがないのにtkinter.TclErrorが出る

suugaku_nyumon

総合スコア37

Python

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

0グッド

0クリップ

投稿2018/12/09 14:18

質問内容

この前質問したグラデーションのセルオートマトンは無事完成したと思われたのですが、プログラムを起動してその後閉じた後、意味の分からないエラーが出てきます。下は完成したコードです。

アニメーション作成ではtkinterではなくmatplotlibを使ったはずなのに、何故かプログラムを閉じた後にエラーメッセージが出てきます。エラーメッセージはコードの下に記載します。もしかして、プログラムが正しく動いていないのではないかと不安にもなっています。

なぜtkinterのエラーが出てくるのか、そしてどう解決すればいいのか教えてほしいです。お願いします。

コード

Python3

1import numpy as np 2import time 3import random 4import math 5import matplotlib.pyplot as plt 6 7def array2(N1,N2): 8 return [[0 for j in range(N2)]for k in range(N1)] 9def array3(N1,N2,N3): 10 return [array2(N2,N3)for k in range(N1)] 11 12def initdt(): 13 A=array3(1001,31,2) 14 A[0][15][1]=0.80 15 return A 16 17def execCell(A,i1): 18 i2 = 1 + i1 19 for i in range(1,30): 20 A[i2][i][1] = 2 * A[i1][i][1] * (1 - A[i1][i][1]) + A[i1][i-1][1] * (1 - A[i1][i-1][1]) + A[i1][i+1][1] * (1 - A[i1][i+1][1]) 21 22A = initdt() 23for i in range(1000): 24 execCell(A, i) 25 26ax = plt.subplot(1,1,1) 27img = ax.imshow(A[0], cmap="Greys") 28t = 0 29while True: 30 img.set_data(A[t]) 31 ax.set_title("t = {}".format(t)) 32 plt.pause(0.1) 33 t = (t+1)%1001

エラーコード

Traceback (most recent call last): File "react_kakusan3.py", line 32, in <module> plt.pause(0.1) File "C:\Python37\lib\site-packages\matplotlib\pyplot.py", line 295, in pause canvas.start_event_loop(interval) File "C:\Python37\lib\site-packages\matplotlib\backend_bases.py", line 2251, in start_event_loop self.flush_events() File "C:\Python37\lib\site-packages\matplotlib\backends\_backend_tk.py", line 491, in flush_events self._master.update() File "C:\Python37\lib\tkinter\__init__.py", line 1177, in update self.tk.call('update') _tkinter.TclError: can't invoke "update" command: application has been destroyed

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

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

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

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

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

guest

回答2

0

ベストアンサー

最近似たような問題の質問がありました。

https://teratail.com/questions/161840

原因は上記質問と同様「アニメーションのやりかたの行儀が悪い」ことだと思います。

ご質問のコードでは無限ループによりグラフのAxesに描画し続けています。よってmatplotlibのウィンドウが閉じられた後(そして内部的にFigure/Axesの描画に用いられているであろうTkinterのウィンドウが破棄された後)でもお構いなしにAxesに何かを書こうとしているわけです。

本来はお行儀のよいアニメーション、すなわち「ウィンドウが閉じられた際にアニメーション処理も停止」とすべきです。そのためにはmatplotlibに備わっているアニメーション機能を使うのが最も素直な方法と思います。その方法を使えば「ウィンドウが閉じた後で画面更新を止める」なんて配慮はmatplotlibが自動的にやってくれますのでプログラマーが気にする必要はありません。

以下はmatplotlib.animation.FuncAnimationを用いた例ですが、当然ながらウィンドウを閉じてもエラーが発生することなくプログラムが正常終了します。

python

1import numpy as np 2import matplotlib.pyplot as plt 3from matplotlib import animation 4 5... array2~execCellの定義までは省略 ... 6 7A = initdt() 8for i in range(1000): 9 execCell(A, i) 10 11fig = plt.figure() 12ax = fig.add_subplot(111) 13 14def update_plot(frame): 15 t = frame % 1001 16 ax.cla() 17 ax.set_title("t = {}".format(t)) 18 ax.imshow(A[t], cmap="Greys") 19 20ani = animation.FuncAnimation(fig, update_plot, interval=100, frames=1001) 21plt.show()

なおFuncAnimationを使うと特定のアニメーションフレームを描画するたびに関数(上記のupdate_plot)が起動されるので、全アニメーションフレームの画像データをあらかじめ計算しておかなくても、フレーム描画関数の中で計算してもよいと思います。そう作っておけば任意のフレーム数のアニメーションを描画する際にFuncAnimationのframes引数に値を指定するだけでよくなり、あらかじめ大きなサイズの配列を生成しておかなくてもよくなります。

Python

1import numpy as np 2import matplotlib.pyplot as plt 3from matplotlib import animation 4 5 6padding = np.zeros((1, 1), dtype=np.float64) 7a = None 8 9def update_plot(t): 10 global a 11 ax.cla() 12 ax.set_title("t = {}".format(t)) 13 if t == 0: 14 a = init_array() 15 else: 16 a = next_array(a) 17 ax.imshow(a, cmap="Greys") 18 19def init_array(): 20 a = np.zeros((31, 1), dtype=np.float64) 21 a[15, 0] = 0.8 22 return a 23 24def next_array(a): 25 l, m, r = a[:-2, :], a[1:-1, :], a[2:, :] 26 n = 2 * m * (1 - m) + l * (1 - l) + r * (1 - r) 27 return np.vstack((padding, n, padding)) 28 29 30fig = plt.figure() 31ax = fig.add_subplot(111) 32 33 34ani = animation.FuncAnimation(fig, update_plot, interval=100, frames=10) 35plt.show()

投稿2018/12/09 16:35

KSwordOfHaste

総合スコア18392

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

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

suugaku_nyumon

2018/12/10 02:40

原因まで教えてくれてありがとうございます。 matplotlibについてより理解が深まったと思います。
guest

0

なぜtkinterのエラーが出てくるのか

matplotlibが内部的にtkinterを使っているからです。

どう解決すればいいのか

plt.pause(0.1)を呼ぶ前にplt.draw()が必要と思います。

python

1# 略 2while True: 3 img.set_data(A[t]) 4 ax.set_title("t = {}".format(t)) 5 plt.draw() 6 plt.pause(0.1) 7 t = (t+1)%1001

こうしたところウィンドウが出てアニメーションが表示されました。

投稿2018/12/09 14:33

編集2018/12/09 14:44
hayataka2049

総合スコア30933

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

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

suugaku_nyumon

2018/12/09 15:18

plt.draw()をplt.pause(0.1)の前に入れてみて、再びプログラムを実行しました。 アニメーションは出てくるのですが、相変わらずアニメーションを消した後に、質問文に記載したものと同じエラーコードが出てきました。
hayataka2049

2018/12/09 15:44

恐らくウィンドウが死んでも描画を続けようとしてエラーになるのだと思います。plt.draw()とplt.pause()をtryでくくって、例外を出したらループをbreakするという方法で簡易的に逃げることは可能です。もう少しうまいやり方については少し調べてみます。
suugaku_nyumon

2018/12/09 16:23

ありがとうございます! 私もエラーメッセージなどを検索して明日まで少し探してみますが、もし調べてうまいやり方を見つけたら教えてくれると嬉しいです。 ただしっかり動いていそうで、数値計算的には問題はなさそうなので、もし見つからなければtryでくくる方法でいこうと思います。
suugaku_nyumon

2018/12/10 02:39

ありがとうございます!自分も読んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問