# Copyright 2010, 2011 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys import ttystatus class TerminalStatus(object): '''Show status and progress information on a terminal. All output is provided via widgets of various kinds. Many widgets format data that TerminalStatus stores. TerminalStatus provides a dict interface for setting and retrieving data items. Unlike a real dict, getting a value for a key that has not been set does not result in a KeyError exception, but in the empty string being returned. ''' def __init__(self, period=None, messager=None, _terminal=None): self._m = messager or ttystatus.Messager( period=period, _terminal=_terminal) self.clear() def get_terminal_size(self): # pragma: no cover '''Return terminal width, height.''' return self._m.get_terminal_size() def add(self, widget): '''Add a new widget to the status display.''' if not self._widget_rows: self._widget_rows = [[]] self._widget_rows[-1].append(widget) self._register_interests(widget) self.flush() def _register_interests(self, widget): if getattr(widget, 'interested_in', None) is None: self._unknown_interest.append(widget) else: for key in widget.interested_in: widgets = self._interested_in.get(key, []) widgets.append(widget) self._interested_in[key] = widgets def start_new_line(self): # pragma: no cover '''Start a new line of widgets.''' if not self._widget_rows: self._widget_rows = [[]] self._widget_rows.append([]) self.flush() def format(self, format_string): '''Add new widgets based on format string. The format string is taken literally, except that ``%%`` is a literal percent character, and ``%Foo(a,b,c)`` is a widget of type ``Foo`` with parameters a, b, and c. For example: ``format("hello, %String(name)")``. ''' for i, line in enumerate(format_string.split('\n')): if i > 0: # pragma: no cover self.start_new_line() for widget in ttystatus.parse(line): self.add(widget) self.flush() @property def widgets(self): result = [] for row in self._widget_rows: result += row return result def clear(self): '''Remove all widgets.''' self._widget_rows = [] self._values = {} self._interested_in = {} self._unknown_interest = [] self._m.clear() def __getitem__(self, key): '''Return value for key, or the empty string.''' return self._values.get(key, '') def get(self, key, default=None): '''Like dict.get.''' return self._values.get(key, default) def __setitem__(self, key, value): '''Set value for key.''' self._values[key] = value widget_lists = [ self._interested_in.get(key, []), self._unknown_interest, ] for widgets in widget_lists: for w in widgets: w.update(self) if self._m.enabled and self._m.time_to_write(): self._write() def hide(self): # pragma: no cover '''Hide current progress report. Use .flush() to make it visible again. Hiding is useful if you want to write things to stdout/stderr that might get mixed with progress output. The .notify() and .error() methods get disabled if progress reporting gets disabled, but .hide() doesn't. ''' self._m.clear() def flush(self): '''Force an update of current state to the screen. This happens even if it is not yet time to output the screen. ''' self._write(force=True) def _render(self): '''Render current state of all widgets.''' return '\n'.join(self._render_row(row) for row in self._widget_rows) def _render_row(self, widget_row): max_chars = self._m.get_max_line_length() remaining = max_chars texts = [None] * len(widget_row) for i, w in enumerate(widget_row): if w.static_width: texts[i] = self._make_safe(w.render(0)) remaining -= len(texts[i]) for i, w in enumerate(widget_row): if not w.static_width: texts[i] = self._make_safe(w.render(remaining)) remaining -= len(texts[i]) return (''.join(texts))[:max_chars] def _make_safe(self, line): '''Expand TABs, remove all other ASCII control characters.''' ASCII_SPACE = 32 return ''.join( c if ord(c) >= ASCII_SPACE else '' for c in line.expandtabs()) def _write(self, force=False): '''Render and output current state of all widgets.''' self._m.write(self._render(), force=force) def increase(self, key, delta): '''Increase value for a key by a given amount.''' self[key] = (self[key] or 0) + delta def notify(self, msg): '''Show a message.''' self._m.notify(msg, sys.stdout) def error(self, msg): '''Write an error message.''' self._m.notify(msg, sys.stderr, force=True) def finish(self): '''Finish status display.''' self._write() self._m.finish() def disable(self): '''Disable all output.''' self._m.disable() def enable(self): '''Enable output if it has been disabled.''' self._m.enable()
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__init__.py | File | 1.29 KB | 0644 |
|
__init__.pyc | File | 1.25 KB | 0644 |
|
area.py | File | 3.41 KB | 0644 |
|
area.pyc | File | 3.9 KB | 0644 |
|
bytesize.py | File | 1.39 KB | 0644 |
|
bytesize.pyc | File | 1.37 KB | 0644 |
|
bytesize_tests.py | File | 1.95 KB | 0644 |
|
bytesize_tests.pyc | File | 3.11 KB | 0644 |
|
bytespeed.py | File | 2.26 KB | 0644 |
|
bytespeed.pyc | File | 2.17 KB | 0644 |
|
bytespeed_tests.py | File | 2.54 KB | 0644 |
|
bytespeed_tests.pyc | File | 4.77 KB | 0644 |
|
counter.py | File | 1.1 KB | 0644 |
|
counter.pyc | File | 1.13 KB | 0644 |
|
counter_tests.py | File | 1.42 KB | 0644 |
|
counter_tests.pyc | File | 1.97 KB | 0644 |
|
elapsed.py | File | 1.35 KB | 0644 |
|
elapsed.pyc | File | 1.43 KB | 0644 |
|
elapsed_tests.py | File | 1.38 KB | 0644 |
|
elapsed_tests.pyc | File | 2.14 KB | 0644 |
|
fmt.py | File | 2.15 KB | 0644 |
|
fmt.pyc | File | 1.74 KB | 0644 |
|
fmt_tests.py | File | 2.39 KB | 0644 |
|
fmt_tests.pyc | File | 2.78 KB | 0644 |
|
index.py | File | 1.25 KB | 0644 |
|
index.pyc | File | 1.24 KB | 0644 |
|
index_tests.py | File | 1.27 KB | 0644 |
|
index_tests.pyc | File | 1.7 KB | 0644 |
|
integer.py | File | 1.06 KB | 0644 |
|
integer.pyc | File | 1.12 KB | 0644 |
|
integer_tests.py | File | 1.22 KB | 0644 |
|
integer_tests.pyc | File | 1.64 KB | 0644 |
|
literal.py | File | 877 B | 0644 |
|
literal.pyc | File | 791 B | 0644 |
|
literal_tests.py | File | 984 B | 0644 |
|
literal_tests.pyc | File | 1.09 KB | 0644 |
|
messager.py | File | 4.52 KB | 0644 |
|
messager.pyc | File | 4.61 KB | 0644 |
|
pathname.py | File | 1.07 KB | 0644 |
|
pathname.pyc | File | 1.12 KB | 0644 |
|
pathname_tests.py | File | 1.36 KB | 0644 |
|
pathname_tests.pyc | File | 1.92 KB | 0644 |
|
percent.py | File | 1.4 KB | 0644 |
|
percent.pyc | File | 1.35 KB | 0644 |
|
percent_tests.py | File | 1.42 KB | 0644 |
|
percent_tests.pyc | File | 2 KB | 0644 |
|
progressbar.py | File | 1.46 KB | 0644 |
|
progressbar.pyc | File | 1.41 KB | 0644 |
|
progressbar_tests.py | File | 2.33 KB | 0644 |
|
progressbar_tests.pyc | File | 3.77 KB | 0644 |
|
remtime.py | File | 1.89 KB | 0644 |
|
remtime.pyc | File | 1.81 KB | 0644 |
|
remtime_tests.py | File | 2.08 KB | 0644 |
|
remtime_tests.pyc | File | 3.26 KB | 0644 |
|
speed.py | File | 1.94 KB | 0644 |
|
speed.pyc | File | 1.83 KB | 0644 |
|
speed_tests.py | File | 1001 B | 0644 |
|
speed_tests.pyc | File | 1.1 KB | 0644 |
|
status.py | File | 6.15 KB | 0644 |
|
status.pyc | File | 8.2 KB | 0644 |
|
status_tests.py | File | 5.18 KB | 0644 |
|
status_tests.pyc | File | 8.97 KB | 0644 |
|
string.py | File | 998 B | 0644 |
|
string.pyc | File | 1.02 KB | 0644 |
|
string_tests.py | File | 1.2 KB | 0644 |
|
string_tests.pyc | File | 1.61 KB | 0644 |
|
tty.py | File | 2.87 KB | 0644 |
|
tty.pyc | File | 3.33 KB | 0644 |
|
version.py | File | 48 B | 0644 |
|
version.pyc | File | 225 B | 0644 |
|
widget.py | File | 2.22 KB | 0644 |
|
widget.pyc | File | 2.13 KB | 0644 |
|