Skip to content

Commit 2188394

Browse files
committed
changes to use ProgressBar as an iterable plus examples.
1 parent e9b241d commit 2188394

File tree

3 files changed

+85
-15
lines changed

3 files changed

+85
-15
lines changed

progressbar/ChangeLog.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2010-08-29:
2+
- Refactored some code and made it possible to use a ProgressBar as
3+
an iterator (actually as an iterator that is a proxy to another iterator).
4+
This simplifies showing a progress bar in a number of cases.
5+
6+
2010-08-15:
7+
- Did some minor changes to make it compatible with python 3.
8+
19
2009-05-31:
210
- Included check for calling start before update.
311

progressbar/examples.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,32 @@ def example5():
7373
pbar.finish()
7474
sys.stdout.write('\n')
7575

76+
def example6():
77+
pbar = ProgressBar().start()
78+
for i in range(100):
79+
time.sleep(0.01)
80+
pbar.update(i + 1)
81+
pbar.finish()
82+
sys.stdout.write('\n')
83+
84+
def example7():
85+
pbar = ProgressBar()
86+
for i in pbar(range(80)):
87+
time.sleep(0.01)
88+
sys.stdout.write('\n')
89+
90+
def example8():
91+
pbar = ProgressBar(maxval=80)
92+
for i in pbar((i for i in range(80))):
93+
time.sleep(0.01)
94+
sys.stdout.write('\n')
95+
7696
example0()
7797
example1()
7898
example2()
7999
example3()
80100
example4()
81101
example5()
102+
example6()
103+
example7()
104+
example8()

progressbar/progressbar.py

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def __init__(self, markers='|/-\\'):
137137
def update(self, pbar):
138138
if pbar.finished:
139139
return self.markers[0]
140-
self.curmark = (self.curmark + 1)%len(self.markers)
140+
self.curmark = (self.curmark + 1) % len(self.markers)
141141
return self.markers[self.curmark]
142142

143143
class Percentage(ProgressBarWidget):
@@ -147,8 +147,10 @@ def update(self, pbar):
147147

148148
class SimpleProgress(ProgressBarWidget):
149149
"Returns what is already done and the total, e.g.: '5 of 47'"
150+
def __init__(self, sep=' of '):
151+
self.sep = sep
150152
def update(self, pbar):
151-
return '%d of %d' % (pbar.currval, pbar.maxval)
153+
return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval)
152154

153155
class Bar(ProgressBarWidgetHFill):
154156
"The bar of progress. It will stretch to fill the line."
@@ -183,22 +185,28 @@ def update(self, pbar, width):
183185
class ProgressBar(object):
184186
"""This is the ProgressBar class, it updates and prints the bar.
185187
186-
The term_width parameter must be an integer or None. In the latter case
187-
it will try to guess it, if it fails it will default to 80 columns.
188-
189-
The simple use is like this:
188+
A common way of using it is like:
190189
>>> pbar = ProgressBar().start()
191190
>>> for i in xrange(100):
192191
... # do something
193192
... pbar.update(i+1)
194193
...
195194
>>> pbar.finish()
196195
196+
You can also use a progressbar as an iterator:
197+
>>> progress = ProgressBar()
198+
>>> for i in progress(some_iterable):
199+
... # do something
200+
...
201+
197202
But anything you want to do is possible (well, almost anything).
198203
You can supply different widgets of any type in any order. And you
199204
can even write your own widgets! There are many widgets already
200205
shipped and you should experiment with them.
201206
207+
The term_width parameter must be an integer or None. In the latter case
208+
it will try to guess it, if it fails it will default to 80 columns.
209+
202210
When implementing a widget update method you may access any
203211
attribute or function of the ProgressBar object calling the
204212
widget's update method. The most important attributes you would
@@ -218,11 +226,12 @@ class ProgressBar(object):
218226
__slots__ = ('currval', 'fd', 'finished', 'last_update_time', 'maxval',
219227
'next_update', 'num_intervals', 'seconds_elapsed',
220228
'signal_set', 'start_time', 'term_width', 'update_interval',
221-
'widgets')
229+
'widgets', '_iterable')
222230

223-
def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
231+
_DEFAULT_MAXVAL = 100
232+
233+
def __init__(self, maxval=None, widgets=default_widgets, term_width=None,
224234
fd=sys.stderr):
225-
assert maxval > 0
226235
self.maxval = maxval
227236
self.widgets = widgets
228237
self.fd = fd
@@ -239,15 +248,37 @@ def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
239248
except:
240249
self.term_width = int(os.environ.get('COLUMNS', 80)) - 1
241250

242-
self.num_intervals = max(100, self.term_width)
243-
self.update_interval = self.maxval / self.num_intervals
244-
self.next_update = 0
245-
246251
self.currval = 0
247252
self.finished = False
248253
self.start_time = None
249254
self.last_update_time = None
250255
self.seconds_elapsed = 0
256+
self._iterable = None
257+
258+
def __call__(self, iterable):
259+
try:
260+
self.maxval = len(iterable)
261+
except TypeError:
262+
# If the iterable has no length, then rely on the value provided
263+
# by the user, otherwise fail.
264+
if not (isinstance(self.maxval, (int, long)) and self.maxval > 0):
265+
raise RuntimeError('Could not determine maxval from iterable. '
266+
'You must explicitly provide a maxval.')
267+
self._iterable = iter(iterable)
268+
self.start()
269+
return self
270+
271+
def __iter__(self):
272+
return self
273+
274+
def next(self):
275+
try:
276+
next = self._iterable.next()
277+
self.update(self.currval + 1)
278+
return next
279+
except StopIteration:
280+
self.finish()
281+
raise
251282

252283
def _handle_resize(self, signum, frame):
253284
h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2]
@@ -309,7 +340,7 @@ def _need_update(self):
309340

310341
def update(self, value):
311342
"Updates the progress bar to a new value."
312-
assert 0 <= value <= self.maxval
343+
assert 0 <= value <= self.maxval, '0 <= %d <= %d' % (value, self.maxval)
313344
self.currval = value
314345
if not self._need_update():
315346
return
@@ -322,7 +353,7 @@ def update(self, value):
322353
self.last_update_time = now
323354

324355
def start(self):
325-
"""Start measuring time, and prints the bar at 0%.
356+
"""Starts measuring time, and prints the bar at 0%.
326357
327358
It returns self so you can use it like this:
328359
>>> pbar = ProgressBar().start()
@@ -332,6 +363,14 @@ def start(self):
332363
...
333364
>>> pbar.finish()
334365
"""
366+
if self.maxval is None:
367+
self.maxval = self._DEFAULT_MAXVAL
368+
assert self.maxval > 0
369+
370+
self.num_intervals = max(100, self.term_width)
371+
self.update_interval = self.maxval / self.num_intervals
372+
self.next_update = 0
373+
335374
self.start_time = self.last_update_time = time.time()
336375
self.update(0)
337376
return self

0 commit comments

Comments
 (0)