PySide超入門【第23回】QThreadで学ぶマルチスレッド処理と非同期プログラミング入門

\ 迷ったらまずTechAcademyの無料カウンセリング! /

GUIアプリを作っていると、時間がかかる処理(例:ファイルの読み込み、ネットワーク通信、画像処理など)を行うことがあります。
しかし、これをそのまま実行すると GUIがフリーズして操作できなくなる問題 が起きます。

そこで使うのが QThread です。
QThreadを使うことで、重たい処理を「別スレッド」に分離し、GUIを止めずに処理を進めることができます。

今回はQThreadの基本コードと主なメソッド・プロパティの解説、よくある質問までまとめます。

目次

QThreadとは?

QThread は、Qt(PySide6)が提供する スレッド制御用のクラス です。
Python の標準ライブラリにも threadingconcurrent.futures がありますが、Qt の GUI アプリケーションでは QThreadを使うのが基本 です。

例えば、大量の計算処理やファイルダウンロードなどを QThread に任せれば、
「処理中でもGUIがサクサク動く」 アプリが作れるようになります。

基本コード

まずはQThreadを使った最小コード例です。
ここでは「バックグラウンドでカウントアップし、GUIに表示する」処理をQThreadで実行します。

  • Worker(QThread) に重たい処理を書く
  • progress = Signal(int) でメインに結果を送る
  • .start() でスレッドを開始
    という流れになっています。
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QPushButton
from PySide6.QtCore import QThread, Signal
import sys, time

# Workerクラス(別スレッドで動く処理)
class Worker(QThread):
    progress = Signal(int)  # メインスレッドへ進捗を通知

    def run(self):
        for i in range(1, 6):
            time.sleep(1)  # 重たい処理の代わり
            self.progress.emit(i)  # 結果を通知

# メインウィンドウ
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QThreadの基本")
        self.resize(300, 200)

        self.label = QLabel("待機中…", self)
        self.button = QPushButton("開始", self)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.setLayout(layout)

        self.button.clicked.connect(self.start_thread)

    def start_thread(self):
        self.thread = Worker()
        self.thread.progress.connect(self.update_label)
        self.thread.start()

    def update_label(self, value):
        self.label.setText(f"カウント: {value}")

app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

主なプロパティとメソッド一覧(全解説)

以下はQThreadでよく使うメソッド・プロパティの一覧です。

Noメソッド / プロパティ説明
1start()スレッドを開始し、内部で run() が呼ばれるthread.start()
2run()サブクラスでオーバーライドして処理を書くdef run(self): ...
3quit()イベントループを終了thread.quit()
4terminate()強制終了(非推奨、危険)thread.terminate()
5wait()スレッドの終了を待つthread.wait()
6isRunning()実行中かどうか確認print(thread.isRunning())
7isFinished()終了済みかどうか確認print(thread.isFinished())
8currentThread()現在のスレッドを取得QThread.currentThread()
9msleep(msec)スレッドをミリ秒スリープQThread.msleep(500)
10sleep(sec)スレッドを秒単位でスリープQThread.sleep(1)
11usleep(usec)スレッドをマイクロ秒スリープQThread.usleep(100)
12setPriority()スレッドの優先度を設定thread.setPriority(QThread.HighestPriority)
13priority()優先度を取得print(thread.priority())
14finished (シグナル)スレッド終了時に発火thread.finished.connect(func)
15started (シグナル)スレッド開始時に発火thread.started.connect(func)

各プロパティ・メソッド 詳細解説

【1】スレッドの開始

概要: スレッドを開始し、内部で run() が呼ばれます。
メソッドstart()
使い方

thread = Worker() # Workerは別で定義したサブクラス
thread.start()  # run() が別スレッドで実行される

【2】実行する処理

概要: サブクラスのWorkerでオーバーライドして、実際に実行したい処理を記述します。
メソッドrun()
使い方

class Worker(QThread):
    def run(self):
        print("重たい処理を実行中…")

# 重たい処理を実行中…

【3】イベントループの終了

概要: スレッドのイベントループを終了させます。start() で走らせた後に停止するときに使います。
メソッドquit()
使い方

thread.quit()

なお、run() をオーバーライドするとイベントループが動いていないため quit() は無効です。その場合は「ループの途中でフラグを確認して break する」など別の制御が必要になります

【4】即座に終了

概要: スレッドを即座に強制終了します。安全ではないので基本的には非推奨。
メソッドterminate()
使い方

def __init__(self):
    self.terminate_button = QPushButton("強制終了", self)
    layout.addWidget(self.terminate_button)
    self.setLayout(layout)
    self.terminate_button.clicked.connect(self.terminate_thread)

def terminate_thread(self):
    self.thread.terminate()

【5】終了まで待機

概要: スレッドが終了するまで処理を待機します。start()の下に書くと,カウントが進まなくなります。
メソッドwait()
使い方

self.thread.start()
self.thread.wait()  # スレッドが終わるまでここで止まる

【6】動作中判定

概要: スレッドが現在動作中かを判定します。
メソッドisRunning()
使い方

self.thread = Worker()
self.thread.start()
print(self.thread.isRunning())  # True が返る

# True

【7】終了判定

概要: スレッドがすでに終了しているかを判定します。
メソッドisFinished()
使い方

self.thread = Worker()
self.thread.start()
self.thread.wait()
print(self.thread.isFinished())  # True が返る

# True

【8】現在実行中のスレッド

概要: 現在実行中のスレッドオブジェクトを返します。
メソッドcurrentThread()(クラスメソッド)
使い方

self.thread.start()
print(QThread.currentThread())  # 現在のスレッドを取得

# <PySide6.QtCore.QThread(0x23c1df97500, name = "Qt mainThread") at 0x0000023C1E5C4180>

【9】ミリ秒単位 でスリープ

概要: スレッドを ミリ秒単位 でスリープさせます。
GUIスレッドで使うとUIが止まってしまうため、基本はWorkerスレッド内でのみ利用します。
メソッドmsleep(int msec)(static method)
使い方

class Worker(QThread):
    def run(self):
        print("処理1")
        QThread.msleep(500)  # 0.5秒スリープ
        print("処理2")
        print('重たい処理を実行中...')

# 処理1
# 処理2
# 重たい処理を実行中...

【10】秒単位 でスリープ

概要: スレッドを 秒単位 でスリープさせます。
メソッドsleep(int sec)(static method)
使い方

class Worker(QThread):
    def run(self):
        print("処理1")
        QThread.msleep(500)  # 0.5秒スリープ
        print("処理2")
        QThread.sleep(2)  # 2秒停止
        print('重たい処理を実行中...')

# 処理1
# 処理2
# 重たい処理を実行中...

【11】マイクロ秒単位 でスリープ

概要: スレッドを マイクロ秒単位 でスリープさせます。
メソッドusleep(int usec)(static method)
使い方

class Worker(QThread):
    def run(self):
        print("高速処理")
        QThread.usleep(100)  # 100マイクロ秒停止

# 高速処理

【12】スレッドの優先度

概要: スレッドの優先度を設定します。
優先度は QThread.LowPriorityQThread.NormalPriorityQThread.HighestPriority などが指定できます。
メソッドsetPriority(QThread.Priority)
使い方

self.thread = Worker()
self.thread.setPriority(QThread.HighestPriority)
self.thread.start()
優先度定数説明
IdlePriorityシステムが他のスレッドを処理していないときにのみ実行される最も低い優先度です。 (doc.qt.io, klayout.de)
LowestPriorityLowPriority よりもさらに低い優先度です。 (doc.qt.io, klayout.de)
LowPriority標準より低い優先度でスケジューリングされます。 (doc.qt.io, klayout.de)
NormalPriorityOS の標準的な優先度(デフォルト)。 (doc.qt.io, klayout.de)
HighPriorityNormalPriority より優先度が高く、多くスケジュールされます。 (doc.qt.io, klayout.de)
HighestPriorityHighPriority よりさらに高い優先度でスケジュールされます。 (doc.qt.io, klayout.de)
TimeCriticalPriority可能な限り頻繁にスケジュールされる最も高い優先度。リアルタイム処理向け。 (doc.qt.io, klayout.de)
InheritPriorityスレッドを生成した親スレッドと同じ優先度を使います。デフォルト値です。 (doc.qt.io, klayout.de)

【13】スレッドの優先度を取得

概要: スレッドに設定されている優先度を取得します。
メソッドpriority()
使い方

print(self.thread.priority())]
# Priority.InheritPriority

【14】終了したときのシグナル

概要: スレッドが終了したときに自動的に発火するシグナルです。
終了後の処理をGUI側で行いたいときに便利です。
シグナルfinished
使い方

self.thread = Worker()
self.thread.finished.connect(lambda: print("スレッドが終了しました"))
self.thread.start()

# スレッドが終了しました

【15】開始したときのシグナル

概要: スレッドが開始したときに発火するシグナルです。
処理開始のタイミングをGUIに伝えたいときに使います。
シグナルstarted
使い方

self.thread = Worker()
self.thread.started.connect(lambda: print("スレッドが開始しました"))
self.thread.start()

# スレッドが開始しました

よくある質問(FAQ)

Q1. run() を直接呼んでもいいですか?
A. ダメです。直接呼ぶと「別スレッド」ではなく「同じスレッド」で動いてしまいます。必ず start() を使いましょう。

Q2. terminate() を使ってもいいですか?
A. 強制終了はリソースリークやクラッシュの原因になるので避けるべきです。代わりにフラグを使って run() 内のループを止めるのが安全です。

Q3. QThreadとQTimerの違いは?
A. QTimerは「時間で繰り返すイベント」、QThreadは「別スレッドで処理を動かす」ための仕組みです。両者はよく組み合わせて使います。

Q4. 複数スレッドを同時に走らせることはできますか?
A. はい。Workerクラスを複数作成して .start() すれば並列処理できます。

Q5. GUI操作はスレッドから直接できますか?
A. いいえ。GUI操作はメインスレッドでしか行えません。スレッドからは Signal を使って結果を送信し、メインスレッド側でUIを更新してください。


まとめ

QThreadを使うと、GUIが止まらないままバックグラウンド処理を動かすことができます。
特に「ファイル処理」「ネットワーク通信」「長時間の計算」などでは必須の仕組みです。

  • 基本は start() + run() で処理を動かす
  • 結果をGUIに渡すには Signal を使う
  • 停止には quit()、強制終了の terminate() は極力避ける

これらを意識すれば、GUIアプリの快適さが一気に上がります。

シェアしてくださると嬉しいです!
  • URLをコピーしました!

コメント

コメントする

目次