Как получить ширину окна консоли Linux в Python
Есть ли в Python способ программно определить ширину консоли? Я имею в виду количество символов, которые помещаются в одну строку без переноса, а не ширину окна в пикселях.
редактировать
Ищете решение, которое работает на Linux
15 ответов
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 (предположим, что пользователь изменил размер окна с тех пор).
Не уверен, почему это в модуле 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 и ниже:
Использование
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])
Приведенный выше код не вернул правильный результат в моем 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 должны содержать ширину и высоту пикселя, но не должны.
Это либо:
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
является аргументом, указывающим, что дескриптор файла stdin должен использоваться вместо стандартного stdout. Мы хотим использовать stdin, потому что stdout отсоединяется, когда он передается по каналу, что в этом случае вызывает ошибку.
Я попытался выяснить, когда имеет смысл использовать stdout вместо аргумента stdin, и понятия не имею, почему это значение по умолчанию.
Я искал вокруг и нашел решение для 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: http://stackru.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
Начиная с Python 3.3, это просто: https://docs.python.org/3/library/os.html
>>> import os
>>> ts = os.get_terminal_size()
>>> ts.lines
24
>>> ts.columns
80
Похоже, с этим кодом есть некоторые проблемы, Йоханнес:
getTerminalSize
нуждается вimport os
- что такое
env
? похожеos.environ
,
Кроме того, почему переключатель lines
а также cols
до возвращения? Если TIOCGWINSZ
а также stty
оба говорят lines
затем cols
Я говорю, оставь это так. Это смутило меня на целых 10 минут, прежде чем я заметил несоответствие.
Шридхар, я не получил эту ошибку, когда передал вывод. Я почти уверен, что это правильно поймано в попытке.
паскаль, "HHHH"
не работает на моей машине, но "hh"
делает. У меня были проблемы с поиском документации для этой функции. Похоже, это зависит от платформы.
Хохем, включены.
Вот моя версия:
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)
Многие из реализаций Python 2 здесь потерпят неудачу, если при вызове этого скрипта не будет управляющего терминала. Вы можете проверить sys.stdout.isatty(), чтобы определить, является ли это на самом деле терминалом, но это исключит кучу случаев, поэтому я считаю, что самый питонный способ выяснить размер терминала - это использовать встроенный пакет curses.
import curses
w = curses.initscr()
height, width = w.getmaxyx()
Попробуйте "благословения"
Я искал то же самое. Он очень прост в использовании и предлагает инструменты для окраски, укладки и позиционирования в терминале. То, что вам нужно, так же просто, как:
from blessings import Terminal
t = Terminal()
w = t.width
h = t.height
Работает как шарм в Linux. (Я не уверен насчет MacOSX и Windows)
Скачать и документацию здесь
или вы можете установить его с помощью pip:
pip install blessings
Я пробовал решение отсюда, которое призывает 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()
Ответ @reannual работает хорошо, но есть проблема с ним: os.popen
сейчас устарела. subprocess
модуль должен использоваться вместо этого, так что вот версия кода @reannual, который использует subprocess
и прямо отвечает на вопрос (давая ширину столбца непосредственно в виде int
:
import subprocess
columns = int(subprocess.check_output(['stty', 'size']).split()[1])
Протестировано на OS X 10.9
Использоватьsubprocess
, это наиболее удобный способ сделать это:
Импорт:
import subprocess
Пример использования:
print(subprocess.check_output(['stty', 'size']).split())
Примечание. Эта функция возвращает байты, но вы можете привести ее к целому числу с помощью
int()
функция.
Примечание. Эта функция возвращает массив следующего вида:
array[0]
= строки иarray[1]
= столбцы.
Выход:
[b'46', b'188']
Например, если вам нужно сравнить, превышает ли ширина вашей консолиW
, вы можете сделать что-то вроде этого:
if int(subprocess.check_output(['stty', 'size']).split()[1]) > W:
...
Если вы используете Python 3.3 или выше, я бы порекомендовал встроенный get_terminal_size()
как уже рекомендовано. Однако, если вы застряли в более старой версии и хотите простой кроссплатформенный способ сделать это, вы можете использовать asciimatics. Этот пакет поддерживает версии Python до 2.7 и использует опции, аналогичные тем, которые предложены выше, чтобы получить текущий размер терминала / консоли.
Просто создайте свой Screen
класс и использовать dimensions
свойство, чтобы получить высоту и ширину. Доказано, что это работает на Linux, OSX и Windows.
О - и полное раскрытие здесь: я автор, поэтому, пожалуйста, не стесняйтесь открывать новый выпуск, если у вас есть какие-либо проблемы, чтобы заставить это работать.
Вот версия, которая должна быть совместима с Linux и Solaris. Основано на сообщениях и комментариях от brokkr. Требуется модуль подпроцесса.
def termize(): импорт шлекс, подпроцесс, ре output = subprocess.check_output(shlex.split('/bin/stty -a')) m = re.search('строки \D+(?P\d+); столбцы \D+(?P\d+);', выходные данные) если М: вернуть m.group("строки"), m.group("столбцы") поднять OSError('Неверный ответ: %s' % (вывод))
>>> termize() ("40", "100")