class TimedProgressBar(ProgressBar): """A 3-line progress bar, which looks like:: title 39% [================>----------------------------] ETA mm:ss message p = ProgressBar('spam') # create bar p.update(0, 'starting spam') # start printing it out p.update(50, 'spam almost ready') # progress p.update(100, 'spam complete') """ BAR_FORMAT = { 'text': ' %3d%% ' + '[%s' + display('dim') + '%s' + display('default') + ']', 'length': 13, 'padding': 2 } ' ETA 12:23' # what fraction of percent it acurate too precision = 100 def __init__(self, title=None): ProgressBar.__init__(self, title) self.start = datetime.today() def get_bar(self, percent): now = datetime.today() timed = now - self.start etatext = '' etadiv = int(percent * self.precision) if timed.seconds >= 1: etatext += ' ' if int(percent * self.precision) != 0: eta = (timed * 100 * self.precision) / int( percent * self.precision) days = eta.days min, sec = divmod(eta.seconds, 60) hours, min = divmod(min, 60) if days == 1: etatext += '1 day, ' elif days: etatext += '%d days, ' % days if hours: etatext += '%02d:' % hours etatext += '%02d:%02d' % (min, sec) else: etatext += 'Never' barlength = (self.width - self.BAR_FORMAT['padding'] * 2 - self.BAR_FORMAT['length'] - len(etatext)) full = int(math.ceil(barlength * (percent / 100.0))) empty = int(barlength - full) if full == 0 or empty == 0: fullpiece = ('=' * full) else: fullpiece = ('=' * (full - 1)) + '>' emptypiece = ('-' * empty) return [(self.BAR_FORMAT['text'] % (percent, fullpiece, emptypiece)) + etatext]
def query(question, values, default=None, list_values = False, ignorecase = True ): """Preset a few options The question argument is a string, nothing magical. The values argument accepts input in two different forms. The simpler form (a tuple with strings) looks like: .. code-block:: python ('Male','Female') And it will pop up a question asking the user for a gender and requiring the user to enter either 'male' or 'female' (case doesn't matter unless you set the third arguement to false). The other form is something like: .. code-block:: python ({'values':('Male','M'),'fg':'cyan'}, {'values':('Female','F'),'fg':'magenta'}) This will pop up a question with Male/Female (each with appropriate colouring). Additionally, if the user types in just 'M', it will be treated as if 'Male' was typed in. The first item in the 'values' tuple is treated as default and is the one that is returned by the function if the user chooses one in that group. In addition the function can handle non-string objects quite fine. It simple displays the output object.__str__() and compares the user's input against that. So the the code .. code-block:: python query("Python rocks? ",(True, False)) will return a bool (True) when the user types in the string 'True' (Of course there isn't any other reasonable answer than True anyways :P) ``default`` is the value function returns if the user types nothing in. This is can be used to cancel the input so-to-speek Using list_values = False will display a list, with descriptions printed out from the 'desc' keyword """ values = list(values) for i in range(len(values)): if not isinstance(values[i], dict): values[i] = {'values': [values[i]]} try: import readline, rlcomplete wordlist = [ str(v) for value in values for v in value['values']] completer = rlcomplete.ListCompleter(wordlist, ignorecase) readline.parse_and_bind("tab: complete") readline.set_completer(completer.complete) except ImportError: pass valuelist = [] for item in values: entry = ( display('bright', item.get('fg'), item.get('bg')) + str(item['values'][0]) + display(['default']) ) if str(item['values'][0]) == str(default): entry = '['+entry+']' if list_values: entry += ' : ' + item['desc'] valuelist.append(entry) if list_values: question += os.linesep + os.linesep.join(valuelist) + os.linesep else: question += ' (' + '/'.join(valuelist) + ')' return input_object(question, cast = query_cast, default=default, castarg=[values,ignorecase])
"""Input and output functions """ import sys import os import os.path import term from term import stdout, stderr, display __all__ = ["input_object","query","file_chooser"] # this constant is here because float() and int() give error messages # that would confuse most sane users. ERROR_MESSAGE = ( display('bright','red') + 'Error: ' + display('default') + '%s' + '\a' + os.linesep ) NICE_INPUT_ERRORS = { float: "The input ('%s') must be a number", int: "The input ('%s') must be an integer (-1, 0, 1, 2, etc.)" } DEFAULT_INPUT_ERRORS = "Bad input (%s)" def input_object(prompt_text, cast = None, default = None, prompt_ext = ': ', castarg = [], castkwarg = {}): """Gets input from the command line and validates it. prompt_text A string. Used to prompt the user. Do not include a trailing space.
def query(question, values, default=None, list_values=False, ignorecase=True): """Preset a few options The question argument is a string, nothing magical. The values argument accepts input in two different forms. The simpler form (a tuple with strings) looks like: .. code-block:: python ('Male','Female') And it will pop up a question asking the user for a gender and requiring the user to enter either 'male' or 'female' (case doesn't matter unless you set the third arguement to false). The other form is something like: .. code-block:: python ({'values':('Male','M'),'fg':'cyan'}, {'values':('Female','F'),'fg':'magenta'}) This will pop up a question with Male/Female (each with appropriate colouring). Additionally, if the user types in just 'M', it will be treated as if 'Male' was typed in. The first item in the 'values' tuple is treated as default and is the one that is returned by the function if the user chooses one in that group. In addition the function can handle non-string objects quite fine. It simple displays the output object.__str__() and compares the user's input against that. So the the code .. code-block:: python query("Python rocks? ",(True, False)) will return a bool (True) when the user types in the string 'True' (Of course there isn't any other reasonable answer than True anyways :P) ``default`` is the value function returns if the user types nothing in. This is can be used to cancel the input so-to-speek Using list_values = False will display a list, with descriptions printed out from the 'desc' keyword """ values = list(values) for i in range(len(values)): if not isinstance(values[i], dict): values[i] = {'values': [values[i]]} try: import readline, rlcomplete wordlist = [str(v) for value in values for v in value['values']] completer = rlcomplete.ListCompleter(wordlist, ignorecase) readline.parse_and_bind("tab: complete") readline.set_completer(completer.complete) except ImportError: pass valuelist = [] for item in values: entry = (display('bright', item.get('fg'), item.get('bg')) + str(item['values'][0]) + display(['default'])) if str(item['values'][0]) == str(default): entry = '[' + entry + ']' if list_values: entry += ' : ' + item['desc'] valuelist.append(entry) if list_values: question += os.linesep + os.linesep.join(valuelist) + os.linesep else: question += ' (' + '/'.join(valuelist) + ')' return input_object(question, cast=query_cast, default=default, castarg=[values, ignorecase])
# limitations under the License. """Input and output functions """ import sys import os import os.path import term from term import stdout, stderr, display __all__ = ["input_object", "query", "file_chooser"] # this constant is here because float() and int() give error messages # that would confuse most sane users. ERROR_MESSAGE = (display('bright', 'red') + 'Error: ' + display('default') + '%s' + '\a' + os.linesep) NICE_INPUT_ERRORS = { float: "The input ('%s') must be a number", int: "The input ('%s') must be an integer (-1, 0, 1, 2, etc.)" } DEFAULT_INPUT_ERRORS = "Bad input (%s)" def input_object(prompt_text, cast=None, default=None, prompt_ext=': ', castarg=[], castkwarg={}):
class ProgressBar(object): """A 3-line progress bar, which looks like:: title 39% [================>----------------------------] message p = ProgressBar('spam') # create bar p.update(0, 'starting spam') # start printing it out p.update(50, 'spam almost ready') # progress p.update(100, 'spam complete') """ # content, length TITLE_FORMAT = { 'text': display('bright', 'cyan') + '%s' + display('default'), 'length': 0, 'padding': 0 } BAR_FORMAT = { 'text': ' %3d%% ' + '[%s' + display('dim') + '%s' + display('default') + ']', 'length': 8, 'padding': 2 } MESSAGE_FORMAT = {'text': '%s', 'length': 0, 'padding': 0} def __init__(self, title=None): """ """ self.drawn = False cols = stdout.get_size()[0] self.width = cols - 1 # TODO: make a better fix for systems that put \n on new line self.title = [] self.barlines = 0 self.message = [] self.messageline = None self.refresh = False self.set_title(title) def set_title(self, title=None): """ """ if title == None: self.title = [] else: length = self.width - self.TITLE_FORMAT[ 'padding'] * 2 - self.TITLE_FORMAT['length'] text = title[:length].center( length) # we need to keep it on one line for now padding = ' ' * self.TITLE_FORMAT['padding'] self.title = [ padding + (self.TITLE_FORMAT['text'] % text) + padding ] self.refresh = self.drawn #lines = [(padding + line + padding) for line in textwrap.wrap( # text, self.width - (self.TITLE_FORMAT['padding']*2), # replace_whitespace=False)] #self.title = os.linesep.split( # self.TITLE_FORMAT['text'] % os.linesep.join(lines)) def get_title(self): """ """ return self.title def get_bar(self, percent): """ """ barlength = self.width - self.BAR_FORMAT[ 'padding'] * 2 - self.BAR_FORMAT['length'] full = int(math.ceil(barlength * (percent / 100.0))) empty = int(barlength - full) if full == 0 or empty == 0: fullpiece = ('=' * full) else: fullpiece = ('=' * (full - 1)) + '>' emptypiece = ('-' * empty) return [(self.BAR_FORMAT['text'] % (percent, fullpiece, emptypiece))] def set_message(self, message=None): """ """ """""" if message == None: self.message = [] else: length = self.width - self.MESSAGE_FORMAT[ 'padding'] * 2 - self.MESSAGE_FORMAT['length'] text = message[:length].center( length) # we need to keep it on one line for now padding = ' ' * self.MESSAGE_FORMAT['padding'] self.message = [ padding + (self.MESSAGE_FORMAT['text'] % text) + padding ] def get_message(self): """returns None or string""" if self.message == []: return None else: return os.linesep.join(self.message) def update(self, percent, message=None, test=False): """ """ if self.refresh: self.clear() if self.drawn: stdout.move('beginning of line') stdout.move('up', len(self.message) + self.barlines) else: title = self.get_title() if title != None: for line in self.get_title(): stdout.write(line + os.linesep) self.drawn = True bar = self.get_bar(percent) refresh = (len(bar) != self.barlines) self.barlines = len(bar) for line in bar: stdout.clear('line') stdout.write(line) stdout.move('down') stdout.move('beginning of line') if (message != self.get_message()) or refresh: stdout.clear('end of screen') self.set_message(message) for line in self.message: stdout.write(line) stdout.move('down') else: stdout.move('down', len(self.message)) def clear(self): """ """ if self.drawn: stdout.move('beginning of line') stdout.move('up', len(self.message)) stdout.move('up', self.barlines) stdout.move('up', len(self.get_title())) stdout.clear('end of screen') self.drawn = False self.refresh = False