[Tutor] clearing lines for a 'front end' to a tool
James
jtp at nc.rr.com
Wed Sep 17 23:02:48 CEST 2008
Kent / Alan,
Thanks for the responses. I'm not completely certain that urwid is
appropriate for the program I'm writing, as it seems to be more of a
framework for developing a text GUI application. (similar to curses?)
The behavior of the program I'm writing is actually identical to that
of an open source product. I asked the developers of the project where
I could find the code that actually produced the output that I'm
mimicking, and he pointed me to the correct portion of the open source
code. Below is the class that generates the output.
-----
class JobStatusDisplay(object):
_bound_properties = ("curval", "failed", "running")
_jobs_column_width = 48
# Don't update the display unless at least this much
# time has passed, in units of seconds.
_min_display_latency = 2
_default_term_codes = {
'cr' : '\r',
'el' : '\x1b[K',
'nel' : '\n',
}
_termcap_name_map = {
'carriage_return' : 'cr',
'clr_eol' : 'el',
'newline' : 'nel',
}
def __init__(self, out=sys.stdout, quiet=False):
object.__setattr__(self, "out", out)
object.__setattr__(self, "quiet", quiet)
object.__setattr__(self, "maxval", 0)
object.__setattr__(self, "merges", 0)
object.__setattr__(self, "_changed", False)
object.__setattr__(self, "_displayed", False)
object.__setattr__(self, "_last_display_time", 0)
self.reset()
isatty = hasattr(out, "isatty") and out.isatty()
object.__setattr__(self, "_isatty", isatty)
if not isatty or not self._init_term():
term_codes = {}
for k, capname in self._termcap_name_map.iteritems():
term_codes[k] =
self._default_term_codes[capname]
object.__setattr__(self, "_term_codes", term_codes)
def _init_term(self):
"""
Initialize term control codes.
@rtype: bool
@returns: True if term codes were successfully initialized,
False otherwise.
"""
term_type = os.environ.get("TERM", "vt100")
tigetstr = None
try:
import curses
try:
curses.setupterm(term_type, self.out.fileno())
tigetstr = curses.tigetstr
except curses.error:
pass
except ImportError:
pass
if tigetstr is None:
return False
term_codes = {}
for k, capname in self._termcap_name_map.iteritems():
code = tigetstr(capname)
if code is None:
code = self._default_term_codes[capname]
term_codes[k] = code
object.__setattr__(self, "_term_codes", term_codes)
return True
def _format_msg(self, msg):
return ">>> %s" % msg
def _erase(self):
self.out.write(
self._term_codes['carriage_return'] + \
self._term_codes['clr_eol'])
self._displayed = False
def _display(self, line):
self.out.write(line)
self._displayed = True
def _update(self, msg):
out = self.out
if not self._isatty:
out.write(self._format_msg(msg) +
self._term_codes['newline'])
return
if self._displayed:
self._erase()
self._display(self._format_msg(msg))
def displayMessage(self, msg):
was_displayed = self._displayed
if self._isatty and self._displayed:
self._erase()
self.out.write(self._format_msg(msg) +
self._term_codes['newline'])
self._displayed = False
if was_displayed:
self._changed = True
self.display()
def reset(self):
self.maxval = 0
self.merges = 0
for name in self._bound_properties:
object.__setattr__(self, name, 0)
if self._displayed:
self.out.write(self._term_codes['newline'])
self._displayed = False
def __setattr__(self, name, value):
old_value = getattr(self, name)
if value == old_value:
return
object.__setattr__(self, name, value)
if name in self._bound_properties:
self._property_change(name, old_value, value)
def _property_change(self, name, old_value, new_value):
self._changed = True
self.display()
def _load_avg_str(self, digits=2):
try:
avg = os.getloadavg()
except OSError, e:
return str(e)
return ", ".join(fpformat.fix(x, digits) for x in avg)
def _display_status(self):
# Don't use len(self._completed_tasks) here since that also
# can include uninstall tasks.
curval_str = str(self.curval)
maxval_str = str(self.maxval)
running_str = str(self.running)
failed_str = str(self.failed)
load_avg_str = self._load_avg_str()
color_output = StringIO.StringIO()
plain_output = StringIO.StringIO()
style_file = portage.output.ConsoleStyleFile(color_output)
style_file.write_listener = plain_output
style_writer =
portage.output.StyleWriter(file=style_file, maxcol=9999)
style_writer.style_listener = style_file.new_styles
f = formatter.AbstractFormatter(style_writer)
number_style = "INFORM"
f.add_literal_data("Jobs: ")
f.push_style(number_style)
f.add_literal_data(curval_str)
f.pop_style()
f.add_literal_data(" of ")
f.push_style(number_style)
f.add_literal_data(maxval_str)
f.pop_style()
f.add_literal_data(" complete")
if self.running:
f.add_literal_data(", ")
f.push_style(number_style)
f.add_literal_data(running_str)
f.pop_style()
f.add_literal_data(" running")
if self.failed:
f.add_literal_data(", ")
f.push_style(number_style)
f.add_literal_data(failed_str)
f.pop_style()
f.add_literal_data(" failed")
padding = self._jobs_column_width - len(plain_output.getvalue())
if padding > 0:
f.add_literal_data(padding * " ")
f.add_literal_data("Load avg: ")
f.add_literal_data(load_avg_str)
self._update(color_output.getvalue())
xtermTitle(" ".join(plain_output.getvalue().split()))
-----
I don't see anything in the code that particularly "stands out" on how
to go about generating the output that I'd like. I do see, however,
that a curses object is being instantiated.
I think I'm a bit in over my head this time. ;) I did look through
some of the curses documentation on the web and I didn't see much on
how to clear lines. I don't really want to set up a "text GUI"
application (where there are windows and menus at the top); I just
want more flexibility in outputting text to the console.
btw, Alan, I'm using Linux. :)
-j
On Mon, Sep 15, 2008 at 8:42 PM, Alan Gauld <alan.gauld at btinternet.com> wrote:
> "James" <jtp at nc.rr.com> wrote
>
>> I'm writing a 'front end' which will show a list of the "things" that
>> are currently happening in the background in a simple
>> (non-interactive) interface. Here's an idea of what the terminal will
>> look like when the program is running.
>
> The critical piece of missing info is which platform/OS we
> are dealing with. Anyy kind of terminal control is going to be
> platform dependant - which is why curses may not be overkill!
> (except it doesn't work well on Windows)
>
>> The question here is mostly about how to generate this output. The
>> bottom line (the 'status') *always* remains at the bottom of the
>
> This is where terminal control comes in and a lot depends on
> what terninal you are using.
>
>> I'm unsure how to go about doing this... I imagine that the ANSI
>> "clear line" value may be helpful
>
> That would work on Windows but most *nix consoles don't use
> the ANSI codes consistently, most will be haoppier with DEC VT
> control codes (or Tektronics or Wyse). Thats where curses helps
> by hiding all that stuff.
>
> If on *nix use curses or any of several other console UI libraries.
> On Windows use native ANSI codes and/or the Conio library.
>
> Several other options here:
>
> http://py.vaults.ca/parnassus/apyllo.py/808292924.243256747
>
> Although many may be outdated now.
>
> Finally, this would be very easy to do in a Tkinter GUI.
> All you need is a containing frame, a text widget and a label and
> either a timer or a callback structure from your threads bto trigger
> updates. Probably easier than using a terninal interface!
>
> HTH,
>
> --
> Alan Gauld
> Author of the Learn to Program web site
> http://www.freenetpages.co.uk/hp/alan.gauld
>
> _______________________________________________
> Tutor maillist - Tutor at python.org
> http://mail.python.org/mailman/listinfo/tutor
>
More information about the Tutor
mailing list