progress.py 5.49 KB
#!/usr/bin/env python

from __future__ import print_function

import use_lldb_suite
import six

import sys
import time


class ProgressBar(object):
    """ProgressBar class holds the options of the progress bar.
    The options are:
        start   State from which start the progress. For example, if start is
                5 and the end is 10, the progress of this state is 50%
        end     State in which the progress has terminated.
        width   --
        fill    String to use for "filled" used to represent the progress
        blank   String to use for "filled" used to represent remaining space.
        format  Format
        incremental
    """
    light_block = six.unichr(0x2591).encode("utf-8")
    solid_block = six.unichr(0x2588).encode("utf-8")
    solid_right_arrow = six.unichr(0x25BA).encode("utf-8")

    def __init__(self,
                 start=0,
                 end=10,
                 width=12,
                 fill=six.unichr(0x25C9).encode("utf-8"),
                 blank=six.unichr(0x25CC).encode("utf-8"),
                 marker=six.unichr(0x25CE).encode("utf-8"),
                 format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%',
                 incremental=True):
        super(ProgressBar, self).__init__()

        self.start = start
        self.end = end
        self.width = width
        self.fill = fill
        self.blank = blank
        self.marker = marker
        self.format = format
        self.incremental = incremental
        self.step = 100 / float(width)  # fix
        self.reset()

    def __add__(self, increment):
        increment = self._get_progress(increment)
        if 100 > self.progress + increment:
            self.progress += increment
        else:
            self.progress = 100
        return self

    def complete(self):
        self.progress = 100
        return self

    def __str__(self):
        progressed = int(self.progress / self.step)  # fix
        fill = progressed * self.fill
        blank = (self.width - progressed) * self.blank
        return self.format % {
            'fill': fill,
            'blank': blank,
            'marker': self.marker,
            'progress': int(
                self.progress)}

    __repr__ = __str__

    def _get_progress(self, increment):
        return float(increment * 100) / self.end

    def reset(self):
        """Resets the current progress to the start point"""
        self.progress = self._get_progress(self.start)
        return self


class AnimatedProgressBar(ProgressBar):
    """Extends ProgressBar to allow you to use it straighforward on a script.
    Accepts an extra keyword argument named `stdout` (by default use sys.stdout)
    and may be any file-object to which send the progress status.
    """

    def __init__(self,
                 start=0,
                 end=10,
                 width=12,
                 fill=six.unichr(0x25C9).encode("utf-8"),
                 blank=six.unichr(0x25CC).encode("utf-8"),
                 marker=six.unichr(0x25CE).encode("utf-8"),
                 format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%',
                 incremental=True,
                 stdout=sys.stdout):
        super(
            AnimatedProgressBar,
            self).__init__(
            start,
            end,
            width,
            fill,
            blank,
            marker,
            format,
            incremental)
        self.stdout = stdout

    def show_progress(self):
        if hasattr(self.stdout, 'isatty') and self.stdout.isatty():
            self.stdout.write('\r')
        else:
            self.stdout.write('\n')
        self.stdout.write(str(self))
        self.stdout.flush()


class ProgressWithEvents(AnimatedProgressBar):
    """Extends AnimatedProgressBar to allow you to track a set of events that
       cause the progress to move. For instance, in a deletion progress bar, you
       can track files that were nuked and files that the user doesn't have access to
    """

    def __init__(self,
                 start=0,
                 end=10,
                 width=12,
                 fill=six.unichr(0x25C9).encode("utf-8"),
                 blank=six.unichr(0x25CC).encode("utf-8"),
                 marker=six.unichr(0x25CE).encode("utf-8"),
                 format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%',
                 incremental=True,
                 stdout=sys.stdout):
        super(
            ProgressWithEvents,
            self).__init__(
            start,
            end,
            width,
            fill,
            blank,
            marker,
            format,
            incremental,
            stdout)
        self.events = {}

    def add_event(self, event):
        if event in self.events:
            self.events[event] += 1
        else:
            self.events[event] = 1

    def show_progress(self):
        isatty = hasattr(self.stdout, 'isatty') and self.stdout.isatty()
        if isatty:
            self.stdout.write('\r')
        else:
            self.stdout.write('\n')
        self.stdout.write(str(self))
        if len(self.events) == 0:
            return
        self.stdout.write('\n')
        for key in list(self.events.keys()):
            self.stdout.write(str(key) + ' = ' + str(self.events[key]) + ' ')
        if isatty:
            self.stdout.write('\033[1A')
        self.stdout.flush()


if __name__ == '__main__':
    p = AnimatedProgressBar(end=200, width=200)

    while True:
        p + 5
        p.show_progress()
        time.sleep(0.3)
        if p.progress == 100:
            break
    print()  # new line