非同期処理と GUI (P)

こちら にしたがって、PySimpleGUI を用いて GUI プログラムを書いている場合についてです。

定期的な処理

アニメーションなど、定期的に実行したい処理がある場合、window.read() に `timeout`` option をつければ大丈夫です。 これで、GUI イベントが来なくても、指定時間が来たらイベントとして認識してくれます。 後は、定期的に実行したい処理を、対応 event として記述するだけ。

while True:
    # 定期的に処理したいことがある場合は、timeout を指定するので十分
    event, values = self.window.read(timeout=1000, timeout_key='timeout')
    if event == sg.WIN_CLOSED:
        break
    print(f"event='{event}', code={ord(event[0])}, values={values}\n")
    if event == 'timeout':
        self.my_time.update(str(datetime.datetime.now()))

asyncio とGUI の協調

まだ詳しくは分かっていないところもあるのですが、多分、以下のような形で組んで大丈夫じゃないかと。

  • 作成プログラム

  • このプログラムは、webserver_test/test_server2.pyで起動される(偽)Web API にアクセスするプログラムとなっています。先に、test_server2.py を実行した状態で、simpleA_coroutine.py を実行させてください。

  • GUI のハンドラの中では、await は実行せず、web API 呼び出しも create_task して add_done_callbackを指定するだけにする。

    • これで、ハンドラの中で長時間停止することはない
    • 以下はclick のハンドラの例
def listbox_click(self, value):  # 学生クリック時の動作
    if len(value) == 0:  # value はクリック要素が list として渡される
        return
    task0 = asyncio.create_task(self.score_service.get_scores(value[0]))
    task0.add_done_callback(callback_scores)
    print('Click performed.', value)
  • 一方で、定期的に co-routine にスケジュールの機会を与えないといけないので、Eventチェックの前に asyncio 処理を入れる
while True:
    await asyncio.sleep(0.01)  # 他の co-routines にスケジュール機会を与えるため
    # GUI Event は、timeout を設定。しないと、GUI event 来るまで「止まり」、
    # co-routine を見てくれなくなる
    event, values = self.window.read(timeout=200, timeout_key='timeout')
    if event == sg.WIN_CLOSED:
        break
    if event == 'timeout':
        continue
    if ...
  • この例では、window が閉じたら、async タスクがすべて終了したか確認せずに終了することになりますが、それはそれでいいかと。