Как получить ширину окна консоли Linux в Python - PullRequest
225 голосов
/ 19 февраля 2009

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

Редактировать

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

Ответы [ 14 ]

238 голосов
/ 03 июня 2009
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 (предположим, что пользователь изменил размер окна с тех пор).

214 голосов
/ 20 января 2013

Не уверен, почему он находится в модуле 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 и ниже:

65 голосов
/ 19 февраля 2009

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

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

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

РЕДАКТИРОВАТЬ : о, прости. Это не стандартная библиотека 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])
53 голосов
/ 10 июня 2010

Код выше не вернул правильный результат в моем linux, потому что winsize-struct имеет 4 шорта без знака, а не 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 должны содержать ширину и высоту пикселя, но не должны.

38 голосов
/ 01 июля 2011

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

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

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

Итак, вот версия, которая работает как на Linux, OS X и 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's python in cygwin's 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: /181698/kak-mne-naiti-shirinu-i-vysotu-okna-terminala
    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
15 голосов
/ 28 апреля 2014

Начиная с Python 3.3, это просто: https://docs.python.org/3/library/os.html#querying-the-size-of-a-terminal

>>> import os
>>> ts = os.get_terminal_size()
>>> ts.lines
24
>>> ts.columns
80
13 голосов
/ 26 января 2017

Это либо:

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, и понятия не имею, почему он используется по умолчанию.

6 голосов
/ 01 июня 2016

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

import curses
w = curses.initscr()
height, width = w.getmaxyx()
6 голосов
/ 16 июня 2010

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

  • getTerminalSize необходимо import os
  • что такое env? выглядит как os.environ.

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

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

паскаль, "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)
2 голосов
/ 08 сентября 2014

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

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

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

Я смог заставить его работать так:

with open('/dev/tty') as tty:
    height, width = subprocess.check_output(['stty', 'size'], stdin=tty).split()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...