1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Text progressbar library for python.
23
24 This library provides a text mode progressbar. This is typically used
25 to display the progress of a long running operation, providing a
26 visual clue that processing is underway.
27
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.
37
38 The progressbar module is very easy to use, yet very powerful. And
39 automatically supports features like auto-resizing when available.
40 """
41
42 __author__ = "Nilton Volpato"
43 __author_email__ = "first-name dot last-name @ gmail.com"
44 __date__ = "2006-05-07"
45 __version__ = "2.2"
46
47
48
49
50
51
52
53
54
55
56 import sys
57 import time
58 from array import array
59 try:
60 from fcntl import ioctl
61 import termios
62 except ImportError:
63 pass
64 import signal
65
66
83
84
104
105
106 -class ETA(ProgressBarWidget):
107 "Widget for the Estimated Time of Arrival"
110
112 if pbar.currval == 0:
113 return 'ETA: --:--:--'
114 elif pbar.finished:
115 return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
116 else:
117 elapsed = pbar.seconds_elapsed
118 eta = elapsed * pbar.maxval / pbar.currval - elapsed
119 return 'ETA: %s' % self.format_time(eta)
120
121
123 "Widget for showing the transfer speed (useful for file transfers)."
125 self.fmt = '%6.2f %s'
126 self.units = ['B', 'K', 'M', 'G', 'T', 'P']
127
129 if pbar.seconds_elapsed < 2e-6:
130 bps = 0.0
131 else:
132 bps = float(pbar.currval) / pbar.seconds_elapsed
133 spd = bps
134 for u in self.units:
135 if spd < 1000:
136 break
137 spd /= 1000
138 return self.fmt % (spd, u + '/s')
139
140
142 "A rotating marker for filling the bar of progress."
144 self.markers = markers
145 self.curmark = -1
146
148 if pbar.finished:
149 return self.markers[0]
150 self.curmark = (self.curmark + 1) % len(self.markers)
151 return self.markers[self.curmark]
152
153
155 "Just the percentage done."
158
159
161 "Just the fraction done."
163 return "%d/%d" % (pbar.currval, pbar.maxval)
164
165
166 -class Bar(ProgressBarWidgetHFill):
167 "The bar of progress. It will strech to fill the line."
168 - def __init__(self, marker='#', left='|', right='|'):
169 self.marker = marker
170 self.left = left
171 self.right = right
172
178
179 - def update(self, pbar, width):
180 percent = pbar.percentage()
181 cwidth = width - len(self.left) - len(self.right)
182 marked_width = int(percent * cwidth / 100)
183 m = self._format_marker(pbar)
184 bar = (self.left + (m * marked_width).ljust(cwidth) + self.right)
185 return bar
186
187
189 "The reverse bar of progress, or bar of regress. :)"
190 - def update(self, pbar, width):
191 percent = pbar.percentage()
192 cwidth = width - len(self.left) - len(self.right)
193 marked_width = int(percent * cwidth / 100)
194 m = self._format_marker(pbar)
195 bar = (self.left + (m * marked_width).rjust(cwidth) + self.right)
196 return bar
197
198 default_widgets = [Percentage(), ' ', Bar()]
199
200
202 """This is the ProgressBar class, it updates and prints the bar.
203
204 The term_width parameter may be an integer. Or None, in which case
205 it will try to guess it, if it fails it will default to 80 columns.
206
207 The simple use is like this:
208 >>> pbar = ProgressBar().start()
209 >>> for i in xrange(100):
210 ... # do something
211 ... pbar.update(i+1)
212 ...
213 >>> pbar.finish()
214
215 But anything you want to do is possible (well, almost anything).
216 You can supply different widgets of any type in any order. And you
217 can even write your own widgets! There are many widgets already
218 shipped and you should experiment with them.
219
220 When implementing a widget update method you may access any
221 attribute or function of the ProgressBar object calling the
222 widget's update method. The most important attributes you would
223 like to access are:
224 - currval: current value of the progress, 0 <= currval <= maxval
225 - maxval: maximum (and final) value of the progress
226 - finished: True if the bar is have finished (reached 100%), False o/w
227 - start_time: first time update() method of ProgressBar was called
228 - seconds_elapsed: seconds elapsed since start_time
229 - percentage(): percentage of the progress (this is a method)
230 """
233 assert maxval > 0
234 self.maxval = maxval
235 self.widgets = widgets
236 self.fd = fd
237 self.signal_set = False
238 if term_width is None:
239 try:
240 self.handle_resize(None, None)
241 signal.signal(signal.SIGWINCH, self.handle_resize)
242 self.signal_set = True
243 except:
244 self.term_width = 79
245 else:
246 self.term_width = term_width
247
248 self.currval = 0
249 self.finished = False
250 self.prev_percentage = -1
251 self.start_time = None
252 self.seconds_elapsed = 0
253 self.force_update = force_update
254
256 h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2]
257 self.term_width = w
258
260 "Returns the percentage of the progress."
261 return self.currval * 100.0 / self.maxval
262
284
287
289 if self.force_update:
290 return True
291 return int(self.percentage()) != int(self.prev_percentage)
292
294 if not self.finished and self.start_time:
295 self.finish()
296 self.finished = False
297 self.currval = 0
298 self.start_time = None
299 self.seconds_elapsed = None
300 self.prev_percentage = None
301 return self
302
304 "Updates the progress bar to a new value."
305 assert 0 <= value <= self.maxval
306 self.currval = value
307 if not self._need_update() or self.finished:
308 return
309 if not self.start_time:
310 self.start_time = time.time()
311 self.seconds_elapsed = time.time() - self.start_time
312 self.prev_percentage = self.percentage()
313 if value != self.maxval:
314 self.fd.write(self._format_line() + '\r')
315 else:
316 self.finished = True
317 self.fd.write(self._format_line() + '\n')
318
320 """Start measuring time, and prints the bar at 0%.
321
322 It returns self so you can use it like this:
323 >>> pbar = ProgressBar().start()
324 >>> for i in xrange(100):
325 ... # do something
326 ... pbar.update(i+1)
327 ...
328 >>> pbar.finish()
329 """
330 self.update(0)
331 return self
332
334 """Used to tell the progress is finished."""
335 self.update(self.maxval)
336 if self.signal_set:
337 signal.signal(signal.SIGWINCH, signal.SIG_DFL)
338
339
349
350
352 class CrazyFileTransferSpeed(FileTransferSpeed):
353 "It's bigger between 45 and 80 percent"
354 def update(self, pbar):
355 if 45 < pbar.percentage() < 80:
356 return 'Bigger Now ' + FileTransferSpeed.update(self, pbar)
357 else:
358 return FileTransferSpeed.update(self, pbar)
359
360 widgets = [CrazyFileTransferSpeed(), ' <<<',
361 Bar(), '>>> ', Percentage(), ' ', ETA()]
362 pbar = ProgressBar(widgets=widgets, maxval=10000000)
363
364 pbar.start()
365 for i in range(2000000):
366
367 pbar.update(5 * i + 1)
368 pbar.finish()
369 return pbar
370
371
380
381
383 widgets = ['Test: ', Percentage(), ' ',
384 Bar(marker='0', left='[', right=']'),
385 ' ', ETA(), ' ', FileTransferSpeed()]
386 pbar = ProgressBar(widgets=widgets, maxval=500)
387 pbar.start()
388 for i in range(100, 500 + 1, 50):
389 time.sleep(0.2)
390 pbar.update(i)
391 pbar.finish()
392 return pbar
393
394
396 widgets = ['Test: ', Fraction(), ' ', Bar(marker=RotatingMarker()),
397 ' ', ETA(), ' ', FileTransferSpeed()]
398 pbar = ProgressBar(widgets=widgets, maxval=10, force_update=True).start()
399 for i in range(1, 11):
400
401 time.sleep(0.5)
402 pbar.update(i)
403 pbar.finish()
404 return pbar
405
406
408 example1()
409 print
410 example2()
411 print
412 example3()
413 print
414 example4()
415 print
416 example5()
417 print
418
419 if __name__ == '__main__':
420 main()
421