├── .gitignore ├── AUTHORS ├── LICENSE ├── README.markdown ├── demo.py ├── progressbar.py ├── setup.py └── tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Maintainer: 2 | 3 | * Anler Hp 4 | 5 | Contributors: 6 | 7 | * Anler Hp (ikame) 8 | * Mark Mossberg (markmossberg) 9 | * Hubert Grzywacz (HGrzywacz) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2013 Anler Hp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Python Progressbar 2 | 3 | A progressbar utility for command line programs. 4 | Progressbar is a Python module which contains two class so far: 5 | 6 | 1. ProgressBar 7 | 2. AnimatedProgressBar 8 | 9 | `ProgressBar` class implements all the base stuff that makes progress bars work 10 | as they do and allows some basic customization. `AnimatedProgressBar` class 11 | extends `ProgressBar` to allow you to use it straightforward in your scripts. 12 | By default the `AnimatedProgressBar` sends the output to sys.stdout but you 13 | can change this passing the `stdout` keyword parameter which must be a 14 | file-like object. 15 | 16 | ## Usage 17 | 18 | Here is some basic usage with the default options: 19 | 20 | >>> from progressbar import ProgressBar 21 | >>> p = ProgressBar() 22 | >>> print p 23 | [>............] 0% 24 | >>> p + 1 25 | >>> print p 26 | [=>...........] 10% 27 | >>> p + 9 28 | >>> print p 29 | [============>] 0% 30 | 31 | And here another example with different options: 32 | 33 | >>> from progressbar import ProgressBar 34 | >>> custom_options = { 35 | ... 'end': 100, 36 | ... 'width': 20, 37 | ... 'fill': '#', 38 | ... 'format': '%(progress)s%% [%(fill)s%(blank)s]' 39 | ... } 40 | >>> p = ProgressBar(**custom_options) 41 | >>> print p 42 | 0% [....................] 43 | >>> p + 5 44 | >>> print p 45 | 5% [#...................] 46 | >>> p + 95 47 | >>> print p 48 | 100% [####################] 49 | 50 | Finally, a real example where I had to use it: 51 | 52 | >>> import ftplib 53 | >>> import progressbar 54 | >>> 55 | >>> ftp = ftplib.FTP('ftp.myserver.com', 'user', 'passwd') 56 | >>> filesize = ftp.size('path/to/remotefile.zip') 57 | >>> progress = progressbar.AnimatedProgressBar(end=filesize, width=50) 58 | >>> 59 | >>> with open('localfile.zip', 'w') as f: 60 | >>> def callback(chunk): 61 | >>> f.write(chunk) 62 | >>> progress + len(chunk) 63 | >>> 64 | >>> # Visual feedback of the progress! 65 | >>> progress.show_progress() 66 | >>> 67 | >>> ftp.retrbinary('RETR path/to/remotefile.zip', callback) 68 | 69 | ## Contribution 70 | 71 | If you find it useful, fork the repository, improve it and request a pull. 72 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | import progressbar 2 | 3 | 4 | FILESIZE = 2045824 5 | CHUNK = 1 6 | 7 | 8 | def main(): 9 | progress = progressbar.AnimatedProgressBar(end=FILESIZE, width=50) 10 | 11 | for i in range(0, FILESIZE, CHUNK): 12 | progress + CHUNK 13 | progress.show_progress() 14 | 15 | 16 | if __name__ == "__main__": 17 | main() 18 | -------------------------------------------------------------------------------- /progressbar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | progressbar.py 4 | 5 | A Python module with a ProgressBar class which can be used to represent a 6 | task's progress in the form of a progress bar and it can be formated in a 7 | basic way. 8 | 9 | Here is some basic usage with the default options: 10 | 11 | >>> from progressbar import ProgressBar 12 | >>> p = ProgressBar() 13 | >>> print p 14 | [>............] 0% 15 | >>> p + 1 16 | >>> print p 17 | [=>...........] 10% 18 | >>> p + 9 19 | >>> print p 20 | [============>] 0% 21 | 22 | And here another example with different options: 23 | 24 | >>> from progressbar import ProgressBar 25 | >>> custom_options = { 26 | ... 'end': 100, 27 | ... 'width': 20, 28 | ... 'fill': '#', 29 | ... 'format': '%(progress)s%% [%(fill)s%(blank)s]' 30 | ... } 31 | >>> p = ProgressBar(**custom_options) 32 | >>> print p 33 | 0% [....................] 34 | >>> p + 5 35 | >>> print p 36 | 5% [#...................] 37 | >>> p + 9 38 | >>> print p 39 | 100% [####################] 40 | """ 41 | import sys 42 | import time 43 | 44 | class ProgressBar(object): 45 | """ProgressBar class holds the options of the progress bar. 46 | The options are: 47 | start State from which start the progress. For example, if start is 48 | 5 and the end is 10, the progress of this state is 50% 49 | end State in which the progress has terminated. 50 | width -- 51 | fill String to use for "filled" used to represent the progress 52 | blank String to use for "filled" used to represent remaining space. 53 | format Format 54 | incremental 55 | """ 56 | def __init__(self, start=0, end=10, width=12, fill='=', blank='.', format='[%(fill)s>%(blank)s] %(progress)s%%', incremental=True): 57 | super(ProgressBar, self).__init__() 58 | 59 | self.start = start 60 | self.end = end 61 | self.width = width 62 | self.fill = fill 63 | self.blank = blank 64 | self.format = format 65 | self.incremental = incremental 66 | self.reset() 67 | 68 | def __add__(self, increment): 69 | if self.end > self.progress + increment: 70 | self.progress += increment 71 | else: 72 | self.progress = float(self.end) 73 | return self 74 | 75 | def __sub__(self, decrement): 76 | if self.start < self.progress - decrement: 77 | self.progress -= decrement 78 | else: 79 | self.progress = float(self.start) 80 | return self 81 | 82 | def __str__(self): 83 | cur_width = int(self.progress / self.end * self.width) 84 | fill = cur_width * self.fill 85 | blank = (self.width - cur_width) * self.blank 86 | percentage = int(self.progress / self.end * 100) 87 | return self.format % {'fill': fill, 'blank': blank, 'progress': percentage} 88 | 89 | __repr__ = __str__ 90 | 91 | def reset(self): 92 | """Resets the current progress to the start point""" 93 | self.progress = float(self.start) 94 | return self 95 | 96 | 97 | class AnimatedProgressBar(ProgressBar): 98 | """Extends ProgressBar to allow you to use it straighforward on a script. 99 | Accepts an extra keyword argument named `stdout` (by default use sys.stdout) 100 | and may be any file-object to which send the progress status. 101 | """ 102 | def __init__(self, *args, **kwargs): 103 | super(AnimatedProgressBar, self).__init__(*args, **kwargs) 104 | self.stdout = kwargs.get('stdout', sys.stdout) 105 | 106 | def show_progress(self): 107 | if hasattr(self.stdout, 'isatty') and self.stdout.isatty(): 108 | self.stdout.write('\r') 109 | else: 110 | self.stdout.write('\n') 111 | self.stdout.write(str(self)) 112 | self.stdout.flush() 113 | 114 | 115 | if __name__ == '__main__': 116 | p = AnimatedProgressBar(end=100, width=80) 117 | 118 | while True: 119 | p + 5 120 | p.show_progress() 121 | time.sleep(0.1) 122 | if p.progress == 100: 123 | break 124 | print #new line 125 | 126 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='progressbar', 5 | version='0.1', 6 | description='Utility module to represent progress in the form of a progress bar.', 7 | url='http://github.com/ikame/progressbar', 8 | author='Anler Peral', 9 | author_email='anler86@gmail.com', 10 | license='MIT', 11 | packages=find_packages() 12 | ) 13 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from progressbar import ProgressBar 3 | 4 | 5 | class DefaultsTestCase(unittest.TestCase): 6 | """ 7 | ProgressBar defaults: 8 | start = 0 9 | end = 10 10 | width = 12 11 | fill = '=' 12 | blank = '.' 13 | format = '[%(fill)s>%(blank)s] %(progress)s%%' 14 | incremental = True 15 | """ 16 | def setUp(self): 17 | self.p = ProgressBar() 18 | 19 | def tearDown(self): 20 | del(self.p) 21 | 22 | def test_initialization(self): 23 | """ 24 | >>> p = ProgressBar() 25 | >>> p 26 | [>............] 0% 27 | """ 28 | self.assertEqual(str(self.p), '[>............] 0%') 29 | 30 | def test_increment(self): 31 | """ 32 | >>> p = ProgressBar() 33 | >>> p + 1 34 | [=>...........] 10% 35 | """ 36 | self.p + 1 37 | self.assertEqual(str(self.p), '[=>...........] 10%') 38 | 39 | def test_reset(self): 40 | """ 41 | >>> p = ProgressBar() 42 | >>> p += 8 43 | >>> p.reset() 44 | [>............] 0% 45 | """ 46 | self.p += 8 47 | self.p.reset() 48 | self.assertEqual(str(self.p), '[>............] 0%') 49 | 50 | def test_full_progress(self): 51 | """ 52 | >>> p = ProgressBar() 53 | >>> p + 10 54 | [============>] 100% 55 | """ 56 | self.p + 10 57 | self.assertEqual(str(self.p), '[============>] 100%') 58 | self.p + 10 59 | self.assertEqual(str(self.p), '[============>] 100%') 60 | 61 | 62 | class CustomizedTestCase(unittest.TestCase): 63 | """ 64 | ProgressBar custom: 65 | start = 0 66 | end = 100 67 | width = 20 68 | fill = '#' 69 | blank = '.' 70 | format = '%(progress)s%% [%(fill)s%(blank)s]' 71 | incremental = True 72 | """ 73 | custom = { 74 | 'end': 100, 75 | 'width': 20, 76 | 'fill': '#', 77 | 'format': '%(progress)s%% [%(fill)s%(blank)s]' 78 | } 79 | def setUp(self): 80 | self.p = ProgressBar(**self.custom) 81 | 82 | def tearDown(self): 83 | del(self.p) 84 | 85 | def test_initialization(self): 86 | """ 87 | >>> p = ProgressBar(custom) 88 | >>> p 89 | 0% [....................] 90 | """ 91 | self.assertEqual(str(self.p), '0% [....................]') 92 | 93 | def test_increment(self): 94 | """ 95 | >>> p = ProgressBar(custom) 96 | >>> p + 1 97 | 1% [....................] 98 | """ 99 | self.p + 1 100 | self.assertEqual(str(self.p), '1% [....................]') 101 | self.p + 4 102 | self.assertEqual(str(self.p), '5% [#...................]') 103 | 104 | def test_reset(self): 105 | """ 106 | >>> p = ProgressBar(custom) 107 | >>> p += 8 108 | >>> p.reset() 109 | 0% [....................] 110 | """ 111 | self.p += 8 112 | self.p.reset() 113 | self.assertEqual(str(self.p), '0% [....................]') 114 | 115 | def test_full_progress(self): 116 | """ 117 | >>> p = ProgressBar() 118 | >>> p + 10 119 | 100% [####################] 120 | """ 121 | self.p + 100 122 | self.assertEqual(str(self.p), '100% [####################]') 123 | self.p + 100 124 | self.assertEqual(str(self.p), '100% [####################]') 125 | 126 | 127 | if __name__ == '__main__': 128 | unittest.main() 129 | --------------------------------------------------------------------------------