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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

985閲覧

sleep関数と非同期処理

eggplant935

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/12/23 05:36

前提

プログラミング(python)の超初心者です!
ちょっとしたゲームを作ろうとしているのですが、画像の点滅とキャラクターの移動を一緒に動かしたいです。
そのため、ネットなどで色々調べたりしたのですが理解力がなさ過ぎてよくわかりませんでした...
是非わかりやすく教えてくれると幸いです。

実現したいこと

ここに実現したいことを箇条書きで書いてください。

  • ▲▲機能を動作するようにする

発生している問題・エラーメッセージ

RuntimeWarning: coroutine 'func1' was never awaited func(*args) RuntimeWarning: Enable tracemalloc to get the object allocation traceback

該当のソースコード

python

1import tkinter 2import asyncio 3import time 4import random 5 6key = "" 7 8 9def key_down(e): 10 global key 11 key = e.keysym 12 13 14def key_up(e): 15 global key 16 key = "" 17 18 19cx = 400 20cy = 300 21 22 23def ran_ran(): 24 r = random.randint(1, 2) 25 if r == 1: 26 accident() 27 if r == 2: 28 accident2() 29 30 31async def func1(z): 32 global cx, cy 33 if key == "w": 34 cy = cy - 1 35 if key == "s": 36 cy = cy + 1 37 if key == "a": 38 cx = cx - 1 39 if key == "d": 40 cx = cx + 1 41 canvas.coords("SHIP", cx, cy) 42 root.after(10, func1) 43 44 45def draw_1(): 46 x = 200 47 y = 400 48 canvas.create_image(x, y, image=img2, tag="acc") 49 50 51def draw_2(): 52 x = 200 53 y = 400 54 canvas.create_image(x, y, image=img3, tag="enemy1") 55 56 57def draw_3(): 58 x = 500 59 y = 300 60 canvas.create_image(x, y, image=img4, tag="enemy2") 61 62 63async def accident(): 64 for i in range(5): 65 draw_1() 66 canvas.update() 67 #time.sleep(0.3) 68 await asyncio.sleep(0.3) 69 canvas.delete("acc") 70 canvas.update() 71 await asyncio.sleep(0.3) 72 draw_2() 73 canvas.update() 74 await asyncio.sleep(3) 75 canvas.delete("enemy1") 76 77 78 79async def accident2(): 80 for i in range(5): 81 draw_1() 82 canvas.update() 83 await asyncio.sleep(0.3) 84 canvas.delete("acc") 85 canvas.update() 86 await asyncio.sleep(0.3) 87 draw_3() 88 canvas.update() 89 await asyncio.sleep(3) 90 canvas.delete("enemy2") 91 92 93 94async def main(): 95 task1 = asyncio.create_task(accident()) 96 task2 = asyncio.create_task(accident()) 97 task3 = asyncio.create_task(func1()) 98 await task1 99 await task2 100 await task3 101 102 103root = tkinter.Tk() 104root.title("避け") 105root.bind("<KeyPress>", key_down) 106root.bind("<KeyRelease>", key_up) 107canvas = tkinter.Canvas(width=800, height=600, bg="blue") 108canvas.pack() 109img = tkinter.PhotoImage(file="ship1.png") 110img2 = tkinter.PhotoImage(file="tyuui.png") 111img3 = tkinter.PhotoImage(file="hosonagaiyatu.png") 112img4 = tkinter.PhotoImage(file="bakemonmon.png") 113canvas.create_image(cx, cy, image=img, tag="SHIP") 114asyncio.run(main()) 115root.mainloop()

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

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

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

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

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

guest

回答1

0

ベストアンサー

エラー内容は

func1 はasync と宣言されてるコルーチンなので tkinter のタイマーとして呼び出す関数には使えません。
コルーチンとしては await がない為エラーが起こっています。
asyncio と tkinter のタイマーの混在に為起こっています。

一定期間毎に処理は、asyncio では普通に await asyncio.sleep を挟んだループ処理で行えます。

問題点:
tkinter, asyncio は、それぞれ
ライブラリを機能させるために必要なイベントループを持ち、
イベントループを呼び出す関数(asyncio.run, root.mainloop)は、
どちらも終了まで待ち続ける為、両方を同時に使うことはできません。

解決方法
スレッドを使う方法もありますが、
どちらのライブラリもメインスレッド外で動かした場合に、手間が増える部分があるので、
asyncio もしくは tkinter のどちらかを、
もう片方のイベントループ内で動かすことになります。

python

1 2async def gui_event_loop(root, interval=0.01): 3 while True: 4 root.update() 5 await asyncio.sleep(interval) 6 7# interval の部分は、FPSに応じて適宜調整。

上記のコルーチンを、asyncio のタスクとして動かすことで
asyncio 内で tkinter のイベントループを稼働させることができます。

ただし、asyncio が必要かどうかは検討の余地があって、
参考までに、tkinter のみで同様のことを行うには
ジェネレーターとタイマー機能で疑似的なスリープができます。

https://github.com/teamikl/gentimer

使い方は、sleep したいところを yield で遅延秒数を返し、
タイマーとして実行するだけです。


追記:
もう少し掘り下げると、
点滅させるために毎回 delete/create していますが、
特定のタグのみを表示非表示状態を切り替えるようにすることも可能です。
タグは複数指定可能です。

python

1import tkinter 2# import asyncio 3# import time 4import random 5 6try: 7 # pip install gentimer 8 from gentimer.timer_tk import gen_timer 9except ImportError: 10 # 代替実装、必要な部分のみ抜粋 11 def gen_timer(root, gen): 12 def _tick(): 13 interval = next(gen, None) 14 if interval is not None: 15 root.after(max(1, interval*1000), _tick) 16 root.after_idle(_tick) 17 18 19 20key = "" 21 22 23def key_down(e): 24 global key 25 key = e.keysym 26 27 28def key_up(e): 29 global key 30 key = "" 31 32 33def func1(cx=400, cy=300): 34 # NOTE: ジェネレーター内なので、cx, cy はローカル変数で管理できるようになります 35 while True: 36 if key == "w": 37 cy = cy - 1 38 if key == "s": 39 cy = cy + 1 40 if key == "a": 41 cx = cx - 1 42 if key == "d": 43 cx = cx + 1 44 canvas.coords("SHIP", cx, cy) 45 yield 0.01 46 47 48def draw_1(x=200, y=400): 49 canvas.create_text(x, y, text="acc", fill="yellow", tag=("acc", "blink")) 50 51 52def draw_2(x=200, y=400): 53 canvas.create_text(x, y, text="敵1", fill="green", tag=["enemy1", "blink"]) 54 55 56def draw_3(x=500, y=300): 57 canvas.create_text(x, y, text="敵2", fill="red", tag="enemy2 blink") 58 59 60def accident(): 61 draw_1() 62 draw_2() 63 yield 3 64 canvas.delete("enemy1") 65 66 67def accident2(): 68 draw_1() 69 draw_3() 70 yield 3 71 canvas.delete("enemy2") 72 73 74def blinker(canvas): 75 # "hidden" と "normal" を繰り返す無限リスト 76 from itertools import cycle 77 status = cycle(["hidden", "normal"]) 78 79 while True: 80 # "blink" タグの付いた要素の表示非表示を 300ms 毎に切り替え 81 canvas.itemconfigure("blink", state=next(status)) 82 yield 0.3 83 84 85root = tkinter.Tk() 86root.title("避け") 87root.bind("<KeyPress>", key_down) 88root.bind("<KeyRelease>", key_up) 89canvas = tkinter.Canvas(width=800, height=600, bg="blue") 90canvas.pack() 91 92canvas.create_text(cx, cy, text="船", fill="black", tag="SHIP") 93 94gen_timer(root, accident()) 95gen_timer(root, accident2()) 96gen_timer(root, func1()) # 船の移動 97gen_timer(root, blinker(canvas)) # 点滅アニメーション用 98 99root.mainloop() 100

投稿2022/12/23 09:52

編集2022/12/23 12:33
teamikl

総合スコア8664

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

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

eggplant935

2023/01/05 14:08

返信遅れてすみません... ようやくわかりました!!本当にありがとうございます。 これからも精進していきます!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問