From b15fcce5b6d2046e3419b3201e38667554de53fd Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 08:34:16 +1200 Subject: Speed things up by only updating widgets interested in the updated value. --- ttystatus/bytesize.py | 1 + ttystatus/counter.py | 1 + ttystatus/elapsed.py | 1 + ttystatus/index.py | 1 + ttystatus/literal.py | 2 ++ ttystatus/pathname.py | 1 + ttystatus/percent.py | 1 + ttystatus/progressbar.py | 1 + ttystatus/remtime.py | 1 + ttystatus/status.py | 7 ++++--- ttystatus/string.py | 1 + ttystatus/widget.py | 4 ++++ 12 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ttystatus/bytesize.py b/ttystatus/bytesize.py index 0652b2f..3a2e88f 100644 --- a/ttystatus/bytesize.py +++ b/ttystatus/bytesize.py @@ -24,6 +24,7 @@ class ByteSize(ttystatus.Widget): def __init__(self, name): self.name = name self.value = self.format(0) + self.interesting_keys = set([name]) def format(self, bytes): units = ( diff --git a/ttystatus/counter.py b/ttystatus/counter.py index b409114..a401d8d 100644 --- a/ttystatus/counter.py +++ b/ttystatus/counter.py @@ -26,6 +26,7 @@ class Counter(ttystatus.Widget): self.prev = None self.count = 0 self.value = '0' + self.interesting_keys = set([name]) def update(self, master, width): if master[self.name] != self.prev: diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py index bcea7df..1e14ecf 100644 --- a/ttystatus/elapsed.py +++ b/ttystatus/elapsed.py @@ -26,6 +26,7 @@ class ElapsedTime(ttystatus.Widget): def __init__(self): self.started = None self.value = self.format(0) + self.interesting_keys = set() def get_time(self): # pragma: no cover '''Wrapper around time.time() for unit tests to override.''' diff --git a/ttystatus/index.py b/ttystatus/index.py index 2d51189..f402bea 100644 --- a/ttystatus/index.py +++ b/ttystatus/index.py @@ -25,6 +25,7 @@ class Index(ttystatus.Widget): self.name = name self.listname = listname self.value = self.format(0, 0) + self.interesting_keys = set([name, listname]) def format(self, index, listlen): return '%d/%d' % (index, listlen) diff --git a/ttystatus/literal.py b/ttystatus/literal.py index 7d8eb19..5ccfdf2 100644 --- a/ttystatus/literal.py +++ b/ttystatus/literal.py @@ -23,3 +23,5 @@ class Literal(ttystatus.Widget): def __init__(self, string): self.value = string + self.interesting_keys = set() + diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py index ac51a31..6933d03 100644 --- a/ttystatus/pathname.py +++ b/ttystatus/pathname.py @@ -27,6 +27,7 @@ class Pathname(ttystatus.Widget): def __init__(self, key): self._key = key + self.interesting_keys = set([key]) def update(self, master, width): v = master.get(self._key, '') diff --git a/ttystatus/percent.py b/ttystatus/percent.py index 9750cac..b06d070 100644 --- a/ttystatus/percent.py +++ b/ttystatus/percent.py @@ -26,6 +26,7 @@ class PercentDone(ttystatus.Widget): self.total_name = total_name self.decimals = decimals self.value = self.format(0, 1) + self.interesting_keys = set([done_name, total_name]) def format(self, done, total): try: diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py index aca8ea3..52a2b11 100644 --- a/ttystatus/progressbar.py +++ b/ttystatus/progressbar.py @@ -24,6 +24,7 @@ class ProgressBar(ttystatus.Widget): def __init__(self, done_name, total_name): self.done_name = done_name self.total_name = total_name + self.interesting_keys = set([done_name, total_name]) def update(self, master, width): done = float(master.get(self.done_name, 0)) diff --git a/ttystatus/remtime.py b/ttystatus/remtime.py index b1cffe5..7602e54 100644 --- a/ttystatus/remtime.py +++ b/ttystatus/remtime.py @@ -29,6 +29,7 @@ class RemainingTime(ttystatus.Widget): self.started = None self.default = '--h--m--s' self.value = self.default + self.interesting_keys = set([done_name, total_name]) def get_time(self): # pragma: no cover '''Return current time. diff --git a/ttystatus/status.py b/ttystatus/status.py index f810525..012014a 100644 --- a/ttystatus/status.py +++ b/ttystatus/status.py @@ -43,7 +43,7 @@ class TerminalStatus(object): self._widgets = [] self._values = dict() self._m.clear() - + def __getitem__(self, key): '''Return value for key, or the empty string.''' return self._values.get(key, '') @@ -57,8 +57,9 @@ class TerminalStatus(object): self._values[key] = value width = self._m.width for w in self._widgets: - w.update(self, width) - width -= len(str(w)) + if w.interested_in(key): + w.update(self, width) + width -= len(str(w)) self._m.write(''.join(str(w) for w in self._widgets)) def increase(self, key, delta): diff --git a/ttystatus/string.py b/ttystatus/string.py index 34b2638..6806b01 100644 --- a/ttystatus/string.py +++ b/ttystatus/string.py @@ -23,6 +23,7 @@ class String(ttystatus.Widget): def __init__(self, key): self._key = key + self.interesting_keys = set([key]) def update(self, master, width): self.value = master[self._key] diff --git a/ttystatus/widget.py b/ttystatus/widget.py index 34d0787..c498e96 100644 --- a/ttystatus/widget.py +++ b/ttystatus/widget.py @@ -32,6 +32,10 @@ class Widget(object): else: return '' + def interested_in(self, key): + '''Are we interested in this specific value?''' + return key in self.interesting_keys + def update(self, master, width): '''Update displayed value for widget, from values in master. -- cgit v1.2.1 From c6071c10b8859f09f9cc7f0949d2fd7e3ad2d9c0 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 08:41:18 +1200 Subject: Speed things up further by pre-computing which widgets are interested in which key. --- ttystatus/bytesize.py | 2 +- ttystatus/counter.py | 2 +- ttystatus/elapsed.py | 2 +- ttystatus/index.py | 2 +- ttystatus/literal.py | 2 +- ttystatus/pathname.py | 2 +- ttystatus/percent.py | 2 +- ttystatus/progressbar.py | 2 +- ttystatus/remtime.py | 2 +- ttystatus/status.py | 10 ++++++---- ttystatus/string.py | 2 +- ttystatus/widget.py | 7 +++---- 12 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ttystatus/bytesize.py b/ttystatus/bytesize.py index 3a2e88f..56c6d4c 100644 --- a/ttystatus/bytesize.py +++ b/ttystatus/bytesize.py @@ -24,7 +24,7 @@ class ByteSize(ttystatus.Widget): def __init__(self, name): self.name = name self.value = self.format(0) - self.interesting_keys = set([name]) + self.interesting_keys = [name] def format(self, bytes): units = ( diff --git a/ttystatus/counter.py b/ttystatus/counter.py index a401d8d..770b5e7 100644 --- a/ttystatus/counter.py +++ b/ttystatus/counter.py @@ -26,7 +26,7 @@ class Counter(ttystatus.Widget): self.prev = None self.count = 0 self.value = '0' - self.interesting_keys = set([name]) + self.interesting_keys = [name] def update(self, master, width): if master[self.name] != self.prev: diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py index 1e14ecf..c9bfc42 100644 --- a/ttystatus/elapsed.py +++ b/ttystatus/elapsed.py @@ -26,7 +26,7 @@ class ElapsedTime(ttystatus.Widget): def __init__(self): self.started = None self.value = self.format(0) - self.interesting_keys = set() + self.interesting_keys = [] def get_time(self): # pragma: no cover '''Wrapper around time.time() for unit tests to override.''' diff --git a/ttystatus/index.py b/ttystatus/index.py index f402bea..a9e1541 100644 --- a/ttystatus/index.py +++ b/ttystatus/index.py @@ -25,7 +25,7 @@ class Index(ttystatus.Widget): self.name = name self.listname = listname self.value = self.format(0, 0) - self.interesting_keys = set([name, listname]) + self.interesting_keys = [name, listname] def format(self, index, listlen): return '%d/%d' % (index, listlen) diff --git a/ttystatus/literal.py b/ttystatus/literal.py index 5ccfdf2..a1ffa18 100644 --- a/ttystatus/literal.py +++ b/ttystatus/literal.py @@ -23,5 +23,5 @@ class Literal(ttystatus.Widget): def __init__(self, string): self.value = string - self.interesting_keys = set() + self.interesting_keys = [] diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py index 6933d03..9cf3971 100644 --- a/ttystatus/pathname.py +++ b/ttystatus/pathname.py @@ -27,7 +27,7 @@ class Pathname(ttystatus.Widget): def __init__(self, key): self._key = key - self.interesting_keys = set([key]) + self.interesting_keys = [key] def update(self, master, width): v = master.get(self._key, '') diff --git a/ttystatus/percent.py b/ttystatus/percent.py index b06d070..fec0dfc 100644 --- a/ttystatus/percent.py +++ b/ttystatus/percent.py @@ -26,7 +26,7 @@ class PercentDone(ttystatus.Widget): self.total_name = total_name self.decimals = decimals self.value = self.format(0, 1) - self.interesting_keys = set([done_name, total_name]) + self.interesting_keys = [done_name, total_name] def format(self, done, total): try: diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py index 52a2b11..ac44eb1 100644 --- a/ttystatus/progressbar.py +++ b/ttystatus/progressbar.py @@ -24,7 +24,7 @@ class ProgressBar(ttystatus.Widget): def __init__(self, done_name, total_name): self.done_name = done_name self.total_name = total_name - self.interesting_keys = set([done_name, total_name]) + self.interesting_keys = [done_name, total_name] def update(self, master, width): done = float(master.get(self.done_name, 0)) diff --git a/ttystatus/remtime.py b/ttystatus/remtime.py index 7602e54..e2ee684 100644 --- a/ttystatus/remtime.py +++ b/ttystatus/remtime.py @@ -29,7 +29,7 @@ class RemainingTime(ttystatus.Widget): self.started = None self.default = '--h--m--s' self.value = self.default - self.interesting_keys = set([done_name, total_name]) + self.interesting_keys = [done_name, total_name] def get_time(self): # pragma: no cover '''Return current time. diff --git a/ttystatus/status.py b/ttystatus/status.py index 012014a..d7d8ef4 100644 --- a/ttystatus/status.py +++ b/ttystatus/status.py @@ -37,11 +37,14 @@ class TerminalStatus(object): def add(self, widget): '''Add a new widget to the status display.''' self._widgets.append(widget) + for key in widget.interesting_keys: + self._interests[key] = self._interests.get(key, []) + [widget] def clear(self): '''Remove all widgets.''' self._widgets = [] self._values = dict() + self._interests = dict() self._m.clear() def __getitem__(self, key): @@ -56,10 +59,9 @@ class TerminalStatus(object): '''Set value for key.''' self._values[key] = value width = self._m.width - for w in self._widgets: - if w.interested_in(key): - w.update(self, width) - width -= len(str(w)) + for w in self._interests.get(key, []): + w.update(self, width) + width -= len(str(w)) self._m.write(''.join(str(w) for w in self._widgets)) def increase(self, key, delta): diff --git a/ttystatus/string.py b/ttystatus/string.py index 6806b01..950791c 100644 --- a/ttystatus/string.py +++ b/ttystatus/string.py @@ -23,7 +23,7 @@ class String(ttystatus.Widget): def __init__(self, key): self._key = key - self.interesting_keys = set([key]) + self.interesting_keys = [key] def update(self, master, width): self.value = master[self._key] diff --git a/ttystatus/widget.py b/ttystatus/widget.py index c498e96..e6fa9bc 100644 --- a/ttystatus/widget.py +++ b/ttystatus/widget.py @@ -23,6 +23,9 @@ class Widget(object): master TerminalStatus widget. They return the formatted string via __str__. + Widgets must have an attribute 'interesting_keys', listing the + keys it is interested in. + ''' def __str__(self): @@ -32,10 +35,6 @@ class Widget(object): else: return '' - def interested_in(self, key): - '''Are we interested in this specific value?''' - return key in self.interesting_keys - def update(self, master, width): '''Update displayed value for widget, from values in master. -- cgit v1.2.1 From 3a1ecab53b935e6c794d1080939884286a119320 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 08:46:03 +1200 Subject: Make value attribute required for widgets. Having to check it is costly. --- ttystatus/pathname.py | 1 + ttystatus/progressbar.py | 1 + ttystatus/string.py | 1 + ttystatus/widget.py | 7 ++----- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py index 9cf3971..0cc8106 100644 --- a/ttystatus/pathname.py +++ b/ttystatus/pathname.py @@ -28,6 +28,7 @@ class Pathname(ttystatus.Widget): def __init__(self, key): self._key = key self.interesting_keys = [key] + self.value = '' def update(self, master, width): v = master.get(self._key, '') diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py index ac44eb1..23574f7 100644 --- a/ttystatus/progressbar.py +++ b/ttystatus/progressbar.py @@ -25,6 +25,7 @@ class ProgressBar(ttystatus.Widget): self.done_name = done_name self.total_name = total_name self.interesting_keys = [done_name, total_name] + self.value = '' def update(self, master, width): done = float(master.get(self.done_name, 0)) diff --git a/ttystatus/string.py b/ttystatus/string.py index 950791c..f80c0ec 100644 --- a/ttystatus/string.py +++ b/ttystatus/string.py @@ -24,6 +24,7 @@ class String(ttystatus.Widget): def __init__(self, key): self._key = key self.interesting_keys = [key] + self.value = '' def update(self, master, width): self.value = master[self._key] diff --git a/ttystatus/widget.py b/ttystatus/widget.py index e6fa9bc..c3cb214 100644 --- a/ttystatus/widget.py +++ b/ttystatus/widget.py @@ -24,16 +24,13 @@ class Widget(object): via __str__. Widgets must have an attribute 'interesting_keys', listing the - keys it is interested in. + keys it is interested in, and 'value', for the formatted value. ''' def __str__(self): '''Return current value to be displayed for this widget.''' - if hasattr(self, 'value'): - return self.value - else: - return '' + return self.value def update(self, master, width): '''Update displayed value for widget, from values in master. -- cgit v1.2.1 From dc830c6e3bdaa9f2caf766a541a33767c14eacc3 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 09:20:10 +1200 Subject: Change update methods to only cache information and compute minimal things. Add a format() method that actually do the heavy duty lifting, such as string formatting, which is only needed during actual output. --- ttystatus/bytesize.py | 12 ++++++------ ttystatus/counter.py | 6 +++--- ttystatus/elapsed.py | 11 +++++------ ttystatus/index.py | 22 +++++++++++----------- ttystatus/index_tests.py | 2 +- ttystatus/literal.py | 2 ++ ttystatus/pathname.py | 24 +++++++++++++++--------- ttystatus/percent.py | 19 ++++++++++--------- ttystatus/progressbar.py | 27 +++++++++++++++++++-------- ttystatus/remtime.py | 31 +++++++++++++++---------------- ttystatus/remtime_tests.py | 1 + ttystatus/string.py | 3 +++ ttystatus/widget.py | 29 +++++++++++++++++++++++++++-- 13 files changed, 118 insertions(+), 71 deletions(-) diff --git a/ttystatus/bytesize.py b/ttystatus/bytesize.py index 56c6d4c..f47d453 100644 --- a/ttystatus/bytesize.py +++ b/ttystatus/bytesize.py @@ -23,10 +23,10 @@ class ByteSize(ttystatus.Widget): def __init__(self, name): self.name = name - self.value = self.format(0) self.interesting_keys = [name] + self._bytes = 0 - def format(self, bytes): + def format(self): units = ( (1024**4, 2, 'TiB'), (1024**3, 2, 'GiB'), @@ -35,11 +35,11 @@ class ByteSize(ttystatus.Widget): ) for factor, decimals, unit in units: - if bytes >= factor: + if self._bytes >= factor: return '%.*f %s' % (decimals, - float(bytes) / float(factor), + float(self._bytes) / float(factor), unit) - return '%d B' % bytes + return '%d B' % self._bytes def update(self, master, width): - self.value = self.format(master[self.name]) + self._bytes = master[self.name] diff --git a/ttystatus/counter.py b/ttystatus/counter.py index 770b5e7..5d3e11f 100644 --- a/ttystatus/counter.py +++ b/ttystatus/counter.py @@ -25,12 +25,12 @@ class Counter(ttystatus.Widget): self.name = name self.prev = None self.count = 0 - self.value = '0' self.interesting_keys = [name] + + def format(self): + return str(self.count) def update(self, master, width): if master[self.name] != self.prev: self.prev = master[self.name] self.count += 1 - self.value = str(self.count) - diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py index c9bfc42..e9a9a85 100644 --- a/ttystatus/elapsed.py +++ b/ttystatus/elapsed.py @@ -25,14 +25,15 @@ class ElapsedTime(ttystatus.Widget): def __init__(self): self.started = None - self.value = self.format(0) - self.interesting_keys = [] + self.interesting_keys = [] # FIXME: Need to declare interest in all keys? + self.secs = 0 def get_time(self): # pragma: no cover '''Wrapper around time.time() for unit tests to override.''' return time.time() - def format(self, secs): + def format(self): + secs = self.secs hours = secs / 3600 secs %= 3600 mins = secs / 60 @@ -42,6 +43,4 @@ class ElapsedTime(ttystatus.Widget): def update(self, master, width): if self.started is None: self.started = self.get_time() - secs = self.get_time() - self.started - self.value = self.format(secs) - + self.secs = self.get_time() - self.started diff --git a/ttystatus/index.py b/ttystatus/index.py index a9e1541..941165a 100644 --- a/ttystatus/index.py +++ b/ttystatus/index.py @@ -24,18 +24,18 @@ class Index(ttystatus.Widget): def __init__(self, name, listname): self.name = name self.listname = listname - self.value = self.format(0, 0) self.interesting_keys = [name, listname] + self.value = None + self.listvalue = [] - def format(self, index, listlen): - return '%d/%d' % (index, listlen) - - def update(self, master, width): - value = master[self.name] - listvalue = master[self.listname] + def format(self): try: - index = listvalue.index(value) + 1 + index = self.listvalue.index(self.value) + 1 except ValueError: - pass - else: - self.value = self.format(index, len(listvalue)) + index = 0 + return '%d/%d' % (index, len(self.listvalue)) + + def update(self, master, width): + self.value = master[self.name] + self.listvalue = master[self.listname] + diff --git a/ttystatus/index_tests.py b/ttystatus/index_tests.py index 9d825b3..3c91903 100644 --- a/ttystatus/index_tests.py +++ b/ttystatus/index_tests.py @@ -33,5 +33,5 @@ class IndexTests(unittest.TestCase): def test_handles_value_not_in_list(self): self.w.update({ 'foo': 'xxx', 'foos': ['a', 'x', 'b'] }, 999) - self.assertEqual(str(self.w), '0/0') + self.assertEqual(str(self.w), '0/3') diff --git a/ttystatus/literal.py b/ttystatus/literal.py index a1ffa18..5c41b69 100644 --- a/ttystatus/literal.py +++ b/ttystatus/literal.py @@ -25,3 +25,5 @@ class Literal(ttystatus.Widget): self.value = string self.interesting_keys = [] + def format(self): + return self.value diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py index 0cc8106..0f5fb43 100644 --- a/ttystatus/pathname.py +++ b/ttystatus/pathname.py @@ -28,14 +28,20 @@ class Pathname(ttystatus.Widget): def __init__(self, key): self._key = key self.interesting_keys = [key] - self.value = '' - - def update(self, master, width): - v = master.get(self._key, '') - if len(v) > width: + self.pathname = '' + self.width = 0 + + def format(self): + v = self.pathname + if len(v) > self.width: ellipsis = '...' - if len(ellipsis) < width: - v = ellipsis + v[-(width - len(ellipsis)):] + if len(ellipsis) < self.width: + v = ellipsis + v[-(self.width - len(ellipsis)):] else: - v = v[-width:] - self.value = v + v = v[-self.width:] + return v + + def update(self, master, width): + self.pathname = master.get(self._key, '') + self.width = width + diff --git a/ttystatus/percent.py b/ttystatus/percent.py index fec0dfc..8914f41 100644 --- a/ttystatus/percent.py +++ b/ttystatus/percent.py @@ -25,18 +25,19 @@ class PercentDone(ttystatus.Widget): self.done_name = done_name self.total_name = total_name self.decimals = decimals - self.value = self.format(0, 1) + self.done = 0 + self.total = 1 self.interesting_keys = [done_name, total_name] - def format(self, done, total): + def format(self): try: - done = float(done) - total = float(total) + done = float(self.done) + total = float(self.total) except ValueError: - return self.value - else: - return '%.*f %%' % (self.decimals, 100.0 * done / total) + done = 0 + total = 1 + return '%.*f %%' % (self.decimals, 100.0 * done / total) def update(self, master, width): - self.value = self.format(master[self.done_name], - master[self.total_name]) + self.done = master[self.done_name] + self.total = master[self.total_name] diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py index 23574f7..e8549f2 100644 --- a/ttystatus/progressbar.py +++ b/ttystatus/progressbar.py @@ -25,16 +25,27 @@ class ProgressBar(ttystatus.Widget): self.done_name = done_name self.total_name = total_name self.interesting_keys = [done_name, total_name] - self.value = '' - - def update(self, master, width): - done = float(master.get(self.done_name, 0)) - total = float(master.get(self.total_name, 1) or 0) + self.done = 0 + self.total = 1 + self.width = 0 + + def format(self): + try: + done = float(self.done) + total = float(self.total) + except ValueError: + done = 0 + total = 1 if total == 0: fraction = 0 else: fraction = done / total - n_stars = int(round(fraction * width)) - n_dashes = int(width - n_stars) - self.value = ('#' * n_stars) + ('-' * n_dashes) + n_stars = int(round(fraction * self.width)) + n_dashes = int(self.width - n_stars) + return ('#' * n_stars) + ('-' * n_dashes) + + def update(self, master, width): + self.done = master[self.done_name] + self.total = master[self.total_name] + self.width = width diff --git a/ttystatus/remtime.py b/ttystatus/remtime.py index e2ee684..fd0d07c 100644 --- a/ttystatus/remtime.py +++ b/ttystatus/remtime.py @@ -28,8 +28,9 @@ class RemainingTime(ttystatus.Widget): self.total_name = total_name self.started = None self.default = '--h--m--s' - self.value = self.default self.interesting_keys = [done_name, total_name] + self.done = 0 + self.total = 1 def get_time(self): # pragma: no cover '''Return current time. @@ -41,24 +42,22 @@ class RemainingTime(ttystatus.Widget): return time.time() - def format(self, secs): - hours = secs / (60 * 60) - secs %= (60 * 60) - mins = secs / 60 - secs %= 60 - return '%02dh%02dm%02ds' % (hours, mins, secs) - - def update(self, master, width): + def format(self): if self.started is None: self.started = self.get_time() duration = self.get_time() - self.started if duration >= 1.0: - done = float(master.get(self.done_name, 0) or 0) - total = float(master.get(self.total_name, 0) or 0) - speed = done / duration - remaining = total - done + speed = self.done / duration + remaining = self.total - self.done if speed > 0: secs = remaining / speed - self.value = self.format(secs) - else: - self.value = self.default + hours = secs / (60 * 60) + secs %= (60 * 60) + mins = secs / 60 + secs %= 60 + return '%02dh%02dm%02ds' % (hours, mins, secs) + return self.default + + def update(self, master, width): + self.done = master[self.done_name] + self.total = master[self.total_name] diff --git a/ttystatus/remtime_tests.py b/ttystatus/remtime_tests.py index 6c6f452..2c67345 100644 --- a/ttystatus/remtime_tests.py +++ b/ttystatus/remtime_tests.py @@ -29,6 +29,7 @@ class RemainingTimeTests(unittest.TestCase): self.assertEqual(str(self.w), '--h--m--s') def test_estimates_and_formats_correctly(self): + self.assertEqual(str(self.w), '--h--m--s') self.w.update({ 'done': 0, 'total': 100 }, 999) self.w.get_time = lambda: 5.0 self.w.update({ 'done': 5, 'total': 100 }, 999) diff --git a/ttystatus/string.py b/ttystatus/string.py index f80c0ec..1dc3023 100644 --- a/ttystatus/string.py +++ b/ttystatus/string.py @@ -25,6 +25,9 @@ class String(ttystatus.Widget): self._key = key self.interesting_keys = [key] self.value = '' + + def format(self): + return self.value def update(self, master, width): self.value = master[self._key] diff --git a/ttystatus/widget.py b/ttystatus/widget.py index c3cb214..0f03365 100644 --- a/ttystatus/widget.py +++ b/ttystatus/widget.py @@ -23,14 +23,39 @@ class Widget(object): master TerminalStatus widget. They return the formatted string via __str__. + A widget's value may be derived from values stored in the TerminalStatus + widget (called master). Each such value has a key. Computing a widget's + value is a two-step process: when the values associated with keys + are updated, the widget's update() method is called to notify it of + this. update() may compute intermediate values, such as maintain a + counter of the number of changes. It should avoid costly operations + that are only needed when the widget's formatted value is needed. + Those should go into the format() method instead. Thus, update() would + update a counter, format() would create a string representing the + counter. + + This is necessary because actual on-screen updates only happen + every so often, not every time a value in the master changes, and + often the string formatting part is expensive. + Widgets must have an attribute 'interesting_keys', listing the - keys it is interested in, and 'value', for the formatted value. + keys it is interested in. ''' def __str__(self): '''Return current value to be displayed for this widget.''' - return self.value + return self.format() + + def format(self): + '''Format the current value. + + This will be called only when the value actually needs to be + formatted. + + ''' + + return '' def update(self, master, width): '''Update displayed value for widget, from values in master. -- cgit v1.2.1 From 129e223f07e7d090655da2bfdaa6262cfa399f2e Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 09:27:00 +1200 Subject: Don't attempt to write until it is time. --- ttystatus/messager.py | 9 ++++++--- ttystatus/messager_tests.py | 12 ++++++++++++ ttystatus/status.py | 3 ++- ttystatus/status_tests.py | 3 +++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ttystatus/messager.py b/ttystatus/messager.py index 43fb0e1..c322590 100644 --- a/ttystatus/messager.py +++ b/ttystatus/messager.py @@ -85,14 +85,17 @@ class Messager(object): self._raw_write('\r' + (' ' * len(self._last_msg)) + '\r') self._raw_write(string) self._last_msg = string + + def time_to_write(self): + '''Is it time to write now?''' + return self._now() - self._last_time >= self._period def write(self, string): '''Write raw data, but only once per period.''' string = string[:self.width] - now = self._now() - if now - self._last_time >= self._period: + if self.time_to_write(): self._overwrite(string) - self._last_time = now + self._last_time = self._now() self._cached_msg = string def clear(self): diff --git a/ttystatus/messager_tests.py b/ttystatus/messager_tests.py index 4830727..b57fbaf 100644 --- a/ttystatus/messager_tests.py +++ b/ttystatus/messager_tests.py @@ -46,6 +46,18 @@ class MessagerTests(unittest.TestCase): def test_raw_writes_something_if_output_is_not_a_terminal(self): self.messager._raw_write('foo') self.assertEqual(self.output.getvalue(), 'foo') + + def test_knows_it_is_time_to_write_at_start(self): + self.assert_(self.messager.time_to_write()) + + def test_knows_it_is_not_time_to_write_right_after_previous_one(self): + self.messager._last_time = self.messager._now() + self.assertFalse(self.messager.time_to_write()) + + def test_knows_it_is_time_to_write_after_a_period(self): + self.messager._last_time = (self.messager._now() - + self.messager._period*2) + self.assert_(self.messager.time_to_write()) def test_cached_write_writes_first_thing(self): self.messager.write('foo') diff --git a/ttystatus/status.py b/ttystatus/status.py index d7d8ef4..67443fc 100644 --- a/ttystatus/status.py +++ b/ttystatus/status.py @@ -62,7 +62,8 @@ class TerminalStatus(object): for w in self._interests.get(key, []): w.update(self, width) width -= len(str(w)) - self._m.write(''.join(str(w) for w in self._widgets)) + if self._m.time_to_write(): + self._m.write(''.join(str(w) for w in self._widgets)) def increase(self, key, delta): '''Increase value for a key by a given amount.''' diff --git a/ttystatus/status_tests.py b/ttystatus/status_tests.py index 36bc4bd..d22db53 100644 --- a/ttystatus/status_tests.py +++ b/ttystatus/status_tests.py @@ -25,6 +25,9 @@ class DummyMessager(object): def clear(self): pass + + def time_to_write(self): + return True def write(self, string): pass -- cgit v1.2.1 From e6618ad0ed66ed459a55ecd903a5627dacf97915 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 7 Jul 2010 09:41:02 +1200 Subject: Add a way for widgets to say "I'm always interested in updates". Fix ElapsedTime to use that. --- example.py | 6 ++++-- ttystatus/elapsed.py | 2 +- ttystatus/status.py | 10 +++++++--- ttystatus/status_tests.py | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/example.py b/example.py index a1a2c73..3b7b8c8 100644 --- a/example.py +++ b/example.py @@ -25,7 +25,8 @@ import ttystatus def main(): ts = ttystatus.TerminalStatus(period=0.1) - ts.add(ttystatus.Literal('Looking for files: ')) + ts.add(ttystatus.ElapsedTime()) + ts.add(ttystatus.Literal(' Looking for files: ')) ts.add(ttystatus.Counter('pathname')) ts.add(ttystatus.Literal(' found, currently in ')) ts.add(ttystatus.Pathname('dirname')) @@ -39,7 +40,8 @@ def main(): pathnames.append(pathname) ts.clear() - ts.add(ttystatus.Literal('Finding symlinks: ')) + ts.add(ttystatus.ElapsedTime()) + ts.add(ttystatus.Literal(' Finding symlinks: ')) ts.add(ttystatus.Counter('symlink')) ts.add(ttystatus.Literal(' found; now at ')) ts.add(ttystatus.Index('pathname', 'pathnames')) diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py index e9a9a85..2032e6e 100644 --- a/ttystatus/elapsed.py +++ b/ttystatus/elapsed.py @@ -25,7 +25,7 @@ class ElapsedTime(ttystatus.Widget): def __init__(self): self.started = None - self.interesting_keys = [] # FIXME: Need to declare interest in all keys? + self.interesting_keys = None self.secs = 0 def get_time(self): # pragma: no cover diff --git a/ttystatus/status.py b/ttystatus/status.py index 67443fc..138b615 100644 --- a/ttystatus/status.py +++ b/ttystatus/status.py @@ -37,14 +37,18 @@ class TerminalStatus(object): def add(self, widget): '''Add a new widget to the status display.''' self._widgets.append(widget) - for key in widget.interesting_keys: - self._interests[key] = self._interests.get(key, []) + [widget] + if widget.interesting_keys is None: + self._wildcards += [widget] + else: + for key in widget.interesting_keys: + self._interests[key] = self._interests.get(key, []) + [widget] def clear(self): '''Remove all widgets.''' self._widgets = [] self._values = dict() self._interests = dict() + self._wildcards = list() self._m.clear() def __getitem__(self, key): @@ -59,7 +63,7 @@ class TerminalStatus(object): '''Set value for key.''' self._values[key] = value width = self._m.width - for w in self._interests.get(key, []): + for w in self._interests.get(key, []) + self._wildcards: w.update(self, width) width -= len(str(w)) if self._m.time_to_write(): diff --git a/ttystatus/status_tests.py b/ttystatus/status_tests.py index d22db53..36716fd 100644 --- a/ttystatus/status_tests.py +++ b/ttystatus/status_tests.py @@ -52,6 +52,22 @@ class TerminalStatusTests(unittest.TestCase): self.ts.add(w) self.assertEqual(self.ts._widgets, [w]) + def test_adds_widget_as_interested_in_keys(self): + class W(ttystatus.Widget): + def __init__(self): + self.interesting_keys = ['foo'] + w = W() + self.ts.add(w) + self.assert_(w in self.ts._interests['foo']) + + def test_adds_widget_to_wildcards(self): + class W(ttystatus.Widget): + def __init__(self): + self.interesting_keys = None + w = W() + self.ts.add(w) + self.assert_(w in self.ts._wildcards) + def test_removes_all_widgets(self): self.ts.add(ttystatus.Literal('foo')) self.ts.clear() -- cgit v1.2.1