315 lines
9.9 KiB
Python
315 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
OverCode Progress Bars - Epic animated progress indicators
|
|
"""
|
|
|
|
import time
|
|
import sys
|
|
import threading
|
|
from colorama import Fore, Style, init
|
|
|
|
init(autoreset=True)
|
|
|
|
class ProgressBar:
|
|
"""Animated progress bar with multiple styles"""
|
|
|
|
def __init__(self, total=100, width=40, style='modern', color='cyan'):
|
|
self.total = total
|
|
self.current = 0
|
|
self.width = width
|
|
self.style = style
|
|
self.color = color
|
|
self.start_time = time.time()
|
|
self.is_running = False
|
|
self.animation_thread = None
|
|
|
|
# Color mapping
|
|
self.colors = {
|
|
'cyan': Fore.CYAN,
|
|
'green': Fore.GREEN,
|
|
'yellow': Fore.YELLOW,
|
|
'red': Fore.RED,
|
|
'magenta': Fore.MAGENTA,
|
|
'blue': Fore.BLUE
|
|
}
|
|
|
|
# Style configurations
|
|
self.styles = {
|
|
'modern': {
|
|
'filled': '█',
|
|
'empty': '░',
|
|
'prefix': '[',
|
|
'suffix': ']'
|
|
},
|
|
'classic': {
|
|
'filled': '=',
|
|
'empty': '-',
|
|
'prefix': '[',
|
|
'suffix': ']'
|
|
},
|
|
'dots': {
|
|
'filled': '●',
|
|
'empty': '○',
|
|
'prefix': '(',
|
|
'suffix': ')'
|
|
},
|
|
'arrows': {
|
|
'filled': '▶',
|
|
'empty': '▷',
|
|
'prefix': '⟨',
|
|
'suffix': '⟩'
|
|
},
|
|
'blocks': {
|
|
'filled': '■',
|
|
'empty': '□',
|
|
'prefix': '⌊',
|
|
'suffix': '⌋'
|
|
},
|
|
'fire': {
|
|
'filled': '🔥',
|
|
'empty': '💨',
|
|
'prefix': '🚀',
|
|
'suffix': '✨'
|
|
}
|
|
}
|
|
|
|
def update(self, value, message=""):
|
|
"""Update progress bar value"""
|
|
self.current = min(value, self.total)
|
|
self._render(message)
|
|
|
|
def increment(self, amount=1, message=""):
|
|
"""Increment progress by amount"""
|
|
self.current = min(self.current + amount, self.total)
|
|
self._render(message)
|
|
|
|
def _render(self, message=""):
|
|
"""Render the progress bar"""
|
|
if self.total == 0:
|
|
percentage = 100
|
|
else:
|
|
percentage = (self.current / self.total) * 100
|
|
|
|
filled_width = int((self.current / self.total) * self.width) if self.total > 0 else 0
|
|
empty_width = self.width - filled_width
|
|
|
|
style_config = self.styles.get(self.style, self.styles['modern'])
|
|
color = self.colors.get(self.color, Fore.CYAN)
|
|
|
|
# Build progress bar
|
|
filled = style_config['filled'] * filled_width
|
|
empty = style_config['empty'] * empty_width
|
|
prefix = style_config['prefix']
|
|
suffix = style_config['suffix']
|
|
|
|
# Calculate ETA
|
|
elapsed = time.time() - self.start_time
|
|
if self.current > 0:
|
|
eta = (elapsed / self.current) * (self.total - self.current)
|
|
eta_str = f" ETA: {eta:.1f}s"
|
|
else:
|
|
eta_str = ""
|
|
|
|
# Format the bar
|
|
bar = f"{color}{prefix}{filled}{Fore.LIGHTBLACK_EX}{empty}{color}{suffix}"
|
|
percentage_str = f" {percentage:6.1f}%"
|
|
|
|
if message:
|
|
message = f" {Fore.WHITE}{message}"
|
|
|
|
# Print with carriage return to overwrite
|
|
sys.stdout.write(f"\r{bar}{percentage_str}{eta_str}{message}")
|
|
sys.stdout.flush()
|
|
|
|
if self.current >= self.total:
|
|
print() # New line when complete
|
|
|
|
class SpinnerProgress:
|
|
"""Animated spinner for indefinite operations"""
|
|
|
|
def __init__(self, message="Loading", style='dots'):
|
|
self.message = message
|
|
self.style = style
|
|
self.is_running = False
|
|
self.thread = None
|
|
|
|
self.spinners = {
|
|
'dots': ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
'arrows': ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'],
|
|
'bars': ['|', '/', '-', '\\'],
|
|
'blocks': ['▁', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃'],
|
|
'braille': ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'],
|
|
'fire': ['🔥', '🚀', '⚡', '✨', '💫', '🌟']
|
|
}
|
|
|
|
def start(self):
|
|
"""Start the spinner animation"""
|
|
if self.is_running:
|
|
return
|
|
|
|
self.is_running = True
|
|
self.thread = threading.Thread(target=self._animate)
|
|
self.thread.daemon = True
|
|
self.thread.start()
|
|
|
|
def stop(self, final_message="Done"):
|
|
"""Stop the spinner"""
|
|
self.is_running = False
|
|
if self.thread:
|
|
self.thread.join()
|
|
|
|
# Clear the line and show final message
|
|
sys.stdout.write(f"\r{Fore.GREEN}✓ {final_message}{' ' * 20}\n")
|
|
sys.stdout.flush()
|
|
|
|
def _animate(self):
|
|
"""Animation loop"""
|
|
frames = self.spinners.get(self.style, self.spinners['dots'])
|
|
i = 0
|
|
|
|
while self.is_running:
|
|
frame = frames[i % len(frames)]
|
|
sys.stdout.write(f"\r{Fore.CYAN}{frame} {Fore.WHITE}{self.message}")
|
|
sys.stdout.flush()
|
|
time.sleep(0.1)
|
|
i += 1
|
|
|
|
class MultiProgress:
|
|
"""Multiple progress bars for complex operations"""
|
|
|
|
def __init__(self):
|
|
self.bars = {}
|
|
self.order = []
|
|
|
|
def add_bar(self, name, total, style='modern', color='cyan'):
|
|
"""Add a new progress bar"""
|
|
self.bars[name] = {
|
|
'bar': ProgressBar(total, style=style, color=color),
|
|
'total': total,
|
|
'current': 0,
|
|
'message': ''
|
|
}
|
|
self.order.append(name)
|
|
|
|
def update(self, name, value, message=""):
|
|
"""Update a specific progress bar"""
|
|
if name in self.bars:
|
|
self.bars[name]['current'] = value
|
|
self.bars[name]['message'] = message
|
|
self._render_all()
|
|
|
|
def increment(self, name, amount=1, message=""):
|
|
"""Increment a specific progress bar"""
|
|
if name in self.bars:
|
|
self.bars[name]['current'] = min(
|
|
self.bars[name]['current'] + amount,
|
|
self.bars[name]['total']
|
|
)
|
|
self.bars[name]['message'] = message
|
|
self._render_all()
|
|
|
|
def _render_all(self):
|
|
"""Render all progress bars"""
|
|
# Move cursor up to overwrite previous output
|
|
if len(self.bars) > 1:
|
|
sys.stdout.write(f"\033[{len(self.bars)}A")
|
|
|
|
for name in self.order:
|
|
bar_data = self.bars[name]
|
|
bar_data['bar'].current = bar_data['current']
|
|
print(f"{Fore.YELLOW}{name:<15}", end=" ")
|
|
bar_data['bar']._render(bar_data['message'])
|
|
|
|
# Utility functions for easy use
|
|
def progress_bar(iterable, desc="Processing", style='modern', color='cyan'):
|
|
"""Wrapper for iterables with progress bar"""
|
|
total = len(iterable) if hasattr(iterable, '__len__') else 100
|
|
bar = ProgressBar(total, style=style, color=color)
|
|
|
|
for i, item in enumerate(iterable):
|
|
bar.update(i + 1, desc)
|
|
yield item
|
|
|
|
bar.update(total, f"{desc} - Complete!")
|
|
|
|
def simulate_progress(message="Processing", duration=3, style='modern', color='cyan'):
|
|
"""Simulate progress for demo purposes"""
|
|
bar = ProgressBar(100, style=style, color=color)
|
|
|
|
for i in range(101):
|
|
time.sleep(duration / 100)
|
|
bar.update(i, message)
|
|
|
|
def with_spinner(func, message="Loading", style='dots'):
|
|
"""Decorator to show spinner during function execution"""
|
|
def wrapper(*args, **kwargs):
|
|
spinner = SpinnerProgress(message, style)
|
|
spinner.start()
|
|
try:
|
|
result = func(*args, **kwargs)
|
|
spinner.stop("Complete!")
|
|
return result
|
|
except Exception as e:
|
|
spinner.stop(f"Error: {str(e)}")
|
|
raise
|
|
return wrapper
|
|
|
|
# Demo functions
|
|
def demo_progress_bars():
|
|
"""Demonstrate different progress bar styles"""
|
|
print(Fore.CYAN + "🚀 OverCode Progress Bar Demo")
|
|
print("=" * 50)
|
|
|
|
styles = ['modern', 'classic', 'dots', 'arrows', 'blocks', 'fire']
|
|
colors = ['cyan', 'green', 'yellow', 'magenta', 'blue']
|
|
|
|
for style in styles:
|
|
color = colors[styles.index(style) % len(colors)]
|
|
print(f"\n{Fore.WHITE}Style: {style.title()} ({color})")
|
|
|
|
bar = ProgressBar(50, style=style, color=color)
|
|
for i in range(51):
|
|
bar.update(i, f"Processing {style} style...")
|
|
time.sleep(0.02)
|
|
|
|
print(f"\n{Fore.GREEN}✨ All styles demonstrated!")
|
|
|
|
def demo_spinner():
|
|
"""Demonstrate spinner animations"""
|
|
print(Fore.CYAN + "\n🔄 Spinner Demo")
|
|
|
|
styles = ['dots', 'arrows', 'bars', 'blocks', 'braille', 'fire']
|
|
|
|
for style in styles:
|
|
spinner = SpinnerProgress(f"Loading with {style} style", style)
|
|
spinner.start()
|
|
time.sleep(2)
|
|
spinner.stop(f"{style.title()} complete!")
|
|
|
|
def demo_multi_progress():
|
|
"""Demonstrate multiple progress bars"""
|
|
print(Fore.CYAN + "\n📊 Multi-Progress Demo")
|
|
|
|
multi = MultiProgress()
|
|
multi.add_bar("Download", 100, 'modern', 'green')
|
|
multi.add_bar("Extract", 80, 'arrows', 'yellow')
|
|
multi.add_bar("Install", 60, 'dots', 'blue')
|
|
|
|
import random
|
|
|
|
for i in range(100):
|
|
if i < 80:
|
|
multi.increment("Download", 1, f"Downloading file {i+1}")
|
|
if i > 20 and i < 60:
|
|
multi.increment("Extract", random.randint(1, 2), "Extracting files...")
|
|
if i > 40:
|
|
multi.increment("Install", 1, "Installing packages...")
|
|
|
|
time.sleep(0.05)
|
|
|
|
print(f"\n{Fore.GREEN}🎉 All operations complete!")
|
|
|
|
if __name__ == "__main__":
|
|
demo_progress_bars()
|
|
demo_spinner()
|
|
demo_multi_progress() |