Package starcluster :: Module progressbar
[hide private]
[frames] | no frames]

Source Code for Module starcluster.progressbar

  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 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 differently 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 different 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  # Changelog 
 48  # 
 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 
 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   
67 -class ProgressBarWidget(object):
68 """This is an element of ProgressBar formatting. 69 70 The ProgressBar object will call it's update value when an update 71 is needed. It's size may change between call, but the results will 72 not be good if the size changes drastically and repeatedly. 73 """
74 - def update(self, pbar):
75 """Returns the string representing the widget. 76 77 The parameter pbar is a reference to the calling ProgressBar, 78 where one can access attributes of the class for knowing how 79 the update must be made. 80 81 At least this function must be overridden.""" 82 pass
83 84
85 -class ProgressBarWidgetHFill(object):
86 """This is a variable width element of ProgressBar formatting. 87 88 The ProgressBar object will call it's update value, informing the 89 width this object must the made. This is like TeX \\hfill, it will 90 expand to fill the line. You can use more than one in the same 91 line, and they will all have the same width, and together will 92 fill the line. 93 """
94 - def update(self, pbar, width):
95 """Returns the string representing the widget. 96 97 The parameter pbar is a reference to the calling ProgressBar, 98 where one can access attributes of the class for knowing how 99 the update must be made. The parameter width is the total 100 horizontal width the widget must have. 101 102 At least this function must be overridden.""" 103 pass
104 105
106 -class ETA(ProgressBarWidget):
107 "Widget for the Estimated Time of Arrival"
108 - def format_time(self, seconds):
109 return time.strftime('%H:%M:%S', time.gmtime(seconds))
110
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 121
122 -class FileTransferSpeed(ProgressBarWidget):
123 "Widget for showing the transfer speed (useful for file transfers)."
124 - def __init__(self):
125 self.fmt = '%6.2f %s' 126 self.units = ['B', 'K', 'M', 'G', 'T', 'P']
127
128 - def update(self, pbar):
129 if pbar.seconds_elapsed < 2e-6: # == 0: 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
141 -class RotatingMarker(ProgressBarWidget):
142 "A rotating marker for filling the bar of progress."
143 - def __init__(self, markers='|/-\\'):
144 self.markers = markers 145 self.curmark = -1
146
147 - def update(self, pbar):
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
154 -class Percentage(ProgressBarWidget):
155 "Just the percentage done."
156 - def update(self, pbar):
157 return '%3d%%' % pbar.percentage()
158 159
160 -class Fraction(ProgressBarWidget):
161 "Just the fraction done."
162 - def update(self, pbar):
163 return "%d/%d" % (pbar.currval, pbar.maxval)
164 165
166 -class Bar(ProgressBarWidgetHFill):
167 "The bar of progress. It will stretch to fill the line."
168 - def __init__(self, marker='#', left='|', right='|'):
169 self.marker = marker 170 self.left = left 171 self.right = right
172
173 - def _format_marker(self, pbar):
174 if isinstance(self.marker, (str, unicode)): 175 return self.marker 176 else: 177 return self.marker.update(pbar)
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
188 -class ReverseBar(Bar):
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
201 -class ProgressBar(object):
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 """
231 - def __init__(self, maxval=100, widgets=default_widgets, term_width=79, 232 fd=sys.stderr, force_update=False):
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
255 - def handle_resize(self, signum, frame):
256 h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] 257 self.term_width = w
258
259 - def percentage(self):
260 "Returns the percentage of the progress." 261 return self.currval * 100.0 / self.maxval
262
263 - def _format_widgets(self):
264 r = [] 265 hfill_inds = [] 266 num_hfill = 0 267 currwidth = 0 268 for i, w in enumerate(self.widgets): 269 if isinstance(w, ProgressBarWidgetHFill): 270 r.append(w) 271 hfill_inds.append(i) 272 num_hfill += 1 273 elif isinstance(w, (str, unicode)): 274 r.append(w) 275 currwidth += len(w) 276 else: 277 weval = w.update(self) 278 currwidth += len(weval) 279 r.append(weval) 280 for iw in hfill_inds: 281 r[iw] = r[iw].update(self, 282 (self.term_width - currwidth) / num_hfill) 283 return r
284
285 - def _format_line(self):
286 return ''.join(self._format_widgets()).ljust(self.term_width)
287
288 - def _need_update(self):
289 if self.force_update: 290 return True 291 return int(self.percentage()) != int(self.prev_percentage)
292
293 - def reset(self):
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
303 - def update(self, value):
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
319 - def start(self):
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
333 - def finish(self):
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
340 -def example1():
341 widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()), 342 ' ', ETA(), ' ', FileTransferSpeed()] 343 pbar = ProgressBar(widgets=widgets, maxval=10000000).start() 344 for i in range(1000000): 345 # do something 346 pbar.update(10 * i + 1) 347 pbar.finish() 348 return pbar
349 350
351 -def example2():
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 # maybe do something 364 pbar.start() 365 for i in range(2000000): 366 # do something 367 pbar.update(5 * i + 1) 368 pbar.finish() 369 return pbar 370 371
372 -def example3():
373 widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')] 374 pbar = ProgressBar(widgets=widgets, maxval=10000000).start() 375 for i in range(1000000): 376 # do something 377 pbar.update(10 * i + 1) 378 pbar.finish() 379 return pbar
380 381
382 -def example4():
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
395 -def example5():
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 # do something 401 time.sleep(0.5) 402 pbar.update(i) 403 pbar.finish() 404 return pbar
405 406
407 -def main():
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