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

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

# -*- coding: utf-8 -*- 

# 

# progressbar - Text progress bar library for Python. 

# Copyright (c) 2005 Nilton Volpato 

# 

# This library is free software; you can redistribute it and/or 

# modify it under the terms of the GNU Lesser General Public 

# License as published by the Free Software Foundation; either 

# version 2.1 of the License, or (at your option) any later version. 

# 

# This library is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

# Lesser General Public License for more details. 

# 

# You should have received a copy of the GNU Lesser General Public 

# License along with this library; if not, write to the Free Software 

# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 

 

"""Default ProgressBar widgets.""" 

 

from __future__ import division 

 

import datetime 

import math 

 

try: 

from abc import ABCMeta, abstractmethod 

except ImportError: 

AbstractWidget = object 

abstractmethod = lambda fn: fn 

else: 

AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) 

 

class UnknownLength: 

pass 

 

def format_updatable(updatable, pbar): 

if hasattr(updatable, 'update'): return updatable.update(pbar) 

else: return updatable 

 

 

class Widget(AbstractWidget): 

"""The base class for all widgets. 

 

The ProgressBar will call the widget's update value when the widget should 

be updated. The widget's size may change between calls, but the widget may 

display incorrectly if the size changes drastically and repeatedly. 

 

The boolean TIME_SENSITIVE informs the ProgressBar that it should be 

updated more often because it is time sensitive. 

""" 

 

TIME_SENSITIVE = False 

__slots__ = () 

 

@abstractmethod 

def update(self, pbar): 

"""Updates the widget. 

 

pbar - a reference to the calling ProgressBar 

""" 

 

 

class WidgetHFill(Widget): 

"""The base class for all variable width widgets. 

 

This widget is much like the \\hfill command in TeX, it will expand to 

fill the line. You can use more than one in the same line, and they will 

all have the same width, and together will fill the line. 

""" 

 

@abstractmethod 

def update(self, pbar, width): 

"""Updates the widget providing the total width the widget must fill. 

 

pbar - a reference to the calling ProgressBar 

width - The total width the widget must fill 

""" 

 

 

class Timer(Widget): 

"""Widget which displays the elapsed seconds.""" 

 

__slots__ = ('format_string',) 

TIME_SENSITIVE = True 

 

def __init__(self, format='Elapsed Time: %s'): 

self.format_string = format 

 

@staticmethod 

def format_time(seconds): 

"""Formats time as the string "HH:MM:SS".""" 

 

return str(datetime.timedelta(seconds=int(seconds))) 

 

 

def update(self, pbar): 

"""Updates the widget to show the elapsed time.""" 

 

return self.format_string % self.format_time(pbar.seconds_elapsed) 

 

 

class ETA(Timer): 

"""Widget which attempts to estimate the time of arrival.""" 

 

TIME_SENSITIVE = True 

 

def update(self, pbar): 

"""Updates the widget to show the ETA or total time when finished.""" 

 

if pbar.maxval is UnknownLength or pbar.currval == 0: 

return 'ETA: --:--:--' 

elif pbar.finished: 

return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 

else: 

elapsed = pbar.seconds_elapsed 

eta = elapsed * pbar.maxval / pbar.currval - elapsed 

return 'ETA: %s' % self.format_time(eta) 

 

 

class AdaptiveETA(Timer): 

"""Widget which attempts to estimate the time of arrival. 

 

Uses a weighted average of two estimates: 

1) ETA based on the total progress and time elapsed so far 

2) ETA based on the progress as per the last 10 update reports 

 

The weight depends on the current progress so that to begin with the 

total progress is used and at the end only the most recent progress is 

used. 

""" 

 

TIME_SENSITIVE = True 

NUM_SAMPLES = 10 

 

def _update_samples(self, currval, elapsed): 

sample = (currval, elapsed) 

if not hasattr(self, 'samples'): 

self.samples = [sample] * (self.NUM_SAMPLES + 1) 

else: 

self.samples.append(sample) 

return self.samples.pop(0) 

 

def _eta(self, maxval, currval, elapsed): 

return elapsed * maxval / float(currval) - elapsed 

 

def update(self, pbar): 

"""Updates the widget to show the ETA or total time when finished.""" 

if pbar.maxval is UnknownLength or pbar.currval == 0: 

return 'ETA: --:--:--' 

elif pbar.finished: 

return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 

else: 

elapsed = pbar.seconds_elapsed 

currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) 

eta = self._eta(pbar.maxval, pbar.currval, elapsed) 

if pbar.currval > currval1: 

etasamp = self._eta(pbar.maxval - currval1, 

pbar.currval - currval1, 

elapsed - elapsed1) 

weight = (pbar.currval / float(pbar.maxval)) ** 0.5 

eta = (1 - weight) * eta + weight * etasamp 

return 'ETA: %s' % self.format_time(eta) 

 

 

class FileTransferSpeed(Widget): 

"""Widget for showing the transfer speed (useful for file transfers).""" 

 

FMT = '%6.2f %s%s/s' 

PREFIXES = ' kMGTPEZY' 

__slots__ = ('unit',) 

 

def __init__(self, unit='B'): 

self.unit = unit 

 

def update(self, pbar): 

"""Updates the widget with the current SI prefixed speed.""" 

 

if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 

scaled = power = 0 

else: 

speed = pbar.currval / pbar.seconds_elapsed 

power = int(math.log(speed, 1000)) 

scaled = speed / 1000.**power 

 

return self.FMT % (scaled, self.PREFIXES[power], self.unit) 

 

 

class AnimatedMarker(Widget): 

"""An animated marker for the progress bar which defaults to appear as if 

it were rotating. 

""" 

 

__slots__ = ('markers', 'curmark') 

 

def __init__(self, markers='|/-\\'): 

self.markers = markers 

self.curmark = -1 

 

def update(self, pbar): 

"""Updates the widget to show the next marker or the first marker when 

finished""" 

 

if pbar.finished: return self.markers[0] 

 

self.curmark = (self.curmark + 1) % len(self.markers) 

return self.markers[self.curmark] 

 

# Alias for backwards compatibility 

RotatingMarker = AnimatedMarker 

 

 

class Counter(Widget): 

"""Displays the current count.""" 

 

__slots__ = ('format_string',) 

 

def __init__(self, format='%d'): 

self.format_string = format 

 

def update(self, pbar): 

return self.format_string % pbar.currval 

 

 

class Percentage(Widget): 

"""Displays the current percentage as a number with a percent sign.""" 

 

def update(self, pbar): 

return '%3d%%' % pbar.percentage() 

 

 

class FormatLabel(Timer): 

"""Displays a formatted label.""" 

 

mapping = { 

'elapsed': ('seconds_elapsed', Timer.format_time), 

'finished': ('finished', None), 

'last_update': ('last_update_time', None), 

'max': ('maxval', None), 

'seconds': ('seconds_elapsed', None), 

'start': ('start_time', None), 

'value': ('currval', None) 

} 

 

__slots__ = ('format_string',) 

def __init__(self, format): 

self.format_string = format 

 

def update(self, pbar): 

context = {} 

for name, (key, transform) in self.mapping.items(): 

try: 

value = getattr(pbar, key) 

 

if transform is None: 

context[name] = value 

else: 

context[name] = transform(value) 

except: pass 

 

return self.format_string % context 

 

 

class SimpleProgress(Widget): 

"""Returns progress as a count of the total (e.g.: "5 of 47").""" 

 

__slots__ = ('sep',) 

 

def __init__(self, sep=' of '): 

self.sep = sep 

 

def update(self, pbar): 

return '%d%s%s' % (pbar.currval, self.sep, pbar.maxval) 

 

 

class Bar(WidgetHFill): 

"""A progress bar which stretches to fill the line.""" 

 

__slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') 

 

def __init__(self, marker='#', left='|', right='|', fill=' ', 

fill_left=True): 

"""Creates a customizable progress bar. 

 

marker - string or updatable object to use as a marker 

left - string or updatable object to use as a left border 

right - string or updatable object to use as a right border 

fill - character to use for the empty part of the progress bar 

fill_left - whether to fill from the left or the right 

""" 

self.marker = marker 

self.left = left 

self.right = right 

self.fill = fill 

self.fill_left = fill_left 

 

 

def update(self, pbar, width): 

"""Updates the progress bar and its subcomponents.""" 

 

left, marked, right = (format_updatable(i, pbar) for i in 

(self.left, self.marker, self.right)) 

 

width -= len(left) + len(right) 

# Marked must *always* have length of 1 

if pbar.maxval is not UnknownLength and pbar.maxval: 

marked *= int(pbar.currval / pbar.maxval * width) 

else: 

marked = '' 

 

if self.fill_left: 

return '%s%s%s' % (left, marked.ljust(width, self.fill), right) 

else: 

return '%s%s%s' % (left, marked.rjust(width, self.fill), right) 

 

 

class ReverseBar(Bar): 

"""A bar which has a marker which bounces from side to side.""" 

 

def __init__(self, marker='#', left='|', right='|', fill=' ', 

fill_left=False): 

"""Creates a customizable progress bar. 

 

marker - string or updatable object to use as a marker 

left - string or updatable object to use as a left border 

right - string or updatable object to use as a right border 

fill - character to use for the empty part of the progress bar 

fill_left - whether to fill from the left or the right 

""" 

self.marker = marker 

self.left = left 

self.right = right 

self.fill = fill 

self.fill_left = fill_left 

 

 

class BouncingBar(Bar): 

def update(self, pbar, width): 

"""Updates the progress bar and its subcomponents.""" 

 

left, marker, right = (format_updatable(i, pbar) for i in 

(self.left, self.marker, self.right)) 

 

width -= len(left) + len(right) 

 

if pbar.finished: return '%s%s%s' % (left, width * marker, right) 

 

position = int(pbar.currval % (width * 2 - 1)) 

if position > width: position = width * 2 - position 

lpad = self.fill * (position - 1) 

rpad = self.fill * (width - len(marker) - len(lpad)) 

 

# Swap if we want to bounce the other way 

if not self.fill_left: rpad, lpad = lpad, rpad 

 

return '%s%s%s%s%s' % (left, lpad, marker, rpad, right)