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

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

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

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

Tkinter

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

Q&A

解決済

3回答

14371閲覧

Python3 tkinter whileループを開始するとボタンが押せなくなる

opyon

総合スコア1009

Python 3.x

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

Tkinter

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

1グッド

1クリップ

投稿2018/09/19 05:21

参考にした質問と回答:Python3のtkinterで並行処理?を行いたい
参考にしたサイト:簡易ストップウォッチを作ってみた @Python3

似たような質問で恐縮です。
上記を参考に見よう見真似で簡易ストップウォッチを作ったのですがスタートした後に他ボタンを押すと
反応が無く強制終了するしかない状態です。
他のボタンを押さなければ1秒毎に標準出力で秒数のカウントは出来ています。

スレッド化無しで動くならスレッド化は無くてもよいのですがwhileループ中にボタンを反応させるにはどこが間違っているのでしょうか?
ヒントだけでもご教示いただけると助かります。

###実現したいこと
スタート,ストップ,終了ボタン制御
ループを使ったラベルの常時更新

###試したこと
Python3のtkinterで並行処理?を行いたいの@KSwordOfHasteさんの回答コードで動作確認済み。
クラス化したこと以外は同じような処理にしたつもりなのですがうまく動きません。
フラグをself.インスタンス変数にしてみたりフラグ変数の配置場所を変えてみましたが結果は変わりませんでした。
ループ無しでの動作は一通り問題無く動いています。

Python3.7.0

Python3

1# coding=UTF-8 2import sys 3import time 4import tkinter as tk 5import threading as th 6 7 8class SSW(tk.Frame): 9 10 11 def __init__(self, master = None): 12 13 tk.Frame.__init__(self, master) 14 self.master.title("Sample Stop Watch") 15 self.master.bind("<Control-q>", self.quit) 16 self.master.geometry("280x160") 17 18 self.lbl_msg = tk.Label(master, text = "<quit:Control-q>") 19 self.lbl_msg.pack(side = "top") 20 self.lbl_lap = tk.Label(master, text = "0.00") 21 self.lbl_lap.pack(side = "top") 22 23 self.btn_run = tk.Button(master, text = "run", command = self.run) 24 self.btn_run.pack() 25 self.btn_end = tk.Button(master, text = "end", command = self.end) 26 self.btn_end.pack() 27 self.btn_quit = tk.Button(master, text = "quit", command = self.quit) 28 self.btn_quit.pack() 29 30 def loop(self): 31 32 global timer_start 33 timer_start = time.time() 34 35 global stop_flg 36 stop_flg = False 37 38 while not stop_flg: 39 time.sleep(1) 40 timer_lap = time.time() - timer_start 41 out_lap = round(timer_lap, 2) 42 self.lbl_lap.configure(text = out_lap) 43 print(out_lap) 44 45 def run(self): 46 print('Start!') 47 # スレッド開始 48 th.Thread(target = self.loop()).start() 49 50 def end(self): 51 print('Stop!') 52 global stop_flg 53 stop_flg = True 54 55 def quit(self): 56 sys.exit() 57 58 59# main 60if __name__ == '__main__': 61 ss = SSW() 62 ss.pack() 63 ss.mainloop()
kotai2001👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

スレッド化対応を行う時のよくあるミスとして、関数を渡さずに関数呼び出を行ってしまうというのがあります。

th.Thread(target = self.loop()).start()

Python

1th.Thread(target = self.loop).start()

別解)スレッドを使わなくても、afterを使う形でも可能です。あとはイベントスケジューラdbader/scheduleなどでも。

以下はafterを使ったサンプルコードです。

Python

1# -*- coding: utf8 -*- 2# coding=UTF-8 3import sys 4import time 5import tkinter as tk 6 7stop_flg = False 8ROOT = tk.Tk() 9 10 11def stop_watch(): 12 timer_start = time.time() 13 14 def elapsed(): 15 timer_lap = time.time() - timer_start 16 out_lap = round(timer_lap, 2) 17 return out_lap 18 return elapsed 19 20 21class SSW(tk.Frame): 22 def __init__(self, master = None): 23 super().__init__(master) 24 self.master.title("Sample Stop Watch") 25 self.master.bind("<Control-q>", self.quit) 26 self.master.geometry("280x160") 27 # masterではなく、親コンポーネントはselfで。 28 self.lbl_msg = tk.Label(self, text="<quit:Control-q>") 29 self.lbl_msg.pack(side = "top") 30 self.lbl_lap = tk.Label(self, text="0.00") 31 self.lbl_lap.pack(side = "top") 32 33 self.btn_run = tk.Button(self, text="run", command = self.run) 34 self.btn_run.pack() 35 self.btn_end = tk.Button(self, text="end", command = self.end) 36 self.btn_end.pack() 37 self.btn_quit = tk.Button(self, text="quit", command = self.quit) 38 self.btn_quit.pack() 39 40 def loop(self): 41 self.lbl_lap.configure(text=self.clock()) 42 if not stop_flg: 43 # 次のイベント呼び出しを予約 44 self.after(1000, self.loop) 45 46 def run(self): 47 global stop_flg 48 print('Start!') 49 stop_flg = False 50 self.clock = stop_watch() 51 # 52 self.after(0, self.loop) 53 54 def end(self): 55 global stop_flg 56 print('Stop!') 57 stop_flg = True 58 59 def quit(self): 60 sys.exit() 61 62 63# main 64if __name__ == '__main__': 65 ss = SSW(ROOT) 66 ss.pack() 67 ss.mainloop()

Python言語のスレッドは他の言語と違って、IO律速の処理の高速化に使われることが多いです。
■参考情報

並列方法律速ポイントGILの制限データの受け渡し
プロセス演算律速影響を受けないpickle可能なオブジェクト
スレッドIO律速影響を受ける制約なし

※GILはグローバルインタプリタロックといいます。

投稿2018/09/19 05:33

編集2018/09/19 06:24
umyu

総合スコア5846

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

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

opyon

2018/09/19 05:36

ありがとうございます。 動きました!素人が理解もなく写経やコピペでやるとハマりますね^^;
opyon

2018/09/19 06:26

ありがとうございます。 最初afterで書いてたのですが例の記述ミスでハマってしまいまして。 参考にしたコードに近づけるために削りました。
umyu

2018/09/19 06:29

@opyonさんへ 解決してよかったですー。
opyon

2018/09/19 06:31

参考資料も確認しておきます!
opyon

2018/09/19 07:02

演算だけならマルチスレッド化しないほうが良さそうですね。
opyon

2018/09/19 07:44

@umyuさんのように英語堪能で海外情報詳しい方が羨ましいです。 日本語の数倍数十倍の情報量ですよね。 Google翻訳先生に頑張ってもらいます!
guest

0

検索で辿り着いた方へ解決済みコード載せておきます。
質問解決後別のエラーが残っていたので修正。
スレッド終了処理をしていなかったので追記。
@magichanさん回答コード内参考

レイアウトは冗長なので割愛。

Python3

1# coding=UTF-8 2import sys 3import time 4import tkinter as tk 5import threading as th 6 7 8class SSW(tk.Frame): 9 10 def __init__(self, master = None): 11 12 self.th_main = None 13 self.update_time = 0.02 14 15 super().__init__(master) 16 self.master.title("Sample Stop Watch") 17 self.master.geometry("300x200") 18 19 # レイアウト? 20 # must be top, bottom, left, or right 21 self.f1 = tk.Frame(self) 22 self.f1.lbl_total = tk.Label(self, text = "0.00", font = ("", 30)) 23 self.f1.lbl_total.pack() 24 self.f1.lbl_lap = tk.Label(self, text = "0.00", font = ("", 20)) 25 self.f1.lbl_lap.pack() 26 self.f1.pack() 27 28 self.f2 = tk.Frame(self) 29 self.f2.btn_run = tk.Button(self.f2, text = "run" 30 , width = 20, command = self.run) 31 self.f2.btn_run.pack() 32 self.f2.btn_lap = tk.Button(self.f2, text = "lap" 33 , width = 20, command = self.lap) 34 self.f2.btn_lap.pack() 35 self.f2.btn_end = tk.Button(self.f2, text = "end" 36 , width = 20, command = self.end) 37 self.f2.btn_end.pack() 38 self.f2.btn_quit = tk.Button(self.f2, text = "quit" 39 , width = 20, command = self.quit) 40 self.f2.btn_quit.pack() 41 self.f2.pack() 42 43 # メインループ 44 def loop(self): 45 46 timer_start = time.time() 47 self.timer_lap_total = 0 48 49 self.stop_flg = False 50 self.stop_lap = False 51 52 while not self.stop_flg: 53 # 更新時間 54 time.sleep(self.update_time) 55 56 # トータル更新 57 timer_total = time.time() - timer_start 58 out_total = round(timer_total, 2) 59 60 self.f1.lbl_total.configure(text = out_total) 61 62 # ラップ更新 63 if not self.stop_lap: 64 timer_lap = timer_total - self.timer_lap_total 65 out_lap = round(timer_lap, 2) 66 self.f1.lbl_lap.configure(text = out_lap) 67 68 # スレッド開始処理 69 def run(self): 70 print('Start!') 71 if self.th_main == None: 72 self.th_main = th.Thread(target = self.loop) 73 self.th_main.start() 74 75 # ラップ計算 76 def lap(self): 77 if self.stop_lap: 78 print('Start Lap!') 79 self.timer_lap_tmp = time.time() - self.timer_lap_tmp 80 self.timer_lap_total += self.timer_lap_tmp 81 self.stop_lap = False 82 else: 83 print('Stop Lap!') 84 self.stop_lap = True 85 self.timer_lap_tmp = time.time() 86 87 # スレッド終了処理 88 def end(self): 89 print('Stop!') 90 self.stop_flg = True 91 92 if self.th_main: 93 94 print("end 2-1") 95 print(self.th_main) 96 self.th_main = None 97 print(self.th_main) 98 99 # うまく止まらないので=Noneに変更 100 # self.th_main.th_join() 101 # self.th_main.stop() 102 103 print("end 3-1") 104 105 # フォームを閉じる 106 def quit(self): 107 self.stop_flg = True 108 self.th_main = None 109 sys.exit() 110 111 112# main 113if __name__ == '__main__': 114 ss = SSW() 115 ss.pack() 116 ss.mainloop()

投稿2018/09/19 06:37

編集2018/09/19 12:33
opyon

総合スコア1009

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

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

0

並行処理技術を応用して、動的に時間を更新できる(ストップウォッチのように時間が常に更新される)ストップウォッチを作ってみました
役に立てれれば幸いです

python3

1import time 2import tkinter as tk 3import threading 4 5class Test(tk.Frame): 6 def __init__(self, master=None): 7 super().__init__(master) 8 self.stop_fg=False 9 self.frame=0.00 10 self.time_start=None 11 self.time_now=None 12 self.GUI() 13 14 def GUI(self): 15 self.Text001=tk.Label(text=self.frame) 16 self.Text001.grid(column=0,row=0) 17 Text001=tk.Label(text="sec") 18 Text001.grid(column=1,row=0) 19 Button001=tk.Button(text="Start",command=self.on_start) 20 Button001.grid(column=0,row=1) 21 Button002=tk.Button(text="Stop",command=self.on_stop) 22 Button002.grid(column=1,row=1) 23 Button002=tk.Button(text="Reset",command=self.on_reset) 24 Button002.grid(column=2,row=1) 25 26 def run(self): 27 self.stop_fg=False 28 if self.time_start==None: 29 self.time_start=time.time() 30 31 while self.stop_fg!=True: 32 self.time_now=time.time() 33 self.frame=round((self.time_now-self.time_start),2) 34 print(self.frame) 35 self.Text001["text"]=self.frame 36 37 def on_start(self): 38 threading.Thread(target=self.run).start() 39 40 def on_stop(self): 41 self.stop_fg=True 42 43 def on_reset(self): 44 self.on_stop() 45 self.time_start=None 46 self.time_now=None 47 self.frame=0.00 48 self.Text001["text"]=self.frame 49 50root=tk.Tk() 51app=Test(master=root) 52app.mainloop() 53

投稿2018/09/21 09:19

tomo1998

総合スコア34

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

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

tomo1998

2018/09/21 09:22

umyuさんの掲示したプログラムでは、時間がたつにつれズレが少しづつ目立つのでちょっと自分なりに組んでみました(汗)
kotai2001

2020/05/26 07:28

大変参考になりました。ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問