2

Debian の apt ツールの出力結果は、均一な幅の列になります。たとえば、「aptitude search svn」を実行すると、すべての名前が同じ幅の最初の列に表示されます。

端末のサイズを変更すると、それに応じて列幅が調整されます。

これを可能にする Python ライブラリはありますか? ライブラリは端末の幅を認識し、テーブルを入力として受け取る必要があることに注意してください。これは、たとえば[('rapidsvn', 'A GUI client for subversion'), ...].. であり、最初の列 (または任意の列) の最大幅を指定することもできます。また、端末の幅を超える場合、下の 2 列目の文字列がどのようにトリミングされるかにも注意してください。したがって、望ましくない 2 行目が導入されません。

$ 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
$

編集:(以下のアレックスの回答に応じて)...出力は、1)最後の列(行で最も長い文字列を持つ唯一の列)のみがトリミングされるという点で「適性検索」に似ています。 2) 通常は 2 ~ 4 列しかありませんが、最後の列 (「説明」) は端末幅の少なくとも半分を占めると予想されます。3) すべての行に同じ数の列が含まれている、4) すべてのエントリが文字列のみである

4

4 に答える 4

4

更新: このcolprintルーチンは、GitHub でホストされているapplib Python ライブラリで利用できるようになりました。

興味のある方のための完全なプログラムは次のとおりです。

# 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
于 2009-09-18T21:45:44.363 に答える
2

さて、aptitude はcwidget、テキストのみの表示で列をフォーマットするために使用します。そのための python 拡張機能を作成するように呼び出すこともできますcwidgetが、問題を起こす価値はないと思います...実際の水平サイズを文字で取得し、自分で計算するお好みの方法を使用できます。

于 2009-09-09T01:17:06.683 に答える
2

まず、ioctlTTY のサイズを取得するために使用します。

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)))

これらをまとめると、コンソールの列のサイズを変更できます!

于 2009-09-09T03:14:57.233 に答える
2

「端末の幅を取得する」ための一般的なクロスプラットフォームの方法はないと思います- 「COLUMNS環境変数を見て」は絶対にありません(質問に対する私のコメントを参照してください)。Linux と Mac OS X (そして最新のすべての Unix バージョンを想定しています) では、

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

列数を返します。しかし、 wcursesが Windows でこれをサポートしているかどうかはわかりません。

(主張する場合は os.environ['COLUMNS'] から、または呪いを介して、またはオラクルから、またはデフォルトで 80 に、またはその他の任意の方法で)必要な出力幅を取得したら、残りは非常に実行可能です。これは細かい作業で、オフバイワンの種類のエラーが発生する可能性が高く、次のような完全に明確にしない多くの詳細な仕様に対して非常に脆弱です。最後か…?あなたの質問によれば、2 つしか渡されないのに、サンプル出力に 3 つの列が表示されているのはなぜですか? すべての行の列数が同じでない場合はどうなりますか? テーブル内のすべてのエントリは文字列でなければなりませんか? そして、この同類の他の多くの謎。

したがって、あなたが表現していないすべての仕様についてやや恣意的な推測を行うと、1 つのアプローチは次のようになります...:

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')
于 2009-09-09T03:19:43.930 に答える