Overwriting a Single Line
To overwrite a single line, use \r (carriage return). This control character moves the cursor to the beginning of the current line.
print("\r" + "Overwriting a single line!", end="")
Use end="" to prevent a newline character from being printed, keeping the cursor on the same line.
Countdown Timer Example
Here is a practical use of \r to implement a countdown timer.
import time
import sys
def countdown(seconds):
for i in range(seconds, 0, -1):
# \r returns to the start of the line, overwriting previous output
sys.stdout.write(f"\rTime remaining: {i:3d} seconds")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("\rDone! \n")
countdown(10)
Here we use sys.stdout.write and sys.stdout.flush(). While print internally calls sys.stdout.write, using sys.stdout directly gives more explicit control over buffering. Calling flush() ensures that output is immediately displayed on screen.
Note: If the new string is shorter than the previous one, leftover characters from the previous output will remain visible. In the example above, spaces are added after "Done!" to ensure the previous text is fully cleared.
Spinning Cursor Implementation
A spinning cursor (loading animation) that visually indicates ongoing processing can also be implemented with \r.
import sys
import time
import itertools
def spinning_cursor(duration=5):
spinner = itertools.cycle(['|', '/', '-', '\\'])
end_time = time.time() + duration
while time.time() < end_time:
sys.stdout.write(f"\rProcessing... {next(spinner)}")
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write("\rComplete! \n")
spinning_cursor(3)
Using itertools.cycle allows the spinner characters to repeat indefinitely.
Overwriting Multiple Lines
To overwrite multiple lines, you can use special ANSI escape codes. The sequence \033[nA moves the cursor up n lines.
import time
print("First line")
print("Second line")
print("Third line")
time.sleep(1) # Wait for a second to see the initial output
# Move cursor up 2 lines (from the current position, which is after "Third line")
# and then overwrite the second and third lines.
print("\033[2A" + "New Second line\n" + "New Third line")
ANSI Escape Sequence Reference
Here is a summary of the main ANSI escape sequences for cursor control and text decoration in the terminal.
Cursor Control
| Sequence | Description | Example |
|---|---|---|
\033[nA | Move cursor up n lines | print("\033[2A") |
\033[nB | Move cursor down n lines | print("\033[1B") |
\033[nC | Move cursor right n columns | print("\033[5C") |
\033[nD | Move cursor left n columns | print("\033[3D") |
\033[2K | Clear current line | print("\033[2K", end="") |
\033[J | Clear from cursor to end | print("\033[J", end="") |
\033[H | Move cursor to top-left | print("\033[H") |
\033[{r};{c}H | Move cursor to row r, column c | print("\033[5;10H") |
Text Decoration (Colors & Styles)
| Sequence | Description |
|---|---|
\033[0m | Reset (clear all) |
\033[1m | Bold |
\033[4m | Underline |
\033[31m | Red text |
\033[32m | Green text |
\033[33m | Yellow text |
\033[34m | Blue text |
\033[41m | Red background |
\033[42m | Green background |
For example, colored status messages can be written as follows.
# Display in green for success, red for failure
def print_status(message, success=True):
color = "\033[32m" if success else "\033[31m"
reset = "\033[0m"
print(f"{color}{message}{reset}")
print_status("Test passed", success=True)
print_status("Test failed", success=False)
Download Progress Bar Example
By combining \r and ANSI escape sequences, you can build a practical download progress display.
import sys
import time
def download_progress(total_size, chunk_size=1024):
"""Display download progress with a progress bar"""
downloaded = 0
bar_length = 40
while downloaded < total_size:
downloaded += chunk_size
if downloaded > total_size:
downloaded = total_size
# Calculate progress
progress = downloaded / total_size
filled = int(bar_length * progress)
bar = "█" * filled + "░" * (bar_length - filled)
# Display in MB
dl_mb = downloaded / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
# Color-coded progress
if progress < 0.5:
color = "\033[33m" # Yellow
else:
color = "\033[32m" # Green
reset = "\033[0m"
sys.stdout.write(
f"\r{color}[{bar}]{reset} "
f"{progress:6.1%} "
f"({dl_mb:.1f}/{total_mb:.1f} MB)"
)
sys.stdout.flush()
time.sleep(0.01) # Simulate download
sys.stdout.write("\n")
print("Download complete!")
# Simulate a 10MB file download
download_progress(10 * 1024 * 1024, chunk_size=100 * 1024)
Multi-Line Real-Time Update Example
By combining ANSI escape sequences, you can update the progress of multiple tasks simultaneously in real time.
import sys
import time
import random
def multi_task_progress():
"""Update progress for multiple tasks simultaneously"""
tasks = ["Fetching ", "Preprocess", "Training ", "Evaluation"]
progress = [0] * len(tasks)
# Initial display
for task in tasks:
print(f" {task}: [{'░' * 30}] 0%")
while not all(p >= 100 for p in progress):
# Randomly advance progress
for i in range(len(tasks)):
if progress[i] < 100:
progress[i] = min(100, progress[i] + random.randint(0, 5))
# Move cursor up by the number of tasks
sys.stdout.write(f"\033[{len(tasks)}A")
for i, task in enumerate(tasks):
filled = int(30 * progress[i] / 100)
bar = "█" * filled + "░" * (30 - filled)
if progress[i] >= 100:
color = "\033[32m" # Complete: green
else:
color = "\033[33m" # In progress: yellow
reset = "\033[0m"
print(f" {task}: {color}[{bar}]{reset} {progress[i]:3d}%")
time.sleep(0.1)
multi_task_progress()
Cross-Platform Considerations
ANSI escape sequences work natively on Linux and macOS terminals, but the traditional Windows Command Prompt (cmd.exe) does not support them.
Handling Windows
Windows Terminal and PowerShell on Windows 10+ do support ANSI escape sequences, but for compatibility with older environments, use the colorama library.
# pip install colorama
from colorama import init, Fore, Style
# Enable ANSI escapes on Windows
init()
# Colored output with colorama
print(Fore.GREEN + "Success" + Style.RESET_ALL)
print(Fore.RED + "Error" + Style.RESET_ALL)
Simply calling colorama.init() makes ANSI escape sequences work correctly on Windows. On Linux/macOS it does nothing (no side effects), making it suitable for cross-platform code.
Branching with os.name
If you prefer not to use colorama, you can branch based on os.name.
import os
import sys
def supports_ansi():
"""Check if the terminal supports ANSI escape sequences"""
if os.name == "nt":
# Supported on Windows 10 build 10586+
return os.environ.get("WT_SESSION") is not None # Windows Terminal
return hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
if supports_ansi():
GREEN = "\033[32m"
RESET = "\033[0m"
else:
GREEN = ""
RESET = ""
print(f"{GREEN}Status: OK{RESET}")
Library Comparison: Manual vs tqdm vs rich
Choose the right approach based on your use case.
| Feature | Manual (\r / ANSI) | tqdm | rich |
|---|---|---|---|
| External dependency | None | pip install | pip install |
| Progress bar | Must build yourself | One-liner | One-liner |
| Multiple bars | Must build yourself | Supported | Supported |
| Table display | Not available | Not available | Supported |
| Colored output | Manual ANSI codes | Limited | Easy with Rich Markup |
| Windows support | Needs colorama | Automatic | Automatic |
| Customizability | Full control | Moderate | High |
| Learning curve | Low (good for learning) | Low | Moderate |
| Best for | Lightweight, learning | Loop processing | Rich CLI applications |
For simple overwrite displays, use the techniques from this article. For progress bars in loops, consider tqdm. For rich CLI applications, consider rich.
Modern Python Output Libraries
Beyond overwriting print output, there are libraries that provide rich terminal output.
The rich Library
rich can display colored text, tables, progress bars, and more in the terminal.
from rich.console import Console
from rich.table import Table
console = Console()
# Progress display
from rich.progress import track
import time
for i in track(range(100), description="Processing..."):
time.sleep(0.01)
# Status display (with spinner)
with console.status("Computing..."):
time.sleep(2)
console.print("[bold green]Done![/bold green]")
Progress Bars with tqdm
tqdm provides progress bars with minimal code. It is widely used as an alternative to manual overwrite-based displays.
from tqdm import tqdm
import time
for i in tqdm(range(100)):
time.sleep(0.01)
Related Articles
- Building a Progress Bar in Python from Scratch (Without tqdm) - A practical application of the carriage return and ANSI escape techniques covered in this article, building a full progress bar from scratch.
- Creating 3D Animations (GIF) with Python Matplotlib - Rich output and visualization techniques with Matplotlib
- Python Regex Practical Guide: From re Module Basics to Performance Optimization - Practical usage of Python standard libraries
