回答編集履歴
4
文章の修性
test
CHANGED
@@ -60,7 +60,7 @@
|
|
60
60
|
|
61
61
|
Tkinter のイベントループ終了後にもGUIへのアクセスが発生する。
|
62
62
|
|
63
|
-
対策A => WM_WINDOW
|
63
|
+
対策A => WM_DELETE_WINDOW のハンドラで、tkのイベントループ終了前にスレッドを終了する。
|
64
64
|
|
65
65
|
対策B => 独自のqueue 経由して GUI 更新を処理する。
|
66
66
|
|
@@ -68,7 +68,7 @@
|
|
68
68
|
|
69
69
|
----
|
70
70
|
|
71
|
-
解決策: メインスレッド側でGUIの更新をする
|
71
|
+
解決策1-1: メインスレッド側でGUIの更新をする
|
72
72
|
|
73
73
|
|
74
74
|
|
@@ -118,9 +118,7 @@
|
|
118
118
|
|
119
119
|
|
120
120
|
|
121
|
-
解決策2:
|
122
|
-
|
123
|
-
simpledialog.Dialog の実装を使う。
|
121
|
+
解決策2: simpledialog.Dialog の実装を使う。
|
124
122
|
|
125
123
|
|
126
124
|
|
3
3つ目の問題点追記により文章を修正
test
CHANGED
@@ -18,13 +18,13 @@
|
|
18
18
|
|
19
19
|
|
20
20
|
|
21
|
-
以下は、
|
21
|
+
以下は、3つのトピックがあるので注意してください。
|
22
22
|
|
23
23
|
- GUIでのマルチスレッドで、スレッド側からGUIを更新する方法
|
24
24
|
|
25
25
|
- SimpleDialogの実装の問題点
|
26
26
|
|
27
|
-
|
27
|
+
- アプリ終了時に、スレッドが終了済みのGUIへアクセスする問題
|
28
28
|
|
29
29
|
|
30
30
|
|
2
問題点3を追記
test
CHANGED
@@ -50,6 +50,22 @@
|
|
50
50
|
|
51
51
|
|
52
52
|
|
53
|
+
問題点3:
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
スレッドとTkinter の生存期間について、
|
58
|
+
|
59
|
+
スレッド内からTkinter へ直接アクセスが発生しているが、
|
60
|
+
|
61
|
+
Tkinter のイベントループ終了後にもGUIへのアクセスが発生する。
|
62
|
+
|
63
|
+
対策A => WM_WINDOW_DELETE のハンドラで、tkのイベントループ終了前にスレッドを終了する。
|
64
|
+
|
65
|
+
対策B => 独自のqueue 経由して GUI 更新を処理する。
|
66
|
+
|
67
|
+
|
68
|
+
|
53
69
|
----
|
54
70
|
|
55
71
|
解決策: メインスレッド側でGUIの更新をする
|
1
タイマー(after) でのサンプルを追加
test
CHANGED
@@ -120,6 +120,168 @@
|
|
120
120
|
|
121
121
|
|
122
122
|
|
123
|
+
追記: タイマーでの実装。(※スレッドで扱う場合の問題解決にはなりません)
|
124
|
+
|
125
|
+
```python
|
126
|
+
|
127
|
+
#!/usr/bin/env python3.8
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
import datetime
|
132
|
+
|
133
|
+
import tkinter as tk
|
134
|
+
|
135
|
+
from tkinter import ttk
|
136
|
+
|
137
|
+
import mydialog # https://teratail.com/questions/275549 質問文から借用
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
def getTimestamp(_time_format="%Y/%m/%d %H:%M:%S.%f"):
|
144
|
+
|
145
|
+
return datetime.datetime.now().strftime(_time_format)
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
class App:
|
152
|
+
|
153
|
+
def __init__(self, win):
|
154
|
+
|
155
|
+
self.win = win
|
156
|
+
|
157
|
+
self._running = False
|
158
|
+
|
159
|
+
self._timer = None
|
160
|
+
|
161
|
+
self._init_widgets()
|
162
|
+
|
163
|
+
self._init_events()
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
def _init_widgets(self):
|
168
|
+
|
169
|
+
time_text = self._time_text = tk.StringVar(self.win)
|
170
|
+
|
171
|
+
input_text = self._input_text = tk.StringVar(self.win)
|
172
|
+
|
173
|
+
frame = self._frame = ttk.Frame(self.win)
|
174
|
+
|
175
|
+
label = self._label = ttk.Label(frame, textvar=time_text)
|
176
|
+
|
177
|
+
entry = self._entry = ttk.Entry(frame, textvar=input_text)
|
178
|
+
|
179
|
+
stop_button = self._stop_button = ttk.Button(frame, text="Stop")
|
180
|
+
|
181
|
+
start_button = self._start_button = ttk.Button(frame, text="Start")
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
frame.pack(fill=tk.BOTH, expand=True)
|
186
|
+
|
187
|
+
label.pack()
|
188
|
+
|
189
|
+
entry.pack()
|
190
|
+
|
191
|
+
stop_button.pack()
|
192
|
+
|
193
|
+
start_button.pack()
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
def start_timer(self):
|
198
|
+
|
199
|
+
assert not self._running
|
200
|
+
|
201
|
+
self._running = True
|
202
|
+
|
203
|
+
self._timer = self.win.after_idle(self._on_timer)
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
def _init_events(self):
|
208
|
+
|
209
|
+
self._stop_button.bind("<Button-1>", self._on_stop_timer)
|
210
|
+
|
211
|
+
self._start_button.bind("<Button-1>", self._on_start_timer)
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
def _on_timer(self):
|
216
|
+
|
217
|
+
if not self._running and self._timer:
|
218
|
+
|
219
|
+
self.win.after_cancel(self._timer)
|
220
|
+
|
221
|
+
self._timer = None
|
222
|
+
|
223
|
+
return
|
224
|
+
|
225
|
+
|
226
|
+
|
227
|
+
self._time_text.set(getTimestamp())
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
# NOTE: 60FPS: 16.666 (1000/60)
|
232
|
+
|
233
|
+
# あまり小さくし過ぎても無効な値になります。
|
234
|
+
|
235
|
+
self._timer = self.win.after(15, self._on_timer)
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
def _on_start_timer(self, event):
|
240
|
+
|
241
|
+
if not self._running:
|
242
|
+
|
243
|
+
self.start_timer()
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
def _on_stop_timer(self, event):
|
248
|
+
|
249
|
+
import mydialog
|
250
|
+
|
251
|
+
if not self._input_text.get():
|
252
|
+
|
253
|
+
ret = mydialog.askyesno(message="中断します", parent=self.win)
|
254
|
+
|
255
|
+
if ret == "OK":
|
256
|
+
|
257
|
+
self._running = False
|
258
|
+
|
259
|
+
|
260
|
+
|
261
|
+
|
262
|
+
|
263
|
+
def main():
|
264
|
+
|
265
|
+
win = tk.Tk()
|
266
|
+
|
267
|
+
app = App(win)
|
268
|
+
|
269
|
+
app.start_timer()
|
270
|
+
|
271
|
+
win.mainloop()
|
272
|
+
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
if __name__ == '__main__':
|
278
|
+
|
279
|
+
main()
|
280
|
+
|
281
|
+
```
|
282
|
+
|
283
|
+
|
284
|
+
|
123
285
|
----
|
124
286
|
|
125
287
|
「スレッド側でダイアログを開いて更にモーダルにしたい」
|