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

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

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

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

Tkinter

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

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Q&A

解決済

2回答

5518閲覧

Raspberry pi & Python tkinterで時計表示したいが、メモリリークでフリーズする

yamamototd

総合スコア10

Python 3.x

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

Tkinter

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

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

0グッド

0クリップ

投稿2021/04/14 05:48

前提・実現したいこと

Raspberry Pi 3
Python3
Tkinter
を使用して、時計表示するアプリケーションを作成していますが、長時間の動作でフリーズしてしまい困っております。
どうかアドバイスを頂けないでしょうか?

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

1日中動作させていると、Raspberry Piのメモリの使用量がいっぱいになりフリーズしてしまいます。

該当のソースコード

Python3

1class GUI(tkinter.Frame): 2 # ==================================== 3 # 初期化 4 # ==================================== 5 def __init__(self, master = None): 6 super().__init__(master) 7 8 #------------------------ 9 # 日時表示 10 #------------------------ 11 self.now = datetime.datetime.now() 12 self.Date_label = tkinter.Label(master, text = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S')) 13 self.Date_label.place(x=DATEPOSX, y=DATEPOSY) 14 15 #------------------------ 16 # 定期処理開始 17 #------------------------ 18 self.timeEvent() 19 20 # ==================================== 21 # タイマー起動用関数 22 # ==================================== 23 def timeEvent(self): 24 th1 = threading.Thread(target = self.update_clock) # スレッドインスタンス生成 25 th1.start() # スレッドスタート 26 try: 27 self.after(1000, self.timeEvent) # ここで、再帰的に関数を呼び出す(1秒毎) 28 except: 29 pass 30 31 # ==================================== 32 # 日時更新処理タスク 33 # ==================================== 34 def update_clock(self): 35 self.now = datetime.datetime.now() 36 self.Date_label["text"] = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S') 37 self.update() 38 39# ==================================== 40# メイン処理 41# ==================================== 42if __name__ == "__main__": 43 gui = tkinter.Tk() # .Tk class instance 44 app = GUI(master = gui) 45 app.mainloop()

試したこと

原因調査の為、ラベルに時刻を1秒間隔で表示するだけのシンプルなもの(上記ソースコード)を作成しましたが、
ラベルに時刻を代入するだけでメモリ使用量が増加していきます。
メモリの使用量の増加は、Raspberry PiのタスクマネージャーのPython3のRSSで確認しています。

またフリーズ直前では、タスクマネージャーのメモリ使用量がRaspberry Piのメモリ容量いっぱいになります。

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

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

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

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

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

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

guest

回答2

0

自己解決

他の方の質問等を参考にして色々試した所、以下のようなコードにしたらメモリ使用量は増えませんでした。

python3

1import tkinter 2import datetime 3 4class GUI: 5 # ==================================== 6 # 初期化 7 # ==================================== 8 def __init__(self): 9 self.root = tkinter.Tk() 10 11 #------------------------ 12 # 日時表示 13 #------------------------ 14 self.now = datetime.datetime.now() 15 self.root.Date_label = tkinter.Label(self.root, text = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S')) 16 self.root.Date_label.place(x=0, y=0) 17 18 self.root.Date_label2 = tkinter.Label(self.root, text = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S')) 19 self.root.Date_label2.place(x=0, y=20) 20 21 #------------------------ 22 # 定期処理開始 23 #------------------------ 24 self.timeEvent() 25 self.timeEvent2() 26 self.root.mainloop() 27 28 # ==================================== 29 # タイマー起動用関数 30 # ==================================== 31 def timeEvent(self): 32 self.update_clock() # スレッドインスタンス生成 33 self.root.after(1000, self.timeEvent) # ここで、再帰的に関数を呼び出す(1秒毎) 34 35 # ==================================== 36 # タイマー起動用関数 37 # ==================================== 38 def timeEvent2(self): 39 self.update_clock2() # スレッドインスタンス生成 40 self.root.after(3000, self.timeEvent2) # ここで、再帰的に関数を呼び出す(1秒毎) 41 42 # ==================================== 43 # 日時更新処理タスク 44 # ==================================== 45 def update_clock(self): 46 self.now = datetime.datetime.now() 47 self.root.Date_label["text"] = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S') 48 49 # ==================================== 50 # 日時更新処理タスク 51 # ==================================== 52 def update_clock2(self): 53 self.now = datetime.datetime.now() 54 self.root.Date_label2["text"] = self.now.strftime('%Y年 %m月 %d日 %H:%M:%S') 55 56# ==================================== 57# メイン処理 58# ==================================== 59if __name__ == "__main__": 60 GUI() 61 62

投稿2021/04/14 09:46

yamamototd

総合スコア10

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

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

0

def timeEvent(self): th1 = threading.Thread(target = self.update_clock) # スレッドインスタンス生成 th1.start() # スレッドスタート try: self.after(1000, self.timeEvent) # ここで、再帰的に関数を呼び出す(1秒毎) except: pass

この部分のスレッド生成によって、毎ループインスタンスが生成されている恐れがあります(このような使い方について自分は詳しくありませんが……)。また、再起的に関数を呼び出す場合、関数を抜け切るまで、大元の関数は保持されます
スレッドで呼び出す関数を無限ループさせることで、スレッドを乱立させる恐れもなく、再帰も利用しないプログラムになります

投稿2021/04/14 07:38

Third_Kei

総合スコア65

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

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

yamamototd

2021/04/14 09:48

ご返信ありがとう御座います。 スレッドを使用した再帰的な呼び出しだけでは、メモリ使用量の増加はなかったのですが、 ラベルへの書換えを加えるとメモリ使用量の増加がある状態でした。 問題の理解は出来ておりませんが、とりあえずは回避策が見つかりました。 ありがとう御座います。
teamikl

2021/04/15 02:34

>再起的に関数を呼び出す場合、関数を抜け切るまで、大元の関数は保持されます 一般的な再帰関数の説明としては正しいのですが。 タイマーを使っての呼び出しの場合は、呼び出す関数を登録するのみで、 「大元の関数」自体は、その関数が実行されるのを待つことなく終了します。 登録された関数はイベントループ(mainloop)内から呼びだされます。 timeEvent() 内で直接 timeEvent() を呼ばれるような形の再帰ではないので、 「大元の関数は保持されます」という状況には該当しません。 ---- >毎ループインスタンスが生成されている 問題点についての指摘はそのとおりで、リーク自体は かつスレッド内からGUI操作をしている為(GUI への参照を持つ為、自動回収されない) 不要になったスレッドが残り続けている状況だと予想されます。 回避策は、スレッド生成をひとつのみにする。スレッドを使わずタイマーで処理する。 どちらでもメモリーリーク自体の回避はできますが、別スレッドからのGUI操作は他の問題もあるので、 今回のケースでは、スレッドを使わずtkinterのタイマーのみで処理する別回答の案が最善です。
Third_Kei

2021/04/15 04:02

@teamiki さん、ありがとうございます afterで呼び出す際は再起動のようなイメージなのですね 勉強になりました、ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問