In standard Tkinter, running a long loop (like processing thousands of database rows) will freeze the user interface. This happens because the "Main Loop" is busy with your task and cannot redraw the screen. To keep the Progressbar moving and the window responsive, we use Threading.
IntVar to communicate progress from the background to the UI.import tkinter as tk
from tkinter import ttk
import threading
import time
def background_task(progress_var, status_label, start_btn):
# This simulates a heavy task (e.g., file download or DB sync)
for i in range(1, 101):
time.sleep(0.05) # Simulate work
# Safely update the shared Tkinter variable
progress_var.set(i)
status_label.config(text=f"Processing: {i}%")
status_label.config(text="Task Finished!")
start_btn.config(state=tk.NORMAL)
def run_thread():
# Disable button to prevent multiple threads
btn_start.config(state=tk.DISABLED)
# Initialize Thread
t1 = threading.Thread(target=background_task, args=(prog_v, lbl_msg, btn_start))
t1.start()
root = tk.Tk()
root.title("plus2net - Progress Threading")
root.geometry("400x200")
prog_v = tk.IntVar()
pb = ttk.Progressbar(root, variable=prog_v, maximum=100, length=300)
pb.pack(pady=20)
lbl_msg = tk.Label(root, text="Click to start background work")
lbl_msg.pack()
btn_start = tk.Button(root, text="Start Task", command=run_thread)
btn_start.pack(pady=10)
root.mainloop()
Sometimes you don't know exactly how long a task will take—for example, waiting for a server response or searching a large directory. In these cases, we use the Indeterminate Mode.
The challenge is that pb.start() must be called in the Main Thread, but the task runs in the Background. Here is how to synchronize them:
import tkinter as tk
from tkinter import ttk
import threading
import time
def process_data():
# Start the bouncing animation
pb.start(10)
lbl_status.config(text="Fetching data from server...")
# Simulate a heavy network request
time.sleep(5)
# Stop the animation and reset UI
pb.stop()
lbl_status.config(text="Data Received Successfully!")
btn_start.config(state=tk.NORMAL)
def run_indeterminate_task():
btn_start.config(state=tk.DISABLED)
# Launch the worker thread
threading.Thread(target=process_data).start()
root = tk.Tk()
root.title("plus2net - Indeterminate Threading")
root.geometry("400x180")
pb = ttk.Progressbar(root, mode='indeterminate', length=280)
pb.pack(pady=30)
lbl_status = tk.Label(root, text="Click to begin server request")
lbl_status.pack()
btn_start = tk.Button(root, text="Search / Fetch", command=run_indeterminate_task)
btn_start.pack(pady=10)
root.mainloop()
pb.start() and pb.stop() directly from the worker thread in most modern Python versions, but for maximum compatibility with older Tkinter builds, it is best to trigger these via the .after() method or virtual events.
For maximum cross-platform compatibility, you should avoid modifying widgets directly inside a worker thread. Instead, use the .after() method to schedule the update on the Main Thread. This prevents threading conflicts that can lead to application crashes.
import tkinter as tk
from tkinter import ttk
import threading
import time
def worker_logic():
# This runs in the background
time.sleep(4) # Simulate work
# Instead of pb.stop(), we schedule it on the main thread
root.after(0, stop_ui)
def stop_ui():
# This runs safely on the Main Thread
pb.stop()
lbl_status.config(text="Thread finished safely!")
btn_start.config(state=tk.NORMAL)
def start_task():
btn_start.config(state=tk.DISABLED)
lbl_status.config(text="Thread running...")
pb.start(10)
threading.Thread(target=worker_logic).start()
root = tk.Tk()
root.geometry("400x180")
pb = ttk.Progressbar(root, mode='indeterminate', length=280)
pb.pack(pady=30)
lbl_status = tk.Label(root, text="Status: Ready")
lbl_status.pack()
btn_start = tk.Button(root, text="Start Safe Thread", command=start_task)
btn_start.pack(pady=10)
root.mainloop()
root.after(0, stop_ui) command tells Tkinter: "As soon as you are free (0 milliseconds from now), execute the stop_ui function using the Main Thread." This is the gold standard for thread-safe GUI programming.
Mastering the relationship between Threading and the Progressbar is a turning point for any Python developer. By offloading heavy computations to a background thread, you ensure that your application remains professional, responsive, and free from the dreaded "Not Responding" freeze. Whether you are using Determinate mode for known file sizes or Indeterminate mode for unpredictable server requests, the core principle remains the same: keep the Main Loop free to handle the UI.
Author
🎥 Join me live on YouTubePassionate about coding and teaching, I publish practical tutorials on PHP, Python, JavaScript, SQL, and web development. My goal is to make learning simple, engaging, and project‑oriented with real examples and source code.