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

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

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

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Tkinter

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

Python

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

Q&A

解決済

1回答

9378閲覧

forループの処理でtkinter GUI上のラベルを動的に変更したい

DataAnalOjisan

総合スコア49

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Tkinter

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

Python

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

0グッド

1クリップ

投稿2020/08/20 13:04

編集2020/08/20 13:16

前提・実現したいこと

現在,python, tkinterを用いたGUIアプリケーションの作成をしています。そのアプリの中で,ボタン押し後にループ処理を開始し,そのループ内で動的にGUI上のラベルのテキストを変更をする機能を持たせたいと考えています。GUIの表示変更処理と,ループ処理をそれぞれ異なるスレッドで動かさなければいけないと考え,コードを書いている状況です。

発生している問題

そこで以下のコードのよう,GUI表示をスレッド化し,ボタン押しに伴うループ処理内でラベルのテキストを変更するよう処理を記述したつもりでした。しかしながら,ボタンを押したとしてもGUI上のテキストは動的に更新されず,ループ処理全体が終了したのちにやっとテキストが更新されてしまいます。ループ処理の実行はprint処理にて確認をしています。

イメージ説明
ボタン押し前の状態

イメージ説明
ループ処理中の状態:ボタンが押された状態でテキストが更新されない

イメージ説明
ループ処理後の状態

該当のソースコード

python

1import time 2import threading 3import tkinter as tk 4 5class GUI(threading.Thread): 6 7 def __init__(self): 8 threading.Thread.__init__(self) 9 self.value = 0 10 11 def start(self): 12 self.root = tk.Tk() 13 # ラベルの表示 14 self.label = tk.Label(self.root, text=self.value) 15 self.label.pack() 16 # ボタンの表示 17 self.button = tk.Button(self.root, text='push', command=self.change_value) 18 self.button.pack() 19 self.root.mainloop() 20 21 22 def change_value(self): 23 24 for value in range(100): 25 time.sleep(0.05) 26 self.value = value 27 # ラベルの値を変更 28 self.label['text'] = str(self.value) 29 # ラベルに表示されるだろう値を表示 30 print(self.value) 31 32if __name__ == '__main__': 33 gui = GUI() 34 gui.start()

試したこと

そこで,ラベルの値を変更する処理もさらに別のスレッドで処理する必要があるのかと考え,以下のようにコードを変更しました。しかしながら,コードの変更前と変わらず,ボタン押しをしても,GUI上のテキストは動的に更新されない状況です。
スレッド別に処理を実行することで,テキスト表示の変更をループ処理内で動的に変更可能と考えていたのですが,発想から根本的に間違っているのでしょうか?あるいはコードそのものが根本的に間違っているのでしょうか?恐れ入りますが,ご示唆いただけると幸いです。

python

1import time 2import threading 3import tkinter as tk 4 5class GUI(threading.Thread): 6 7 def __init__(self): 8 threading.Thread.__init__(self) 9 self.value = 0 10 11 def start(self): 12 self.root = tk.Tk() 13 # ラベルの表示 14 self.label = tk.Label(self.root, text=self.value) 15 self.label.pack() 16 # ボタンの表示 17 self.button = tk.Button(self.root, text='push', command=self.change_value) 18 self.button.pack() 19 self.root.mainloop() 20 21 22 def change_value(self): 23 24 for value in range(100): 25 time.sleep(0.05) 26 self.value = value 27 28 # 新たに変更した個所 29 new_thread = threading.Thread(target=self.change_label) 30 new_thread.start() 31 32 # ラベルに表示されるだろう値を表示 33 print(self.value) 34 35 def change_label(self): 36 self.label['text'] = str(self.value) 37 38if __name__ == '__main__': 39 gui = GUI() 40 gui.start()

補足

python = 3.7.6

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

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

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

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

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

guest

回答1

0

ベストアンサー

StringVarを利用します。
labelの中身をStringVarにバインドし、データそのものの更新はStringVarに対し実施するとGUIスレッドがlabelの中身をStringVarに変更してくれます。
StringVarを更新する処理を別スレッドで実行すればGUIスレッドがブロックされることなく画面表示を更新することができます。

以下サンプルソースです。ご自身のソースと相違点を見比べてみてください。

python

1import time 2import threading 3import tkinter as tk 4 5class GUI(threading.Thread): 6 7 def __init__(self): 8 threading.Thread.__init__(self) 9 self.value = 0 10 11 def start(self): 12 self.root = tk.Tk() 13 # StringVarをフィールドに定義する 14 self.sv = tk.StringVar() 15 self.sv.set("0") 16 # ラベルの表示 データはStringVarをバインドする 17 self.label = tk.Label(self.root, textvariable=self.sv) 18 self.label.pack() 19 # ボタンの表示 20 self.button = tk.Button(self.root, text='push', command=self.change_value_callback) 21 self.button.pack() 22 self.root.mainloop() 23 24 # change_valueを別スレッドで実行するコールバック 25 def change_value_callback(self): 26 th = threading.Thread(target=self.change_value, args=()) 27 th.start() 28 29 # StringVarを更新するように変更する 30 def change_value(self): 31 32 for value in range(100): 33 time.sleep(0.05) 34 # StringVarを変更するとGUIスレッドでラベル文字列が更新される 35 self.sv.set(str(value)) 36 # ラベルに表示されるだろう値を表示 37 print(value) 38 39if __name__ == '__main__': 40 gui = GUI() 41 gui.start()

投稿2020/08/20 14:35

hope_mucci

総合スコア4447

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

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

DataAnalOjisan

2020/08/20 22:40

頂いたコードを実行したところ,目的とする動作を達成することができました。 テキスト変更を動的に行いたい場合には,一度テキストをStringVarとして宣言する必要があったのですね。大変勉強になりました。ご回答いただきありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問