Python Progress Bar

Learn how to create a progress bar in Python using print(), carriage return, sys.stdout.flush(), tqdm, progressbar2, and alive-progress. See examples for terminal loops, file processing, downloads, asyncio, and common progress bar mistakes.

Published

Updated

Read time 7 min read

Reviewed byDeepak Prasad

Python Progress Bar

A progress bar shows how much of a long-running task is complete. In Python, you can build a simple terminal bar without libraries, or use a package such as tqdm for a cleaner default.

Tested on: Python 3.13.3; kernel 6.14.0-37-generic; tqdm 4.67.1.


Quick answer: Python progress bar in the terminal

Use tqdm for the easiest progress bar. Use print(..., end="\r", flush=True) or sys.stdout.write("\r...") when you want a bar without a library.

python
from tqdm import tqdm

for item in tqdm(range(100)):
    _ = item * 2
Output

The loop shows a live terminal bar with count, percentage, and ETA. For a no-library version, see Progress bar without a library below.


Python progress bar quick reference

Task Use
Easiest progress bar tqdm(range(total))
Progress bar without library print(..., end="\r", flush=True)
Manual terminal bar sys.stdout.write("\r...") + sys.stdout.flush()
Show percentage only print(f"{percent:.0f}%", end="\r")
Progress bar for file lines tqdm(file, total=line_count)
Progress bar for downloads tqdm(total=size, unit="B", unit_scale=True)
Progress bar for pandas tqdm.pandas()
Async progress bar tqdm.asyncio
Nested progress bars tqdm inside tqdm
Unknown total spinner or alive-progress
GUI progress bar PySimpleGUI / Tkinter
Web progress bar Django + Celery/WebSocket

What is a Python progress bar?

A progress bar gives feedback during long-running work. It usually shows current progress, total progress, percentage, elapsed time, or estimated time remaining. It is useful for loops, file processing, downloads, scraping, data processing, and batch jobs.


Progress bar without a library

Use carriage return \r to return to the start of the same terminal line. Use end="\r" to avoid printing a new line each update. Use flush=True or sys.stdout.flush() so output appears immediately.

The print() documentation describes end and flush. The sys module exposes stdout for direct terminal writes.

Percentage-only bar with print()

A percentage-only bar is enough for many scripts.

python
total = 10

for i in range(total + 1):
    percent = (i / total) * 100
    print(f"{percent:.0f}%", end="\r", flush=True)

print()
Output

You can also show count and percentage together:

python
done = 0
total = 50

for _ in range(total):
    done += 1
    print(f"Progress: {done}/{total} ({done / total:.0%})", end="\r", flush=True)

print()
Output

Avoid ordinary print() calls inside the loop unless you intentionally want output on separate lines. End with a blank print() to move to a new line after the bar finishes.

Custom bar with sys.stdout

Use sys.stdout.write() for direct terminal updates when you want a visible bar, not just a percentage.

python
import sys

def show_bar(done, total, width=20):
    filled = int(width * done / total)
    bar = "#" * filled + "-" * (width - filled)
    sys.stdout.write(f"\r[{bar}] {done}/{total} ({done / total:.0%})")
    sys.stdout.flush()

for i in range(1, 11):
    show_bar(i, 10)

print()
Output

Common problems without a library

  • Output does not update because buffering is not flushed.
  • The bar prints on many lines instead of one line.
  • Other print() calls break the bar layout.
  • The terminal window is too narrow for the bar text.
  • IDE or notebook output may not handle \r like a real terminal.
  • Total work is unknown, so a percentage bar is misleading.
  • Division by zero when total is 0.

Progress bar with tqdm

tqdm is the easiest option for most terminal loops. Wrap any iterable with tqdm(). It shows count, percentage, speed, elapsed time, and estimated remaining time automatically.

Install it when needed:

text
pip install tqdm

Basic usage:

python
from tqdm import tqdm

for item in tqdm(range(100)):
    _ = item
Output

The tqdm documentation describes it as a fast progress meter you wrap around an iterable.

Basic loop or iterable

python
from tqdm import tqdm

items = ["alpha", "beta", "gamma"]

for name in tqdm(items, desc="Processing"):
    _ = name.upper()
Output

Use desc for a label and unit for custom units such as files or rows.

File line processing

Wrap a file iterator when you process lines one at a time:

python
from tqdm import tqdm

lines = ["row1", "row2", "row3"]

for line in tqdm(lines, desc="Lines"):
    _ = line.strip()
Output

For a real file, use for line in tqdm(open("data.txt")): or pass total=line_count when you know the line count ahead of time.

File download by chunk size

When the total byte size is known, pass it to tqdm and update by chunk size.

python
import urllib.request
from tqdm import tqdm

url = "https://example.com/file.bin"

with urllib.request.urlopen(url) as response:
    size = int(response.headers.get("Content-Length", 0))
    with tqdm(total=size, unit="B", unit_scale=True, desc="Download") as bar:
        while chunk := response.read(8192):
            bar.update(len(chunk))

Not every server sends Content-Length. When size is unknown, use a count-only bar or a spinner instead of a percentage bar.

pandas progress_apply

For pandas workflows, register tqdm once and use progress_apply().

python
import pandas as pd
from tqdm import tqdm

tqdm.pandas(desc="Rows")

df = pd.DataFrame({"value": range(1000)})
df["double"] = df["value"].progress_apply(lambda x: x * 2)

See pandas DataFrame for table basics. Run pandas examples locally; the site Run button does not execute them reliably.

asyncio with as_completed

Use tqdm.asyncio for async iterators and task progress. This fits asyncio.as_completed() workflows better than forcing a sync bar around async code.

python
import asyncio
from tqdm.asyncio import tqdm_asyncio

async def work(n):
    await asyncio.sleep(0.01)
    return n

async def main():
    tasks = [work(i) for i in range(20)]
    results = []
    for coro in tqdm_asyncio.as_completed(tasks):
        results.append(await coro)
    print(len(results))

asyncio.run(main())

For a deep async tutorial, a separate article on tqdm asyncio as_completed is a better fit than expanding this page.

Disable in CI or non-interactive runs

Hide the bar when stdout is not a terminal, such as in cron jobs or CI logs.

python
import sys
from tqdm import tqdm

for i in tqdm(range(20), disable=not sys.stdout.isatty()):
    _ = i
Output

Other terminal progress bar libraries

Library Best for
tqdm Simple terminal loops
progressbar2 Custom terminal widgets
progress Minimal terminal bars/spinners
alive-progress Animated terminal progress
rich Rich terminal UI with progress, tables, logs

Install examples:

text
pip install progressbar2
pip install progress
pip install alive-progress
pip install rich

Brief notes:

  • progressbar2 — widget-style terminal bars with timers and ETA.
  • progress — lightweight bars and spinners.
  • alive-progress — animated bars; good when total is unknown.
  • rich — progress plus tables, logging, and styled terminal output.

rich progress example

python
from rich.progress import track

for value in track(range(100), description="Working"):
    _ = value

alive-progress for unknown total

A percentage progress bar needs a known total. When total is unknown, use alive-progress or rich for indeterminate-style feedback.

python
from alive_progress import alive_bar

with alive_bar(total=None, title="Working") as bar:
    for _ in range(50):
        bar()

Which approach should you use?

Situation Use
Simple script, no extra packages No-library bar with print or sys.stdout
Most CLI or data-processing loops tqdm
App already uses rich output rich
Animated bar or unknown total alive-progress or rich
Desktop GUI app PySimpleGUI / Tkinter
Web app with long tasks Django + Celery + browser updates
  • Use a no-library bar for simple scripts or restricted environments.
  • Use tqdm for most terminal workflows.
  • Use GUI or web patterns only for desktop or browser apps.

GUI and web progress bars

Terminal progress bars do not belong in browser or desktop UI code. Use platform-specific patterns instead.

PySimpleGUI

PySimpleGUI provides a ProgressBar element for desktop GUI apps, not terminal scripts. It is a two-color horizontal or vertical bar updated from your event loop.

Use it when you build a GUI window. For terminal loops, use tqdm or a no-library bar instead. A dedicated PySimpleGUI progress bar article is a better home for full GUI examples.

Django / Celery / WebSocket

Django progress bars usually need backend task progress plus frontend updates. Common patterns combine Celery (or similar) for background work with polling, Server-Sent Events, or WebSockets in the browser.

Split the problem into task state on the server and progress UI in the client. A separate article such as “Django progress bar with Celery” fits that search intent better than a full tutorial on this page.


Progress bar best practices

  • Update once per item or batch, not on every tiny sub-step.
  • Avoid normal print() inside the progress loop; use tqdm.write() when you must log.
  • Do not show fake percentages when total is unknown.
  • Use meaningful units: files, rows, bytes, tasks.
  • Disable bars in logs, cron jobs, CI, or non-interactive output when they add noise.
  • Measure wall-clock time with Python measure execution time before tuning worker counts.

Summary

Use tqdm for most Python progress bars. Use print with end="\r" and flush=True, or sys.stdout.write() with flush(), for a no-library terminal bar. Use progressbar2, alive-progress, progress, or rich when you need custom terminal output. Use separate GUI or web patterns for PySimpleGUI and Django apps.


References


Frequently Asked Questions

1. What is the easiest way to add a progress bar in Python?

Use tqdm. Wrap any iterable with tqdm(iterable) to show count, percentage, speed, and estimated remaining time in the terminal.

2. How do you make a progress bar without a library in Python?

Use print with end set to carriage return and flush=True, or sys.stdout.write with sys.stdout.flush(), to update the same terminal line as work completes.

3. Why does my Python progress bar print on many lines?

You likely used a normal newline instead of carriage return, or you forgot flush=True or sys.stdout.flush(). Regular print() calls inside the loop also break the bar.

4. Can tqdm work with asyncio?

Yes. tqdm provides tqdm.asyncio helpers for async iterators and task progress. Use them for async workflows instead of forcing a sync terminal bar into async code.

5. Do I need a progress bar library for a Django or GUI app?

No for terminal scripts. Web and desktop apps usually need backend task state plus frontend updates, not a CLI tqdm bar in the browser.

6. What if the total work is unknown?

A percentage bar needs a known total. Use a spinner, a count-only display, or libraries such as alive-progress or rich that support indeterminate-style progress.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …