2009-09-08 60 views
2

Debian的apt工具输出导致统一宽度的列。例如,尝试运行“aptitude search svn”..并且所有名称都出现在同一宽度的第一列中。像列输出一样 - python库

现在,如果您调整终端大小,则会相应地调整列宽。

有没有一个Python库,使人能做到这一点?请注意,库必须知道终端宽度并将表作为输入 - 例如,它可以是[('rapidsvn', 'A GUI client for subversion'), ...] ..并且您还可以为第一列(或任何列)指定最大宽度。另请注意,如果超过终端宽度,下面第二列中的字符串将被修剪。因此,不会引入不需要的第二行。

$ aptitude search svn 
[...] 
p python-svn-dbg     - A(nother) Python interface to Subversion (d 
v python2.5-svn      -            
v python2.6-svn      -            
p rapidsvn       - A GUI client for subversion     
p statsvn       - SVN repository statistics     
p svn-arch-mirror     - one-way mirroring from Subversion to Arch r 
p svn-autoreleasedeb    - Automatically release/upload debian package 
p svn-buildpackage     - helper programs to maintain Debian packages 
p svn-load       - An enhanced import facility for Subversion 
p svn-workbench      - A Workbench for Subversion     
p svnmailer       - extensible Subversion commit notification t 
p websvn       - interface for subversion repositories writt 
$ 

编辑:(响应下面Alex的答案)...输出将类似于在1“资质搜索”),只有最后一列(这是最长的串唯一的列在一行中)将被修剪,2)通常只有2-4列,但最后一列(“描述”)预计至少需要一半的终端宽度。 3)所有行包含相同数量的列,4)所有条目仅包含字符串

+0

我想它使用诅咒/为什么ncurses的需要在所有的容易的ncurses – tonfa 2009-09-08 23:17:31

+0

? apt,在Linux上,只需要查看'COLUMNS'环境变量来获取终端的宽度。但是,我正在寻找一种跨平台(* nix和Windows)解决方案。 – 2009-09-08 23:23:28

+1

当我进入Linux(来自Terminal.App,但我认为不重要)时,我的环境中没有COLUMNS。不过,'aptitude search'显示正常。所以,我认为在你断言“你只需要查看COLUMNS环境变量”的说法中,你不可能是任何人,它不是一个可用的解决方案,也不是什么才能。 – 2009-09-09 02:21:40

回答

2

我不认为有一个通用的,跨平台的方式来“获取终端的宽度” - 绝对不是“查看COLUMNS环境变量”(请参阅​​我对该问题的评论)。在Linux和Mac OS X上(我预计所有现代Unix版本),

curses.wrapper(lambda _: curses.tigetnum('cols')) 

返回列数;但我不知道wcurses是否支持Windows。一旦你有(如果你坚持,或通过诅咒,或从甲骨文,或默认80,或任何其他方式,你喜欢os.environ ['COLUMNS'])所需的输出宽度,其余的是 比较可行。这是一项非常精简的工作,有很多错误的机会,并且非常容易受到很多您不清楚的详细规格的影响,例如:哪些列被剪切以避免包装 - 它总是最后一个,还是......?如果根据你的问题,你如何在样本输出中显示3列,只有两列通过...?如果不是所有行都具有相同数量的列,那么应该发生什么?表中的所有条目是否必须是字符串?以及许多其他许多此类之谜。

因此,对所有没有表达的规格进行一些随意的猜测,一种方法可能是......:

import sys 

def colprint(totwidth, table): 
    numcols = max(len(row) for row in table) 
    # ensure all rows have >= numcols columns, maybe empty 
    padded = [row+numcols*('',) for row in table] 
    # compute col widths, including separating space (except for last one) 
    widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)] 
    widths[-1] -= 1 
    # drop or truncate columns from the right in order to fit 
    while sum(widths) > totwidth: 
    mustlose = sum(widths) - totwidth 
    if widths[-1] <= mustlose: 
     del widths[-1] 
    else: 
     widths[-1] -= mustlose 
     break 
    # and finally, the output phase! 
    for row in padded: 
    for w, i in zip(widths, row): 
     sys.stdout.write('%*s' % (-w, i[:w])) 
    sys.stdout.write('\n') 
+0

至于假设,一般来说......输出将类似于'适应性搜索',因为1)只有最后一列(这是连续最长字符串中唯一的一列)将被修剪,2)通常只有2-4列,但最后一列(“描述”)预计至少需要一半的终端宽度。 3)所有行都包含相同数量的列,4)所有条目都只是字符串 – 2009-09-09 16:46:20

+0

您可能希望**格式**出现的代码,而不用换行符和缩进。 – 2009-09-09 16:47:15

+0

@Sridhar,TX格式化(我通过在2小时前看到您的评论后立即编辑回复来修复)。我的代码应该符合你现在给出的所有规范,顺便说一句 - 他们大概是我的猜测;试试看。 – 2009-09-10 04:09:32

2

那么,aptitude使用cwidget来格式化纯文本显示中的列。你可以调用cwidget为它编写一个python扩展,但我认为这不值得麻烦......你可以使用你的首选方法来获得字符的实际水平尺寸并计算自己。

+0

aptitude是否也使用'cwidget'打印表格输出(当运行'aptitude search foo'命令时)? – 2009-09-09 16:40:38

+0

@Sridhar:是的。我检查了源代码。 – nosklo 2009-09-09 16:44:12

+0

好的,是的。我也只是看到了源头;它使用'columnizer.layout_columns'和'de_columnize',依次依靠'cwidget' api。 – 2009-09-09 16:53:33

2

首先,使用ioctl得到TTY的大小:

import termios, fcntl, struct, sys 

def get_tty_size(): 
    s = struct.pack("HHHH", 0, 0, 0, 0) 
    fd_stdout = sys.stdout.fileno() 
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) 
    return struct.unpack("HHHH", size)[:2] 

print get_tty_size() 

然后使用这样的函数,使列:

pad = lambda s, n=20: "%s%s" % (s,' '*(n-len(s))) 

把这些放在一起,你已经有了调整列为控制台!

+0

'termios'和'fcntl'在Windows上不可用。 – 2009-09-10 18:00:03

+0

对于Windows,有一种获取控制台宽度的方法:http://code.activestate.com/recipes/440694/ – 2009-09-10 18:10:35

+0

如果'stdout'不是tty,那么此代码将不起作用。请看下面的更新程序。 – 2009-09-18 21:51:51

4

更新:该colprint程序现已在applib Python库hosted in GitHub可用。

下面是对于那些你感兴趣的完整的程序:

# This function was written by Alex Martelli 
# http://stackoverflow.com/questions/1396820/ 
def colprint(table, totwidth=None): 
    """Print the table in terminal taking care of wrapping/alignment 

    - `table`: A table of strings. Elements must not be `None` 
    - `totwidth`: If None, console width is used 
    """ 
    if not table: return 
    if totwidth is None: 
     totwidth = find_console_width() 
     totwidth -= 1 # for not printing an extra empty line on windows 
    numcols = max(len(row) for row in table) 
    # ensure all rows have >= numcols columns, maybe empty 
    padded = [row+numcols*('',) for row in table] 
    # compute col widths, including separating space (except for last one) 
    widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)] 
    widths[-1] -= 1 
    # drop or truncate columns from the right in order to fit 
    while sum(widths) > totwidth: 
     mustlose = sum(widths) - totwidth 
     if widths[-1] <= mustlose: 
      del widths[-1] 
     else: 
      widths[-1] -= mustlose 
      break 
    # and finally, the output phase! 
    for row in padded: 
     print(''.join([u'%*s' % (-w, i[:w]) 
         for w, i in zip(widths, row)])) 

def find_console_width(): 
    if sys.platform.startswith('win'): 
     return _find_windows_console_width() 
    else: 
     return _find_unix_console_width() 
def _find_unix_console_width(): 
    """Return the width of the Unix terminal 

    If `stdout` is not a real terminal, return the default value (80) 
    """ 
    import termios, fcntl, struct, sys 

    # fcntl.ioctl will fail if stdout is not a tty 
    if not sys.stdout.isatty(): 
     return 80 

    s = struct.pack("HHHH", 0, 0, 0, 0) 
    fd_stdout = sys.stdout.fileno() 
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) 
    height, width = struct.unpack("HHHH", size)[:2] 
    return width 
def _find_windows_console_width(): 
    """Return the width of the Windows console 

    If the width cannot be determined, return the default value (80) 
    """ 
    # http://code.activestate.com/recipes/440694/ 
    from ctypes import windll, create_string_buffer 
    STDIN, STDOUT, STDERR = -10, -11, -12 

    h = windll.kernel32.GetStdHandle(STDERR) 
    csbi = create_string_buffer(22) 
    res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) 

    if res: 
     import struct 
     (bufx, bufy, curx, cury, wattr, 
     left, top, right, bottom, 
     maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) 
     sizex = right - left + 1 
     sizey = bottom - top + 1 
    else: 
     sizex, sizey = 80, 25 

    return sizex 
+0

您能否提供一个小例子?什么是“表”对象的样子?如果我用这样的表调用它:'table = [[“HEAD1”,“HEAD2”,“HEAD3”],[“text1”,“text2”,“text3”]]',我得到一个错误'TypeError :只能连接列表(不是“元组”)列表“ – ifischer 2012-01-11 09:29:18

+0

@ifischer - 你可以在GitHub https://github.com/ActiveState/applib/blob/master/applib/textui尝试上述代码的更新版本。 py#L213 - 让我知道它是否有效(这样我可以在这里更新答案)? – 2012-01-11 20:09:16

+0

是的,它的工作原理,当我从我以前的评论表中调用时不再有TypeError – ifischer 2012-01-12 08:54:51