#!/usr/bin/python """ Based on the code that Lyle Scott, III wrote. lyle@digitalfoo.net Watches the output of a command in a scrollable curses window. """ import curses import sys import random import locale import threading import subprocess import time locale.setlocale(locale.LC_ALL,"") class UnixWatchCommand: DOWN = 1 UP = -1 SPACE_KEY = 32 ESC_KEY = 27 PREFIX_SELECTED = '-' PREFIX_DESELECTED = '' outputLines = [] screen = None def __init__(self, command): self.stop = threading.Event() self.timer=threading.Thread(target=self.display) self.command = ' '.join(command) self.screen = curses.initscr() curses.noecho() curses.cbreak() self.screen.keypad(1) self.screen.border(0) self.topLineNum = 0 self.highlightLineNum = 0 self.markedLineNums = [] self.getOutputLines() self.timer.start() self.run() self.timer.join() def display(self): while not self.stop.is_set(): try: self.displayScreen() time.sleep(1) except: break def run(self): while True: try: self.displayScreen() # get user command c = self.screen.getch() if c == curses.KEY_UP: self.updown(self.UP) elif c == curses.KEY_DOWN: self.updown(self.DOWN) elif c == self.SPACE_KEY: self.markLine() elif c == self.ESC_KEY: self.stop.set() break except: self.stop.set() break def markLine(self): linenum = self.topLineNum + self.highlightLineNum if linenum in self.markedLineNums: self.markedLineNums.remove(linenum) else: self.markedLineNums.append(linenum) def check_output(self, command): process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output,stderr = process.communicate() retcode = process.poll() if retcode: raise subprocess.CalledProcessError(retcode, command, output=output[0]) return output.splitlines() def getOutputLines(self): self.outputLines = self.check_output(self.command) self.nOutputLines = len(self.outputLines) def displayScreen(self): # clear screen self.screen.clear() self.getOutputLines() # now paint the rows top = self.topLineNum bottom = self.topLineNum+curses.LINES for (index,line,) in enumerate(self.outputLines[top:bottom]): linenum = self.topLineNum + index if linenum in self.markedLineNums: prefix = self.PREFIX_SELECTED else: prefix = self.PREFIX_DESELECTED #line = '%s %s' % (prefix, line,) # highlight current line if index != self.highlightLineNum: self.screen.addstr(index, 0, line) else: self.screen.addstr(index, 0, line, curses.A_BOLD) self.screen.refresh() # move highlight up/down one line def updown(self, increment): nextLineNum = self.highlightLineNum + increment # paging if increment == self.UP and self.highlightLineNum == 0\ and self.topLineNum != 0: self.topLineNum += self.UP return elif increment == self.DOWN and nextLineNum == curses.LINES\ and (self.topLineNum+curses.LINES) != self.nOutputLines: self.topLineNum += self.DOWN return # scroll highlight line if increment == self.UP and (self.topLineNum != 0 or\ self.highlightLineNum != 0): self.highlightLineNum = nextLineNum elif increment == self.DOWN and\ (self.topLineNum+self.highlightLineNum+1) != self.nOutputLines\ and self.highlightLineNum != curses.LINES: self.highlightLineNum = nextLineNum def restoreScreen(self): curses.initscr() curses.nocbreak() curses.echo() curses.endwin() # catch any weird termination situations def __del__(self): self.restoreScreen() if __name__ == '__main__': ih = UnixWatchCommand(sys.argv[1:])