2023-01-23

QThreadPool と QRunnable 〜 PySide6 〜

PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。配布ライセンスは LGPL で公開されています。最新のバージョンは Qt6 に対応した PySide6(記事執筆時点で 6.4.2)です。

参考サイト [1] に QThreadPool と QRunnable を利用したマルチスレッディングの記事が判り易くまとめられていたので、早速記事を参考にしながら自分でもサンプルを作ってみました。

下記の OS 環境で動作確認をしました。

Fedora Linux 37 (Server Edition) x86_64
python3.11 python3-3.11.1-3.fc37.x86_64
PySide6 6.4.2

qt_threadpool.py
#!/usr/bin/env python
# coding: utf-8
import sys
import time
from PySide6.QtCore import (
QObject,
QRunnable,
QThreadPool,
Signal,
Slot,
)
from PySide6.QtWidgets import (
QApplication,
QPlainTextEdit,
QPushButton,
QSizePolicy,
QVBoxLayout,
QWidget,
)
class WorkerSignal(QObject):
progress = Signal(str)
finished = Signal()
class Worker(QRunnable):
def __init__(self):
super().__init__()
self.signals = WorkerSignal()
@Slot()
def run(self):
for count in range(0, 5):
time.sleep(1)
self.signals.progress.emit('%d%% done.' % (count * 100 / 4))
self.signals.finished.emit()
class Example(QWidget):
log: QPlainTextEdit = None
def __init__(self):
super().__init__()
self.threadpool = QThreadPool()
self.init_ui()
self.show_log(
'Multithreading with maximum %d threads' % self.threadpool.maxThreadCount()
)
self.setWindowTitle('QThreadPool')
self.resize(400, 200)
def init_ui(self):
layout = QVBoxLayout()
self.setLayout(layout)
btn = QPushButton('START')
btn.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
btn.clicked.connect(self.button_clicked)
layout.addWidget(btn)
self.log = QPlainTextEdit()
self.log.setMaximumBlockCount(1000)
self.log.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
layout.addWidget(self.log)
def button_clicked(self):
obj = self.sender()
self.show_log('%s button is clicked and starts thread task.' % obj.text())
worker = Worker()
worker.signals.progress.connect(self.show_log)
worker.signals.finished.connect(self.thread_complete)
self.threadpool.start(worker)
def thread_complete(self):
self.show_log('Thread completed!')
def show_log(self, msg: str):
self.log.insertPlainText(msg + '\n')
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

別スレッド側では簡単な処理しかしていませんが、QThread でゴリゴリと記述するのに比べると、QThreadPool と QRunnable を利用すると、随分とスッキリ記述できました。

ただ、QRunnable と Signal を多重継承ができなかったので、Signal 用に別クラスを作らなければならなかったのが、ちょっと面倒なことぐらいです。

このサンプルでは START ボタンを続けてクリックすれば、スレッドが次々できますが、そのようなマルチスレッディングが必要な場面は、自分の用途ではとりあえずありません。むしろ、スレッドが処理している間は、同じ処理のスレッドができないように管理しなければならない場面のほうが多そうです。

参考サイト

  1. Multithreading PySide6 applications with QThreadPool [2022-08-11]
  2. QThreadPool - Qt for Python
  3. QRunnable - Qt for Python

 

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



0 件のコメント: