~maddevelopers/mg5amcnlo/2.9.4

« back to all changes in this revision

Viewing changes to madgraph/various/progressbar.py

pass to v2.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# -*- coding: iso-8859-1 -*-
 
3
#
 
4
# progressbar  - Text progressbar 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
 
 
22
"""Text progressbar library for python.
 
23
 
 
24
This library provides a text mode progressbar. This is tipically used
 
25
to display the progress of a long running operation, providing a
 
26
visual clue that processing is underway.
 
27
 
 
28
The ProgressBar class manages the progress, and the format of the line
 
29
is given by a number of widgets. A widget is an object that may
 
30
display diferently depending on the state of the progress. There are
 
31
three types of widget:
 
32
- a string, which always shows itself;
 
33
- a ProgressBarWidget, which may return a diferent value every time
 
34
it's update method is called; and
 
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. And
 
39
automatically supports features like auto-resizing when available.
 
40
"""
 
41
 
 
42
__author__ = "Nilton Volpato"
 
43
__author_email__ = "first-name dot last-name @ gmail.com"
 
44
__date__ = "2006-05-07"
 
45
__version__ = "2.2"
 
46
 
 
47
# Changelog
 
48
#
 
49
# 2006-05-07: v2.2 fixed bug in windows
 
50
# 2005-12-04: v2.1 autodetect terminal width, added start method
 
51
# 2005-12-04: v2.0 everything is now a widget (wow!)
 
52
# 2005-12-03: v1.0 rewrite using widgets
 
53
# 2005-06-02: v0.5 rewrite
 
54
# 2004-??-??: v0.1 first version
 
55
 
 
56
 
 
57
import sys, time
 
58
from array import array
 
59
try:
 
60
    from fcntl import ioctl
 
61
    import termios
 
62
except ImportError:
 
63
    pass
 
64
import signal
 
65
 
 
66
class ProgressBarWidget(object):
 
67
    """This is an element of ProgressBar formatting.
 
68
 
 
69
    The ProgressBar object will call it's update value when an update
 
70
    is needed. It's size may change between call, but the results will
 
71
    not be good if the size changes drastically and repeatedly.
 
72
    """
 
73
    def update(self, pbar):
 
74
        """Returns the string representing the widget.
 
75
 
 
76
        The parameter pbar is a reference to the calling ProgressBar,
 
77
        where one can access attributes of the class for knowing how
 
78
        the update must be made.
 
79
 
 
80
        At least this function must be overriden."""
 
81
        pass
 
82
 
 
83
class ProgressBarWidgetHFill(object):
 
84
    """This is a variable width element of ProgressBar formatting.
 
85
 
 
86
    The ProgressBar object will call it's update value, informing the
 
87
    width this object must the made. This is like TeX \\hfill, it will
 
88
    expand to fill the line. You can use more than one in the same
 
89
    line, and they will all have the same width, and together will
 
90
    fill the line.
 
91
    """
 
92
    def update(self, pbar, width):
 
93
        """Returns the string representing the widget.
 
94
 
 
95
        The parameter pbar is a reference to the calling ProgressBar,
 
96
        where one can access attributes of the class for knowing how
 
97
        the update must be made. The parameter width is the total
 
98
        horizontal width the widget must have.
 
99
 
 
100
        At least this function must be overriden."""
 
101
        pass
 
102
 
 
103
 
 
104
class ETA(ProgressBarWidget):
 
105
    "Widget for the Estimated Time of Arrival"
 
106
    def format_time(self, seconds):
 
107
        return time.strftime('%H:%M:%S', time.gmtime(seconds))
 
108
    def update(self, pbar):
 
109
        if pbar.currval == 0:
 
110
            return 'ETA:  --:--:--'
 
111
        elif pbar.finished:
 
112
            return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
 
113
        else:
 
114
            elapsed = pbar.seconds_elapsed
 
115
            eta = elapsed * pbar.maxval / pbar.currval - elapsed
 
116
            return 'ETA:  %s' % self.format_time(eta)
 
117
 
 
118
class FileTransferSpeed(ProgressBarWidget):
 
119
    "Widget for showing the transfer speed (useful for file transfers)."
 
120
    def __init__(self):
 
121
        self.fmt = '%6.2f %s'
 
122
        self.units = ['B','K','M','G','T','P']
 
123
    def update(self, pbar):
 
124
        if pbar.seconds_elapsed < 2e-6:#== 0:
 
125
            bps = 0.0
 
126
        else:
 
127
            bps = float(pbar.currval) / pbar.seconds_elapsed
 
128
        spd = bps
 
129
        for u in self.units:
 
130
            if spd < 1000:
 
131
                break
 
132
            spd /= 1000
 
133
        return self.fmt % (spd, u+'/s')
 
134
 
 
135
class RotatingMarker(ProgressBarWidget):
 
136
    "A rotating marker for filling the bar of progress."
 
137
    def __init__(self, markers='|/-\\'):
 
138
        self.markers = markers
 
139
        self.curmark = -1
 
140
    def update(self, pbar):
 
141
        if pbar.finished:
 
142
            return self.markers[0]
 
143
        self.curmark = (self.curmark + 1)%len(self.markers)
 
144
        return self.markers[self.curmark]
 
145
 
 
146
class Percentage(ProgressBarWidget):
 
147
    "Just the percentage done."
 
148
    def update(self, pbar):
 
149
        return '%3d%%' % pbar.percentage()
 
150
 
 
151
class Bar(ProgressBarWidgetHFill):
 
152
    "The bar of progress. It will strech to fill the line."
 
153
    def __init__(self, marker='#', left='|', right='|'):
 
154
        self.marker = marker
 
155
        self.left = left
 
156
        self.right = right
 
157
    def _format_marker(self, pbar):
 
158
        if isinstance(self.marker, (str, unicode)):
 
159
            return self.marker
 
160
        else:
 
161
            return self.marker.update(pbar)
 
162
    def update(self, pbar, width):
 
163
        percent = pbar.percentage()
 
164
        cwidth = width - len(self.left) - len(self.right)
 
165
        marked_width = int(percent * cwidth / 100)
 
166
        m = self._format_marker(pbar)
 
167
        bar = (self.left + (m*marked_width).ljust(cwidth) + self.right)
 
168
        return bar
 
169
 
 
170
class ReverseBar(Bar):
 
171
    "The reverse bar of progress, or bar of regress. :)"
 
172
    def update(self, pbar, width):
 
173
        percent = pbar.percentage()
 
174
        cwidth = width - len(self.left) - len(self.right)
 
175
        marked_width = int(percent * cwidth / 100)
 
176
        m = self._format_marker(pbar)
 
177
        bar = (self.left + (m*marked_width).rjust(cwidth) + self.right)
 
178
        return bar
 
179
 
 
180
default_widgets = [Percentage(), ' ', Bar()]
 
181
class ProgressBar(object):
 
182
    """This is the ProgressBar class, it updates and prints the bar.
 
183
 
 
184
    The term_width parameter may be an integer. Or None, in which case
 
185
    it will try to guess it, if it fails it will default to 80 columns.
 
186
 
 
187
    The simple use is like this:
 
188
    >>> pbar = ProgressBar().start()
 
189
    >>> for i in xrange(100):
 
190
    ...    # do something
 
191
    ...    pbar.update(i+1)
 
192
    ...
 
193
    >>> pbar.finish()
 
194
 
 
195
    But anything you want to do is possible (well, almost anything).
 
196
    You can supply different widgets of any type in any order. And you
 
197
    can even write your own widgets! There are many widgets already
 
198
    shipped and you should experiment with them.
 
199
 
 
200
    When implementing a widget update method you may access any
 
201
    attribute or function of the ProgressBar object calling the
 
202
    widget's update method. The most important attributes you would
 
203
    like to access are:
 
204
    - currval: current value of the progress, 0 <= currval <= maxval
 
205
    - maxval: maximum (and final) value of the progress
 
206
    - finished: True if the bar is have finished (reached 100%), False o/w
 
207
    - start_time: first time update() method of ProgressBar was called
 
208
    - seconds_elapsed: seconds elapsed since start_time
 
209
    - percentage(): percentage of the progress (this is a method)
 
210
    """
 
211
    def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
 
212
                 fd=sys.stderr):
 
213
        assert maxval > 0
 
214
        self.maxval = maxval
 
215
        self.widgets = widgets
 
216
        self.fd = fd
 
217
        self.signal_set = False
 
218
        if term_width is None:
 
219
            try:
 
220
                self.handle_resize(None,None)
 
221
                signal.signal(signal.SIGWINCH, self.handle_resize)
 
222
                self.signal_set = True
 
223
            except Exception:
 
224
                self.term_width = 79
 
225
        else:
 
226
            self.term_width = term_width
 
227
 
 
228
        self.currval = 0
 
229
        self.finished = False
 
230
        self.prev_percentage = -1
 
231
        self.start_time = None
 
232
        self.seconds_elapsed = 0
 
233
 
 
234
    def handle_resize(self, signum, frame):
 
235
        h,w=array('h', ioctl(self.fd,termios.TIOCGWINSZ,'\0'*8))[:2]
 
236
        self.term_width = w
 
237
 
 
238
    def percentage(self):
 
239
        "Returns the percentage of the progress."
 
240
        return self.currval*100.0 / self.maxval
 
241
 
 
242
    def _format_widgets(self):
 
243
        r = []
 
244
        hfill_inds = []
 
245
        num_hfill = 0
 
246
        currwidth = 0
 
247
        for i, w in enumerate(self.widgets):
 
248
            if isinstance(w, ProgressBarWidgetHFill):
 
249
                r.append(w)
 
250
                hfill_inds.append(i)
 
251
                num_hfill += 1
 
252
            elif isinstance(w, (str, unicode)):
 
253
                r.append(w)
 
254
                currwidth += len(w)
 
255
            else:
 
256
                weval = w.update(self)
 
257
                currwidth += len(weval)
 
258
                r.append(weval)
 
259
        for iw in hfill_inds:
 
260
            r[iw] = r[iw].update(self, (self.term_width-currwidth)/num_hfill)
 
261
        return r
 
262
 
 
263
    def _format_line(self):
 
264
        return ''.join(self._format_widgets()).ljust(self.term_width)
 
265
 
 
266
    def _need_update(self):
 
267
        return int(self.percentage()) != int(self.prev_percentage)
 
268
 
 
269
    def update(self, value):
 
270
        "Updates the progress bar to a new value."
 
271
        assert 0 <= value <= self.maxval
 
272
        self.currval = value
 
273
        if not self._need_update() or self.finished:
 
274
            return
 
275
        if not self.start_time:
 
276
            self.start_time = time.time()
 
277
        self.seconds_elapsed = time.time() - self.start_time
 
278
        self.prev_percentage = self.percentage()
 
279
        if value != self.maxval:
 
280
            self.fd.write(self._format_line() + '\r')
 
281
        else:
 
282
            self.finished = True
 
283
            self.fd.write(self._format_line() + '\n')
 
284
 
 
285
    def start(self):
 
286
        """Start measuring time, and prints the bar at 0%.
 
287
 
 
288
        It returns self so you can use it like this:
 
289
        >>> pbar = ProgressBar().start()
 
290
        >>> for i in xrange(100):
 
291
        ...    # do something
 
292
        ...    pbar.update(i+1)
 
293
        ...
 
294
        >>> pbar.finish()
 
295
        """
 
296
        self.update(0)
 
297
        return self
 
298
 
 
299
    def finish(self):
 
300
        """Used to tell the progress is finished."""
 
301
        self.update(self.maxval)
 
302
        self.fd.flush()
 
303
        if self.signal_set:
 
304
            signal.signal(signal.SIGWINCH, signal.SIG_DFL)
 
305
 
 
306
if __name__=='__main__':
 
307
    import os
 
308
    import time
 
309
    def exampleVal():
 
310
        widgets = ['ValTest', Percentage(), ' ', Bar(),' ', ETA(), ' ']
 
311
        pbar = ProgressBar(widgets=widgets, maxval=100).start()
 
312
        for i in range(100):
 
313
            time.sleep(0.02)            
 
314
            # do something
 
315
            pbar.update(1*i+1)
 
316
        pbar.finish()
 
317
        print
 
318
 
 
319
    def example1():
 
320
        widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
 
321
                   ' ', ETA(), ' ', FileTransferSpeed()]
 
322
        pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
 
323
        for i in range(1000000):
 
324
            # do something
 
325
            pbar.update(10*i+1)
 
326
        pbar.finish()
 
327
        print
 
328
 
 
329
    def example2():
 
330
        class CrazyFileTransferSpeed(FileTransferSpeed):
 
331
            "It's bigger between 45 and 80 percent"
 
332
            def update(self, pbar):
 
333
                if 45 < pbar.percentage() < 80:
 
334
                    return 'Bigger Now ' + FileTransferSpeed.update(self,pbar)
 
335
                else:
 
336
                    return FileTransferSpeed.update(self,pbar)
 
337
 
 
338
        widgets = [CrazyFileTransferSpeed(),' <<<', Bar(), '>>> ', Percentage(),' ', ETA()]
 
339
        pbar = ProgressBar(widgets=widgets, maxval=10000000)
 
340
        # maybe do something
 
341
        pbar.start()
 
342
        for i in range(2000000):
 
343
            # do something
 
344
            pbar.update(5*i+1)
 
345
        pbar.finish()
 
346
        print
 
347
 
 
348
    def example3():
 
349
        widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')]
 
350
        pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
 
351
        for i in range(1000000):
 
352
            # do something
 
353
            pbar.update(10*i+1)
 
354
        pbar.finish()
 
355
        print
 
356
 
 
357
    def example4():
 
358
        widgets = ['Test: ', Percentage(), ' ',
 
359
                   Bar(marker='0',left='[',right=']'),
 
360
                   ' ', ETA(), ' ', FileTransferSpeed()]
 
361
        pbar = ProgressBar(widgets=widgets, maxval=500)
 
362
        pbar.start()
 
363
        for i in range(100,500+1,50):
 
364
            time.sleep(0.2)
 
365
            pbar.update(i)
 
366
        pbar.finish()
 
367
        print
 
368
 
 
369
    exampleVal()
 
370
    example1()
 
371
    example2()
 
372
    example3()
 
373
    example4()
 
374