summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--ttystatus/bytesize.py12
-rw-r--r--ttystatus/bytesize_tests.py33
-rw-r--r--ttystatus/bytespeed.py7
-rw-r--r--ttystatus/bytespeed_tests.py27
-rw-r--r--ttystatus/counter.py7
-rw-r--r--ttystatus/counter_tests.py21
-rw-r--r--ttystatus/elapsed.py5
-rw-r--r--ttystatus/elapsed_tests.py15
-rw-r--r--ttystatus/fmt_tests.py8
-rw-r--r--ttystatus/index.py7
-rw-r--r--ttystatus/index_tests.py13
-rw-r--r--ttystatus/integer.py19
-rw-r--r--ttystatus/integer_tests.py13
-rw-r--r--ttystatus/literal.py3
-rw-r--r--ttystatus/literal_tests.py9
-rw-r--r--ttystatus/pathname.py20
-rw-r--r--ttystatus/pathname_tests.py31
-rw-r--r--ttystatus/percent.py7
-rw-r--r--ttystatus/percent_tests.py17
-rw-r--r--ttystatus/progressbar.py14
-rw-r--r--ttystatus/progressbar_tests.py38
-rw-r--r--ttystatus/remtime.py5
-rw-r--r--ttystatus/remtime_tests.py33
-rw-r--r--ttystatus/status.py45
-rw-r--r--ttystatus/status_tests.py56
-rw-r--r--ttystatus/string.py7
-rw-r--r--ttystatus/string_tests.py13
-rw-r--r--ttystatus/widget.py63
29 files changed, 303 insertions, 250 deletions
diff --git a/NEWS b/NEWS
index 31f8482..63dcdb9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,11 @@
NEWS file for ttystatus
=======================
+Version 0.17, released UNRELEASED
+---------------------------------
+
+* Committed genocide towards whole civilizations of rendering problems.
+
Version 0.16, released 2012-04-08
---------------------------------
diff --git a/ttystatus/bytesize.py b/ttystatus/bytesize.py
index f47d453..13ee68b 100644
--- a/ttystatus/bytesize.py
+++ b/ttystatus/bytesize.py
@@ -20,13 +20,17 @@ import ttystatus
class ByteSize(ttystatus.Widget):
'''Display data size in bytes, KiB, etc.'''
+
+ static_width = False
def __init__(self, name):
self.name = name
- self.interesting_keys = [name]
self._bytes = 0
+
+ def update(self, ts):
+ self._bytes = ts[self.name]
- def format(self):
+ def render(self, width):
units = (
(1024**4, 2, 'TiB'),
(1024**3, 2, 'GiB'),
@@ -40,6 +44,4 @@ class ByteSize(ttystatus.Widget):
float(self._bytes) / float(factor),
unit)
return '%d B' % self._bytes
-
- def update(self, master, width):
- self._bytes = master[self.name]
+
diff --git a/ttystatus/bytesize_tests.py b/ttystatus/bytesize_tests.py
index 11958f4..15a0ff5 100644
--- a/ttystatus/bytesize_tests.py
+++ b/ttystatus/bytesize_tests.py
@@ -24,34 +24,37 @@ class ByteSizeTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.ByteSize('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_formats_zero_bytes_without_update(self):
- self.assertEqual(str(self.w), '0 B')
+ self.assertEqual(self.w.render(0), '0 B')
def test_formats_zero_bytes_correctly(self):
- self.w.update({ 'foo': 0 }, 999)
- self.assertEqual(str(self.w), '0 B')
+ self.w.update({ 'foo': 0 })
+ self.assertEqual(self.w.render(0), '0 B')
def test_formats_one_bytes_correctly(self):
- self.w.update({ 'foo': 1 }, 999)
- self.assertEqual(str(self.w), '1 B')
+ self.w.update({ 'foo': 1 })
+ self.assertEqual(self.w.render(0), '1 B')
def test_formats_1023_bytes_correctly(self):
- self.w.update({ 'foo': 1023 }, 999)
- self.assertEqual(str(self.w), '1023 B')
+ self.w.update({ 'foo': 1023 })
+ self.assertEqual(self.w.render(0), '1023 B')
def test_formats_1024_bytes_correctly(self):
- self.w.update({ 'foo': 1024 }, 999)
- self.assertEqual(str(self.w), '1.0 KiB')
+ self.w.update({ 'foo': 1024 })
+ self.assertEqual(self.w.render(0), '1.0 KiB')
def test_formats_1_MiB_bytes_correctly(self):
- self.w.update({ 'foo': 1024**2 }, 999)
- self.assertEqual(str(self.w), '1.00 MiB')
+ self.w.update({ 'foo': 1024**2 })
+ self.assertEqual(self.w.render(0), '1.00 MiB')
def test_formats_1_GiB_bytes_correctly(self):
- self.w.update({ 'foo': 1024**3 }, 999)
- self.assertEqual(str(self.w), '1.00 GiB')
+ self.w.update({ 'foo': 1024**3 })
+ self.assertEqual(self.w.render(0), '1.00 GiB')
def test_formats_1_TiB_bytes_correctly(self):
- self.w.update({ 'foo': 1024**4 }, 999)
- self.assertEqual(str(self.w), '1.00 TiB')
+ self.w.update({ 'foo': 1024**4 })
+ self.assertEqual(self.w.render(0), '1.00 TiB')
diff --git a/ttystatus/bytespeed.py b/ttystatus/bytespeed.py
index 7f54b86..3331069 100644
--- a/ttystatus/bytespeed.py
+++ b/ttystatus/bytespeed.py
@@ -22,10 +22,11 @@ import ttystatus
class ByteSpeed(ttystatus.Widget):
'''Display data size in bytes, KiB, etc.'''
+
+ static_width = False
def __init__(self, name):
self.name = name
- self.interesting_keys = [name]
self._bytes = 0
self._started = None
@@ -34,7 +35,7 @@ class ByteSpeed(ttystatus.Widget):
return time.time()
- def format(self):
+ def render(self, width):
units = (
(1024**4, 2, 'TiB/s'),
(1024**3, 2, 'GiB/s'),
@@ -55,7 +56,7 @@ class ByteSpeed(ttystatus.Widget):
unit)
return '%.0f B/s' % speed
- def update(self, master, width):
+ def update(self, master):
if self._started is None:
self._started = self.now()
self._bytes = master[self.name]
diff --git a/ttystatus/bytespeed_tests.py b/ttystatus/bytespeed_tests.py
index 4c481fe..9bcc1e0 100644
--- a/ttystatus/bytespeed_tests.py
+++ b/ttystatus/bytespeed_tests.py
@@ -24,31 +24,34 @@ class ByteSpeedTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.ByteSpeed('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_formats_zero_speed_without_update(self):
- self.assertEqual(str(self.w), '0 B/s')
+ self.assertEqual(self.w.render(0), '0 B/s')
def test_formats_zero_bytes_correctly(self):
- self.w.update({ 'foo': 0 }, 999)
- self.assertEqual(str(self.w), '0 B/s')
+ self.w.update({ 'foo': 0 })
+ self.assertEqual(self.w.render(0), '0 B/s')
def test_formats_one_byte_per_second_correctly(self):
self.w.now = lambda: 1
- self.w.update({ 'foo': 0 }, 999)
+ self.w.update({ 'foo': 0 })
self.w.now = lambda: 2
- self.w.update({ 'foo': 1 }, 999)
- self.assertEqual(str(self.w), '1 B/s')
+ self.w.update({ 'foo': 1 })
+ self.assertEqual(self.w.render(0), '1 B/s')
def test_formats_ten_bytes_per_second_correctly(self):
self.w.now = lambda: 1
- self.w.update({ 'foo': 0 }, 999)
+ self.w.update({ 'foo': 0 })
self.w.now = lambda: 11
- self.w.update({ 'foo': 100 }, 999)
- self.assertEqual(str(self.w), '10 B/s')
+ self.w.update({ 'foo': 100 })
+ self.assertEqual(self.w.render(0), '10 B/s')
def test_formats_ten_tibs_per_second_correctly(self):
self.w.now = lambda: 1
- self.w.update({ 'foo': 0 }, 999)
+ self.w.update({ 'foo': 0 })
self.w.now = lambda: 2
- self.w.update({ 'foo': 10 * 1024**4 }, 999)
- self.assertEqual(str(self.w), '10.00 TiB/s')
+ self.w.update({ 'foo': 10 * 1024**4 })
+ self.assertEqual(self.w.render(0), '10.00 TiB/s')
diff --git a/ttystatus/counter.py b/ttystatus/counter.py
index 5d3e11f..d37ed90 100644
--- a/ttystatus/counter.py
+++ b/ttystatus/counter.py
@@ -21,16 +21,17 @@ class Counter(ttystatus.Widget):
'''Display a count of how many times a value has changed.'''
+ static_width = False
+
def __init__(self, name):
self.name = name
self.prev = None
self.count = 0
- self.interesting_keys = [name]
- def format(self):
+ def render(self, width):
return str(self.count)
- def update(self, master, width):
+ def update(self, master):
if master[self.name] != self.prev:
self.prev = master[self.name]
self.count += 1
diff --git a/ttystatus/counter_tests.py b/ttystatus/counter_tests.py
index 866bcbd..cadadea 100644
--- a/ttystatus/counter_tests.py
+++ b/ttystatus/counter_tests.py
@@ -24,20 +24,23 @@ class CounterTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.Counter('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_counts_zero_initially(self):
- self.assertEqual(str(self.w), '0')
+ self.assertEqual(self.w.render(0), '0')
def test_counts_one_change(self):
- self.w.update({ 'foo': 'a' }, 999)
- self.assertEqual(str(self.w), '1')
+ self.w.update({ 'foo': 'a' })
+ self.assertEqual(self.w.render(0), '1')
def test_counts_two_changes(self):
- self.w.update({ 'foo': 'a' }, 999)
- self.w.update({ 'foo': 'b' }, 999)
- self.assertEqual(str(self.w), '2')
+ self.w.update({ 'foo': 'a' })
+ self.w.update({ 'foo': 'b' })
+ self.assertEqual(self.w.render(0), '2')
def test_does_not_count_if_value_does_not_change(self):
- self.w.update({ 'foo': 'a' }, 999)
- self.w.update({ 'foo': 'a' }, 999)
- self.assertEqual(str(self.w), '1')
+ self.w.update({ 'foo': 'a' })
+ self.w.update({ 'foo': 'a' })
+ self.assertEqual(self.w.render(0), '1')
diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py
index 2032e6e..cb75722 100644
--- a/ttystatus/elapsed.py
+++ b/ttystatus/elapsed.py
@@ -25,14 +25,13 @@ class ElapsedTime(ttystatus.Widget):
def __init__(self):
self.started = None
- self.interesting_keys = None
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):
+ def render(self, width):
secs = self.secs
hours = secs / 3600
secs %= 3600
@@ -40,7 +39,7 @@ class ElapsedTime(ttystatus.Widget):
secs %= 60
return '%02dh%02dm%02ds' % (hours, mins, secs)
- def update(self, master, width):
+ def update(self, master):
if self.started is None:
self.started = self.get_time()
self.secs = self.get_time() - self.started
diff --git a/ttystatus/elapsed_tests.py b/ttystatus/elapsed_tests.py
index dc2bfe7..b87d57c 100644
--- a/ttystatus/elapsed_tests.py
+++ b/ttystatus/elapsed_tests.py
@@ -24,18 +24,21 @@ class ElapsedtimeTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.ElapsedTime()
+ def test_is_static_width(self):
+ self.assertTrue(self.w.static_width)
+
def test_shows_zero_initially(self):
- self.assertEqual(str(self.w), '00h00m00s')
+ self.assertEqual(self.w.render(0), '00h00m00s')
def test_shows_zero_after_first_update(self):
self.w.get_time = lambda: 1
- self.w.update({}, 999)
- self.assertEqual(str(self.w), '00h00m00s')
+ self.w.update({})
+ self.assertEqual(self.w.render(0), '00h00m00s')
def test_shows_one_one_one_after_second_update(self):
self.w.get_time = lambda: 0
- self.w.update({}, 999)
+ self.w.update({})
self.w.get_time = lambda: 60*60 + 60 + 1
- self.w.update({}, 999)
- self.assertEqual(str(self.w), '01h01m01s')
+ self.w.update({})
+ self.assertEqual(self.w.render(0), '01h01m01s')
diff --git a/ttystatus/fmt_tests.py b/ttystatus/fmt_tests.py
index af39948..310f131 100644
--- a/ttystatus/fmt_tests.py
+++ b/ttystatus/fmt_tests.py
@@ -32,13 +32,13 @@ class FormatTests(unittest.TestCase):
x = ttystatus.fmt.parse('hello, world')
self.assertEqual(len(x), 1)
self.assertEqual(type(x[0]), ttystatus.Literal)
- self.assertEqual(str(x[0]), 'hello, world')
+ self.assertEqual(x[0].render(0), 'hello, world')
def test_parses_escaped_pecent(self):
x = ttystatus.fmt.parse('%%')
self.assertEqual(len(x), 1)
self.assertEqual(type(x[0]), ttystatus.Literal)
- self.assertEqual(str(x[0]), '%')
+ self.assertEqual(x[0].render(0), '%')
def test_parses_parameterless_widget(self):
x = ttystatus.fmt.parse('%ElapsedTime()')
@@ -60,12 +60,12 @@ class FormatTests(unittest.TestCase):
self.assertEqual(len(x), 4)
self.assertEqual(type(x[0]), ttystatus.Literal)
- self.assertEqual(str(x[0]), 'hello, ')
+ self.assertEqual(x[0].render(0), 'hello, ')
self.assertEqual(type(x[1]), ttystatus.String)
self.assertEqual(type(x[2]), ttystatus.Literal)
- self.assertEqual(str(x[2]), ': ')
+ self.assertEqual(x[2].render(0), ': ')
self.assertEqual(type(x[3]), ttystatus.ElapsedTime)
diff --git a/ttystatus/index.py b/ttystatus/index.py
index 941165a..0158037 100644
--- a/ttystatus/index.py
+++ b/ttystatus/index.py
@@ -21,21 +21,22 @@ class Index(ttystatus.Widget):
'''Display the position of a value in a list of values.'''
+ static_width = False
+
def __init__(self, name, listname):
self.name = name
self.listname = listname
- self.interesting_keys = [name, listname]
self.value = None
self.listvalue = []
- def format(self):
+ def render(self, render):
try:
index = self.listvalue.index(self.value) + 1
except ValueError:
index = 0
return '%d/%d' % (index, len(self.listvalue))
- def update(self, master, width):
+ def update(self, master):
self.value = master[self.name]
self.listvalue = master[self.listname]
diff --git a/ttystatus/index_tests.py b/ttystatus/index_tests.py
index 3c91903..ccc4d4c 100644
--- a/ttystatus/index_tests.py
+++ b/ttystatus/index_tests.py
@@ -24,14 +24,17 @@ class IndexTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.Index('foo', 'foos')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_is_zero_initially(self):
- self.assertEqual(str(self.w), '0/0')
+ self.assertEqual(self.w.render(0), '0/0')
def test_gets_index_right(self):
- self.w.update({ 'foo': 'x', 'foos': ['a', 'x', 'b'] }, 999)
- self.assertEqual(str(self.w), '2/3')
+ self.w.update({ 'foo': 'x', 'foos': ['a', 'x', 'b'] })
+ self.assertEqual(self.w.render(0), '2/3')
def test_handles_value_not_in_list(self):
- self.w.update({ 'foo': 'xxx', 'foos': ['a', 'x', 'b'] }, 999)
- self.assertEqual(str(self.w), '0/3')
+ self.w.update({ 'foo': 'xxx', 'foos': ['a', 'x', 'b'] })
+ self.assertEqual(self.w.render(0), '0/3')
diff --git a/ttystatus/integer.py b/ttystatus/integer.py
index b86dbe0..7995bee 100644
--- a/ttystatus/integer.py
+++ b/ttystatus/integer.py
@@ -21,17 +21,18 @@ class Integer(ttystatus.Widget):
'''Display a value as an integer.'''
+ static_width = False
+
def __init__(self, key):
self._key = key
- self.interesting_keys = [key]
- self.value = '#'
+ self.value = None
- def format(self):
- return self.value
-
- def update(self, master, width):
+ def render(self, width):
try:
- self.value = str(int(master[self._key]))
- except ValueError:
- self.value = '#'
+ return str(int(self.value))
+ except (TypeError, ValueError):
+ return '#'
+
+ def update(self, master):
+ self.value = master[self._key]
diff --git a/ttystatus/integer_tests.py b/ttystatus/integer_tests.py
index a3f04fe..e064621 100644
--- a/ttystatus/integer_tests.py
+++ b/ttystatus/integer_tests.py
@@ -24,14 +24,17 @@ class IntegerTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.Integer('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_is_error_initially(self):
- self.assertEqual(str(self.w), '#')
+ self.assertEqual(self.w.render(0), '#')
def test_updates(self):
- self.w.update({'foo': 123}, 999)
- self.assertEqual(str(self.w), '123')
+ self.w.update({'foo': 123})
+ self.assertEqual(self.w.render(0), '123')
def test_becomes_error_symbol_if_value_is_not_integer(self):
- self.w.update({'foo': 'bar'}, 999)
- self.assertEqual(str(self.w), '#')
+ self.w.update({'foo': 'bar'})
+ self.assertEqual(self.w.render(0), '#')
diff --git a/ttystatus/literal.py b/ttystatus/literal.py
index 5c41b69..cdeb4d7 100644
--- a/ttystatus/literal.py
+++ b/ttystatus/literal.py
@@ -23,7 +23,6 @@ class Literal(ttystatus.Widget):
def __init__(self, string):
self.value = string
- self.interesting_keys = []
- def format(self):
+ def render(self, width):
return self.value
diff --git a/ttystatus/literal_tests.py b/ttystatus/literal_tests.py
index 969439d..b1e4537 100644
--- a/ttystatus/literal_tests.py
+++ b/ttystatus/literal_tests.py
@@ -21,6 +21,11 @@ import ttystatus
class LiteralTests(unittest.TestCase):
+ def setUp(self):
+ self.w = ttystatus.Literal('foo')
+
+ def test_is_static_width(self):
+ self.assertTrue(self.w.static_width)
+
def test_sets_value_correctly(self):
- literal = ttystatus.Literal('foo')
- self.assertEqual(str(literal), 'foo')
+ self.assertEqual(self.w.render(0), 'foo')
diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py
index 0f5fb43..0fbdbd7 100644
--- a/ttystatus/pathname.py
+++ b/ttystatus/pathname.py
@@ -24,24 +24,16 @@ class Pathname(ttystatus.Widget):
If it won't fit completely, truncate from the beginning of the string.
'''
+
+ static_width = False
def __init__(self, key):
self._key = key
- self.interesting_keys = [key]
self.pathname = ''
- self.width = 0
-
- def format(self):
- v = self.pathname
- if len(v) > self.width:
- ellipsis = '...'
- if len(ellipsis) < self.width:
- v = ellipsis + v[-(self.width - len(ellipsis)):]
- else:
- v = v[-self.width:]
- return v
+
+ def render(self, width):
+ return self.pathname[-width:]
- def update(self, master, width):
+ def update(self, master):
self.pathname = master.get(self._key, '')
- self.width = width
diff --git a/ttystatus/pathname_tests.py b/ttystatus/pathname_tests.py
index f0c54d5..70d6c1e 100644
--- a/ttystatus/pathname_tests.py
+++ b/ttystatus/pathname_tests.py
@@ -24,30 +24,21 @@ class PathnameTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.Pathname('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_is_empty_initially(self):
- self.assertEqual(str(self.w), '')
+ self.assertEqual(self.w.render(10), '')
def test_updates(self):
- self.w.update({'foo': 'bar'}, 999)
- self.assertEqual(str(self.w), 'bar')
+ self.w.update({'foo': 'bar'})
+ self.assertEqual(self.w.render(10), 'bar')
def test_handles_update_to_other_value(self):
- self.w.update({'other': 1}, 999)
- self.assertEqual(str(self.w), '')
-
+ self.w.update({'other': 1})
+ self.assertEqual(self.w.render(10), '')
+
def test_truncates_from_beginning(self):
- self.w.update({'foo': 'foobar'}, 5)
- self.assertEqual(str(self.w), '...ar')
-
- def test_does_not_truncate_for_exact_fit(self):
- self.w.update({'foo': 'foobar'}, 6)
- self.assertEqual(str(self.w), 'foobar')
-
- def test_does_not_add_ellipsis_if_it_will_not_fit(self):
- self.w.update({'foo': 'foobar'}, 3)
- self.assertEqual(str(self.w), 'bar')
-
- def test_adds_ellipsis_if_it_just_fits(self):
- self.w.update({'foo': 'foobar'}, 4)
- self.assertEqual(str(self.w), '...r')
+ self.w.update({'foo': '/this/is/a/path'})
+ self.assertEqual(self.w.render(6), 'a/path')
diff --git a/ttystatus/percent.py b/ttystatus/percent.py
index af2935c..1a60c23 100644
--- a/ttystatus/percent.py
+++ b/ttystatus/percent.py
@@ -20,6 +20,8 @@ import ttystatus
class PercentDone(ttystatus.Widget):
'''Display percent of task done.'''
+
+ static_width = False
def __init__(self, done_name, total_name, decimals=0):
self.done_name = done_name
@@ -27,9 +29,8 @@ class PercentDone(ttystatus.Widget):
self.decimals = decimals
self.done = 0
self.total = 1
- self.interesting_keys = [done_name, total_name]
- def format(self):
+ def render(self, render):
try:
done = float(self.done)
total = float(self.total)
@@ -40,6 +41,6 @@ class PercentDone(ttystatus.Widget):
total = 1
return '%.*f %%' % (self.decimals, 100.0 * done / total)
- def update(self, master, width):
+ def update(self, master):
self.done = master[self.done_name]
self.total = master[self.total_name]
diff --git a/ttystatus/percent_tests.py b/ttystatus/percent_tests.py
index 29c7cb4..9ffe0b9 100644
--- a/ttystatus/percent_tests.py
+++ b/ttystatus/percent_tests.py
@@ -24,18 +24,21 @@ class PercentDoneTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.PercentDone('done', 'total', decimals=1)
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
+
def test_shows_zero_value_initially(self):
- self.assertEqual(str(self.w), '0.0 %')
+ self.assertEqual(self.w.render(0), '0.0 %')
def test_sets_value(self):
- self.w.update({ 'done': 50, 'total': 100 }, 999)
- self.assertEqual(str(self.w), '50.0 %')
+ self.w.update({ 'done': 50, 'total': 100 })
+ self.assertEqual(self.w.render(0), '50.0 %')
def test_handles_empty_strings_as_values(self):
- self.w.update({ 'done': '', 'total': '' }, 999)
- self.assertEqual(str(self.w), '0.0 %')
+ self.w.update({ 'done': '', 'total': '' })
+ self.assertEqual(self.w.render(0), '0.0 %')
def test_handles_zero_total(self):
- self.w.update({ 'done': 0, 'total': 0 }, 999)
- self.assertEqual(str(self.w), '0.0 %')
+ self.w.update({ 'done': 0, 'total': 0 })
+ self.assertEqual(self.w.render(0), '0.0 %')
diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py
index e8549f2..cd0f1a3 100644
--- a/ttystatus/progressbar.py
+++ b/ttystatus/progressbar.py
@@ -20,16 +20,16 @@ import ttystatus
class ProgressBar(ttystatus.Widget):
'''Display a progress bar.'''
+
+ static_width = False
def __init__(self, done_name, total_name):
self.done_name = done_name
self.total_name = total_name
- self.interesting_keys = [done_name, total_name]
self.done = 0
self.total = 1
- self.width = 0
- def format(self):
+ def render(self, width):
try:
done = float(self.done)
total = float(self.total)
@@ -40,12 +40,10 @@ class ProgressBar(ttystatus.Widget):
fraction = 0
else:
fraction = done / total
- n_stars = int(round(fraction * self.width))
- n_dashes = int(self.width - n_stars)
+ n_stars = int(round(fraction * width))
+ n_dashes = int(width - n_stars)
return ('#' * n_stars) + ('-' * n_dashes)
- def update(self, master, width):
+ def update(self, master):
self.done = master[self.done_name]
self.total = master[self.total_name]
- self.width = width
-
diff --git a/ttystatus/progressbar_tests.py b/ttystatus/progressbar_tests.py
index 0e3bf5a..a5dd425 100644
--- a/ttystatus/progressbar_tests.py
+++ b/ttystatus/progressbar_tests.py
@@ -23,39 +23,43 @@ class ProgressBarTests(unittest.TestCase):
def setUp(self):
self.w = ttystatus.ProgressBar('done', 'total')
+ self.width = 10
+
+ def test_is_not_static_width(self):
+ self.assertFalse(self.w.static_width)
def test_sets_initial_value_to_empty(self):
- self.assertEqual(str(self.w), '')
+ self.assertEqual(self.w.render(self.width), '-' * 10)
def test_shows_zero_percent_for_empty_string_total(self):
- self.w.update({ 'done': 1, 'total': '' }, 10)
- self.assertEqual(str(self.w), '-' * 10)
+ self.w.update({ 'done': 1, 'total': '' })
+ self.assertEqual(self.w.render(self.width), '-' * 10)
def test_shows_zero_percent_for_zero_total(self):
- self.w.update({ 'done': 1, 'total': 0 }, 10)
- self.assertEqual(str(self.w), '-' * 10)
+ self.w.update({ 'done': 1, 'total': 0 })
+ self.assertEqual(self.w.render(self.width), '-' * 10)
def test_shows_zero_percent_correctly(self):
- self.w.update({ 'done': 0, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '-' * 10)
+ self.w.update({ 'done': 0, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '-' * 10)
def test_shows_one_percent_correctly(self):
- self.w.update({ 'done': 1, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '-' * 10)
+ self.w.update({ 'done': 1, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '-' * 10)
def test_shows_ten_percent_correctly(self):
- self.w.update({ 'done': 10, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '#' + '-' * 9)
+ self.w.update({ 'done': 10, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '#' + '-' * 9)
def test_shows_ninety_percent_correctly(self):
- self.w.update({ 'done': 90, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '#' * 9 + '-')
+ self.w.update({ 'done': 90, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '#' * 9 + '-')
def test_shows_ninety_ine_percent_correctly(self):
- self.w.update({ 'done': 99, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '#' * 10)
+ self.w.update({ 'done': 99, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '#' * 10)
def test_shows_one_hundred_percent_correctly(self):
- self.w.update({ 'done': 100, 'total': 100 }, 10)
- self.assertEqual(str(self.w), '#' * 10)
+ self.w.update({ 'done': 100, 'total': 100 })
+ self.assertEqual(self.w.render(self.width), '#' * 10)
diff --git a/ttystatus/remtime.py b/ttystatus/remtime.py
index fd0d07c..cbf55bd 100644
--- a/ttystatus/remtime.py
+++ b/ttystatus/remtime.py
@@ -28,7 +28,6 @@ class RemainingTime(ttystatus.Widget):
self.total_name = total_name
self.started = None
self.default = '--h--m--s'
- self.interesting_keys = [done_name, total_name]
self.done = 0
self.total = 1
@@ -42,7 +41,7 @@ class RemainingTime(ttystatus.Widget):
return time.time()
- def format(self):
+ def render(self, render):
if self.started is None:
self.started = self.get_time()
duration = self.get_time() - self.started
@@ -58,6 +57,6 @@ class RemainingTime(ttystatus.Widget):
return '%02dh%02dm%02ds' % (hours, mins, secs)
return self.default
- def update(self, master, width):
+ def update(self, master):
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 2c67345..90afd42 100644
--- a/ttystatus/remtime_tests.py
+++ b/ttystatus/remtime_tests.py
@@ -25,31 +25,34 @@ class RemainingTimeTests(unittest.TestCase):
self.w = ttystatus.RemainingTime('done', 'total')
self.w.get_time = lambda: 0.0
+ def test_is_static_width(self):
+ self.assertTrue(self.w.static_width)
+
def test_is_dashes_initially(self):
- self.assertEqual(str(self.w), '--h--m--s')
+ self.assertEqual(self.w.render(0), '--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.assertEqual(self.w.render(0), '--h--m--s')
+ self.w.update({ 'done': 0, 'total': 100 })
self.w.get_time = lambda: 5.0
- self.w.update({ 'done': 5, 'total': 100 }, 999)
- self.assertEqual(str(self.w), '00h01m35s')
+ self.w.update({ 'done': 5, 'total': 100 })
+ self.assertEqual(self.w.render(0), '00h01m35s')
self.w.get_time = lambda: 10.0
- self.w.update({ 'done': 5, 'total': 100 }, 999)
- self.assertEqual(str(self.w), '00h03m10s')
+ self.w.update({ 'done': 5, 'total': 100 })
+ self.assertEqual(self.w.render(0), '00h03m10s')
self.w.get_time = lambda: 20.0
- self.w.update({ 'done': 80, 'total': 100 }, 999)
- self.assertEqual(str(self.w), '00h00m05s')
+ self.w.update({ 'done': 80, 'total': 100 })
+ self.assertEqual(self.w.render(0), '00h00m05s')
def test_handles_zero_speed(self):
- self.w.update({ 'done': 0, 'total': 100 }, 999)
+ self.w.update({ 'done': 0, 'total': 100 })
self.w.get_time = lambda: 5.0
- self.w.update({ 'done': 0, 'total': 100 }, 999)
- self.assertEqual(str(self.w), '--h--m--s')
+ self.w.update({ 'done': 0, 'total': 100 })
+ self.assertEqual(self.w.render(0), '--h--m--s')
def test_handles_empty_strings_for_done_and_total(self):
- self.w.update({ 'done': '', 'total': '' }, 999)
+ self.w.update({ 'done': '', 'total': '' })
self.w.get_time = lambda: 5.0
- self.w.update({ 'done': '', 'total': '' }, 999)
- self.assertEqual(str(self.w), '--h--m--s')
+ self.w.update({ 'done': '', 'total': '' })
+ self.assertEqual(self.w.render(0), '--h--m--s')
diff --git a/ttystatus/status.py b/ttystatus/status.py
index 25fd1c1..b98fecd 100644
--- a/ttystatus/status.py
+++ b/ttystatus/status.py
@@ -39,11 +39,6 @@ class TerminalStatus(object):
def add(self, widget):
'''Add a new widget to the status display.'''
self._widgets.append(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 format(self, format_string):
'''Add new widgets based on format string.
@@ -61,9 +56,6 @@ class TerminalStatus(object):
'''Remove all widgets.'''
self._widgets = []
self._values = dict()
- self._interests = dict()
- self._wildcards = list()
- self._latest_width = None
self._m.clear()
def __getitem__(self, key):
@@ -77,17 +69,34 @@ class TerminalStatus(object):
def __setitem__(self, key, value):
'''Set value for key.'''
self._values[key] = value
+ for w in self._widgets:
+ w.update(self)
if self._m.time_to_write():
- self._format()
+ self._write()
+
+ def _render(self):
+ '''Render current state of all widgets.'''
+
+ remaining = self._m.width
+
+ texts = [None] * len(self._widgets)
+
+ for i, w in enumerate(self._widgets):
+ if w.static_width:
+ texts[i] = w.render(0)
+ remaining -= len(texts[i])
+
+ for i, w in enumerate(self._widgets):
+ if not w.static_width:
+ texts[i] = w.render(remaining)
+ remaining -= len(texts[i])
+
+ return (''.join(texts))[:self._m.width]
+
+ def _write(self):
+ '''Render and output current state of all widgets.'''
+ self._m.write(self._render())
- def _format(self):
- '''Format and output all widgets.'''
- width = self._m.width
- for w in self._widgets:
- w.update(self, width)
- width -= len(str(w))
- 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.'''
self[key] = (self[key] or 0) + delta
@@ -102,7 +111,7 @@ class TerminalStatus(object):
def finish(self):
'''Finish status display.'''
- self._format()
+ self._write()
self._m.finish()
def disable(self):
diff --git a/ttystatus/status_tests.py b/ttystatus/status_tests.py
index 3583028..ef8959a 100644
--- a/ttystatus/status_tests.py
+++ b/ttystatus/status_tests.py
@@ -63,22 +63,6 @@ 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_adds_widgets_from_format_string(self):
self.ts.format('hello, %String(name)')
self.assertEqual(len(self.ts._widgets), 2)
@@ -110,9 +94,9 @@ class TerminalStatusTests(unittest.TestCase):
def test_updates_widgets_when_value_is_set(self):
w = ttystatus.String('foo')
self.ts.add(w)
- self.assertEqual(str(w), '')
+ self.assertEqual(w.render(0), '')
self.ts['foo'] = 'bar'
- self.assertEqual(str(w), 'bar')
+ self.assertEqual(w.render(0), 'bar')
def test_increases_value(self):
self.ts['foo'] = 10
@@ -138,3 +122,39 @@ class TerminalStatusTests(unittest.TestCase):
self.ts.enable()
self.assert_(self.ts._m.enabled)
+ def test_counts_correctly_even_without_rendering(self):
+ w = ttystatus.Counter('value')
+ n = 42
+ self.ts.add(w)
+ for i in range(n):
+ self.ts['value'] = i
+ self.assertEqual(w.render(0), str(n))
+
+ def test_renders_everything_when_there_is_space(self):
+ w1 = ttystatus.Literal('foo')
+ w2 = ttystatus.ProgressBar('done', 'total')
+ self.ts.add(w1)
+ self.ts.add(w2)
+ text = self.ts._render()
+ self.assertEqual(len(text), self.ts._m.width)
+
+ def test_renders_from_beginning_if_there_is_not_enough_space(self):
+ w1 = ttystatus.Literal('foo')
+ w2 = ttystatus.Literal('bar')
+ self.ts.add(w1)
+ self.ts.add(w2)
+ self.ts._m.width = 4
+ text = self.ts._render()
+ self.assertEqual(text, 'foob')
+
+ def test_renders_variable_size_width_according_to_space_keep_static(self):
+ w1 = ttystatus.Literal('foo')
+ w2 = ttystatus.ProgressBar('done', 'total')
+ w3 = ttystatus.Literal('bar')
+ self.ts.add(w1)
+ self.ts.add(w2)
+ self.ts.add(w3)
+ self.ts._m.width = 9
+ text = self.ts._render()
+ self.assertEqual(text, 'foo---bar')
+
diff --git a/ttystatus/string.py b/ttystatus/string.py
index 32a76ea..92fbf88 100644
--- a/ttystatus/string.py
+++ b/ttystatus/string.py
@@ -20,14 +20,15 @@ import ttystatus
class String(ttystatus.Widget):
'''Display a value as a string.'''
+
+ static_width = False
def __init__(self, key):
self._key = key
- self.interesting_keys = [key]
self.value = ''
- def format(self):
+ def render(self, render):
return str(self.value)
- def update(self, master, width):
+ def update(self, master):
self.value = master[self._key]
diff --git a/ttystatus/string_tests.py b/ttystatus/string_tests.py
index 39f480a..faa266b 100644
--- a/ttystatus/string_tests.py
+++ b/ttystatus/string_tests.py
@@ -24,13 +24,16 @@ class StringTests(unittest.TestCase):
def setUp(self):
self.s = ttystatus.String('foo')
+ def test_is_not_static_width(self):
+ self.assertFalse(self.s.static_width)
+
def test_is_empty_initially(self):
- self.assertEqual(str(self.s), '')
+ self.assertEqual(self.s.render(0), '')
def test_updates(self):
- self.s.update({'foo': 'bar'}, 999)
- self.assertEqual(str(self.s), 'bar')
+ self.s.update({'foo': 'bar'})
+ self.assertEqual(self.s.render(0), 'bar')
def test_handles_non_string_value(self):
- self.s.update({'foo': 123}, 999)
- self.assertEqual(str(self.s), '123')
+ self.s.update({'foo': 123})
+ self.assertEqual(self.s.render(0), '123')
diff --git a/ttystatus/widget.py b/ttystatus/widget.py
index 0f03365..9d14683 100644
--- a/ttystatus/widget.py
+++ b/ttystatus/widget.py
@@ -18,37 +18,39 @@ class Widget(object):
'''Base class for ttystatus widgets.
- Widgets are responsible for formatting part of the output. They
- get a value or values either directly from the user, or from the
- master TerminalStatus widget. They return the formatted string
- via __str__.
+ Widgets display stuff on screen. The value may depend on data provided
+ by the user (at creation time), or may be computed from one or more
+ values in the TerminalStatus object to which the widget object belongs.
- 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.
+ There are two steps:
- 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.
+ * the widget `update` method is called by TerminalStatus whenever
+ any of the values change
+ * the widget `render` method is called by TerminalStatus when it is
+ time to display things
+
+ Widgets may have a static size, or their size may vary. The
+ ``static_width`` property reveals this. This affects rendering:
+ static sized widgets are rendered at their one static size; variable
+ sized widgets are shrunk, if necessary, to make everything fit into
+ the available space. If it's not possible to shrink enough, widgets
+ are rendered from beginning until the space is full: variable sized
+ widgets are rendered as small as possible in this case.
+
+ '''
- Widgets must have an attribute 'interesting_keys', listing the
- keys it is interested in.
+ static_width = True
- '''
-
def __str__(self):
- '''Return current value to be displayed for this widget.'''
- return self.format()
-
- def format(self):
+ raise NotImplementedError()
+
+ def render(self, width):
'''Format the current value.
+
+ ``width`` is the available width for the widget. It need not use
+ all of it. If it's not possible for the widget to render itself
+ small enough to fit into the given width, it may return a larger
+ string, but the caller will probably truncate it.
This will be called only when the value actually needs to be
formatted.
@@ -57,11 +59,6 @@ class Widget(object):
return ''
- def update(self, master, width):
- '''Update displayed value for widget, from values in master.
-
- 'width' gives the width for which the widget should aim to fit.
- It is OK if it does not: for some widgets there is no way to
- adjust to a smaller size.
-
- '''
+ def update(self, terminal_status):
+ '''React to changes in values stored in a TerminalStatus.'''
+