Qtブログ(日本語)

QtAsyncio 技術プレビューの紹介

作成者: Qt Group 日本オフィス|Feb 19, 2024 10:45:00 PM
本稿は「Introducing QtAsyncio in technical preview」の抄訳です。
 

Qt for Python は、単なる Qt や単なる Python ではありません。両方の長所を併せ持っています!しかし、Pythonは単なる言語以上のものです。Pythonユーザーは、広大なPythonエコシステムからお気に入りのライブラリやモジュールを幅広く相互運用できることを期待しています。

非同期I/Oを使用したアプリケーションを書きたい場合、asyncioが人気のある選択肢です。asyncとawaitの構文を使用でき、コルーチン(それが何かご存じない場合は、「非同期関数」とお考えください)と連携できます。これは標準ライブラリの一部であり、AIOHTTPやFastAPIのような複数の非同期I/Oフレームワークの基盤となっています。また、TelegramやDiscordのボットを書くのにも使用されています。(本当です!)一般的に、プログラムがウェブサーバーなど、多くの入出力元からの多くのI/O操作を処理する必要がある場合は、非同期フレームワークが適しています。

ご存知のように、Qtはイベントループをベースとしており、asyncioも同様です。エンジニア、あるいは単にクレイジーな人であれば、どちらを選ぶのが明白でしょうか?もちろん、両方をミキサーに入れて混ぜ合わせてみることです!

asyncioは、デフォルトの実装の代わりに使用できるカスタムイベントループを実装するための広範なAPIを提供します。私たちはこの数か月間、この作業に取り組んできました。Qtベースのasyncio API実装であるQtAsyncioは、アプリケーションがQtと共にasyncioを使用することを可能にします。これにより、Pythonエコシステムで定評のあるライブラリの一つと組み合わせた非同期プログラムロジックに最適な選択肢として、Qt for Pythonが位置づけられます。Qt for Python 6.6.2のリリースに伴い、QtAsyncioを技術プレビューとして発表できることを嬉しく思います。

QtAsyncio の使い方

QtAsyncioでプログラムを書くには、まずモジュールをインポートします。例えば

import PySide6.QtAsyncio as QtAsyncio

QtAsyncioは、特定のコルーチンを完了するまで実行したり、Qtとasyncioのイベントループを開始したりするために使用できるrun()関数を提供しています。追加の任意引数により、コルーチンが終了した後にイベントループを停止するかどうか、およびasyncioが終了したときにQtAsyncioの中核であるQCoreApplicationをシャットダウンするかどうかを設定できます。

QtAsyncio.run(coro=None, keep_running=True, quit_qapp=True)

この機能の使用方法の詳細については、ドキュメントを参照してください。

使用例

以下は、QtとasyncioをQtAsyncioを介して連携させ、QtベースのUIでユーザーとの対話を通じてasyncioベースのタスクを作成および管理する方法を示す、非常に単純な例です。

まず、単純なQApplicationと派生クラスのWindowオブジェクトを作成します。このMainWindowには、UIとコルーチンが含まれます。

app = QApplication(sys.argv)
main_window = MainWindow()

MainWindowにはテキストフィールドが含まれています。

self.text = QLabel("The answer is 42.")

このテキストフィールドは、コルーチンによって編集されます。これは、QPushButtonによってトリガーされます。このボタンを押すと、QtAsyncioのイベントループ内でコルーチンset_text がスケジュールされます。

async_trigger = QPushButton(text="What is the question?")
async_trigger.clicked.connect(lambda: asyncio.ensure_future(self.set_text()))

そして、これがコルーチン内のコードです。通常の Qt スロットの関数ではなく、コルーチン内にあるため、同期コードに制限されることはありません。他のコルーチンを待機させることで、非同期コードも実行できます。

async def set_text(self):
    await asyncio.sleep(1)
    self.text.setText("What do you get if you multiply six by nine?")

実際の動作は次のようになります。

このサンプルコード全体はこちらでご確認いただけます。

技術プレビュー

asyncio API は2つのレベルに分けることができます。

  1. futuretaskhandle、executor、イベントループ管理関数など、イベントループと非同期処理のための基本インフラストラクチャ
  2.  トランスポート、プロトコル、ネットワーク接続、サーバー、ソケット、シグナル、サブプロセスなどが含まれる、アプリケーションで使用するためのユーザー向け API

QtAsyncio は現在、run_until_complete()、 run_forever()stop()close()call_soon()create_future()create_task() などの関数を含む、第一階層全体をカバーしています。これらの関数すべてにおいて、QtAsyncio の API は asyncio のものと同じです(意図したとおりです)。また、executorで同期コードを実行する機能も含まれています。当社はAPIの第2層の実装作業を開始しましたので、今後のQt for Pythonリリースではさらに多くの追加機能が提供される予定です。

皆さまからのフィードバックは貴重です!皆さまにとってAPIのどの部分が最も重要であるかを判断する上で、皆さまのお力添えをいただけます。QtのUIを駆使したウェブサーバーを書きたいですか?IPCの夢を見ますか?サブプロセスのサポートが、皆さまのアプリケーションが卓越性を達成するために必要なものですか?皆さまのコメントは、優先順位の設定に役立ちます。また、JIRAのチケットや機能リクエストをご自由に作成してください。そのときコンポーネントとしてQtAsyncioを選択してください。

コルーチンについて解説

コルーチンは、一時停止(yield)および再開が可能な関数です。このシンプルな概念の裏には、非同期フレームワークによって抽象化された複雑な機構が存在します。以下の講演では、コルーチンが非同期フレームワークに提供されてから完了するまでの流れを図解した図を紹介します。コルーチンとQtAsyncioについてもっと知りたい方は、ぜひご覧ください!