\ 迷ったらまずTechAcademyの無料カウンセリング! /
PySide超入門【第23回】QThreadで学ぶマルチスレッド処理と非同期プログラミング入門

GUIアプリを作っていると、時間がかかる処理(例:ファイルの読み込み、ネットワーク通信、画像処理など)を行うことがあります。
しかし、これをそのまま実行すると GUIがフリーズして操作できなくなる問題 が起きます。
そこで使うのが QThread です。
QThreadを使うことで、重たい処理を「別スレッド」に分離し、GUIを止めずに処理を進めることができます。
今回はQThreadの基本コードと主なメソッド・プロパティの解説、よくある質問までまとめます。
QThreadとは?
QThread
は、Qt(PySide6)が提供する スレッド制御用のクラス です。
Python の標準ライブラリにも threading
や concurrent.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 | メソッド / プロパティ | 説明 | 例 |
---|---|---|---|
1 | start() | スレッドを開始し、内部で run() が呼ばれる | thread.start() |
2 | run() | サブクラスでオーバーライドして処理を書く | def run(self): ... |
3 | quit() | イベントループを終了 | thread.quit() |
4 | terminate() | 強制終了(非推奨、危険) | thread.terminate() |
5 | wait() | スレッドの終了を待つ | thread.wait() |
6 | isRunning() | 実行中かどうか確認 | print(thread.isRunning()) |
7 | isFinished() | 終了済みかどうか確認 | print(thread.isFinished()) |
8 | currentThread() | 現在のスレッドを取得 | QThread.currentThread() |
9 | msleep(msec) | スレッドをミリ秒スリープ | QThread.msleep(500) |
10 | sleep(sec) | スレッドを秒単位でスリープ | QThread.sleep(1) |
11 | usleep(usec) | スレッドをマイクロ秒スリープ | QThread.usleep(100) |
12 | setPriority() | スレッドの優先度を設定 | thread.setPriority(QThread.HighestPriority) |
13 | priority() | 優先度を取得 | print(thread.priority()) |
14 | finished (シグナル) | スレッド終了時に発火 | thread.finished.connect(func) |
15 | started (シグナル) | スレッド開始時に発火 | 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()
【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.LowPriority
、QThread.NormalPriority
、QThread.HighestPriority
などが指定できます。
メソッド: setPriority(QThread.Priority)
使い方:
self.thread = Worker()
self.thread.setPriority(QThread.HighestPriority)
self.thread.start()
優先度定数 | 説明 |
---|---|
IdlePriority | システムが他のスレッドを処理していないときにのみ実行される最も低い優先度です。 (doc.qt.io, klayout.de) |
LowestPriority | LowPriority よりもさらに低い優先度です。 (doc.qt.io, klayout.de) |
LowPriority | 標準より低い優先度でスケジューリングされます。 (doc.qt.io, klayout.de) |
NormalPriority | OS の標準的な優先度(デフォルト)。 (doc.qt.io, klayout.de) |
HighPriority | NormalPriority より優先度が高く、多くスケジュールされます。 (doc.qt.io, klayout.de) |
HighestPriority | HighPriority よりさらに高い優先度でスケジュールされます。 (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アプリの快適さが一気に上がります。
コメント