Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 658
1
"""
2
GUI progressbar decorator for iterators.
3
Includes a default (x)range iterator printing to stderr.
4
5
Usage:
6
>>> from tqdm_gui import tgrange[, tqdm_gui]
7
>>> for i in tgrange(10): #same as: for i in tqdm_gui(xrange(10))
8
... ...
9
"""
10
# future division is important to divide integers and get as
11
# a result precise floating numbers (instead of truncated int)
12
from __future__ import division, absolute_import
13
# import compatibility functions and utilities
14
import sys
15
from time import time
16
from ._utils import _range
17
# to inherit from the tqdm class
18
from ._tqdm import tqdm
19
20
21
__author__ = {"github.com/": ["casperdcl", "lrq3000"]}
22
__all__ = ['tqdm_gui', 'tgrange']
23
24
25
class tqdm_gui(tqdm): # pragma: no cover
26
"""
27
Experimental GUI version of tqdm!
28
"""
29
30
@classmethod
31
def write(cls, s, file=sys.stdout, end="\n"):
32
"""
33
Print a message via tqdm_gui (just an alias for print)
34
"""
35
# TODO: print text on GUI?
36
file.write(s)
37
file.write(end)
38
39
def __init__(self, *args, **kwargs):
40
import matplotlib as mpl
41
import matplotlib.pyplot as plt
42
from collections import deque
43
kwargs['gui'] = True
44
45
super(tqdm_gui, self).__init__(*args, **kwargs)
46
47
# Initialize the GUI display
48
if self.disable or not kwargs['gui']:
49
return
50
51
self.fp.write('Warning: GUI is experimental/alpha\n')
52
self.mpl = mpl
53
self.plt = plt
54
self.sp = None
55
56
# Remember if external environment uses toolbars
57
self.toolbar = self.mpl.rcParams['toolbar']
58
self.mpl.rcParams['toolbar'] = 'None'
59
60
self.mininterval = max(self.mininterval, 0.5)
61
self.fig, ax = plt.subplots(figsize=(9, 2.2))
62
# self.fig.subplots_adjust(bottom=0.2)
63
if self.total:
64
self.xdata = []
65
self.ydata = []
66
self.zdata = []
67
else:
68
self.xdata = deque([])
69
self.ydata = deque([])
70
self.zdata = deque([])
71
self.line1, = ax.plot(self.xdata, self.ydata, color='b')
72
self.line2, = ax.plot(self.xdata, self.zdata, color='k')
73
ax.set_ylim(0, 0.001)
74
if self.total:
75
ax.set_xlim(0, 100)
76
ax.set_xlabel('percent')
77
self.fig.legend((self.line1, self.line2), ('cur', 'est'),
78
loc='center right')
79
# progressbar
80
self.hspan = plt.axhspan(0, 0.001,
81
xmin=0, xmax=0, color='g')
82
else:
83
# ax.set_xlim(-60, 0)
84
ax.set_xlim(0, 60)
85
ax.invert_xaxis()
86
ax.set_xlabel('seconds')
87
ax.legend(('cur', 'est'), loc='lower left')
88
ax.grid()
89
# ax.set_xlabel('seconds')
90
ax.set_ylabel((self.unit if self.unit else 'it') + '/s')
91
if self.unit_scale:
92
plt.ticklabel_format(style='sci', axis='y',
93
scilimits=(0, 0))
94
ax.yaxis.get_offset_text().set_x(-0.15)
95
96
# Remember if external environment is interactive
97
self.wasion = plt.isinteractive()
98
plt.ion()
99
self.ax = ax
100
101
def __iter__(self):
102
# TODO: somehow allow the following:
103
# if not self.gui:
104
# return super(tqdm_gui, self).__iter__()
105
iterable = self.iterable
106
if self.disable:
107
for obj in iterable:
108
yield obj
109
return
110
111
# ncols = self.ncols
112
mininterval = self.mininterval
113
maxinterval = self.maxinterval
114
miniters = self.miniters
115
dynamic_miniters = self.dynamic_miniters
116
unit = self.unit
117
unit_scale = self.unit_scale
118
ascii = self.ascii
119
start_t = self.start_t
120
last_print_t = self.last_print_t
121
last_print_n = self.last_print_n
122
n = self.n
123
# dynamic_ncols = self.dynamic_ncols
124
smoothing = self.smoothing
125
avg_time = self.avg_time
126
bar_format = self.bar_format
127
128
plt = self.plt
129
ax = self.ax
130
xdata = self.xdata
131
ydata = self.ydata
132
zdata = self.zdata
133
line1 = self.line1
134
line2 = self.line2
135
136
for obj in iterable:
137
yield obj
138
# Update and print the progressbar.
139
# Note: does not call self.update(1) for speed optimisation.
140
n += 1
141
delta_it = n - last_print_n
142
# check the counter first (avoid calls to time())
143
if delta_it >= miniters:
144
cur_t = time()
145
delta_t = cur_t - last_print_t
146
if delta_t >= mininterval:
147
elapsed = cur_t - start_t
148
# EMA (not just overall average)
149
if smoothing and delta_t:
150
avg_time = delta_t / delta_it \
151
if avg_time is None \
152
else smoothing * delta_t / delta_it + \
153
(1 - smoothing) * avg_time
154
155
# Inline due to multiple calls
156
total = self.total
157
# instantaneous rate
158
y = delta_it / delta_t
159
# overall rate
160
z = n / elapsed
161
# update line data
162
xdata.append(n * 100.0 / total if total else cur_t)
163
ydata.append(y)
164
zdata.append(z)
165
166
# Discard old values
167
# xmin, xmax = ax.get_xlim()
168
# if (not total) and elapsed > xmin * 1.1:
169
if (not total) and elapsed > 66:
170
xdata.popleft()
171
ydata.popleft()
172
zdata.popleft()
173
174
ymin, ymax = ax.get_ylim()
175
if y > ymax or z > ymax:
176
ymax = 1.1 * y
177
ax.set_ylim(ymin, ymax)
178
ax.figure.canvas.draw()
179
180
if total:
181
line1.set_data(xdata, ydata)
182
line2.set_data(xdata, zdata)
183
try:
184
poly_lims = self.hspan.get_xy()
185
except AttributeError:
186
self.hspan = plt.axhspan(0, 0.001, xmin=0,
187
xmax=0, color='g')
188
poly_lims = self.hspan.get_xy()
189
poly_lims[0, 1] = ymin
190
poly_lims[1, 1] = ymax
191
poly_lims[2] = [n / total, ymax]
192
poly_lims[3] = [poly_lims[2, 0], ymin]
193
if len(poly_lims) > 4:
194
poly_lims[4, 1] = ymin
195
self.hspan.set_xy(poly_lims)
196
else:
197
t_ago = [cur_t - i for i in xdata]
198
line1.set_data(t_ago, ydata)
199
line2.set_data(t_ago, zdata)
200
201
ax.set_title(self.format_meter(
202
n, total, elapsed, 0,
203
self.desc, ascii, unit, unit_scale,
204
1 / avg_time if avg_time else None, bar_format),
205
fontname="DejaVu Sans Mono", fontsize=11)
206
plt.pause(1e-9)
207
208
# If no `miniters` was specified, adjust automatically
209
# to the maximum iteration rate seen so far.
210
if dynamic_miniters:
211
if maxinterval and delta_t > maxinterval:
212
# Set miniters to correspond to maxinterval
213
miniters = delta_it * maxinterval / delta_t
214
elif mininterval and delta_t:
215
# EMA-weight miniters to converge
216
# towards the timeframe of mininterval
217
miniters = smoothing * delta_it * mininterval \
218
/ delta_t + (1 - smoothing) * miniters
219
else:
220
miniters = smoothing * delta_it + \
221
(1 - smoothing) * miniters
222
223
# Store old values for next call
224
last_print_n = n
225
last_print_t = cur_t
226
227
# Closing the progress bar.
228
# Update some internal variables for close().
229
self.last_print_n = last_print_n
230
self.n = n
231
self.close()
232
233
def update(self, n=1):
234
# if not self.gui:
235
# return super(tqdm_gui, self).close()
236
if self.disable:
237
return
238
239
if n < 0:
240
n = 1
241
self.n += n
242
243
delta_it = self.n - self.last_print_n # should be n?
244
if delta_it >= self.miniters:
245
# We check the counter first, to reduce the overhead of time()
246
cur_t = time()
247
delta_t = cur_t - self.last_print_t
248
if delta_t >= self.mininterval:
249
elapsed = cur_t - self.start_t
250
# EMA (not just overall average)
251
if self.smoothing and delta_t:
252
self.avg_time = delta_t / delta_it \
253
if self.avg_time is None \
254
else self.smoothing * delta_t / delta_it + \
255
(1 - self.smoothing) * self.avg_time
256
257
# Inline due to multiple calls
258
total = self.total
259
ax = self.ax
260
261
# instantaneous rate
262
y = delta_it / delta_t
263
# smoothed rate
264
z = self.n / elapsed
265
# update line data
266
self.xdata.append(self.n * 100.0 / total
267
if total else cur_t)
268
self.ydata.append(y)
269
self.zdata.append(z)
270
271
# Discard old values
272
if (not total) and elapsed > 66:
273
self.xdata.popleft()
274
self.ydata.popleft()
275
self.zdata.popleft()
276
277
ymin, ymax = ax.get_ylim()
278
if y > ymax or z > ymax:
279
ymax = 1.1 * y
280
ax.set_ylim(ymin, ymax)
281
ax.figure.canvas.draw()
282
283
if total:
284
self.line1.set_data(self.xdata, self.ydata)
285
self.line2.set_data(self.xdata, self.zdata)
286
try:
287
poly_lims = self.hspan.get_xy()
288
except AttributeError:
289
self.hspan = self.plt.axhspan(0, 0.001, xmin=0,
290
xmax=0, color='g')
291
poly_lims = self.hspan.get_xy()
292
poly_lims[0, 1] = ymin
293
poly_lims[1, 1] = ymax
294
poly_lims[2] = [self.n / total, ymax]
295
poly_lims[3] = [poly_lims[2, 0], ymin]
296
if len(poly_lims) > 4:
297
poly_lims[4, 1] = ymin
298
self.hspan.set_xy(poly_lims)
299
else:
300
t_ago = [cur_t - i for i in self.xdata]
301
self.line1.set_data(t_ago, self.ydata)
302
self.line2.set_data(t_ago, self.zdata)
303
304
ax.set_title(self.format_meter(
305
self.n, total, elapsed, 0,
306
self.desc, self.ascii, self.unit, self.unit_scale,
307
1 / self.avg_time if self.avg_time else None,
308
self.bar_format),
309
fontname="DejaVu Sans Mono", fontsize=11)
310
self.plt.pause(1e-9)
311
312
# If no `miniters` was specified, adjust automatically to the
313
# maximum iteration rate seen so far.
314
# e.g.: After running `tqdm.update(5)`, subsequent
315
# calls to `tqdm.update()` will only cause an update after
316
# at least 5 more iterations.
317
if self.dynamic_miniters:
318
if self.maxinterval and delta_t > self.maxinterval:
319
self.miniters = self.miniters * self.maxinterval \
320
/ delta_t
321
elif self.mininterval and delta_t:
322
self.miniters = self.smoothing * delta_it \
323
* self.mininterval / delta_t + \
324
(1 - self.smoothing) * self.miniters
325
else:
326
self.miniters = self.smoothing * delta_it + \
327
(1 - self.smoothing) * self.miniters
328
329
# Store old values for next call
330
self.last_print_n = self.n
331
self.last_print_t = cur_t
332
333
def close(self):
334
# if not self.gui:
335
# return super(tqdm_gui, self).close()
336
if self.disable:
337
return
338
339
self.disable = True
340
341
self._instances.remove(self)
342
343
# Restore toolbars
344
self.mpl.rcParams['toolbar'] = self.toolbar
345
# Return to non-interactive mode
346
if not self.wasion:
347
self.plt.ioff()
348
if not self.leave:
349
self.plt.close(self.fig)
350
351
352
def tgrange(*args, **kwargs):
353
"""
354
A shortcut for tqdm_gui(xrange(*args), **kwargs).
355
On Python3+ range is used instead of xrange.
356
"""
357
return tqdm_gui(_range(*args), **kwargs)
358
359