PySide6 で GUI アプリを作成している中で、I/O バウンドな処理の完了を待っていると GUI が固まりました。

通常、バックグラウンドで何か処理を行う際に GUI の処理を止めないようにするために、QThread などを使って別スレッドで処理を行いつつ GUI スレッドを回したりします。
今回もそうやって作っていたのですが、完了までに時間のかかる I/O バウンドな処理を行わせたい場合、上手くいかない場合がありました。
I/O バウンドな処理なので、ayncio を使って非同期処理にしてやればいいと思ったのですが、Qt のイベントループと asyncio を適切に統合する必要があるようで、どのように記述すればいいのかいまいち分かりませんでした。
そんな中、qasync
というライブラリを使えば、とても簡単に処理を組み込むことができました。
コードの記述も、QThread を使う場合に比べシンプルでスッキリします。
ちなみに、asyncqt
というライブラリもありますが、こちらは既にメンテナンスされていないようです。
qasync
は asyncqt
のフォークで、現在でもメンテナンスされているようなので、使うなら qasync
のほうが良いと思います。
環境
- python 3.11
- pyside6 6.6.3.1
- qasync 0.27.1
サンプルコード
import sys
import asyncio
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel
from qasync import QEventLoop, asyncSlot
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("qasync sample")
self.label = QLabel("Press the button.", self)
self.button = QPushButton("Start", self)
self.button.clicked.connect(self.on_button_clicked)
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
@asyncSlot()
async def on_button_clicked(self):
self.label.setText("waiting...")
self.button.setEnabled(False)
# I/O バウンドな処理を想定
await asyncio.sleep(5)
self.label.setText("Press the button.")
self.button.setEnabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
window = MainWindow()
window.show()
with loop:
loop.run_forever()