Add progress support to emaint (similar to wget's progress bar).
authorZac Medico <zmedico@gentoo.org>
Sun, 27 May 2007 12:12:51 +0000 (12:12 -0000)
committerZac Medico <zmedico@gentoo.org>
Sun, 27 May 2007 12:12:51 +0000 (12:12 -0000)
svn path=/main/trunk/; revision=6639

bin/emaint
pym/portage/output.py

index 0e9ba853d8f770d037935886065b84e45eda92c3..c44bbe85afd38f989d16baef204c643bbbc41983 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/python -O
 
-import sys, os
+import sys, os, time
 from optparse import OptionParser, OptionValueError
 if not hasattr(__builtins__, "set"):
        from sets import Set as set
@@ -12,7 +12,7 @@ except ImportError:
        sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
        import portage
 
-import portage.const, portage.exception
+import portage.const, portage.exception, portage.output
 class WorldHandler(object):
 
        def name():
@@ -132,6 +132,24 @@ class VdbKeyHandler(object):
                
                return errors
 
+class ProgressHandler(object):
+       def __init__(self):
+               self.curval = 0
+               self.maxval = 0
+               self.last_update = 0
+               self.min_display_latency = 0.2
+
+       def onProgress(self, maxval, curval):
+               self.maxval = maxval
+               self.curval = curval
+               cur_time = time.time()
+               if cur_time - self.last_update >= self.min_display_latency:
+                       self.last_update = cur_time
+                       self.display()
+
+       def display(self):
+               raise NotImplementedError(self)
+
 def emaint_main(myargv):
 
        # TODO: Create a system that allows external modules to be added without
@@ -191,11 +209,22 @@ def emaint_main(myargv):
                status = "Attempting to fix %s"
                func = "fix"
 
-
+       isatty = sys.stdout.isatty()
        for task in tasks:
                print status % task.name()
                inst = task()
-               result = getattr(inst, func)()
+               onProgress = None
+               if isatty:
+                       progressBar = portage.output.TermProgressBar()
+                       progressHandler = ProgressHandler()
+                       def display():
+                               progressBar.set(progressHandler.maxval, progressHandler.curval)
+                       progressHandler.display = display
+               result = getattr(inst, func)(onProgress=progressHandler.onProgress)
+               if isatty:
+                       # make sure the final progress is displayed
+                       progressHandler.display()
+                       print
                if result:
                        print
                        print "\n".join(result)
index 01595e5b14121ae4b64f0f7e3d5e86737049318e..d2122e0b51bf1f1f22f7d8ec4b0a04e503b956d5 100644 (file)
@@ -4,7 +4,7 @@
 
 __docformat__ = "epytext"
 
-import commands,errno,os,re,shlex,sys
+import commands, errno, os, re, shlex, sys, time
 from portage.const import COLOR_MAP_FILE
 from portage.util import writemsg
 from portage.exception import PortageException, ParseError, PermissionDenied, FileNotFound
@@ -407,3 +407,129 @@ class EOutput:
                if not self.quiet:
                        self.__eend("ewend", errno, msg)
                self.__last_e_cmd = "ewend"
+
+class ProgressBar(object):
+       """The interface is copied from the ProgressBar class from the EasyDialogs
+       module (which is Mac only)."""
+       def __init__(self, title=None, maxval=0, label=None):
+               self._title = title
+               self._maxval = maxval
+               self._label = maxval
+               self._curval = 0
+
+       @property
+       def curval(self):
+               """
+               The current value (of type integer or long integer) of the progress
+               bar. The normal access methods coerce curval between 0 and maxval. This
+               attribute should not be altered directly.
+               """
+               return self._curval
+
+       @property
+       def maxval(self):
+               """
+               The maximum value (of type integer or long integer) of the progress
+               bar; the progress bar (thermometer style) is full when curval equals
+               maxval. If maxval is 0, the bar will be indeterminate (barber-pole).
+               This attribute should not be altered directly.
+               """
+               return self._maxval
+
+       def title(self, newstr):
+               """Sets the text in the title bar of the progress dialog to newstr."""
+               self._title = newstr
+
+       def label(self, newstr):
+               """Sets the text in the progress box of the progress dialog to newstr."""
+               self._label = newstr
+
+       def set(self, value, maxval=None):
+               """
+               Sets the progress bar's curval to value, and also maxval to max if the
+               latter is provided. value is first coerced between 0 and maxval. The
+               thermometer bar is updated to reflect the changes, including a change
+               from indeterminate to determinate or vice versa.
+               """
+               if maxval is not None:
+                       self._maxval = maxval
+               if value < 0:
+                       value = 0
+               elif value > maxval:
+                       value = maxval
+               self._curval = value
+
+       def inc(self, n=1):
+               """Increments the progress bar's curval by n, or by 1 if n is not
+               provided. (Note that n may be negative, in which case the effect is a
+               decrement.) The progress bar is updated to reflect the change. If the
+               bar is indeterminate, this causes one ``spin'' of the barber pole. The
+               resulting curval is coerced between 0 and maxval if incrementing causes
+               it to fall outside this range.
+               """
+               self.set(self._curval+n)
+
+class TermProgressBar(ProgressBar):
+       """A tty progress bar similar to wget's."""
+       def __init__(self, **kwargs):
+               ProgressBar.__init__(self, **kwargs)
+               lines, self.term_columns = get_term_size()
+               self.file = sys.stdout
+               self._min_columns = 11
+               # for indeterminate mode, ranges from 0.0 to 1.0
+               self._position = 0.0
+
+       def set(self, value, maxval=None):
+               ProgressBar.set(self, value, maxval=maxval)
+               self._display_image(self._create_image())
+
+       def _display_image(self, image):
+               self.file.write('\r')
+               self.file.write(image)
+               self.file.flush()
+
+       def _create_image(self):
+               cols = self.term_columns
+               min_columns = self._min_columns
+               curval = self._curval
+               maxval = self._maxval
+               position = self._position
+               if cols < 3:
+                       return ""
+               bar_space = cols - 6
+               if maxval == 0:
+                       max_bar_width = bar_space-3
+                       image = "    "
+                       if cols < min_columns:
+                               return image
+                       if position <= 0.5:
+                               offset = 2 * position
+                       else:
+                               offset = 2 * (1 - position)
+                       delta = 0.5 / max_bar_width
+                       position += delta
+                       if position >= 1.0:
+                               position = 0.0
+                       # make sure it touches the ends
+                       if 1.0 - position < delta:
+                               position = 1.0
+                       if position < 0.5 and 0.5 - position < delta:
+                               position = 0.5
+                       self._position = position
+                       bar_width = int(offset * max_bar_width)
+                       image = image + "[" + (bar_width * " ") + \
+                               "<=>" + ((max_bar_width - bar_width) * " ") + "]"
+                       return image
+               else:
+                       max_bar_width = bar_space-1
+                       percentage = int(100 * float(curval) / maxval)
+                       if percentage == 100:
+                               percentage = 99
+                       image = ("%d%% " % percentage).rjust(4)
+                       if cols < min_columns:
+                               return image
+                       offset = float(curval) / maxval
+                       bar_width = int(offset * max_bar_width)
+                       image = image + "[" + (bar_width * "=") + \
+                               ">" + ((max_bar_width - bar_width) * " ") + "]"
+                       return image