PythonのGUIライブラリであるPySideでは、時間のかかる処理をスレッド化するためにQThread
を使用します。Pythonの標準モジュールにはthreading
がありますが、PySideを使っている場合や、Signal/Slotなど、Qtとの対話を行う場合はQThread
を使った方が良いとされています。
QtにはQtConcurrent
というQThread
よりも高レベルなAPIがありますが、PySideでは提供されていないようです。そのため、この記事ではQThread
を使ったマルチスレッド処理について説明します。
以下に、QThread
とmoveToThread
を使ったスレッド化のサンプルコードを示します。
import sys, threading
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout
from PySide6.QtCore import Qt, QObject, QThread, Signal, Slot
import shiboken6
import time
class Worker(QObject):
"""バックグラウンドで処理を行うクラス"""
countup = Signal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.__is_canceled = False
def run(self):
print(f'worker started, thread_id={threading.get_ident()}')
count = 0
while not self.__is_canceled:
count += 1
self.countup.emit(count)
time.sleep(0.001)
print(f'worker finished, thread_id={threading.get_ident()}')
def stop(self):
self.__is_canceled = True
class MainWindow(QWidget):
"""メインウィンドウ"""
def __init__(self, parent=None):
super().__init__(parent)
self.__thread = None
layout = QVBoxLayout()
button = QPushButton('Start')
button.clicked.connect(self.__start)
layout.addWidget(button)
button = QPushButton('Stop')
button.clicked.connect(self.__stop)
layout.addWidget(button)
self.__label = QLabel()
layout.addWidget(self.__label)
self.setLayout(layout)
def __start(self):
"""開始"""
print(f'start, thread_id={threading.get_ident()}')
self.__stop()
self.__thread = QThread()
self.__worker = Worker()
self.__worker.moveToThread(self.__thread) # 別スレッドで処理を実行する
# シグナルスロットの接続(self.__countup をスレッド側で実行させるために Qt.DirectConnection を指定)
self.__worker.countup.connect(self.__countup, type=Qt.DirectConnection)
# スレッドが開始されたら worker の処理を開始する
self.__thread.started.connect(self.__worker.run)
# スレッドが終了したら破棄する
self.__thread.finished.connect(self.__worker.deleteLater)
self.__thread.finished.connect(self.__thread.deleteLater)
self.__thread.start()
def __stop(self):
"""停止"""
if self.__thread is not None:
self.__worker.stop()
self.__thread.quit()
self.__thread.wait()
self.__thread = None
self.__worker = None
def __countup(self, count):
"""カウントアップ"""
self.__label.setText(str(count))
このコードでは、Worker
クラスがバックグラウンドで時間のかかる処理を行い、その結果をメインウィンドウに通知します。Worker
クラスはQObject
を継承しており、QThread
に移動(moveToThread
)されて別スレッドで実行されます。
以上がPythonとQtのQThreadを使ったマルチスレッド処理の基本的な使い方です。GUIアプリケーションの応答性を保つためには、このようなマルチスレッド処理が重要となります。