this object can take an iterable object and show progress bar in a **beautiful** format.
and it has a `log` method that can print any extra logs **between progress**.
```python
class LoadingBar:
def __init__(self,
iterable,
color:str='orange',
pattern:str="━",
end_loaded_pattern:str="╸",
loaded_color:str="\033[38;2;255;165;0m",
unloaded_color:str="\033[38;2;100;100;100m",
bar_length:int=40,
prefix:str="Loading:",
loading_at_down:bool=True
):
self.iterable = iterable
self.pattern = pattern
self.end_loaded_pattern = end_loaded_pattern
self.color_mapper = {
'red': "\033[38;2;255;10;10m",
'blue': "\033[38;2;10;10;255m",
'yellow': "\033[38;2;255;255;50m",
'green': "\033[38;2;10;255;10m",
'pink': "\033[38;2;255;192;203m",
'purple': "\033[38;2;150;10;150m",
'orange': "\033[38;2;255;165;0m",
'white': "\033[38;2;255;255;255m",
'brwon': "\033[38;2;120;100;100m"
}
if color is None:
self.loaded_color = loaded_color if loaded_color else "\033[38;2;255;165;0m"
elif color in self.color_mapper.keys():
self.loaded_color = self.color_mapper[color]
else:
raise f"Color must be one of this list: {list(self.color_mapper.keys())}"
self.unloaded_color = unloaded_color
self.bar_length = bar_length
self.prefix = prefix
self.loading_at_down = loading_at_down
self.iterator = None
self.current = 0
self.total = len(iterable) if hasattr(iterable, '__len__') else None
self.logs = []
self.n_line = 0
def __iter__(self):
self.iterator = iter(self.iterable)
print()
self.current = 0
return self
def __next__(self):
"""Advance the iterator and update the loading bar."""
try:
item = next(self.iterator)
self.current += 1
self._update_display()
return item
except StopIteration:
# Ensure 100% is displayed if total is known
if self.total:
self.current = self.total
# self._update_display()
raise
def _update_display(self):
"""Update and display the loading bar."""
if self.total is None:
# If total length is unknown, show a simple repeating pattern
display = f"{self.prefix} {self.loaded_color}{self.pattern * (self.current % self.bar_length)}{self.end_loaded_pattern}{self.unloaded_color}{self.pattern * (self.bar_length - (self.current % self.bar_length))}\033[0m"
else:
# Calculate progress and build the bar
progress = self.current / self.total
loaded_count = int(progress * self.bar_length)
unloaded_count = self.bar_length - loaded_count - 1 # -1 for end_loaded_pattern
if unloaded_count < 0:
unloaded_count = 0
loaded_count = self.bar_length
self.end_loaded_pattern = "" # Remove end pattern if bar is full
percent = progress * 100
display = f"{self.prefix} {self.loaded_color}{self.pattern * loaded_count}{self.end_loaded_pattern}{self.unloaded_color}{self.pattern * unloaded_count}\033[0m [{percent:.1f}%]"
if not self.logs:
final_display_text = display
else:
display_logs = '\n'.join(self.logs)
if self.loading_at_down:
final_display_text = display_logs + '\n' + display
else:
final_display_text = display + '\n' + display_logs
# if self.n_line>0:
self._move_cursor_to_top(self.n_line)
# final_display_text = final_display_text.replace('\n', '\n\033[K')
print(final_display_text, flush=True)
def _move_cursor_to_top(self, lines=1):
"""Move cursor to the top-left of the terminal."""
# print(f"\033[{lines}A\033[{lcines}K", end="", flush=True)
print(f"\033[{lines}A", end="", flush=True)
def log(self, message):
self.logs.append(str(message))
self.n_line += len(str(message).splitlines())
def run(self, sleep_time=0.1):
"""
Run the loading bar for the entire iterable with a delay for visibility.
Args:
sleep_time (float): Time to sleep between iterations (for visibility).
"""
for item in self:
time.sleep(sleep_time) # Simulate work
print()
```
and to use it you can give the iterable object to it.
```python
import time
loader = LoadingBar(range(10), color='yellow', loading_at_down=False)
for i in loader:
time.sleep(4)
loader.log(f'the line {i} for test log in this class')
```
[](https://i.sstatic.net/wUf9wnY8.png)
this object can take an iterable object and show progress bar in a **beautiful** format.
and it has a `log` method that can print any extra logs **between progress**.
```python
class LoadingBar:
def __init__(self,
iterable,
color:str='orange',
pattern:str="━",
end_loaded_pattern:str="╸",
loaded_color:str="\033[38;2;255;165;0m",
unloaded_color:str="\033[38;2;100;100;100m",
bar_length:int=40,
prefix:str="Loading:",
loading_at_down:bool=True
):
self.iterable = iterable
self.pattern = pattern
self.end_loaded_pattern = end_loaded_pattern
self.color_mapper = {
'red': "\033[38;2;255;10;10m",
'blue': "\033[38;2;10;10;255m",
'yellow': "\033[38;2;255;255;50m",
'green': "\033[38;2;10;255;10m",
'pink': "\033[38;2;255;192;203m",
'purple': "\033[38;2;150;10;150m",
'orange': "\033[38;2;255;165;0m",
'white': "\033[38;2;255;255;255m",
'brwon': "\033[38;2;120;100;100m"
}
if color is None:
self.loaded_color = loaded_color if loaded_color else "\033[38;2;255;165;0m"
elif color in self.color_mapper.keys():
self.loaded_color = self.color_mapper[color]
else:
raise f"Color must be one of this list: {list(self.color_mapper.keys())}"
self.unloaded_color = unloaded_color
self.bar_length = bar_length
self.prefix = prefix
self.loading_at_down = loading_at_down
self.iterator = None
self.current = 0
self.total = len(iterable) if hasattr(iterable, '__len__') else None
self.logs = []
self.n_line = 0
def __iter__(self):
self.iterator = iter(self.iterable)
print()
self.current = 0
return self
def __next__(self):
"""Advance the iterator and update the loading bar."""
try:
item = next(self.iterator)
self.current += 1
self._update_display()
return item
except StopIteration:
# Ensure 100% is displayed if total is known
if self.total:
self.current = self.total
# self._update_display()
raise
def _update_display(self):
"""Update and display the loading bar."""
if self.total is None:
# If total length is unknown, show a simple repeating pattern
display = f"{self.prefix} {self.loaded_color}{self.pattern * (self.current % self.bar_length)}{self.end_loaded_pattern}{self.unloaded_color}{self.pattern * (self.bar_length - (self.current % self.bar_length))}\033[0m"
else:
# Calculate progress and build the bar
progress = self.current / self.total
loaded_count = int(progress * self.bar_length)
unloaded_count = self.bar_length - loaded_count - 1 # -1 for end_loaded_pattern
if unloaded_count < 0:
unloaded_count = 0
loaded_count = self.bar_length
self.end_loaded_pattern = "" # Remove end pattern if bar is full
percent = progress * 100
display = f"{self.prefix} {self.loaded_color}{self.pattern * loaded_count}{self.end_loaded_pattern}{self.unloaded_color}{self.pattern * unloaded_count}\033[0m [{percent:.1f}%]"
if not self.logs:
final_display_text = display
else:
display_logs = '\n'.join(self.logs)
if self.loading_at_down:
final_display_text = display_logs + '\n' + display
else:
final_display_text = display + '\n' + display_logs
# if self.n_line>0:
self._move_cursor_to_top(self.n_line)
# final_display_text = final_display_text.replace('\n', '\n\033[K')
print(final_display_text, flush=True)
def _move_cursor_to_top(self, lines=1):
"""Move cursor to the top-left of the terminal."""
# print(f"\033[{lines}A\033[{lcines}K", end="", flush=True)
print(f"\033[{lines}A", end="", flush=True)
def log(self, message):
self.logs.append(str(message))
self.n_line += len(str(message).splitlines())
def run(self, sleep_time=0.1):
"""
Run the loading bar for the entire iterable with a delay for visibility.
Args:
sleep_time (float): Time to sleep between iterations (for visibility).
"""
for item in self:
time.sleep(sleep_time) # Simulate work
print()
```
and to use it you can give the iterable object to it.
```python
import time
loader = LoadingBar(range(10), color='yellow', loading_at_down=False)
for i in loader:
time.sleep(4)
loader.log(f'the line {i} for test log in this class')
```
[](https://i.sstatic.net/wUf9wnY8.png)