[Python Tkinter] プログレスバーをマルチスレッドで進行させる方法
PythonのTkinterでプログレスバーをマルチスレッドで進行させるには、threading
モジュールを使用します。
Tkinterはシングルスレッドで動作するため、長時間実行される処理をメインスレッドで行うとGUIがフリーズします。
これを防ぐため、バックグラウンドで処理を行うスレッドを作成し、メインスレッドではTkinterのイベントループを維持します。
スレッド内で進行状況を更新し、Tkinterのafter()メソッド
を使って定期的にプログレスバーを更新します。
Tkinterとマルチスレッドを組み合わせる方法
メインスレッドとバックグラウンドスレッドの役割分担
TkinterはGUIアプリケーションを作成するためのライブラリですが、メインスレッドでUIを操作する必要があります。
一方、時間のかかる処理はバックグラウンドスレッドで実行することで、UIがフリーズするのを防ぎます。
以下のように役割を分担します。
スレッドの種類 | 役割 |
---|---|
メインスレッド | GUIの表示とユーザーからの入力処理 |
バックグラウンドスレッド | 時間のかかる処理(データ処理、ファイルのダウンロードなど) |
threading.Threadを使ったスレッドの作成
Pythonのthreading
モジュールを使用して、新しいスレッドを作成します。
以下は、スレッドを作成する基本的なコードです。
import threading
import time
def background_task():
for i in range(5):
print(f"バックグラウンド処理中: {i + 1}")
time.sleep(1)
# スレッドの作成
thread = threading.Thread(target=background_task)
thread.start()
このコードを実行すると、バックグラウンドで処理が行われる間にメインスレッドは他の処理を続けることができます。
バックグラウンド処理中: 1
バックグラウンド処理中: 2
バックグラウンド処理中: 3
バックグラウンド処理中: 4
バックグラウンド処理中: 5
スレッド内での処理とプログレスバーの更新
スレッド内での処理が進行する際に、プログレスバーを更新する方法を考えます。
プログレスバーは、Tkinterのttk
モジュールを使用して作成します。
import tkinter as tk
from tkinter import ttk
import threading
import time
def update_progress_bar(progress_bar):
for i in range(101):
time.sleep(0.1) # 処理のシミュレーション
progress_bar['value'] = i
root.update_idletasks() # UIを更新
root = tk.Tk()
root.title("プログレスバーの例")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
# スレッドの作成
thread = threading.Thread(target=update_progress_bar, args=(progress_bar,))
thread.start()
root.mainloop()
このコードでは、プログレスバーがバックグラウンドスレッドで更新されます。
Tkinterのafter()メソッドを使った安全なUI更新
Tkinterのafter()メソッド
を使用すると、指定した時間後に関数を呼び出すことができます。
これにより、UIの更新をメインスレッドで安全に行うことができます。
import tkinter as tk
import time
def update_progress_bar(progress_bar, value):
progress_bar['value'] = value
if value < 100:
root.after(100, update_progress_bar, progress_bar, value + 1)
root = tk.Tk()
root.title("プログレスバーの例")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
# プログレスバーの更新を開始
update_progress_bar(progress_bar, 0)
root.mainloop()
この方法では、after()メソッド
を使ってUIを更新するため、メインスレッドがフリーズすることはありません。
プログレスバーをマルチスレッドで進行させる実装例
プログレスバーの初期設定
Tkinterを使用してプログレスバーを初期化する方法を説明します。
ttk
モジュールを使って、プログレスバーを作成し、ウィンドウに配置します。
import tkinter as tk
from tkinter import ttk
# Tkinterウィンドウの作成
root = tk.Tk()
root.title("プログレスバーの初期設定")
# プログレスバーの作成
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
# ウィンドウの表示
root.mainloop()
このコードを実行すると、空のウィンドウにプログレスバーが表示されます。
プログレスバーは、後で進行状況を表示するために使用します。
スレッド内での処理の進行状況を取得する方法
スレッド内での処理の進行状況を取得するためには、スレッドが実行する関数内で進行状況を管理します。
以下の例では、処理の進行状況を0から100までの値で更新します。
import threading
import time
def background_task(progress_callback):
for i in range(101):
time.sleep(0.1) # 処理のシミュレーション
progress_callback(i) # 進行状況をコールバックで更新
# コールバック関数
def update_progress(value):
progress_bar['value'] = value
# スレッドの作成
thread = threading.Thread(target=background_task, args=(update_progress,))
thread.start()
このコードでは、background_task関数
が進行状況を更新するためにupdate_progress関数
を呼び出します。
after()メソッドでプログレスバーを更新する方法
after()メソッド
を使用して、メインスレッドでプログレスバーを安全に更新します。
以下のコードでは、スレッド内での処理が進行するたびに、after()メソッド
を使ってプログレスバーを更新します。
def update_progress_bar(progress_bar, value):
progress_bar['value'] = value
if value < 100:
root.after(100, update_progress_bar, progress_bar, value + 1)
# スレッドの作成
thread = threading.Thread(target=background_task, args=(update_progress_bar,))
thread.start()
この方法では、update_progress_bar関数
がafter()メソッド
を使ってプログレスバーを更新します。
これにより、UIがフリーズすることなく、スムーズに進行状況が表示されます。
スレッドの終了とプログレスバーの完了処理
スレッドが終了した際にプログレスバーを完了状態にするためには、スレッドの処理が完了した後にプログレスバーを更新します。
以下のコードでは、スレッドが終了した後にメッセージを表示します。
def update_progress(value):
progress_bar['value'] = value
def background_task(progress_callback):
for i in range(101):
time.sleep(0.1) # 処理のシミュレーション
progress_callback(i) # 進行状況をコールバックで更新
# スレッド終了後に完了メッセージを表示
print("処理が完了しました!")
# スレッドの作成
thread = threading.Thread(target=background_task, args=(update_progress,))
thread.start()
このコードを実行すると、プログレスバーが100%に達した後に「処理が完了しました!」というメッセージが表示されます。
これにより、ユーザーに処理の完了を通知することができます。
応用例
ファイルのダウンロード進行状況を表示するプログレスバー
ファイルのダウンロード進行状況を表示するために、requests
ライブラリを使用してファイルをダウンロードし、その進行状況をプログレスバーで表示します。
import tkinter as tk
from tkinter import ttk
import threading
import requests
def update_progress(value):
progress_bar['value'] = value
def download_file(url, progress_callback):
response = requests.get(url, stream=True)
total_size = int(response.headers.get('content-length', 0))
downloaded_size = 0
with open('downloaded_file', 'wb') as file:
for data in response.iter_content(chunk_size=1024):
file.write(data)
downloaded_size += len(data)
progress_callback(int((downloaded_size / total_size) * 100))
def start_download():
url = "https://example.com/largefile.zip" # ダウンロードするファイルのURL
thread = threading.Thread(target=download_file, args=(url, update_progress))
thread.start()
root = tk.Tk()
root.title("ファイルダウンロード")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
download_button = tk.Button(root, text="ダウンロード開始", command=start_download)
download_button.pack(pady=10)
root.mainloop()
このコードを実行すると、指定したURLからファイルがダウンロードされ、その進行状況がプログレスバーに表示されます。
データ処理の進行状況を表示するプログレスバー
大量のデータを処理する際に、進行状況をプログレスバーで表示する方法を示します。
以下の例では、リスト内の数値を二乗する処理を行います。
import tkinter as tk
from tkinter import ttk
import threading
import time
def update_progress(value):
progress_bar['value'] = value
def process_data(data, progress_callback):
total = len(data)
for i, value in enumerate(data):
time.sleep(0.1) # 処理のシミュレーション
progress_callback(int((i + 1) / total * 100))
data = list(range(100)) # 0から99までのリスト
def start_processing():
thread = threading.Thread(target=process_data, args=(data, update_progress))
thread.start()
root = tk.Tk()
root.title("データ処理")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
process_button = tk.Button(root, text="データ処理開始", command=start_processing)
process_button.pack(pady=10)
root.mainloop()
このコードでは、データ処理の進行状況がプログレスバーに表示されます。
複数のタスクを並列処理するプログレスバー
複数のタスクを並列に処理し、それぞれの進行状況を表示する方法を示します。
以下の例では、2つの異なるタスクを同時に実行します。
import tkinter as tk
from tkinter import ttk
import threading
import time
def update_progress(value):
progress_bar['value'] = value
def task1(progress_callback):
for i in range(101):
time.sleep(0.05) # 処理のシミュレーション
progress_callback(i)
def task2(progress_callback):
for i in range(101):
time.sleep(0.1) # 処理のシミュレーション
progress_callback(i)
def start_tasks():
thread1 = threading.Thread(target=task1, args=(update_progress,))
thread2 = threading.Thread(target=task2, args=(update_progress,))
thread1.start()
thread2.start()
root = tk.Tk()
root.title("複数タスクの並列処理")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
start_button = tk.Button(root, text="タスク開始", command=start_tasks)
start_button.pack(pady=10)
root.mainloop()
このコードでは、2つのタスクが同時に実行され、それぞれの進行状況がプログレスバーに表示されます。
プログレスバーにキャンセルボタンを追加する方法
プログレスバーにキャンセルボタンを追加して、処理を中断できるようにする方法を示します。
以下の例では、キャンセルボタンを押すことで処理を停止します。
import tkinter as tk
from tkinter import ttk
import threading
import time
is_running = True
def update_progress(value):
progress_bar['value'] = value
def long_running_task(progress_callback):
global is_running
for i in range(101):
if not is_running:
break
time.sleep(0.1) # 処理のシミュレーション
progress_callback(i)
def start_task():
global is_running
is_running = True
thread = threading.Thread(target=long_running_task, args=(update_progress,))
thread.start()
def cancel_task():
global is_running
is_running = False
root = tk.Tk()
root.title("キャンセル機能付きプログレスバー")
progress_bar = ttk.Progressbar(root, length=300, mode='determinate')
progress_bar.pack(pady=20)
start_button = tk.Button(root, text="開始", command=start_task)
start_button.pack(pady=10)
cancel_button = tk.Button(root, text="キャンセル", command=cancel_task)
cancel_button.pack(pady=10)
root.mainloop()
このコードでは、キャンセルボタンを押すことで、長時間の処理を中断することができます。
プログレスバーの進行状況は、処理が続いている間のみ更新されます。
まとめ
この記事では、PythonのTkinterを使用してプログレスバーをマルチスレッドで進行させる方法について詳しく解説しました。
具体的には、プログレスバーの初期設定から、スレッド内での処理の進行状況を取得し、after()メソッド
を使って安全にUIを更新する方法、さらには複数のタスクを並列処理する方法やキャンセルボタンの追加についても触れました。
これらの知識を活用することで、よりスムーズでユーザーフレンドリーなGUIアプリケーションを作成することが可能になりますので、ぜひ実際に手を動かして試してみてください。