progressbar.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 #
4 # progressbar - Text progressbar library for python.
5 # Copyright (c) 2005 Nilton Volpato
6 #
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.
11 #
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.
16 #
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
20 
21 
22 """Text progressbar library for python.
23 
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.
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 from __future__ import print_function
42 
43 from builtins import range
44 from builtins import object
45 __author__ = "Nilton Volpato"
46 __author_email__ = "first-name dot last-name @ gmail.com"
47 __date__ = "2006-05-07"
48 __version__ = "2.2"
49 
50 # Changelog
51 #
52 # 2006-05-07: v2.2 fixed bug in windows
53 # 2005-12-04: v2.1 autodetect terminal width, added start method
54 # 2005-12-04: v2.0 everything is now a widget (wow!)
55 # 2005-12-03: v1.0 rewrite using widgets
56 # 2005-06-02: v0.5 rewrite
57 # 2004-??-??: v0.1 first version
58 
59 
60 import sys, time
61 from array import array
62 try:
63  from fcntl import ioctl
64  import termios
65 except ImportError:
66  pass
67 import signal
68 
69 class ProgressBarWidget(object):
70  """This is an element of ProgressBar formatting.
71 
72  The ProgressBar object will call it's update value when an update
73  is needed. It's size may change between call, but the results will
74  not be good if the size changes drastically and repeatedly.
75  """
76  def update(self, pbar):
77  """Returns the string representing the widget.
78 
79  The parameter pbar is a reference to the calling ProgressBar,
80  where one can access attributes of the class for knowing how
81  the update must be made.
82 
83  At least this function must be overriden."""
84  pass
85 
86 class ProgressBarWidgetHFill(object):
87  """This is a variable width element of ProgressBar formatting.
88 
89  The ProgressBar object will call it's update value, informing the
90  width this object must the made. This is like TeX \\hfill, it will
91  expand to fill the line. You can use more than one in the same
92  line, and they will all have the same width, and together will
93  fill the line.
94  """
95  def update(self, pbar, width):
96  """Returns the string representing the widget.
97 
98  The parameter pbar is a reference to the calling ProgressBar,
99  where one can access attributes of the class for knowing how
100  the update must be made. The parameter width is the total
101  horizontal width the widget must have.
102 
103  At least this function must be overriden."""
104  pass
105 
106 
107 class ETA(ProgressBarWidget):
108  "Widget for the Estimated Time of Arrival"
109  def format_time(self, seconds):
110  return time.strftime('%H:%M:%S', time.gmtime(seconds))
111  def update(self, pbar):
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 
122  "Widget for showing the transfer speed (useful for file transfers)."
123  def __init__(self):
124  self.fmt = '%6.2f %s'
125  self.units = ['B','K','M','G','T','P']
126  def update(self, pbar):
127  if pbar.seconds_elapsed < 2e-6:#== 0:
128  bps = 0.0
129  else:
130  bps = float(pbar.currval) / pbar.seconds_elapsed
131  spd = bps
132  for u in self.units:
133  if spd < 1000:
134  break
135  spd /= 1000
136  return self.fmt % (spd, u+'/s')
137 
139  "A rotating marker for filling the bar of progress."
140  def __init__(self, markers='|/-\\'):
141  self.markers = markers
142  self.curmark = -1
143  def update(self, pbar):
144  if pbar.finished:
145  return self.markers[0]
146  self.curmark = (self.curmark + 1)%len(self.markers)
147  return self.markers[self.curmark]
148 
150  "Just the percentage done."
151  def update(self, pbar):
152  return '%3d%%' % pbar.percentage()
153 
155  "Just the percentage done."
156  def update(self, pbar):
157  return '%3d' % pbar.currval
158 
160  "Just the percentage done."
161  def update(self, pbar):
162  return '%3d' % pbar.maxval
163 
165  "The bar of progress. It will strech to fill the line."
166  def __init__(self, marker='#', left='|', right='|'):
167  self.marker = marker
168  self.left = left
169  self.right = right
170  def _format_marker(self, pbar):
171  if isinstance(self.marker, str):
172  return self.marker
173  else:
174  return self.marker.update(pbar)
175  def update(self, pbar, width):
176  percent = pbar.percentage()
177  cwidth = width - len(self.left) - len(self.right)
178  marked_width = int(percent * cwidth // 100)
179  m = self._format_marker(pbar)
180  bar = (self.left + (m*marked_width).ljust(cwidth) + self.right)
181  return bar
182 
184  "The reverse bar of progress, or bar of regress. :)"
185  def update(self, pbar, width):
186  percent = pbar.percentage()
187  cwidth = width - len(self.left) - len(self.right)
188  marked_width = int(percent * cwidth // 100)
189  m = self._format_marker(pbar)
190  bar = (self.left + (m*marked_width).rjust(cwidth) + self.right)
191  return bar
192 
193 default_widgets = [Percentage(), ' ', Bar()]
194 class ProgressBar(object):
195  """This is the ProgressBar class, it updates and prints the bar.
196 
197  The term_width parameter may be an integer. Or None, in which case
198  it will try to guess it, if it fails it will default to 80 columns.
199 
200  The simple use is like this:
201  >>> pbar = ProgressBar().start()
202  >>> for i in xrange(100):
203  ... # do something
204  ... pbar.update(i+1)
205  ...
206  >>> pbar.finish()
207 
208  But anything you want to do is possible (well, almost anything).
209  You can supply different widgets of any type in any order. And you
210  can even write your own widgets! There are many widgets already
211  shipped and you should experiment with them.
212 
213  When implementing a widget update method you may access any
214  attribute or function of the ProgressBar object calling the
215  widget's update method. The most important attributes you would
216  like to access are:
217  - currval: current value of the progress, 0 <= currval <= maxval
218  - maxval: maximum (and final) value of the progress
219  - finished: True if the bar is have finished (reached 100%), False o/w
220  - start_time: first time update() method of ProgressBar was called
221  - seconds_elapsed: seconds elapsed since start_time
222  - percentage(): percentage of the progress (this is a method)
223  """
224  def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
225  fd=sys.stderr):
226  assert maxval > 0
227  self.maxval = maxval
228  self.widgets = widgets
229  self.fd = fd
230  self.signal_set = False
231  if term_width is None:
232  try:
233  self.handle_resize(None,None)
234  signal.signal(signal.SIGWINCH, self.handle_resize)
235  self.signal_set = True
236  except:
237  self.term_width = 79
238  else:
239  self.term_width = term_width
240 
241  self.currval = 0
242  self.finished = False
243  self.prev_percentage = -1
244  self.start_time = None
246 
247  def handle_resize(self, signum, frame):
248  h,w=array('h', ioctl(self.fd,termios.TIOCGWINSZ,'\0'*8))[:2]
249  self.term_width = w
250 
251  def percentage(self):
252  "Returns the percentage of the progress."
253  return self.currval*100.0 / self.maxval
254 
255  def _format_widgets(self):
256  r = []
257  hfill_inds = []
258  num_hfill = 0
259  currwidth = 0
260  for i, w in enumerate(self.widgets):
261  if isinstance(w, ProgressBarWidgetHFill):
262  r.append(w)
263  hfill_inds.append(i)
264  num_hfill += 1
265  elif isinstance(w, str):
266  r.append(w)
267  currwidth += len(w)
268  else:
269  weval = w.update(self)
270  currwidth += len(weval)
271  r.append(weval)
272  for iw in hfill_inds:
273  r[iw] = r[iw].update(self, (self.term_width-currwidth)//num_hfill)
274  return r
275 
276  def _format_line(self):
277  return ''.join(self._format_widgets()).ljust(self.term_width)
278 
279  def _need_update(self):
280  return int(self.percentage()) != int(self.prev_percentage)
281 
282  def update(self, value):
283  "Updates the progress bar to a new value."
284  assert 0 <= value <= self.maxval
285  self.currval = value
286  if not self._need_update() or self.finished:
287  return
288  if not self.start_time:
289  self.start_time = time.time()
290  self.seconds_elapsed = time.time() - self.start_time
291  self.prev_percentage = self.percentage()
292  if value != self.maxval:
293  self.fd.write(self._format_line() + '\r')
294  else:
295  self.finished = True
296  self.fd.write(self._format_line() + '\n')
297 
298  def start(self):
299  """Start measuring time, and prints the bar at 0%.
300 
301  It returns self so you can use it like this:
302  >>> pbar = ProgressBar().start()
303  >>> for i in xrange(100):
304  ... # do something
305  ... pbar.update(i+1)
306  ...
307  >>> pbar.finish()
308  """
309  self.update(0)
310  return self
311 
312  def finish(self):
313  """Used to tell the progress is finished."""
314  self.update(self.maxval)
315  if self.signal_set:
316  signal.signal(signal.SIGWINCH, signal.SIG_DFL)
317 
318 
319 
320 
321 
322 
323 if __name__=='__main__':
324  import os
325 
326  def example1():
327  widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
328  ' ', ETA(), ' ', FileTransferSpeed()]
329  pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
330  for i in range(1000000):
331  # do something
332  pbar.update(10*i+1)
333  pbar.finish()
334  print()
335 
336  def example2():
337  class CrazyFileTransferSpeed(FileTransferSpeed):
338  "It's bigger between 45 and 80 percent"
339  def update(self, pbar):
340  if 45 < pbar.percentage() < 80:
341  return 'Bigger Now ' + FileTransferSpeed.update(self,pbar)
342  else:
343  return FileTransferSpeed.update(self,pbar)
344 
345  widgets = [CrazyFileTransferSpeed(),' <<<', Bar(), '>>> ', Percentage(),' ', ETA()]
346  pbar = ProgressBar(widgets=widgets, maxval=10000000)
347  # maybe do something
348  pbar.start()
349  for i in range(2000000):
350  # do something
351  pbar.update(5*i+1)
352  pbar.finish()
353  print()
354 
355  def example3():
356  widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')]
357  pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
358  for i in range(1000000):
359  # do something
360  pbar.update(10*i+1)
361  pbar.finish()
362  print()
363 
364  def example4():
365  widgets = ['Test: ', Percentage(), ' ',
366  Bar(marker='0',left='[',right=']'),
367  ' ', ETA(), ' ', FileTransferSpeed()]
368  pbar = ProgressBar(widgets=widgets, maxval=500)
369  pbar.start()
370  for i in range(100,500+1,50):
371  time.sleep(0.2)
372  pbar.update(i)
373  pbar.finish()
374  print()
375 
376 
377  example1()
378  example2()
379  example3()
380  example4()
381 
def example3()
Definition: progressbar.py:355
def update(self, pbar)
Definition: progressbar.py:143
def __init__(self, maxval=100, widgets=default_widgets, term_width=None, fd=sys.stderr)
Definition: progressbar.py:225
def update(self, pbar, width)
Definition: progressbar.py:175
def example4()
Definition: progressbar.py:364
def handle_resize(self, signum, frame)
Definition: progressbar.py:247
def update(self, pbar)
Definition: progressbar.py:111
def __init__(self, marker='#', left='|', right='|')
Definition: progressbar.py:166
def update(self, pbar)
Definition: progressbar.py:156
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
def _format_marker(self, pbar)
Definition: progressbar.py:170
def example2()
Definition: progressbar.py:336
def update(self, pbar)
Definition: progressbar.py:151
def update(self, value)
Definition: progressbar.py:282
def update(self, pbar, width)
Definition: progressbar.py:95
auto array(Array const &a)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:228
def update(self, pbar, width)
Definition: progressbar.py:185
def format_time(self, seconds)
Definition: progressbar.py:109
def update(self, pbar)
Definition: progressbar.py:161
def __init__(self, markers='|/-\\')
Definition: progressbar.py:140
def example1()
Definition: progressbar.py:326