Terminal Controller for Python

I was looking for an easy way to control the terminal and print colored text in Python. I found this useful recipe. But I wanted something simpler yet powerful, so I decided to write my own little terminal controller.

Most computer terminals and terminal emulators support color and cursor control through a system of special escape sequences. For example, printing the sequence \x1b[H\x1b[2J will result in clearing the screen. Likewise, printing \x1b[31m changes the current terminal foreground color to red. You can go ahead and try:

# Clear the screen.
print '\x1b[H\x1b[2J'

This lightweight module uses curses to do the job. Curses Programming with Python is a great guide if you want to learn more about it.

The module supports the following colors:
supported colors
It also support various capabilities like going up, down, to beginning of the line and clearing the screen.

# Copyright: 2008 Nadia Alramli
# License: BSD
 
"""Terminal controller module
Example of usage:
    print BG_BLUE + 'Text on blue background' + NORMAL
    print BLUE + UNDERLINE + 'Blue underlined text' + NORMAL
    print BLUE + BG_YELLOW + BOLD + 'text' + NORMAL
"""
 
import sys
 
# The current module
MODULE = sys.modules[__name__]
 
COLORS = "BLUE GREEN CYAN RED MAGENTA YELLOW WHITE BLACK".split()
# List of terminal controls, you can add more to the list.
CONTROLS = {
    'BOL':'cr', 'UP':'cuu1', 'DOWN':'cud1', 'LEFT':'cub1', 'RIGHT':'cuf1',
    'CLEAR_SCREEN':'clear', 'CLEAR_EOL':'el', 'CLEAR_BOL':'el1',
    'CLEAR_EOS':'ed', 'BOLD':'bold', 'BLINK':'blink', 'DIM':'dim',
    'REVERSE':'rev', 'UNDERLINE':'smul', 'NORMAL':'sgr0',
    'HIDE_CURSOR':'cinvis', 'SHOW_CURSOR':'cnorm'
}
 
# List of numeric capabilities
VALUES = {
    'COLUMNS':'cols', # Width of the terminal (None for unknown)
    'LINES':'lines',  # Height of the terminal (None for unknown)
    'MAX_COLORS': 'colors',
}
 
def default():
    """Set the default attribute values"""
    for color in COLORS:
        setattr(MODULE, color, '')
        setattr(MODULE, 'BG_%s' % color, '')
    for control in CONTROLS:
        setattr(MODULE, control, '')
    for value in VALUES:
        setattr(MODULE, value, None)
 
def setup():
    """Set the terminal control strings"""
    # Initializing the terminal
    curses.setupterm()
    # Get the color escape sequence template or '' if not supported
    # setab and setaf are for ANSI escape sequences
    bgColorSeq = curses.tigetstr('setab') or curses.tigetstr('setb') or ''
    fgColorSeq = curses.tigetstr('setaf') or curses.tigetstr('setf') or ''
 
    for color in COLORS:
        # Get the color index from curses
        colorIndex = getattr(curses, 'COLOR_%s' % color)
        # Set the color escape sequence after filling the template with index
        setattr(MODULE, color, curses.tparm(fgColorSeq, colorIndex))
        # Set background escape sequence
        setattr(
            MODULE, 'BG_%s' % color, curses.tparm(bgColorSeq, colorIndex)
        )
    for control in CONTROLS:
        # Set the control escape sequence
        setattr(MODULE, control, curses.tigetstr(CONTROLS[control]) or '')
    for value in VALUES:
        # Set terminal related values
        setattr(MODULE, value, curses.tigetnum(VALUES[value]))
 
def render(text):
    """Helper function to render text easily
    Example:
    render("%(GREEN)s%(BOLD)stext%(NORMAL)s") -> a bold green text
    """
    return text % MODULE.__dict__
 
try:
    import curses
    setup()
except Exception, e:
    # There is a failure; set all attributes to default
    print 'Warning: %s' % e
    default()

The render function provides an easy way to render text using Python string formatting syntax. Example of usage:

from terminal import render
print render('%(BG_YELLOW)s%(RED)s%(BOLD)sHey this is a test%(NORMAL)s')
print render('%(BG_GREEN)s%(RED)s%(UNDERLINE)sAnother test%(NORMAL)s')

The result will look like this:
terminal example
terminal example 2
Note that terminal.NORMAL escape sequence is used to turn off all attributes. You can also access escape sequences directly by calling terminal.BG_GREEN, terminal.GREEN, terminal.BOLD, terminal.UP, etc.

The setup function is where the logic resides. I'm using curses.tigetstr(capability_name) to get the escaping sequence corresponding to the capability name, and curses.tparm(string, *params) to instantiate the string with the supplied parameters. For example, to get the escape sequence for the foreground color blue, I need to do the following steps:

  • Call curses.tigetstr('setaf') which returns '\x1b[3%p1%dm'. Where 'setaf' is the capability that set foreground color using ANSI escape sequences. Note that %p1%d is a parameter to be filled with the color index later using curses.tparm.
  • Then call curses.tparm('\x1b[3%p1%dm', curses.COLOR_BLUE) that returns '\x1b[34m' which corresponds to change the current foreground color to blue.

Unfortunately, those escaping sequences are not supported on all platforms. Therefore, any module that uses them should have a backup plan. The default function is only intended to give a default value to all attributes in case of a failure. This way, if the terminal doesn't support colors or if curses doesn't exist, all attributes will default to '' and won't have any effects.

This guide contains a comprehensive list of all terminfo string and numeric capabilities supported by curses. Any of theses capabilities can be added to the module if needed. Note that some capabilities may not be supported by some terminals.

Related:

Animated terminal progress bar
progressbar

AttachmentSize
Terminal controller module2.45 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Cool!

Pretty nice recipe!
But where can I find more of those sequences?

Thanks!

Thanks! This is very helpful. I found a list of the ANSI escape sequences on Wikipedia. I'm surprised that there is not a simpler way to do cursor locations and screen control in Python (was so easy in DOS/Qbasic) but this is a big help!

If you'd like color and

If you'd like color and cursor control like DOS/QBasic, using the higher-level curses/ncurses functions is for you. Try the module curses documentation page for python and this how-to http://www.amk.ca/python/howto/curses/ You start with initscr which both erases the screen and changes the input mode of the terminal to pass characters immediately, instead of line by line, and to not echo them as you type. Perfect for that DOS/QBasic feel. What What is a little more difficult to figure out while searching around for curses info is how to affect color changes without curses taking over the whole screen or changing the input mode, which is quite handy for making console utilities similar to ls --color or grep --color. In these cases you wish to write a few lines of colored text to the console without interrupting the "visual flow" or lose or overwrite any console history. Nadia has helpfully pointed out the lower-level curses functions that allow you to do this without hard coding escape sequences that will only work on some terminals.

Terminal input

This may sound as offtopic, but dont you know how to add terminal history and hint as in bash to you python script input?

As a mater of fact I do. Here

As a mater of fact I do. Here is an answer I wrote for this exact question in stackoverflow

Another one

Hi Nadia,

I wrote this for similar puposes :
http://code.activestate.com/recipes/574451/

Urwid

A whole console widget library for Python: http://excess.org/urwid

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options