summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2015-09-26 22:08:24 +0300
committerLars Wirzenius <liw@liw.fi>2015-09-26 22:55:42 +0300
commit7161c1eddb99c1210a31743ebe4dd6e3fb6c071f (patch)
treea2462fa2903fecceaffc7baf07eb2ab38e6b8a8a
parent4c3ead874f0b63fbe562957c0a55f452e01db54e (diff)
downloadttystatus-7161c1eddb99c1210a31743ebe4dd6e3fb6c071f.tar.gz
Allow multiline output
-rw-r--r--example2.py59
-rw-r--r--ttystatus/messager.py83
-rw-r--r--ttystatus/status.py30
3 files changed, 139 insertions, 33 deletions
diff --git a/example2.py b/example2.py
new file mode 100644
index 0000000..4600bf2
--- /dev/null
+++ b/example2.py
@@ -0,0 +1,59 @@
+# Copyright 2015 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/>.
+
+
+'''An example program for ttystatus: compute checksums.'''
+
+
+import hashlib
+import os
+import sys
+
+import ttystatus
+
+
+num_bytes = 1024**2
+
+
+def main():
+ ts = ttystatus.TerminalStatus(period=0.1)
+
+ ts.format(
+ 'Elapsed time: %ElapsedTime()\n'
+ 'Current file: %Pathname(filename)\n'
+ 'File count: %Counter(filename)\n'
+ 'Combined size: %ByteSize(bytes-read)\n'
+ 'Checksum speed: %ByteSpeed(bytes-read)'
+ )
+ ts['bytes-read'] = 0
+
+ for dirname, subdirs, basenames in os.walk(sys.argv[1]):
+ for basename in basenames:
+ filename = os.path.join(dirname, basename)
+ if os.path.isfile(filename):
+ ts['filename'] = filename
+ checksum = hashlib.sha512()
+ with open(filename, 'rb') as f:
+ data = f.read(num_bytes)
+ checksum.update(data)
+ ts['bytes-read'] += len(data)
+ if checksum.hexdigest().startswith('0'):
+ ts.notify('%s %s' % (checksum.hexdigest(), filename))
+
+ ts.finish()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ttystatus/messager.py b/ttystatus/messager.py
index cfab8ad..71013e3 100644
--- a/ttystatus/messager.py
+++ b/ttystatus/messager.py
@@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import curses
import fcntl
import struct
import termios
@@ -35,14 +36,16 @@ class Messager(object):
except IOError:
self.output = None
self._period = 1.0 if period is None else period
- self._last_msg = '' # What did we write last?
self._last_time = 0 # When did we write last?
self._cached_msg = '' # Last message from user, to write() method.
+ self._first_output = True # is our next output the first one?
self._fake_width = fake_width
self.set_width(self._get_terminal_width()) # Width of terminal
def _open_tty(self): # pragma: no cover
- return open('/dev/tty', 'w')
+ f = open('/dev/tty', 'wb')
+ curses.setupterm(None, f.fileno())
+ return f
def set_width(self, actual_width):
self.width = actual_width - 1
@@ -78,13 +81,7 @@ class Messager(object):
return width
def update_width(self): # pragma: no cover
- new_width = self._get_terminal_width()
- if new_width != self.width:
- # Clear the terminal from old stuff, using the old width.
- self.clear()
- # Get new width.
- self.set_width(new_width)
- self._overwrite(self._last_msg)
+ self.set_width(self._get_terminal_width())
def _raw_write(self, string):
'''Write raw data if output is terminal.'''
@@ -96,13 +93,6 @@ class Messager(object):
except IOError: # pragma: no cover
self._enabled = False
- def _overwrite(self, string):
- '''Overwrite current message on terminal.'''
- if self._last_msg:
- 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
@@ -110,14 +100,59 @@ class Messager(object):
def write(self, string):
'''Write raw data, always.'''
self.update_width()
- string = string[:self.width]
- self._overwrite(string)
- self._last_time = self._now()
+ rows = string.split('\n')
+
+ raw_parts = []
+
+ if self._first_output:
+ raw_parts.append('\n' * (len(rows) - 1))
+ self._first_output = False
+
+ if rows:
+ raw_parts.extend([
+ curses.tparm(curses.tigetstr('cuu'), len(rows) - 1), # go up
+ curses.tigetstr('cr'), # beginning of line
+ curses.tigetstr('el'), # erase to end of line
+ rows[0][:self.width],
+ ])
+ for row in rows[1:]:
+ raw_parts.extend([
+ curses.tparm(curses.tigetstr('cud'), 1), # down one line
+ curses.tigetstr('cr'), # beginning of line
+ curses.tigetstr('el'), # erase to end of line
+ row[:self.width],
+ ])
+
+ raw = ''.join(raw_parts)
+ self._raw_write(raw)
self._cached_msg = string
+ self._last_time = self._now()
def clear(self):
'''Remove current message from terminal.'''
- self._overwrite('')
+
+ rows = self._cached_msg.split('\n')
+
+ raw_parts = []
+
+ if rows:
+ raw_parts.extend([
+ curses.tparm(curses.tigetstr('cuu'), len(rows) - 1), # go up
+ curses.tigetstr('cr'), # beginning of line
+ curses.tigetstr('el'), # erase to end of line
+ ])
+ for row in rows[1:]:
+ raw_parts.extend([
+ curses.tparm(curses.tigetstr('cud'), 1), # down one line
+ curses.tigetstr('cr'), # beginning of line
+ curses.tigetstr('el'), # erase to end of line
+ ])
+ raw_parts.extend([
+ curses.tparm(curses.tigetstr('cuu'), len(rows) - 1), # go up
+ ])
+
+ raw = ''.join(raw_parts)
+ self._raw_write(raw)
def notify(self, string, f, force=False):
'''Show a notification message string to the user.
@@ -133,7 +168,6 @@ class Messager(object):
'''
if self._enabled or force:
- old = self._last_msg
self.clear()
try:
f.write('%s\n' % string)
@@ -141,12 +175,13 @@ class Messager(object):
except IOError:
# We ignore these. No point in crashing if terminal is bad.
pass
- self._overwrite(old)
+ self._first_output = True
+ self.write(self._cached_msg)
def finish(self):
'''Finalize output.'''
- if self._last_msg or self._cached_msg:
- self._overwrite(self._cached_msg)
+ if self._cached_msg:
+ self.write(self._cached_msg)
self._raw_write('\n')
def disable(self):
diff --git a/ttystatus/status.py b/ttystatus/status.py
index 11676cb..61adcbe 100644
--- a/ttystatus/status.py
+++ b/ttystatus/status.py
@@ -38,7 +38,11 @@ class TerminalStatus(object):
def add(self, widget):
'''Add a new widget to the status display.'''
- self._widgets.append(widget)
+ self._widget_rows[-1].append(widget)
+
+ def start_new_line(self):
+ '''Start a new line of widgets.'''
+ self._widget_rows.append([])
def format(self, format_string):
'''Add new widgets based on format string.
@@ -49,12 +53,16 @@ class TerminalStatus(object):
``format("hello, %String(name)")``.
'''
- for widget in ttystatus.parse(format_string):
- self.add(widget)
+
+ for i, line in enumerate(format_string.split('\n')):
+ if i > 0:
+ self.start_new_line()
+ for widget in ttystatus.parse(line):
+ self.add(widget)
def clear(self):
'''Remove all widgets.'''
- self._widgets = []
+ self._widget_rows = [[]]
self._values = dict()
self._m.clear()
@@ -69,8 +77,9 @@ class TerminalStatus(object):
def __setitem__(self, key, value):
'''Set value for key.'''
self._values[key] = value
- for w in self._widgets:
- w.update(self)
+ for row in self._widget_rows:
+ for w in row:
+ w.update(self)
if self._m.time_to_write():
self._write()
@@ -86,16 +95,19 @@ class TerminalStatus(object):
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):
remaining = self._m.width
- texts = [None] * len(self._widgets)
+ texts = [None] * len(widget_row)
- for i, w in enumerate(self._widgets):
+ for i, w in enumerate(widget_row):
if w.static_width:
texts[i] = w.render(0)
remaining -= len(texts[i])
- for i, w in enumerate(self._widgets):
+ for i, w in enumerate(widget_row):
if not w.static_width:
texts[i] = w.render(remaining)
remaining -= len(texts[i])