回答編集履歴

4

不要な変数名を修正。

2024/12/16 07:58

投稿

teamikl
teamikl

スコア8760

test CHANGED
@@ -278,7 +278,7 @@
278
278
  balloon.destroy()
279
279
  else:
280
280
  def gen_timer(gen): # ジェネレーターをタイマーで処理する(簡易版)
281
- def _next(tid, time):
281
+ def _next(tid, _):
282
282
  timer.kill_timer(tid)
283
283
  if interval := next(gen, None):
284
284
  timer.set_timer(interval*1000, _next)

3

追記: win32gui でタイマー&メッセージループを使う実装

2024/12/16 07:56

投稿

teamikl
teamikl

スコア8760

test CHANGED
@@ -249,3 +249,50 @@
249
249
  if __name__ == '__main__':
250
250
  main()
251
251
  ```
252
+
253
+ ----
254
+ ## 追記: win32gui でタイマー&メッセージループを使う実装
255
+
256
+ 自分の環境では問題の再現が難しいので、(もしくは再現条件を把握できていない)
257
+ 解決に繋がるかはわかりませんが、問題の懸念要素は取り除けると思います。
258
+
259
+ 上述の「ジェネレーターをタイマーで処理する」アプローチの win32gui 版です。
260
+ pywin32 では API の SetTimer/KillTimer を直接扱わず、timer モジュールが提供されてるようです。
261
+
262
+ コードは差分のみ。balloon生成後
263
+ ```python
264
+ if 0:
265
+ import time
266
+ time.sleep(3)
267
+
268
+ # 内容と位置を更新
269
+ balloon.update(message="Updated Content!", x=500, y=300)
270
+
271
+ time.sleep(3)
272
+
273
+ # さらに更新
274
+ balloon.update(message="Final Update!")
275
+ time.sleep(3)
276
+
277
+ # 終了
278
+ balloon.destroy()
279
+ else:
280
+ def gen_timer(gen): # ジェネレーターをタイマーで処理する(簡易版)
281
+ def _next(tid, time):
282
+ timer.kill_timer(tid)
283
+ if interval := next(gen, None):
284
+ timer.set_timer(interval*1000, _next)
285
+ timer.set_timer(0, _next)
286
+
287
+ def timeline(balloon): # time.sleep の替わりに、ジェネレーターで実装
288
+ yield 3
289
+ balloon.update(message="Updated Content!", x=500, y=300)
290
+ yield 3
291
+ balloon.update(message="Final Update!")
292
+ yield 3
293
+ balloon.destroy()
294
+
295
+ gen_timer(timeline(balloon)) # タイマー
296
+ win32gui.PumpMessages() # メッセージループ
297
+
298
+ ```

2

Animationクラスを継承ではなく移譲に

2024/12/16 03:36

投稿

teamikl
teamikl

スコア8760

test CHANGED
@@ -68,11 +68,10 @@
68
68
  alpha: float = -1
69
69
  timeout: int = -1
70
70
 
71
- class BalloonAnimationMixin:
71
+ class BalloonAnimation:
72
- """
73
- `window` ... tkinter.Toplevel
72
+ def __init__(self, balloon):
74
- `label` ... tkinter.Label (XXX: not ttk.Label)
73
+ self.window, self.label = balloon.window, balloon.label
75
- """
74
+
76
75
  def _calc_range_step(self, a, b, steps):
77
76
  return (max(a,b) - min(a,b))/steps * (-1 if a > b else 1)
78
77
 
@@ -142,7 +141,7 @@
142
141
  else:
143
142
  label["fg"] = fg
144
143
 
145
- class BalloonApp(BalloonAnimationMixin):
144
+ class BalloonApp:
146
145
  def __init__(self, *args, **kwargs):
147
146
  window = tk.Toplevel(*args, **kwargs)
148
147
  window.overrideredirect(True) # ウィンドウの枠けし
@@ -154,6 +153,7 @@
154
153
 
155
154
  self.window = window
156
155
  self.label = label
156
+ self.animation = BalloonAnimation(self)
157
157
 
158
158
  def __call__(self, message, **kw) -> None:
159
159
  """Update balloon info"""
@@ -204,10 +204,10 @@
204
204
  timeout=8, # 8秒後に非表示
205
205
  )
206
206
  # Demo: 3秒かけてフェードイン
207
- yield from balloon.fade(alpha=0.5, duration=3)
207
+ yield from balloon.animation.fade(alpha=0.5, duration=3)
208
208
 
209
209
  # Demo: 点滅表示
210
- yield from balloon.blink(duration=3)
210
+ yield from balloon.animation.blink(duration=3)
211
211
 
212
212
  yield 3 # wait 3 sec
213
213
 
@@ -218,17 +218,17 @@
218
218
  yield 3 # wait 3 sec
219
219
 
220
220
  # Demo: 移動アニメーション
221
- yield from balloon.move(x=200, y=200, duration=3)
221
+ yield from balloon.animation.move(x=200, y=200, duration=3)
222
222
 
223
223
  ## 3)
224
224
  balloon("Final update!")
225
225
 
226
- yield from balloon.resize(width=400, height=400)
226
+ yield from balloon.animation.resize(width=400, height=400)
227
227
 
228
228
  yield 3 # wait 3 sec
229
229
 
230
230
  # Demo: フェードアウト
231
- yield from balloon.fade(alpha=0, duration=3)
231
+ yield from balloon.animation.fade(alpha=0, duration=3)
232
232
 
233
233
  def main():
234
234
  root = tk.Tk()

1

main関数の見通しが悪くなったので、program関数を外部へ移動・名称変更timeline関数へ

2024/12/16 02:09

投稿

teamikl
teamikl

スコア8760

test CHANGED
@@ -190,6 +190,46 @@
190
190
  window.deiconify() # 再表示
191
191
  return self
192
192
 
193
+ def timeline(balloon):
194
+ """time.sleep を使ったコードをジェネレーターとして実装する
195
+
196
+ 例:
197
+ time.sleep(3) => yield 3
198
+ """
199
+ ## 1)
200
+ balloon("Hello, world!", x=50, y=50,
201
+ font=("", 40),
202
+ alpha=0.0,
203
+ fg="#00ffff",
204
+ timeout=8, # 8秒後に非表示
205
+ )
206
+ # Demo: 3秒かけてフェードイン
207
+ yield from balloon.fade(alpha=0.5, duration=3)
208
+
209
+ # Demo: 点滅表示
210
+ yield from balloon.blink(duration=3)
211
+
212
+ yield 3 # wait 3 sec
213
+
214
+ ## 2)
215
+ balloon("Updated Content!",
216
+ font=("", 25),
217
+ )
218
+ yield 3 # wait 3 sec
219
+
220
+ # Demo: 移動アニメーション
221
+ yield from balloon.move(x=200, y=200, duration=3)
222
+
223
+ ## 3)
224
+ balloon("Final update!")
225
+
226
+ yield from balloon.resize(width=400, height=400)
227
+
228
+ yield 3 # wait 3 sec
229
+
230
+ # Demo: フェードアウト
231
+ yield from balloon.fade(alpha=0, duration=3)
232
+
193
233
  def main():
194
234
  root = tk.Tk()
195
235
  if not __debug__:
@@ -197,51 +237,11 @@
197
237
  # 事例 ⇒ PID が解らず 大量の python.exe がタスクマネージャーに
198
238
  # __debug__ の True/False は Python の起動オプションを参照
199
239
  root.withdraw() # メインウィンドウを非表示
200
- balloon = BalloonApp(root)
201
-
202
- def program():
203
- """time.sleep を使ったコードをジェネレーターとして実装する
204
-
205
- 例:
206
- time.sleep(3) => yield 3
207
- """
208
- ## 1)
209
- balloon("Hello, world!", x=50, y=50,
210
- font=("", 40),
211
- alpha=0.0,
212
- fg="#00ffff",
213
- timeout=8, # 8秒後に非表示
214
- )
215
- # Demo: 3秒かけてフェードイン
216
- yield from balloon.fade(alpha=0.5, duration=3)
217
-
218
- # Demo: 点滅表示
219
- yield from balloon.blink(duration=3)
220
-
221
- yield 3 # wait 3 sec
222
-
223
- ## 2)
224
- balloon("Updated Content!",
225
- font=("", 25),
226
- )
227
- yield 3 # wait 3 sec
228
-
229
- # Demo: 移動アニメーション
230
- yield from balloon.move(x=200, y=200, duration=3)
231
-
232
- ## 3)
233
- balloon("Final update!")
234
-
235
- yield from balloon.resize(width=400, height=400)
236
-
237
- yield 3 # wait 3 sec
238
-
239
- # Demo: フェードアウト
240
- yield from balloon.fade(alpha=0, duration=3)
241
240
 
242
241
  # NOTE: gen_timer ジェネレーターをtkのタイマーで実行する
243
242
  # done=root.quit ジェネレーター完了後に終了
243
+ balloon = BalloonApp(root)
244
- gen_timer(root, program(), done=root.quit)
244
+ gen_timer(root, timeline(balloon), done=root.quit)
245
245
 
246
246
  # イベントループはGUIプログラムでは必須
247
247
  root.mainloop()