forked from seveas/hacks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprogressbar.py
119 lines (110 loc) · 4.39 KB
/
progressbar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# Abuse the power of unicode to give smooth progressbars in the terminal
#
# Unicode defines the codepoints 0x2588 up to 0x258F as SOLID BLOCK, LEFT SEVEN
# EIGHTS BLOCK etc.. This can be used for almost pixel-by-pixel painting of a
# progressbar. The Progressbar class does that for you and can also guess what
# width your progressbar should be.
#
# Usage:
# Progressbar(target=100, start=0, reserve=20, columns=None)
# - target is the final numerical goal of the progressbar
# - start is the progress set at the beginning
# - reserve is the amount of space to reserve for text to the right of the
# progressbar for extra information
# - columns is the total width of progressbar and text. This can usually be
# autodetected, so you can omit this parameter
#
# Progressbars have one function:
#
# Progressbar.set_progress(progress, text="%(progress)s/%(target)s", args=None):
#
# - progress is the numerical progress towards the target.
# - text is the status text that goes to the right of the bar
# - args is a dict of variables that the text needs. The variables progress and
# target are filled in automatically
#
# When the progress is equal to, or higher than the target. The progressbar is
# finalized. A final newline is printed and further calls to set_progress are
# ignored. Until the progressbar is complete, progress can go backwards if you
# want to. See the bottom of this file for a demonstration.
#
# The following attributes can be read, but should not be changed directly:
#
# - Progressbar.columns -- What the progressbar thinks the wodth of your terminal is
# - Progressbar.target -- The target of the progressbar
# - Progressbar.progress -- The progress towards that target
# - Progressbar.complete -- Whether the target has been reached
from __future__ import division
import fcntl
import termios
import os
import struct
import sys
class Progressbar(object):
chars = [x.encode('utf-8') for x in u' \u258f\u258e\u258d\u258c\u258b\u258a\u2589\u2588']
def __init__(self, target=100, start=0, reserve=20, columns=None):
self.reserve = reserve
if columns:
self.columns = columns
elif 'COLUMNS' in os.environ:
self.columns = os.environ['COLUMNS']
else:
try:
_, self.columns = struct.unpack('hh', fcntl.ioctl(sys.stdin, termios.TIOCGWINSZ, '1234'))
except:
self.columns = 80
self.columns -= reserve
if self.columns < 10:
raise ValueError("Screen too small for a progressbar")
self.target = target
self.complete = False
self.psl = 0
self.set_progress(start)
def set_progress(self, progress, text="%(progress)s/%(target)s", args=None):
if self.complete:
return
self.progress = progress
if progress >= self.target:
self.progress = self.target
self.complete = True
full = self.progress / self.target * self.columns
args_ = {'progress': self.progress, 'target': self.target}
if args:
args_.update(args)
bar = "\r%s%s%s%s%s" % ( self.chars[-1] * int(full),
('' if self.complete else self.chars[int((full-int(full)) * 8)]),
self.chars[0] * (self.columns - int(full) -1),
self.chars[1],
text % args_
)
psl = len(bar)
bar += ' ' * max(0,self.psl-len(bar))
bar += '\b' * max(0,self.psl-len(bar))
self.psl = psl
sys.stdout.write(bar)
if self.complete:
sys.stdout.write('\n')
sys.stdout.flush()
if __name__ == '__main__':
# Demonstration
import time
step = 0.1
target = 111
pb = Progressbar(target=target, reserve=30)
while not pb.complete:
pb.set_progress(pb.progress + step,
"%(progress)s/%(target)s (Demo 1)")
time.sleep(0.02)
pb = Progressbar(target=target, reserve=30)
while pb.progress < target * 0.8:
pb.set_progress(pb.progress + step,
"%(progress)s/%(target)s (Demo 2)")
time.sleep(0.02)
while pb.progress > target * 0.3:
pb.set_progress(pb.progress - step,
"%(progress)s/%(target)s (Demo 2)")
time.sleep(0.02)
while not pb.complete:
pb.set_progress(pb.progress + step,
"%(progress)s/%(target)s (Demo 2)")
time.sleep(0.02)