Welcome, guest | Sign In | My Account | Store | Cart
# -*- coding: Windows-1251 -*-
'''
texttable -- render text tables for console, log files etc
'''
__author__ = 'Denis Barmenkov <denis.barmenkov@gmail.com>'
__source__ = 'http://code.activestate.com/recipes/577202-render-tables-for-text-interface/'

import types

# Align center is not supported because of the terrible form :)
ALIGN_RIGHT   = 0x0001
ALIGN_LEFT    = 0x0002
PADDING_LEFT  = 0x0010
PADDING_RIGHT = 0x0020
PADDING_ALL   = PADDING_LEFT | PADDING_RIGHT

def _rpad_to_maxlen(slist, padchar=' '):
    maxlen = max(map(len, slist))
    res = map(lambda s: s + padchar * (maxlen - len(s)), slist)
    return res

def _lpad_to_maxlen(slist, padchar=' '):
    maxlen = max(map(len, slist))
    res = map(lambda s: padchar * (maxlen - len(s)) + s, slist)
    return res

def test_mask(mask, flag):
    '''
    tests mask for all bits of flag set
    '''
    return mask & flag == flag

def _align_para(para, mode):
    if test_mask(mode, ALIGN_RIGHT):
        para = _lpad_to_maxlen(para) # little confusing: right align == left padding
    elif test_mask(mode, ALIGN_LEFT):
        para = _rpad_to_maxlen(para)
    else:
        raise ValueError, "NO ALIGN SPECIFIED!"

    if test_mask(mode, PADDING_LEFT):
        para = map(lambda x: ' ' + x, para)
    if test_mask(mode, PADDING_RIGHT):
        para = map(lambda x: x + ' ', para)

    return para

def _glue_cols(col1, col2):
    return map(lambda x: '%s|%s' % (x[0], x[1]), zip(col1, col2))

def _has_column_headers(cols):
    fail_count = 0
    for c in cols:
        try:
            a = c[2]
        except IndexError:
            fail_count += 1
    return fail_count == 0

def _join_to_list(*arguments):
    '''
    join all parameters to one list, 
    expanding lists and tuples
    '''
    res = list()
    for arg in arguments:
        if isinstance(arg, types.ListType):
            res.extend(arg)
        elif isinstance(arg, types.TupleType):
            res.extend(list(arg))
        else:
            res.append(arg)
    return res

def format_table(cols):
    # 1. aligning data
    if len(cols) == 0:
        return list()

    header_present = _has_column_headers(cols)
    if header_present:
        # prefix all data with headers // TODO: alignment set to center 
        cols = map(lambda x: [_join_to_list(x[2], x[0]), x[1] ], cols)

    cols2 = map(lambda x: _align_para(x[0], x[1]), cols)
    if len(cols2) == 0:
        return list()

    empty_vert_border = [ '' ] * len(cols2[0])

    cols2.append(empty_vert_border)
    cols2.insert(0, empty_vert_border)

    glued_cols = reduce(_glue_cols, cols2)
    if len(glued_cols) == 0:
        return list()

    border = '=' * len(glued_cols[0])
    glued_cols.insert(0, border)
    glued_cols.append(border)
    if header_present:
        glued_cols.insert(2, border)  # insert delimiter berween header and values        

    return glued_cols

def demo():
    nums = [ '1', '2', '3', '4' ]
    speeds = [ '100', '10000', '1500', '12' ]
    desc = [ '', 'label 1', 'none', 'very long description' ]
    lines = format_table( [(nums, ALIGN_RIGHT|PADDING_ALL, 'NUM'), 
                           (speeds, ALIGN_RIGHT|PADDING_ALL, 'SPEED'), 
                           (desc, ALIGN_LEFT|PADDING_ALL, 'DESC')] )

    print '\n'.join(lines)


if __name__ == '__main__':
    demo()

Diff to Previous Revision

--- revision 1 2010-04-20 18:02:03
+++ revision 2 2010-04-20 18:02:51
@@ -3,7 +3,7 @@
 texttable -- render text tables for console, log files etc
 '''
 __author__ = 'Denis Barmenkov <denis.barmenkov@gmail.com>'
-__source__ = 'http://code.activestate.com/recipes/'
+__source__ = 'http://code.activestate.com/recipes/577202-render-tables-for-text-interface/'
 
 import types
 

History