Как получить ширину окна консоли Linux в Python

Есть ли способ в python программно определить ширину консоли? Я имею в виду количество символов, которые вписываются в одну строку без обертывания, а не ширину пикселя окна.

Изменить

Ищете решение, которое работает в Linux

Ответ 1

import os
rows, columns = os.popen('stty size', 'r').read().split()

использует команду 'stty size', которая согласно поток в списке рассылки python является достаточно универсальным в linux. Он открывает команду "stty size" в виде файла, "читает" ее и использует простую строку для разделения координат.

В отличие от значения os.environ [COLUMNS] (которое я не могу получить, несмотря на использование bash в качестве стандартной оболочки), данные также будут обновлены, тогда как я считаю, что os.environ Значение [COLUMNS] будет действительным только во время запуска интерпретатора python (предположим, что пользователь изменил размер окна с тех пор).

Ответ 2

Не уверен, почему он находится в модуле shutil, но он приземлился там в Python 3.3, Запрос размера выходного терминала:

>>> import shutil
>>> shutil.get_terminal_size((80, 20))  # pass fallback
os.terminal_size(columns=87, lines=23)  # returns a named-tuple

Низкоуровневая реализация находится в модуле os.

Теперь для Python 3.2 и ниже доступен backport:

Ответ 3

использование

import console
(width, height) = console.getTerminalSize()

print "Your terminal width is: %d" % width

EDIT: О, извините. Это не стандартный скрипт python, вот источник console.py(я не знаю, откуда это).

Модуль, похоже, работает следующим образом: он проверяет, доступен ли termcap, когда да. Он использует это; если он не проверяет, поддерживает ли терминал специальный вызов ioctl, и он тоже не работает, он проверяет для переменных среды для экспорта некоторые оболочки. Это, вероятно, будет работать только с UNIX.

def getTerminalSize():
    import os
    env = os.environ
    def ioctl_GWINSZ(fd):
        try:
            import fcntl, termios, struct, os
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
        '1234'))
        except:
            return
        return cr
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
    if not cr:
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:
        cr = (env.get('LINES', 25), env.get('COLUMNS', 80))

        ### Use get(key[, default]) instead of a try/catch
        #try:
        #    cr = (env['LINES'], env['COLUMNS'])
        #except:
        #    cr = (25, 80)
    return int(cr[1]), int(cr[0])

Ответ 4

Код выше не вернул правильный результат в моем Linux, потому что winsize-struct имеет 4 unsigned shorts, а не 2 подписанные шорты:

def terminal_size():
    import fcntl, termios, struct
    h, w, hp, wp = struct.unpack('HHHH',
        fcntl.ioctl(0, termios.TIOCGWINSZ,
        struct.pack('HHHH', 0, 0, 0, 0)))
    return w, h

hp и hp должны содержать ширину и высоту пикселя, но не.

Ответ 5

Я обыскал и нашел решение для окон по адресу:

http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/

и решение для linux здесь.

Итак, вот версия, которая работает как на linux, так и на windows/cygwin:

""" getTerminalSize()
 - get width and height of console
 - works on linux,os x,windows,cygwin(windows)
"""

__all__=['getTerminalSize']


def getTerminalSize():
   import platform
   current_os = platform.system()
   tuple_xy=None
   if current_os == 'Windows':
       tuple_xy = _getTerminalSize_windows()
       if tuple_xy is None:
          tuple_xy = _getTerminalSize_tput()
          # needed for window python in cygwin xterm!
   if current_os == 'Linux' or current_os == 'Darwin' or  current_os.startswith('CYGWIN'):
       tuple_xy = _getTerminalSize_linux()
   if tuple_xy is None:
       print "default"
       tuple_xy = (80, 25)      # default value
   return tuple_xy

def _getTerminalSize_windows():
    res=None
    try:
        from ctypes import windll, create_string_buffer

        # stdin handle is -10
        # stdout handle is -11
        # stderr handle is -12

        h = windll.kernel32.GetStdHandle(-12)
        csbi = create_string_buffer(22)
        res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
    except:
        return None
    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
        return sizex, sizey
    else:
        return None

def _getTerminalSize_tput():
    # get terminal width
    # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window
    try:
       import subprocess
       proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
       output=proc.communicate(input=None)
       cols=int(output[0])
       proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
       output=proc.communicate(input=None)
       rows=int(output[0])
       return (cols,rows)
    except:
       return None


def _getTerminalSize_linux():
    def ioctl_GWINSZ(fd):
        try:
            import fcntl, termios, struct, os
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234'))
        except:
            return None
        return cr
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
    if not cr:
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:
        try:
            cr = (env['LINES'], env['COLUMNS'])
        except:
            return None
    return int(cr[1]), int(cr[0])

if __name__ == "__main__":
    sizex,sizey=getTerminalSize()
    print  'width =',sizex,'height =',sizey

Ответ 7

Похоже, что есть некоторые проблемы с этим кодом, Йоханнес:

  • getTerminalSize требуется import os
  • что такое env? выглядит как os.environ.

Кроме того, зачем переключаться lines и cols перед возвратом? Если TIOCGWINSZ и stty говорят lines, то cols, я говорю, оставьте его таким образом. Это смутило меня за хорошие 10 минут, прежде чем я заметил несогласованность.

Шридхар, я не получил эту ошибку, когда я выпустил вывод. Я уверен, что это правильно поймано в try-except.

pascal, "HHHH" не работает на моей машине, но "hh" делает. У меня возникли проблемы с поиском документации для этой функции. Похоже, что он зависит от платформы.

chochem, зарегистрированный.

Здесь моя версия:

def getTerminalSize():
    """
    returns (lines:int, cols:int)
    """
    import os, struct
    def ioctl_GWINSZ(fd):
        import fcntl, termios
        return struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234"))
    # try stdin, stdout, stderr
    for fd in (0, 1, 2):
        try:
            return ioctl_GWINSZ(fd)
        except:
            pass
    # try os.ctermid()
    try:
        fd = os.open(os.ctermid(), os.O_RDONLY)
        try:
            return ioctl_GWINSZ(fd)
        finally:
            os.close(fd)
    except:
        pass
    # try `stty size`
    try:
        return tuple(int(x) for x in os.popen("stty size", "r").read().split())
    except:
        pass
    # try environment variables
    try:
        return tuple(int(os.getenv(var)) for var in ("LINES", "COLUMNS"))
    except:
        pass
    # i give up. return default.
    return (25, 80)

Ответ 8

Многие из реализаций Python 2 здесь не сработают, если нет управляющего терминала, когда вы вызываете этот script. Вы можете проверить sys.stdout.isatty(), чтобы определить, действительно ли это терминал, но это исключает кучу случаев, поэтому я считаю, что самый питонический способ определить размер терминала - использовать встроенный пакет curses.

import curses
w = curses.initscr()
height, width = w.getmaxyx()

Ответ 9

Это либо:

import os
columns, rows = os.get_terminal_size(0)
# or
import shutil
columns, rows = shutil.get_terminal_size()

Функция shutil - это всего лишь обертка вокруг os, которая ловит некоторые ошибки и настраивает резервную копию, однако у нее есть одно огромное оговорка - она ломается, когда трубопровод!, который является довольно огромная сделка.
Чтобы получить размер терминала при использовании трубопровода вместо os.get_terminal_size(0).

Первый аргумент 0 - это аргумент, указывающий, что вместо стандартного stdout следует использовать дескриптор файла stdin. Мы хотим использовать stdin, потому что stdout отделяется, когда он передается по каналам, который поднимается в этом случае, вызывает ошибку.
Я пытался выяснить, когда было бы целесообразно использовать stdout вместо аргумента stdin и не знаю, почему это значение по умолчанию здесь.

Ответ 10

Реальный ответ работает хорошо, но есть проблема: os.popen теперь устарел. Вместо этого следует использовать модуль subprocess, поэтому здесь приведена версия @reannual code, которая использует subprocess и напрямую отвечает на вопрос (указав ширину столбца непосредственно как int:

import subprocess

columns = int(subprocess.check_output(['stty', 'size']).split()[1])

Протестировано на OS X 10.9

Ответ 11

Я попробовал решение отсюда, которое вызывает stty size:

columns = int(subprocess.check_output(['stty', 'size']).split()[1])

Однако это не помогло мне, потому что я работал над script, который ожидает перенаправленного ввода на stdin, а stty будет жаловаться, что "stdin не является терминалом" в этом случае.

Мне удалось заставить его работать следующим образом:

with open('/dev/tty') as tty:
    height, width = subprocess.check_output(['stty', 'size'], stdin=tty).split()

Ответ 12

Попробуйте "благословения"

Я искал то же самое. Он очень прост в использовании и предлагает инструменты для окраски, стилизации и позиционирования в терминале. Что вам нужно, так просто:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Работает как прелесть в Linux. (Я не уверен в MacOSX и Windows)

Загрузить и документацию здесь

или вы можете установить его с помощью pip:

pip install blessings

Ответ 13

Если вы используете Python 3.3 или выше, я бы рекомендовал встроенный get_terminal_size(), как уже было рекомендовано. Однако, если вы застряли в старой версии и хотите простой, кросс-платформенный способ сделать это, вы можете использовать asciimatics. Этот пакет поддерживает версии Python до версии 2.7 и использует аналогичные варианты для предложенных выше, чтобы получить текущий размер терминала/консоли.

Просто создайте свой класс Screen и используйте свойство dimensions, чтобы получить высоту и ширину. Это было доказано для работы с Linux, OSX и Windows.

Ох - и полное раскрытие здесь: Я автор, поэтому, пожалуйста, не стесняйтесь открывать новую проблему, если у вас есть проблемы с ее работой.

Ответ 14

Вот версия, которая должна быть совместима с Linux и Solaris. На основе сообщений и сообщений от madchine. Требуется модуль подпроцесса.

def termsize():
    import shlex, subprocess, re
    output = subprocess.check_output(shlex.split('/bin/stty -a'))
    m = re.search('rows\D+(?P\d+); columns\D+(?P\d+);', output)
    if m:
        return m.group('rows'), m.group('columns')
    raise OSError('Bad response: %s' % (output))
>>> termsize()
('40', '100')