回答ではなく、紹介のみですが
GUIとwhileの相性が悪いのかもしれないと思ったのですが、どうでしょうか。
GUIに限らず「イベント駆動型プログラミング」との相性が良くないです。
ライブラリ/フレームワーク自身もイベント処理をするループを持つので、
2つのループを同時に実行する方法を考えなければなりません。
whileが必要な場面では、タイマーやスレッドを使った解決策が一般的ですが、
他に、ジェネレータやコルーチンで実装するというアプローチもあります。
メインスレッド上で、GUIのmainloopを阻害することなく動作します。
コードはメイン処理のみ抜粋。
python
1
2def main():
3 # 省略
4
5 def game_main():
6 drawText("Press space key to start")
7
8 # スペースキー入力を待機
9 yield from wait_key_event("<space>", canvas)
10 clear()
11
12 # カウントダウン
13 for num in [3, 2, 1]:
14 item = drawText(str(num), fontsize=100)
15 yield 1000 # 1秒(1000ms) sleep
16 clear(item)
17 else:
18 drawText("start!")
19
20 after_timed_gen(root, game_main())
21 root.mainloop()
質問のコードに適応すると、こんな感じになります。
python
1import tkinter as tk
2
3def after_timed_gen(root, gen, _stop=None):
4 # タイマーでジェネレータを一つづつ読み出し
5 # yield で返された値を遅延時間に指定する(sleep)
6 def next_gen():
7 interval = next(gen, _stop)
8 if interval is not _stop:
9 root.after(interval, next_gen)
10 root.after_idle(next_gen)
11
12def count_down(canvas):
13 for num in [3, 2, 1]:
14 item = canvas.create_text(200, 200, text=str(num), font=("", 50))
15 yield 1000 # 1秒sleep
16 canvas.delete(item)
17
18def game_main(canvas):
19 canvas.create_text(200, 200, text="press space key")
20
21 flag = [True] # 空になったらスペースキーが押されたと判断
22
23 canvas.bind_all("<space>", lambda _: flag.clear())
24 while flag:
25 yield 500 # 0.5秒sleep
26 canvas.unbind_all("<space>")
27
28 canvas.delete("all")
29 yield from count_down(canvas) # (他で定義済み)
30
31 canvas.create_text(200, 200, text="done", font=("", 30))
32
33
34win = tk.Tk()
35win.geometry("400x400")
36canvas = tk.Canvas()
37canvas.pack(fill=tk.BOTH, expand=True)
38
39after_timed_gen(win, game_main(canvas))
40win.mainloop() # ↑ タイマーで実行するので、mainloop内から呼び出される
※ ジェネレータ版の実装は、タイマーの精度(多分10ms程度が最小) の問題が有るので、
あまりリアルタイム性の高いゲーム向けではありません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/08/01 08:10