├── .gitignore ├── ChangeLog.yaml ├── LICENSE.txt ├── MANIFEST.in ├── README.txt ├── examples.py ├── progressbar ├── __init__.py ├── compat.py ├── progressbar.py └── widgets.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /ChangeLog.yaml: -------------------------------------------------------------------------------- 1 | 2012-12-22: 2 | - Added the AdaptiveETA widget contributed Oscar Benjamin on 3 | http://code.google.com/p/python-progressbar/issues/detail?id=22. 4 | 5 | 2011-05-15: 6 | - Removed parse errors for Python2.4 (no, people *should not* be using it 7 | but it is only 3 years old and it does not have that many differences) 8 | 9 | - split up progressbar.py into logical units while maintaining backwards 10 | compatibility 11 | 12 | - Removed MANIFEST.in because it is no longer needed and it was causing 13 | distribute to show warnings 14 | 15 | 16 | 2011-05-14: 17 | - Changes to directory structure so pip can install from Google Code 18 | - Python 3.x related fixes (all examples work on Python 3.1.3) 19 | - Added counters, timers, and action bars for iterators with unknown length 20 | 21 | 2010-08-29: 22 | - Refactored some code and made it possible to use a ProgressBar as 23 | an iterator (actually as an iterator that is a proxy to another iterator). 24 | This simplifies showing a progress bar in a number of cases. 25 | 26 | 2010-08-15: 27 | - Did some minor changes to make it compatible with python 3. 28 | 29 | 2009-05-31: 30 | - Included check for calling start before update. 31 | 32 | 2009-03-21: 33 | - Improved FileTransferSpeed widget, which now supports a unit parameter, 34 | defaulting to 'B' for bytes. It will also show B/s, MB/s, etc instead of 35 | B/s, M/s, etc. 36 | 37 | 2009-02-24: 38 | - Updated licensing. 39 | - Moved examples to separated file. 40 | - Improved _need_update() method, which is now as fast as it can be. IOW, 41 | no wasted cycles when an update is not needed. 42 | 43 | 2008-12-22: 44 | - Added SimpleProgress widget contributed by Sando Tosi 45 | . 46 | 47 | 2006-05-07: 48 | - Fixed bug with terminal width in Windows. 49 | - Released version 2.2. 50 | 51 | 2005-12-04: 52 | - Autodetection of terminal width. 53 | - Added start method. 54 | - Released version 2.1. 55 | 56 | 2005-12-04: 57 | - Everything is a widget now! 58 | - Released version 2.0. 59 | 60 | 2005-12-03: 61 | - Rewrite using widgets. 62 | - Released version 1.0. 63 | 64 | 2005-06-02: 65 | - Rewrite. 66 | - Released version 0.5. 67 | 68 | 2004-06-15: 69 | - First version. 70 | - Released version 0.1. 71 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | You can redistribute and/or modify this library under the terms of the 2 | GNU LGPL license or BSD license (or both). 3 | 4 | --- 5 | 6 | progressbar - Text progress bar library for python. 7 | Copyright (C) 2005 Nilton Volpato 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | 23 | --- 24 | 25 | progressbar - Text progress bar library for python 26 | Copyright (c) 2008 Nilton Volpato 27 | 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without 31 | modification, are permitted provided that the following conditions are met: 32 | 33 | a. Redistributions of source code must retain the above copyright notice, 34 | this list of conditions and the following disclaimer. 35 | b. Redistributions in binary form must reproduce the above copyright 36 | notice, this list of conditions and the following disclaimer in the 37 | documentation and/or other materials provided with the distribution. 38 | c. Neither the name of the author nor the names of its contributors 39 | may be used to endorse or promote products derived from this software 40 | without specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 43 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 44 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 45 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 46 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 48 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 49 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 50 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 51 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 52 | DAMAGE. 53 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.txt LICENSE.txt 2 | include examples.py 3 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Text progress bar library for Python. 2 | 3 | A text progress bar is typically used to display the progress of a long 4 | running operation, providing a visual cue that processing is underway. 5 | 6 | The ProgressBar class manages the current progress, and the format of the line 7 | is given by a number of widgets. A widget is an object that may display 8 | differently depending on the state of the progress bar. There are three types 9 | of widgets: 10 | - a string, which always shows itself 11 | 12 | - a ProgressBarWidget, which may return a different value every time its 13 | update method is called 14 | 15 | - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it 16 | expands to fill the remaining width of the line. 17 | 18 | The progressbar module is very easy to use, yet very powerful. It will also 19 | automatically enable features like auto-resizing when the system supports it. 20 | -------------------------------------------------------------------------------- /examples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import time 6 | 7 | from progressbar import AnimatedMarker, Bar, BouncingBar, Counter, ETA, \ 8 | AdaptiveETA, FileTransferSpeed, FormatLabel, Percentage, \ 9 | ProgressBar, ReverseBar, RotatingMarker, \ 10 | SimpleProgress, Timer, UnknownLength 11 | 12 | examples = [] 13 | def example(fn): 14 | try: name = 'Example %d' % int(fn.__name__[7:]) 15 | except: name = fn.__name__ 16 | 17 | def wrapped(): 18 | try: 19 | sys.stdout.write('Running: %s\n' % name) 20 | fn() 21 | sys.stdout.write('\n') 22 | except KeyboardInterrupt: 23 | sys.stdout.write('\nSkipping example.\n\n') 24 | 25 | examples.append(wrapped) 26 | return wrapped 27 | 28 | @example 29 | def example0(): 30 | pbar = ProgressBar(widgets=[Percentage(), Bar()], maxval=300).start() 31 | for i in range(300): 32 | time.sleep(0.01) 33 | pbar.update(i+1) 34 | pbar.finish() 35 | 36 | @example 37 | def example1(): 38 | widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()), 39 | ' ', ETA(), ' ', FileTransferSpeed()] 40 | pbar = ProgressBar(widgets=widgets, maxval=10000000).start() 41 | for i in range(1000000): 42 | # do something 43 | pbar.update(10*i+1) 44 | pbar.finish() 45 | 46 | @example 47 | def example2(): 48 | class CrazyFileTransferSpeed(FileTransferSpeed): 49 | """It's bigger between 45 and 80 percent.""" 50 | def update(self, pbar): 51 | if 45 < pbar.percentage() < 80: 52 | return 'Bigger Now ' + FileTransferSpeed.update(self,pbar) 53 | else: 54 | return FileTransferSpeed.update(self,pbar) 55 | 56 | widgets = [CrazyFileTransferSpeed(),' <<<', Bar(), '>>> ', 57 | Percentage(),' ', ETA()] 58 | pbar = ProgressBar(widgets=widgets, maxval=10000000) 59 | # maybe do something 60 | pbar.start() 61 | for i in range(2000000): 62 | # do something 63 | pbar.update(5*i+1) 64 | pbar.finish() 65 | 66 | @example 67 | def example3(): 68 | widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')] 69 | pbar = ProgressBar(widgets=widgets, maxval=10000000).start() 70 | for i in range(1000000): 71 | # do something 72 | pbar.update(10*i+1) 73 | pbar.finish() 74 | 75 | @example 76 | def example4(): 77 | widgets = ['Test: ', Percentage(), ' ', 78 | Bar(marker='0',left='[',right=']'), 79 | ' ', ETA(), ' ', FileTransferSpeed()] 80 | pbar = ProgressBar(widgets=widgets, maxval=500) 81 | pbar.start() 82 | for i in range(100,500+1,50): 83 | time.sleep(0.2) 84 | pbar.update(i) 85 | pbar.finish() 86 | 87 | @example 88 | def example5(): 89 | pbar = ProgressBar(widgets=[SimpleProgress()], maxval=17).start() 90 | for i in range(17): 91 | time.sleep(0.2) 92 | pbar.update(i + 1) 93 | pbar.finish() 94 | 95 | @example 96 | def example6(): 97 | pbar = ProgressBar().start() 98 | for i in range(100): 99 | time.sleep(0.01) 100 | pbar.update(i + 1) 101 | pbar.finish() 102 | 103 | @example 104 | def example7(): 105 | pbar = ProgressBar() # Progressbar can guess maxval automatically. 106 | for i in pbar(range(80)): 107 | time.sleep(0.01) 108 | 109 | @example 110 | def example8(): 111 | pbar = ProgressBar(maxval=80) # Progressbar can't guess maxval. 112 | for i in pbar((i for i in range(80))): 113 | time.sleep(0.01) 114 | 115 | @example 116 | def example9(): 117 | pbar = ProgressBar(widgets=['Working: ', AnimatedMarker()]) 118 | for i in pbar((i for i in range(50))): 119 | time.sleep(.08) 120 | 121 | @example 122 | def example10(): 123 | widgets = ['Processed: ', Counter(), ' lines (', Timer(), ')'] 124 | pbar = ProgressBar(widgets=widgets) 125 | for i in pbar((i for i in range(150))): 126 | time.sleep(0.1) 127 | 128 | @example 129 | def example11(): 130 | widgets = [FormatLabel('Processed: %(value)d lines (in: %(elapsed)s)')] 131 | pbar = ProgressBar(widgets=widgets) 132 | for i in pbar((i for i in range(150))): 133 | time.sleep(0.1) 134 | 135 | @example 136 | def example12(): 137 | widgets = ['Balloon: ', AnimatedMarker(markers='.oO@* ')] 138 | pbar = ProgressBar(widgets=widgets) 139 | for i in pbar((i for i in range(24))): 140 | time.sleep(0.3) 141 | 142 | @example 143 | def example13(): 144 | # You may need python 3.x to see this correctly 145 | try: 146 | widgets = ['Arrows: ', AnimatedMarker(markers='←↖↑↗→↘↓↙')] 147 | pbar = ProgressBar(widgets=widgets) 148 | for i in pbar((i for i in range(24))): 149 | time.sleep(0.3) 150 | except UnicodeError: sys.stdout.write('Unicode error: skipping example') 151 | 152 | @example 153 | def example14(): 154 | # You may need python 3.x to see this correctly 155 | try: 156 | widgets = ['Arrows: ', AnimatedMarker(markers='◢◣◤◥')] 157 | pbar = ProgressBar(widgets=widgets) 158 | for i in pbar((i for i in range(24))): 159 | time.sleep(0.3) 160 | except UnicodeError: sys.stdout.write('Unicode error: skipping example') 161 | 162 | @example 163 | def example15(): 164 | # You may need python 3.x to see this correctly 165 | try: 166 | widgets = ['Wheels: ', AnimatedMarker(markers='◐◓◑◒')] 167 | pbar = ProgressBar(widgets=widgets) 168 | for i in pbar((i for i in range(24))): 169 | time.sleep(0.3) 170 | except UnicodeError: sys.stdout.write('Unicode error: skipping example') 171 | 172 | @example 173 | def example16(): 174 | widgets = [FormatLabel('Bouncer: value %(value)d - '), BouncingBar()] 175 | pbar = ProgressBar(widgets=widgets) 176 | for i in pbar((i for i in range(180))): 177 | time.sleep(0.05) 178 | 179 | @example 180 | def example17(): 181 | widgets = [FormatLabel('Animated Bouncer: value %(value)d - '), 182 | BouncingBar(marker=RotatingMarker())] 183 | 184 | pbar = ProgressBar(widgets=widgets) 185 | for i in pbar((i for i in range(180))): 186 | time.sleep(0.05) 187 | 188 | @example 189 | def example18(): 190 | widgets = [Percentage(), 191 | ' ', Bar(), 192 | ' ', ETA(), 193 | ' ', AdaptiveETA()] 194 | pbar = ProgressBar(widgets=widgets, maxval=500) 195 | pbar.start() 196 | for i in range(500): 197 | time.sleep(0.01 + (i < 100) * 0.01 + (i > 400) * 0.9) 198 | pbar.update(i + 1) 199 | pbar.finish() 200 | 201 | @example 202 | def example19(): 203 | pbar = ProgressBar() 204 | for i in pbar([]): 205 | pass 206 | pbar.finish() 207 | 208 | @example 209 | def example20(): 210 | """Widgets that behave differently when length is unknown""" 211 | widgets = ['[When length is unknown at first]', 212 | ' Progress: ', SimpleProgress(), 213 | ', Percent: ', Percentage(), 214 | ' ', ETA(), 215 | ' ', AdaptiveETA()] 216 | pbar = ProgressBar(widgets=widgets, maxval=UnknownLength) 217 | pbar.start() 218 | for i in range(20): 219 | time.sleep(0.5) 220 | if i == 10: 221 | pbar.maxval = 20 222 | pbar.update(i + 1) 223 | pbar.finish() 224 | 225 | if __name__ == '__main__': 226 | try: 227 | for example in examples: example() 228 | except KeyboardInterrupt: 229 | sys.stdout.write('\nQuitting examples.\n') 230 | -------------------------------------------------------------------------------- /progressbar/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # progressbar - Text progress bar 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 | """Text progress bar library for Python. 22 | 23 | A text progress bar is typically used to display the progress of a long 24 | running operation, providing a visual cue that processing is underway. 25 | 26 | The ProgressBar class manages the current progress, and the format of the line 27 | is given by a number of widgets. A widget is an object that may display 28 | differently depending on the state of the progress bar. There are three types 29 | of widgets: 30 | - a string, which always shows itself 31 | 32 | - a ProgressBarWidget, which may return a different value every time its 33 | update method is called 34 | 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. It will also 39 | automatically enable features like auto-resizing when the system supports it. 40 | """ 41 | 42 | __author__ = 'Nilton Volpato' 43 | __author_email__ = 'nilton.volpato@gmail.com' 44 | __date__ = '2011-05-14' 45 | __version__ = '2.5' 46 | 47 | from .compat import * 48 | from .widgets import * 49 | from .progressbar import * 50 | -------------------------------------------------------------------------------- /progressbar/compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # progressbar - Text progress bar library for Python. 4 | # Copyright (c) 2005 Nilton Volpato 5 | # 6 | # This library is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU Lesser General Public 8 | # License as published by the Free Software Foundation; either 9 | # version 2.1 of the License, or (at your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Lesser General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public 17 | # License along with this library; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | """Compatibility methods and classes for the progressbar module.""" 21 | 22 | 23 | # Python 3.x (and backports) use a modified iterator syntax 24 | # This will allow 2.x to behave with 3.x iterators 25 | try: 26 | next 27 | except NameError: 28 | def next(iter): 29 | try: 30 | # Try new style iterators 31 | return iter.__next__() 32 | except AttributeError: 33 | # Fallback in case of a "native" iterator 34 | return iter.next() 35 | 36 | 37 | # Python < 2.5 does not have "any" 38 | try: 39 | any 40 | except NameError: 41 | def any(iterator): 42 | for item in iterator: 43 | if item: return True 44 | return False 45 | -------------------------------------------------------------------------------- /progressbar/progressbar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # progressbar - Text progress bar library for Python. 4 | # Copyright (c) 2005 Nilton Volpato 5 | # 6 | # This library is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU Lesser General Public 8 | # License as published by the Free Software Foundation; either 9 | # version 2.1 of the License, or (at your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Lesser General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public 17 | # License along with this library; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | """Main ProgressBar class.""" 21 | 22 | from __future__ import division 23 | 24 | import math 25 | import os 26 | import signal 27 | import sys 28 | import time 29 | 30 | try: 31 | from fcntl import ioctl 32 | from array import array 33 | import termios 34 | except ImportError: 35 | pass 36 | 37 | from .compat import * # for: any, next 38 | from . import widgets 39 | 40 | 41 | class ProgressBar(object): 42 | """The ProgressBar class which updates and prints the bar. 43 | 44 | A common way of using it is like: 45 | >>> pbar = ProgressBar().start() 46 | >>> for i in range(100): 47 | ... # do something 48 | ... pbar.update(i+1) 49 | ... 50 | >>> pbar.finish() 51 | 52 | You can also use a ProgressBar as an iterator: 53 | >>> progress = ProgressBar() 54 | >>> for i in progress(some_iterable): 55 | ... # do something 56 | ... 57 | 58 | Since the progress bar is incredibly customizable you can specify 59 | different widgets of any type in any order. You can even write your own 60 | widgets! However, since there are already a good number of widgets you 61 | should probably play around with them before moving on to create your own 62 | widgets. 63 | 64 | The term_width parameter represents the current terminal width. If the 65 | parameter is set to an integer then the progress bar will use that, 66 | otherwise it will attempt to determine the terminal width falling back to 67 | 80 columns if the width cannot be determined. 68 | 69 | When implementing a widget's update method you are passed a reference to 70 | the current progress bar. As a result, you have access to the 71 | ProgressBar's methods and attributes. Although there is nothing preventing 72 | you from changing the ProgressBar you should treat it as read only. 73 | 74 | Useful methods and attributes include (Public API): 75 | - currval: current progress (0 <= currval <= maxval) 76 | - maxval: maximum (and final) value 77 | - finished: True if the bar has finished (reached 100%) 78 | - start_time: the time when start() method of ProgressBar was called 79 | - seconds_elapsed: seconds elapsed since start_time and last call to 80 | update 81 | - percentage(): progress in percent [0..100] 82 | """ 83 | 84 | __slots__ = ('currval', 'fd', 'finished', 'last_update_time', 85 | 'left_justify', 'maxval', 'next_update', 'num_intervals', 86 | 'poll', 'seconds_elapsed', 'signal_set', 'start_time', 87 | 'term_width', 'update_interval', 'widgets', '_time_sensitive', 88 | '__iterable') 89 | 90 | _DEFAULT_MAXVAL = 100 91 | _DEFAULT_TERMSIZE = 80 92 | _DEFAULT_WIDGETS = [widgets.Percentage(), ' ', widgets.Bar()] 93 | 94 | def __init__(self, maxval=None, widgets=None, term_width=None, poll=1, 95 | left_justify=True, fd=None): 96 | """Initializes a progress bar with sane defaults.""" 97 | 98 | # Don't share a reference with any other progress bars 99 | if widgets is None: 100 | widgets = list(self._DEFAULT_WIDGETS) 101 | 102 | self.maxval = maxval 103 | self.widgets = widgets 104 | self.fd = fd if fd is not None else sys.stderr 105 | self.left_justify = left_justify 106 | 107 | self.signal_set = False 108 | if term_width is not None: 109 | self.term_width = term_width 110 | else: 111 | try: 112 | self._handle_resize() 113 | signal.signal(signal.SIGWINCH, self._handle_resize) 114 | self.signal_set = True 115 | except (SystemExit, KeyboardInterrupt): raise 116 | except: 117 | self.term_width = self._env_size() 118 | 119 | self.__iterable = None 120 | self._update_widgets() 121 | self.currval = 0 122 | self.finished = False 123 | self.last_update_time = None 124 | self.poll = poll 125 | self.seconds_elapsed = 0 126 | self.start_time = None 127 | self.update_interval = 1 128 | self.next_update = 0 129 | 130 | 131 | def __call__(self, iterable): 132 | """Use a ProgressBar to iterate through an iterable.""" 133 | 134 | try: 135 | self.maxval = len(iterable) 136 | except: 137 | if self.maxval is None: 138 | self.maxval = widgets.UnknownLength 139 | 140 | self.__iterable = iter(iterable) 141 | return self 142 | 143 | 144 | def __iter__(self): 145 | return self 146 | 147 | 148 | def __next__(self): 149 | try: 150 | value = next(self.__iterable) 151 | if self.start_time is None: 152 | self.start() 153 | else: 154 | self.update(self.currval + 1) 155 | return value 156 | except StopIteration: 157 | if self.start_time is None: 158 | self.start() 159 | self.finish() 160 | raise 161 | 162 | 163 | # Create an alias so that Python 2.x won't complain about not being 164 | # an iterator. 165 | next = __next__ 166 | 167 | 168 | def _env_size(self): 169 | """Tries to find the term_width from the environment.""" 170 | 171 | return int(os.environ.get('COLUMNS', self._DEFAULT_TERMSIZE)) - 1 172 | 173 | 174 | def _handle_resize(self, signum=None, frame=None): 175 | """Tries to catch resize signals sent from the terminal.""" 176 | 177 | h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] 178 | self.term_width = w 179 | 180 | 181 | def percentage(self): 182 | """Returns the progress as a percentage.""" 183 | if self.maxval is widgets.UnknownLength: 184 | return float("NaN") 185 | if self.currval >= self.maxval: 186 | return 100.0 187 | return (self.currval * 100.0 / self.maxval) if self.maxval else 100.00 188 | 189 | percent = property(percentage) 190 | 191 | 192 | def _format_widgets(self): 193 | result = [] 194 | expanding = [] 195 | width = self.term_width 196 | 197 | for index, widget in enumerate(self.widgets): 198 | if isinstance(widget, widgets.WidgetHFill): 199 | result.append(widget) 200 | expanding.insert(0, index) 201 | else: 202 | widget = widgets.format_updatable(widget, self) 203 | result.append(widget) 204 | width -= len(widget) 205 | 206 | count = len(expanding) 207 | while count: 208 | portion = max(int(math.ceil(width * 1. / count)), 0) 209 | index = expanding.pop() 210 | count -= 1 211 | 212 | widget = result[index].update(self, portion) 213 | width -= len(widget) 214 | result[index] = widget 215 | 216 | return result 217 | 218 | 219 | def _format_line(self): 220 | """Joins the widgets and justifies the line.""" 221 | 222 | widgets = ''.join(self._format_widgets()) 223 | 224 | if self.left_justify: return widgets.ljust(self.term_width) 225 | else: return widgets.rjust(self.term_width) 226 | 227 | 228 | def _need_update(self): 229 | """Returns whether the ProgressBar should redraw the line.""" 230 | if self.currval >= self.next_update or self.finished: return True 231 | 232 | delta = time.time() - self.last_update_time 233 | return self._time_sensitive and delta > self.poll 234 | 235 | 236 | def _update_widgets(self): 237 | """Checks all widgets for the time sensitive bit.""" 238 | 239 | self._time_sensitive = any(getattr(w, 'TIME_SENSITIVE', False) 240 | for w in self.widgets) 241 | 242 | 243 | def update(self, value=None): 244 | """Updates the ProgressBar to a new value.""" 245 | 246 | if value is not None and value is not widgets.UnknownLength: 247 | if (self.maxval is not widgets.UnknownLength 248 | and not 0 <= value <= self.maxval): 249 | 250 | raise ValueError('Value out of range') 251 | 252 | self.currval = value 253 | 254 | 255 | if not self._need_update(): return 256 | if self.start_time is None: 257 | raise RuntimeError('You must call "start" before calling "update"') 258 | 259 | now = time.time() 260 | self.seconds_elapsed = now - self.start_time 261 | self.next_update = self.currval + self.update_interval 262 | self.fd.write(self._format_line() + '\r') 263 | self.fd.flush() 264 | self.last_update_time = now 265 | 266 | 267 | def start(self): 268 | """Starts measuring time, and prints the bar at 0%. 269 | 270 | It returns self so you can use it like this: 271 | >>> pbar = ProgressBar().start() 272 | >>> for i in range(100): 273 | ... # do something 274 | ... pbar.update(i+1) 275 | ... 276 | >>> pbar.finish() 277 | """ 278 | 279 | if self.maxval is None: 280 | self.maxval = self._DEFAULT_MAXVAL 281 | 282 | self.num_intervals = max(100, self.term_width) 283 | self.next_update = 0 284 | 285 | if self.maxval is not widgets.UnknownLength: 286 | if self.maxval < 0: raise ValueError('Value out of range') 287 | self.update_interval = self.maxval / self.num_intervals 288 | 289 | 290 | self.start_time = self.last_update_time = time.time() 291 | self.update(0) 292 | 293 | return self 294 | 295 | 296 | def finish(self): 297 | """Puts the ProgressBar bar in the finished state.""" 298 | 299 | if self.finished: 300 | return 301 | self.finished = True 302 | self.update(self.maxval) 303 | self.fd.write('\n') 304 | if self.signal_set: 305 | signal.signal(signal.SIGWINCH, signal.SIG_DFL) 306 | -------------------------------------------------------------------------------- /progressbar/widgets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # progressbar - Text progress bar library for Python. 4 | # Copyright (c) 2005 Nilton Volpato 5 | # 6 | # This library is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU Lesser General Public 8 | # License as published by the Free Software Foundation; either 9 | # version 2.1 of the License, or (at your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Lesser General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public 17 | # License along with this library; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | """Default ProgressBar widgets.""" 21 | 22 | from __future__ import division 23 | 24 | import datetime 25 | import math 26 | 27 | try: 28 | from abc import ABCMeta, abstractmethod 29 | except ImportError: 30 | AbstractWidget = object 31 | abstractmethod = lambda fn: fn 32 | else: 33 | AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) 34 | 35 | class UnknownLength: 36 | pass 37 | 38 | def format_updatable(updatable, pbar): 39 | if hasattr(updatable, 'update'): return updatable.update(pbar) 40 | else: return updatable 41 | 42 | 43 | class Widget(AbstractWidget): 44 | """The base class for all widgets. 45 | 46 | The ProgressBar will call the widget's update value when the widget should 47 | be updated. The widget's size may change between calls, but the widget may 48 | display incorrectly if the size changes drastically and repeatedly. 49 | 50 | The boolean TIME_SENSITIVE informs the ProgressBar that it should be 51 | updated more often because it is time sensitive. 52 | """ 53 | 54 | TIME_SENSITIVE = False 55 | __slots__ = () 56 | 57 | @abstractmethod 58 | def update(self, pbar): 59 | """Updates the widget. 60 | 61 | pbar - a reference to the calling ProgressBar 62 | """ 63 | 64 | 65 | class WidgetHFill(Widget): 66 | """The base class for all variable width widgets. 67 | 68 | This widget is much like the \\hfill command in TeX, it will expand to 69 | fill the line. You can use more than one in the same line, and they will 70 | all have the same width, and together will fill the line. 71 | """ 72 | 73 | @abstractmethod 74 | def update(self, pbar, width): 75 | """Updates the widget providing the total width the widget must fill. 76 | 77 | pbar - a reference to the calling ProgressBar 78 | width - The total width the widget must fill 79 | """ 80 | 81 | 82 | class Timer(Widget): 83 | """Widget which displays the elapsed seconds.""" 84 | 85 | __slots__ = ('format_string',) 86 | TIME_SENSITIVE = True 87 | 88 | def __init__(self, format='Elapsed Time: %s'): 89 | self.format_string = format 90 | 91 | @staticmethod 92 | def format_time(seconds): 93 | """Formats time as the string "HH:MM:SS".""" 94 | 95 | return str(datetime.timedelta(seconds=int(seconds))) 96 | 97 | 98 | def update(self, pbar): 99 | """Updates the widget to show the elapsed time.""" 100 | 101 | return self.format_string % self.format_time(pbar.seconds_elapsed) 102 | 103 | 104 | class ETA(Timer): 105 | """Widget which attempts to estimate the time of arrival.""" 106 | 107 | TIME_SENSITIVE = True 108 | 109 | def update(self, pbar): 110 | """Updates the widget to show the ETA or total time when finished.""" 111 | 112 | if pbar.maxval is UnknownLength or 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 AdaptiveETA(Timer): 123 | """Widget which attempts to estimate the time of arrival. 124 | 125 | Uses a weighted average of two estimates: 126 | 1) ETA based on the total progress and time elapsed so far 127 | 2) ETA based on the progress as per the last 10 update reports 128 | 129 | The weight depends on the current progress so that to begin with the 130 | total progress is used and at the end only the most recent progress is 131 | used. 132 | """ 133 | 134 | TIME_SENSITIVE = True 135 | NUM_SAMPLES = 10 136 | 137 | def _update_samples(self, currval, elapsed): 138 | sample = (currval, elapsed) 139 | if not hasattr(self, 'samples'): 140 | self.samples = [sample] * (self.NUM_SAMPLES + 1) 141 | else: 142 | self.samples.append(sample) 143 | return self.samples.pop(0) 144 | 145 | def _eta(self, maxval, currval, elapsed): 146 | return elapsed * maxval / float(currval) - elapsed 147 | 148 | def update(self, pbar): 149 | """Updates the widget to show the ETA or total time when finished.""" 150 | if pbar.maxval is UnknownLength or pbar.currval == 0: 151 | return 'ETA: --:--:--' 152 | elif pbar.finished: 153 | return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 154 | else: 155 | elapsed = pbar.seconds_elapsed 156 | currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) 157 | eta = self._eta(pbar.maxval, pbar.currval, elapsed) 158 | if pbar.currval > currval1: 159 | etasamp = self._eta(pbar.maxval - currval1, 160 | pbar.currval - currval1, 161 | elapsed - elapsed1) 162 | weight = (pbar.currval / float(pbar.maxval)) ** 0.5 163 | eta = (1 - weight) * eta + weight * etasamp 164 | return 'ETA: %s' % self.format_time(eta) 165 | 166 | 167 | class FileTransferSpeed(Widget): 168 | """Widget for showing the transfer speed (useful for file transfers).""" 169 | 170 | FMT = '%6.2f %s%s/s' 171 | PREFIXES = ' kMGTPEZY' 172 | __slots__ = ('unit',) 173 | 174 | def __init__(self, unit='B'): 175 | self.unit = unit 176 | 177 | def update(self, pbar): 178 | """Updates the widget with the current SI prefixed speed.""" 179 | 180 | if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 181 | scaled = power = 0 182 | else: 183 | speed = pbar.currval / pbar.seconds_elapsed 184 | power = int(math.log(speed, 1000)) 185 | scaled = speed / 1000.**power 186 | 187 | return self.FMT % (scaled, self.PREFIXES[power], self.unit) 188 | 189 | 190 | class AnimatedMarker(Widget): 191 | """An animated marker for the progress bar which defaults to appear as if 192 | it were rotating. 193 | """ 194 | 195 | __slots__ = ('markers', 'curmark') 196 | 197 | def __init__(self, markers='|/-\\'): 198 | self.markers = markers 199 | self.curmark = -1 200 | 201 | def update(self, pbar): 202 | """Updates the widget to show the next marker or the first marker when 203 | finished""" 204 | 205 | if pbar.finished: return self.markers[0] 206 | 207 | self.curmark = (self.curmark + 1) % len(self.markers) 208 | return self.markers[self.curmark] 209 | 210 | # Alias for backwards compatibility 211 | RotatingMarker = AnimatedMarker 212 | 213 | 214 | class Counter(Widget): 215 | """Displays the current count.""" 216 | 217 | __slots__ = ('format_string',) 218 | 219 | def __init__(self, format='%d'): 220 | self.format_string = format 221 | 222 | def update(self, pbar): 223 | return self.format_string % pbar.currval 224 | 225 | 226 | class Percentage(Widget): 227 | """Displays the current percentage as a number with a percent sign.""" 228 | 229 | def update(self, pbar): 230 | return '%3.0f%%' % pbar.percentage() 231 | 232 | 233 | class FormatLabel(Timer): 234 | """Displays a formatted label.""" 235 | 236 | mapping = { 237 | 'elapsed': ('seconds_elapsed', Timer.format_time), 238 | 'finished': ('finished', None), 239 | 'last_update': ('last_update_time', None), 240 | 'max': ('maxval', None), 241 | 'seconds': ('seconds_elapsed', None), 242 | 'start': ('start_time', None), 243 | 'value': ('currval', None) 244 | } 245 | 246 | __slots__ = ('format_string',) 247 | def __init__(self, format): 248 | self.format_string = format 249 | 250 | def update(self, pbar): 251 | context = {} 252 | for name, (key, transform) in self.mapping.items(): 253 | try: 254 | value = getattr(pbar, key) 255 | 256 | if transform is None: 257 | context[name] = value 258 | else: 259 | context[name] = transform(value) 260 | except: pass 261 | 262 | return self.format_string % context 263 | 264 | 265 | class SimpleProgress(Widget): 266 | """Returns progress as a count of the total (e.g.: "5 of 47").""" 267 | 268 | __slots__ = ('sep',) 269 | 270 | def __init__(self, sep=' of '): 271 | self.sep = sep 272 | 273 | def update(self, pbar): 274 | if pbar.maxval is UnknownLength: 275 | return '%d%s?' % (pbar.currval, self.sep) 276 | return '%d%s%s' % (pbar.currval, self.sep, pbar.maxval) 277 | 278 | 279 | class Bar(WidgetHFill): 280 | """A progress bar which stretches to fill the line.""" 281 | 282 | __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') 283 | 284 | def __init__(self, marker='#', left='|', right='|', fill=' ', 285 | fill_left=True): 286 | """Creates a customizable progress bar. 287 | 288 | marker - string or updatable object to use as a marker 289 | left - string or updatable object to use as a left border 290 | right - string or updatable object to use as a right border 291 | fill - character to use for the empty part of the progress bar 292 | fill_left - whether to fill from the left or the right 293 | """ 294 | self.marker = marker 295 | self.left = left 296 | self.right = right 297 | self.fill = fill 298 | self.fill_left = fill_left 299 | 300 | 301 | def update(self, pbar, width): 302 | """Updates the progress bar and its subcomponents.""" 303 | 304 | left, marked, right = (format_updatable(i, pbar) for i in 305 | (self.left, self.marker, self.right)) 306 | 307 | width -= len(left) + len(right) 308 | # Marked must *always* have length of 1 309 | if pbar.maxval is not UnknownLength and pbar.maxval: 310 | marked *= int(pbar.currval / pbar.maxval * width) 311 | else: 312 | marked = '' 313 | 314 | if self.fill_left: 315 | return '%s%s%s' % (left, marked.ljust(width, self.fill), right) 316 | else: 317 | return '%s%s%s' % (left, marked.rjust(width, self.fill), right) 318 | 319 | 320 | class ReverseBar(Bar): 321 | """A bar which has a marker which bounces from side to side.""" 322 | 323 | def __init__(self, marker='#', left='|', right='|', fill=' ', 324 | fill_left=False): 325 | """Creates a customizable progress bar. 326 | 327 | marker - string or updatable object to use as a marker 328 | left - string or updatable object to use as a left border 329 | right - string or updatable object to use as a right border 330 | fill - character to use for the empty part of the progress bar 331 | fill_left - whether to fill from the left or the right 332 | """ 333 | self.marker = marker 334 | self.left = left 335 | self.right = right 336 | self.fill = fill 337 | self.fill_left = fill_left 338 | 339 | 340 | class BouncingBar(Bar): 341 | def update(self, pbar, width): 342 | """Updates the progress bar and its subcomponents.""" 343 | 344 | left, marker, right = (format_updatable(i, pbar) for i in 345 | (self.left, self.marker, self.right)) 346 | 347 | width -= len(left) + len(right) 348 | 349 | if pbar.finished: return '%s%s%s' % (left, width * marker, right) 350 | 351 | position = int(pbar.currval % (width * 2 - 1)) 352 | if position > width: position = width * 2 - position 353 | lpad = self.fill * (position - 1) 354 | rpad = self.fill * (width - len(marker) - len(lpad)) 355 | 356 | # Swap if we want to bounce the other way 357 | if not self.fill_left: rpad, lpad = lpad, rpad 358 | 359 | return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) 360 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | from setuptools import setup, find_packages 5 | import progressbar 6 | 7 | # TODO: I don't believe this should be in here. This should be done on package 8 | # creation only 9 | try: 10 | readme = 'README.txt' 11 | info = 'progressbar/__init__.py' 12 | 13 | if (not os.path.exists(readme) or 14 | os.stat(info).st_mtime > os.stat(readme).st_mtime): 15 | 16 | open(readme,'w').write(progressbar.__doc__) 17 | except: pass 18 | 19 | setup( 20 | name='progressbar', 21 | version=progressbar.__version__, 22 | packages=find_packages(), 23 | 24 | description=progressbar.__doc__.split('\n')[0], 25 | long_description=progressbar.__doc__, 26 | 27 | author=progressbar.__author__, 28 | maintainer=progressbar.__author__, 29 | author_email=progressbar.__author_email__, 30 | maintainer_email=progressbar.__author_email__, 31 | 32 | url='http://code.google.com/p/python-progressbar', 33 | license='LICENSE.txt', 34 | classifiers=[ 35 | 'Development Status :: 5 - Production/Stable', 36 | 'Environment :: Console', 37 | 'Intended Audience :: Developers', 38 | 'Intended Audience :: Information Technology', 39 | 'Intended Audience :: System Administrators', 40 | 'License :: OSI Approved :: ' 41 | 'GNU Library or Lesser General Public License (LGPL)', 42 | 'License :: OSI Approved :: BSD License', 43 | 'Operating System :: OS Independent', 44 | 'Programming Language :: Python', 45 | 'Programming Language :: Python :: 2', 46 | 'Programming Language :: Python :: 2.4', 47 | 'Programming Language :: Python :: 2.5', 48 | 'Programming Language :: Python :: 2.6', 49 | 'Programming Language :: Python :: 2.7', 50 | 'Programming Language :: Python :: 3', 51 | 'Topic :: Software Development :: Libraries', 52 | 'Topic :: Software Development :: User Interfaces', 53 | 'Topic :: Terminals' 54 | ], 55 | ) 56 | --------------------------------------------------------------------------------