Skip to content

Commit b39b19e

Browse files
Added progressbar custom formatting.
Now you can use your own formatting for the progressbar instead of the default one.
1 parent 2014c59 commit b39b19e

5 files changed

Lines changed: 173 additions & 51 deletions

File tree

CHANGES-LOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,30 @@ Help this project by [Donation](DONATE.md)
66
Changes log
77
-----------
88

9+
### 2.3.0
10+
11+
Added progressbar custom formatting.
12+
13+
Now you can use your own formatting for the progressbar instead of the default one.
14+
15+
Let's see an example:
16+
17+
```python
18+
# We import the ProgressBar class from log21
19+
from log21 import ProgressBar
20+
# psutil is a module that can be used to get the current memory usage or cpu usage of your system
21+
# If you want to try this example, you need to install psutil: pip install psutil
22+
import psutil
23+
# We use the time module to make a delay between the progressbar updates
24+
import time
25+
26+
cpu_bar = ProgressBar(format_='CPU Usage: {prefix}{bar}{suffix} {percentage}%', style='{', new_line_when_complete=False)
27+
28+
while True:
29+
cpu_bar.update(psutil.cpu_percent(), 100)
30+
time.sleep(0.1)
31+
```
32+
933
### 2.2.0
1034

1135
Added CrashReporter!

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Features
2222
structure. It's also colorized XD.
2323
+ ProgressBar : log21's progress bar can be used to show progress of a process in a beautiful way.
2424
+ LoggingWindow : Helps you to log messages and debug your code in a window other than the console.
25+
+ CrashReporter : log21's crash reporter can be used to report crashes in different ways. You can use it to log crashes
26+
to console or files or use it to receive crash reports of your program through email. And you can also define your own
27+
crash reporter functions and use them instead!
2528
+ Any idea? Feel free to [open an issue](https://github.com/MPCodeWriter21/log21/issues) or submit a pull request.
2629

2730
![issues](https://img.shields.io/github/issues/MPCodeWriter21/log21)
@@ -50,14 +53,29 @@ python setup.py install
5053
Changes
5154
-------
5255

53-
### 2.2.0
56+
### 2.3.0
5457

55-
Added CrashReporter!
58+
Added progressbar custom formatting.
5659

57-
You can use Reporter classes to monitor your program and send crash reports to the developer. It can help you fix the
58-
bugs and improve your program before your users get upset about it. See some examples in
59-
the [log21/CrashReporter/Reporters.py](https://github.com/MPCodeWriter21/log21/blob/master/log21/CrashReporter/Reporters.py)
60-
file.
60+
Now you can use your own formatting for the progressbar instead of the default one.
61+
62+
Let's see an example:
63+
64+
```python
65+
# We import the ProgressBar class from log21
66+
from log21 import ProgressBar
67+
# psutil is a module that can be used to get the current memory usage or cpu usage of your system
68+
# If you want to try this example, you need to install psutil: pip install psutil
69+
import psutil
70+
# We use the time module to make a delay between the progressbar updates
71+
import time
72+
73+
cpu_bar = ProgressBar(format_='CPU Usage: {prefix}{bar}{suffix} {percentage}%', style='{', new_line_when_complete=False)
74+
75+
while True:
76+
cpu_bar.update(psutil.cpu_percent(), 100)
77+
time.sleep(0.1)
78+
```
6179

6280
[Full Changes Log](https://github.com/MPCodeWriter21/log21/blob/master/CHANGES-LOG.md)
6381

log21/ProgressBar.py

Lines changed: 123 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,42 @@
1515

1616

1717
class ProgressBar:
18+
"""
19+
Usage Example:
20+
>>> pb = ProgressBar(width=20, show_percentage=False, prefix='[', suffix=']', fill='=', empty='-')
21+
>>> pb(0, 10)
22+
[/-----------------]
23+
>>> pb(1, 10)
24+
[==----------------]
25+
>>> pb(2, 10)
26+
[====\\-------------]
27+
>>>
28+
>>> # A better example
29+
>>> import time
30+
>>> pb = ProgressBar()
31+
>>> for i in range(500):
32+
... pb(i + 1, 500)
33+
... time.sleep(0.01)
34+
...
35+
|████████████████████████████████████████████████████████████████████████████████████████████| 100%
36+
>>> # Of course, You should try it yourself to see the progress! XD
37+
>>>
38+
"""
1839

1940
def __init__(self, *args, width: int = None, show_percentage: bool = True, prefix: str = '|', suffix: str = '|',
20-
fill: str = '█', empty: str = ' ', colors: dict = None, logger: '_log21.Logger' = _logger):
41+
fill: str = '█', empty: str = ' ', format_: str = None, style: str = '%',
42+
new_line_when_complete: bool = True, colors: dict = None, logger: '_log21.Logger' = _logger):
2143
"""
22-
Example:
23-
>>> pb = ProgressBar(width=20, show_percentage=False, prefix='[', suffix=']', fill='=', empty='-')
24-
>>> pb(0, 10)
25-
[/-----------------]
26-
>>> pb(1, 10)
27-
[==----------------]
28-
>>> pb(2, 10)
29-
[====\\-------------]
30-
>>>
31-
>>> # A better example
32-
>>> import time
33-
>>> pb = ProgressBar()
34-
>>> for i in range(500):
35-
... pb(i + 1, 500)
36-
... time.sleep(0.01)
37-
...
38-
|████████████████████████████████████████████████████████████████████████████████████████████| 100%
39-
>>> # Of course, You should try it yourself to see the progress! XD
40-
>>>
41-
4244
:param args: Prevents the use of positional arguments
4345
:param width: The width of the progress bar
4446
:param show_percentage: Whether to show the percentage of the progress
4547
:param prefix: The prefix of the progress bar
4648
:param suffix: The suffix of the progress bar
4749
:param fill: The fill character of the progress bar
4850
:param empty: The empty character of the progress bar
51+
:param format_: The format of the progress bar
52+
:param style: The style that is used to format the progress bar
53+
:param new_line_when_complete: Whether to print a new line when the progress is complete or failed
4954
:param colors: The colors of the progress bar
5055
:param logger: The logger to use
5156
"""
@@ -64,6 +69,8 @@ def __init__(self, *args, width: int = None, show_percentage: bool = True, prefi
6469
raise ValueError('`fill` must be a single character')
6570
if len(empty) != 1:
6671
raise ValueError('`empty` must be a single character')
72+
if style not in ['%', '{']:
73+
raise ValueError('`style` must be either `%` or `{`')
6774

6875
self.colors = {
6976
'progress in-progress': _gc('LightYellow'),
@@ -86,7 +93,14 @@ def __init__(self, *args, width: int = None, show_percentage: bool = True, prefi
8693
self.empty = empty
8794
self.prefix = prefix
8895
self.suffix = suffix
89-
self.show_percentage = show_percentage
96+
if format_:
97+
self.format = format_
98+
else:
99+
self.format = '%(prefix)s%(bar)s%(suffix)s %(percentage)s%%' if show_percentage else \
100+
'%(prefix)s%(bar)s%(suffix)s'
101+
style = '%'
102+
self.style = style
103+
self.new_line_when_complete = new_line_when_complete
90104
if colors:
91105
for key, value in colors.items():
92106
self.colors[key] = value
@@ -102,47 +116,113 @@ def get_bar(self, progress: float, total: float):
102116
return self.progress_in_progress(progress, total)
103117

104118
def progress_in_progress(self, progress: float, total: float):
105-
percentage = round(progress / total * 100, 2)
106-
percentage_str = f' {percentage}%' if self.show_percentage else ''
107-
fill_length = round(progress / total * (self.width - len(percentage_str) - len(self.prefix) - len(self.suffix)))
108-
empty_length = (self.width - fill_length - len(percentage_str) - len(self.prefix) - len(self.suffix))
119+
percentage = str(round(progress / total * 100, 2))
120+
progress_dict = {
121+
'prefix': self.prefix,
122+
'bar': '',
123+
'suffix': self.suffix,
124+
'percentage': percentage
125+
}
126+
127+
if self.style == '%':
128+
used_characters = len(self.format % progress_dict)
129+
elif self.style == '{':
130+
used_characters = len(self.format.format(**progress_dict))
131+
else:
132+
raise ValueError('`style` must be either `%` or `{`')
133+
134+
fill_length = round(progress / total * (self.width - used_characters))
135+
empty_length = (self.width - (fill_length + used_characters)) - 1
109136

110137
if self.i >= 3:
111138
self.i = 0
112139
else:
113140
self.i += 1
114141
spinner_char = self.spinner[self.i] if empty_length > 0 else ''
115142

116-
bar = '\r' + self.colors['prefix-color in-progress'] + self.prefix + \
117-
self.colors['progress in-progress'] + self.fill * fill_length + spinner_char + \
118-
self.empty * (empty_length - 1) + \
119-
self.colors['suffix-color in-progress'] + self.suffix
143+
progress_dict = {
144+
'prefix': self.colors['prefix-color in-progress'] + self.prefix + self.colors['reset-color'],
145+
'bar': self.colors['progress in-progress'] +
146+
(self.fill * fill_length + spinner_char + self.empty * empty_length) + self.colors['reset-color'],
147+
'suffix': self.colors['suffix-color in-progress'] + self.suffix + self.colors['reset-color'],
148+
'percentage': self.colors["percentage in-progress"] + str(percentage) + self.colors['reset-color']
149+
}
150+
151+
if self.style == '%':
152+
bar = self.format % progress_dict
153+
elif self.style == '{':
154+
bar = self.format.format(**progress_dict)
155+
else:
156+
raise ValueError('`style` must be either `%` or `{`')
120157

121-
return f'{bar}{self.colors["percentage in-progress"]}{percentage_str}' + self.colors['reset-color']
158+
return '\r' + bar + self.colors['reset-color']
122159

123160
def progress_complete(self):
124-
percentage_str = ' 100%' if self.show_percentage else ''
125-
bar_length = self.width - len(percentage_str) - len(self.prefix) - len(self.suffix)
126-
bar = '\r' + self.colors['prefix-color complete'] + self.prefix + \
127-
self.colors['progress complete'] + self.fill * bar_length + \
128-
self.colors['suffix-color complete'] + self.suffix
161+
progress_dict = {
162+
'prefix': self.prefix,
163+
'bar': '',
164+
'suffix': self.suffix,
165+
'percentage': '100'
166+
}
167+
168+
if self.style == '%':
169+
bar_length = self.width - len(self.format % progress_dict)
170+
elif self.style == '{':
171+
bar_length = self.width - len(self.format.format(**progress_dict))
172+
else:
173+
raise ValueError('`style` must be either `%` or `{`')
174+
175+
progress_dict = {
176+
'prefix': self.colors['prefix-color complete'] + self.prefix + self.colors['reset-color'],
177+
'bar': self.colors['progress complete'] + (self.fill * bar_length) + self.colors['reset-color'],
178+
'suffix': self.colors['suffix-color complete'] + self.suffix + self.colors['reset-color'],
179+
'percentage': self.colors["percentage complete"] + '100' + self.colors['reset-color']
180+
}
129181

130-
return f'{bar}{self.colors["percentage complete"]}{percentage_str}\n' + self.colors['reset-color']
182+
if self.style == '%':
183+
bar = self.format % progress_dict
184+
elif self.style == '{':
185+
bar = self.format.format(**progress_dict)
186+
else:
187+
raise ValueError('`style` must be either `%` or `{`')
188+
189+
return '\r' + bar + self.colors['reset-color'] + ('\n' if self.new_line_when_complete else '')
131190

132191
def progress_failed(self, progress: float, total: float):
133-
percentage_str = ' Failed' if self.show_percentage else ''
134-
bar_length = self.width - len(percentage_str) - len(self.prefix) - len(self.suffix)
192+
progress_dict = {
193+
'prefix': self.prefix,
194+
'bar': '',
195+
'suffix': self.suffix,
196+
'percentage': str(round(progress / total * 100, 2))
197+
}
198+
199+
if self.style == '%':
200+
bar_length = self.width - len(self.format % progress_dict)
201+
elif self.style == '{':
202+
bar_length = self.width - len(self.format.format(**progress_dict))
203+
else:
204+
raise ValueError('`style` must be either `%` or `{`')
135205

136206
if progress > total:
137207
bar_char = self.fill
138208
else:
139209
bar_char = self.empty
140210

141-
bar = '\r' + self.colors['prefix-color failed'] + self.prefix + \
142-
self.colors['progress failed'] + bar_char * bar_length + \
143-
self.colors['suffix-color failed'] + self.suffix
211+
progress_dict = {
212+
'prefix': self.colors['prefix-color failed'] + self.prefix + self.colors['reset-color'],
213+
'bar': self.colors['progress failed'] + (bar_char * bar_length) + self.colors['reset-color'],
214+
'suffix': self.colors['suffix-color failed'] + self.suffix + self.colors['reset-color'],
215+
'percentage': self.colors["percentage failed"] + progress_dict['percentage'] + self.colors['reset-color']
216+
}
217+
218+
if self.style == '%':
219+
bar = self.format % progress_dict
220+
elif self.style == '{':
221+
bar = self.format.format(**progress_dict)
222+
else:
223+
raise ValueError('`style` must be either `%` or `{`')
144224

145-
return f'{bar}{self.colors["percentage failed"]}{percentage_str}\n' + self.colors['reset-color']
225+
return '\r' + bar + self.colors['reset-color'] + ('\n' if self.new_line_when_complete else '')
146226

147227
def __call__(self, progress: float, total: float, logger: '_log21.Logger' = None):
148228
if not logger:

log21/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from log21.Formatters import ColorizingFormatter, DecolorizingFormatter
2222
from log21.Colors import Colors, get_color, get_colors, ansi_escape, get_color_name, closest_color
2323

24-
__version__ = "2.2.0"
24+
__version__ = "2.3.0"
2525
__author__ = "CodeWriter21 (Mehrad Pooryoussof)"
2626
__github__ = "Https://GitHub.com/MPCodeWriter21/log21"
2727
__all__ = ['ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter', 'DecolorizingFormatter',

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
long_description = file.read()
88

99
DESCRIPTION = 'A simple logging package that helps you log colorized messages in Windows console.'
10-
VERSION = '2.2.0'
10+
VERSION = '2.3.0'
1111

1212
setup(
1313
name='log21',

0 commit comments

Comments
 (0)