回答編集履歴

4

文章の修性

2020/07/07 04:04

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -60,7 +60,7 @@
60
60
 
61
61
  Tkinter のイベントループ終了後にもGUIへのアクセスが発生する。
62
62
 
63
- 対策A => WM_WINDOW_DELETE のハンドラで、tkのイベントループ終了前にスレッドを終了する。
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つ目の問題点追記により文章を修正

2020/07/07 04:04

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -18,13 +18,13 @@
18
18
 
19
19
 
20
20
 
21
- 以下は、つのトピックがあるので注意してください。
21
+ 以下は、つのトピックがあるので注意してください。
22
22
 
23
23
  - GUIでのマルチスレッドで、スレッド側からGUIを更新する方法
24
24
 
25
25
  - SimpleDialogの実装の問題点
26
26
 
27
-
27
+ - アプリ終了時に、スレッドが終了済みのGUIへアクセスする問題
28
28
 
29
29
 
30
30
 

2

問題点3を追記

2020/07/07 04:01

投稿

teamikl
teamikl

スコア8664

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) でのサンプルを追加

2020/07/07 03:58

投稿

teamikl
teamikl

スコア8664

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
  「スレッド側でダイアログを開いて更にモーダルにしたい」