2
# -*- coding: iso-8859-1 -*-
4
# progressbar - Text progressbar library for python.
5
# Copyright (c) 2005 Nilton Volpato
7
# This library is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
12
# This library is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this library; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
"""Text progressbar library for python.
24
This library provides a text mode progressbar. This is tipically used
25
to display the progress of a long running operation, providing a
26
visual clue that processing is underway.
28
The ProgressBar class manages the progress, and the format of the line
29
is given by a number of widgets. A widget is an object that may
30
display diferently depending on the state of the progress. There are
31
three types of widget:
32
- a string, which always shows itself;
33
- a ProgressBarWidget, which may return a diferent value every time
34
it's update method is called; and
35
- a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it
36
expands to fill the remaining width of the line.
38
The progressbar module is very easy to use, yet very powerful. And
39
automatically supports features like auto-resizing when available.
42
__author__ = "Nilton Volpato"
43
__author_email__ = "first-name dot last-name @ gmail.com"
44
__date__ = "2006-05-07"
49
# 2006-05-07: v2.2 fixed bug in windows
50
# 2005-12-04: v2.1 autodetect terminal width, added start method
51
# 2005-12-04: v2.0 everything is now a widget (wow!)
52
# 2005-12-03: v1.0 rewrite using widgets
53
# 2005-06-02: v0.5 rewrite
54
# 2004-??-??: v0.1 first version
58
from array import array
60
from fcntl import ioctl
66
class ProgressBarWidget(object):
67
"""This is an element of ProgressBar formatting.
69
The ProgressBar object will call it's update value when an update
70
is needed. It's size may change between call, but the results will
71
not be good if the size changes drastically and repeatedly.
73
def update(self, pbar):
74
"""Returns the string representing the widget.
76
The parameter pbar is a reference to the calling ProgressBar,
77
where one can access attributes of the class for knowing how
78
the update must be made.
80
At least this function must be overriden."""
83
class ProgressBarWidgetHFill(object):
84
"""This is a variable width element of ProgressBar formatting.
86
The ProgressBar object will call it's update value, informing the
87
width this object must the made. This is like TeX \\hfill, it will
88
expand to fill the line. You can use more than one in the same
89
line, and they will all have the same width, and together will
92
def update(self, pbar, width):
93
"""Returns the string representing the widget.
95
The parameter pbar is a reference to the calling ProgressBar,
96
where one can access attributes of the class for knowing how
97
the update must be made. The parameter width is the total
98
horizontal width the widget must have.
100
At least this function must be overriden."""
104
class ETA(ProgressBarWidget):
105
"Widget for the Estimated Time of Arrival"
106
def format_time(self, seconds):
107
return time.strftime('%H:%M:%S', time.gmtime(seconds))
108
def update(self, pbar):
109
if pbar.currval == 0:
110
return 'ETA: --:--:--'
112
return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
114
elapsed = pbar.seconds_elapsed
115
eta = elapsed * pbar.maxval / pbar.currval - elapsed
116
return 'ETA: %s' % self.format_time(eta)
118
class FileTransferSpeed(ProgressBarWidget):
119
"Widget for showing the transfer speed (useful for file transfers)."
121
self.fmt = '%6.2f %s'
122
self.units = ['B','K','M','G','T','P']
123
def update(self, pbar):
124
if pbar.seconds_elapsed < 2e-6:#== 0:
127
bps = float(pbar.currval) / pbar.seconds_elapsed
133
return self.fmt % (spd, u+'/s')
135
class RotatingMarker(ProgressBarWidget):
136
"A rotating marker for filling the bar of progress."
137
def __init__(self, markers='|/-\\'):
138
self.markers = markers
140
def update(self, pbar):
142
return self.markers[0]
143
self.curmark = (self.curmark + 1)%len(self.markers)
144
return self.markers[self.curmark]
146
class Percentage(ProgressBarWidget):
147
"Just the percentage done."
148
def update(self, pbar):
149
return '%3d%%' % pbar.percentage()
151
class Bar(ProgressBarWidgetHFill):
152
"The bar of progress. It will strech to fill the line."
153
def __init__(self, marker='#', left='|', right='|'):
157
def _format_marker(self, pbar):
158
if isinstance(self.marker, (str, unicode)):
161
return self.marker.update(pbar)
162
def update(self, pbar, width):
163
percent = pbar.percentage()
164
cwidth = width - len(self.left) - len(self.right)
165
marked_width = int(percent * cwidth / 100)
166
m = self._format_marker(pbar)
167
bar = (self.left + (m*marked_width).ljust(cwidth) + self.right)
170
class ReverseBar(Bar):
171
"The reverse bar of progress, or bar of regress. :)"
172
def update(self, pbar, width):
173
percent = pbar.percentage()
174
cwidth = width - len(self.left) - len(self.right)
175
marked_width = int(percent * cwidth / 100)
176
m = self._format_marker(pbar)
177
bar = (self.left + (m*marked_width).rjust(cwidth) + self.right)
180
default_widgets = [Percentage(), ' ', Bar()]
181
class ProgressBar(object):
182
"""This is the ProgressBar class, it updates and prints the bar.
184
The term_width parameter may be an integer. Or None, in which case
185
it will try to guess it, if it fails it will default to 80 columns.
187
The simple use is like this:
188
>>> pbar = ProgressBar().start()
189
>>> for i in xrange(100):
195
But anything you want to do is possible (well, almost anything).
196
You can supply different widgets of any type in any order. And you
197
can even write your own widgets! There are many widgets already
198
shipped and you should experiment with them.
200
When implementing a widget update method you may access any
201
attribute or function of the ProgressBar object calling the
202
widget's update method. The most important attributes you would
204
- currval: current value of the progress, 0 <= currval <= maxval
205
- maxval: maximum (and final) value of the progress
206
- finished: True if the bar is have finished (reached 100%), False o/w
207
- start_time: first time update() method of ProgressBar was called
208
- seconds_elapsed: seconds elapsed since start_time
209
- percentage(): percentage of the progress (this is a method)
211
def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
215
self.widgets = widgets
217
self.signal_set = False
218
if term_width is None:
220
self.handle_resize(None,None)
221
signal.signal(signal.SIGWINCH, self.handle_resize)
222
self.signal_set = True
226
self.term_width = term_width
229
self.finished = False
230
self.prev_percentage = -1
231
self.start_time = None
232
self.seconds_elapsed = 0
234
def handle_resize(self, signum, frame):
235
h,w=array('h', ioctl(self.fd,termios.TIOCGWINSZ,'\0'*8))[:2]
238
def percentage(self):
239
"Returns the percentage of the progress."
240
return self.currval*100.0 / self.maxval
242
def _format_widgets(self):
247
for i, w in enumerate(self.widgets):
248
if isinstance(w, ProgressBarWidgetHFill):
252
elif isinstance(w, (str, unicode)):
256
weval = w.update(self)
257
currwidth += len(weval)
259
for iw in hfill_inds:
260
r[iw] = r[iw].update(self, (self.term_width-currwidth)/num_hfill)
263
def _format_line(self):
264
return ''.join(self._format_widgets()).ljust(self.term_width)
266
def _need_update(self):
267
return int(self.percentage()) != int(self.prev_percentage)
269
def update(self, value):
270
"Updates the progress bar to a new value."
271
assert 0 <= value <= self.maxval
273
if not self._need_update() or self.finished:
275
if not self.start_time:
276
self.start_time = time.time()
277
self.seconds_elapsed = time.time() - self.start_time
278
self.prev_percentage = self.percentage()
279
if value != self.maxval:
280
self.fd.write(self._format_line() + '\r')
283
self.fd.write(self._format_line() + '\n')
286
"""Start measuring time, and prints the bar at 0%.
288
It returns self so you can use it like this:
289
>>> pbar = ProgressBar().start()
290
>>> for i in xrange(100):
300
"""Used to tell the progress is finished."""
301
self.update(self.maxval)
304
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
306
if __name__=='__main__':
310
widgets = ['ValTest', Percentage(), ' ', Bar(),' ', ETA(), ' ']
311
pbar = ProgressBar(widgets=widgets, maxval=100).start()
320
widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
321
' ', ETA(), ' ', FileTransferSpeed()]
322
pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
323
for i in range(1000000):
330
class CrazyFileTransferSpeed(FileTransferSpeed):
331
"It's bigger between 45 and 80 percent"
332
def update(self, pbar):
333
if 45 < pbar.percentage() < 80:
334
return 'Bigger Now ' + FileTransferSpeed.update(self,pbar)
336
return FileTransferSpeed.update(self,pbar)
338
widgets = [CrazyFileTransferSpeed(),' <<<', Bar(), '>>> ', Percentage(),' ', ETA()]
339
pbar = ProgressBar(widgets=widgets, maxval=10000000)
342
for i in range(2000000):
349
widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')]
350
pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
351
for i in range(1000000):
358
widgets = ['Test: ', Percentage(), ' ',
359
Bar(marker='0',left='[',right=']'),
360
' ', ETA(), ' ', FileTransferSpeed()]
361
pbar = ProgressBar(widgets=widgets, maxval=500)
363
for i in range(100,500+1,50):