Is there a way in python to programmatically determine the width of the console? I mean the number of characters that fits in one line without wrapping, not the pixel width of the window.
Edit
Looking for a solution that works on Linux
Is there a way in python to programmatically determine the width of the console? I mean the number of characters that fits in one line without wrapping, not the pixel width of the window.
Edit
Looking for a solution that works on Linux
Not sure why it is in the module shutil
, but it landed there in Python 3.3. See:
Querying the size of the output terminal
>>> import shutil
>>> shutil.get_terminal_size((80, 20)) # pass fallback
os.terminal_size(columns=87, lines=23) # returns a named-tuple
A low-level implementation is in the os module. Cross-platform—works under Linux, Mac OS, and Windows, probably other Unix-likes. There's a backport as well, though no longer relevant.
os.terminal_size
sadly. The backport package is pure python & MIT licensed however and can just be thrown into your own package source. –
Photochronograph python3.5
installed. –
Siphonostele Inappropriate ioctl for device
errors/warnings, or getting the defined fallback value of 80. –
Paper shutil
means sh
ell util
ities. Getting the terminal size is surely a utility function for dealing with the shell so it's the module you'd expect to have such functionality. Where else do you want to put that? sys
? os
? they are for more general stuff... –
Weltschmerz get_terminal_size()
). I'm now using subprocess.check_output(['tput', 'cols'])
instead. –
Varicose import os
rows, columns = os.popen('stty size', 'r').read().split()
uses the 'stty size' command which according to a thread on the python mailing list is reasonably universal on linux. It opens the 'stty size' command as a file, 'reads' from it, and uses a simple string split to separate the coordinates.
Unlike the os.environ["COLUMNS"] value (which I can't access in spite of using bash as my standard shell) the data will also be up-to-date whereas I believe the os.environ["COLUMNS"] value would only be valid for the time of the launch of the python interpreter (suppose the user resized the window since then).
(See answer by @GringoSuave on how to do this on python 3.3+)
columns, rows = os.popen('stty size', 'r').read().split()
–
Hagiology stty size
works under solaris 5.8 (no need to parse -a).You may want to use rows, columns = [int(i) for i in os.popen('stty size', 'r').read().split()]
to get integers directly –
Stevana rows, columns = subprocess.check_output(['stty', 'size']).split()
is a little shorter, plus subprocess is the future –
Revet rows, columns = subprocess.check_output(['stty', 'size']).decode().split()
If you want unicode strings for py2/3 compatibility –
Photochronograph nohup
it gives following error: ValueError: not enough values to unpack (expected 2, got 0) @Breccia
–
Correctitude stty: 'standard input': Inappropriate ioctl for device
–
Correctitude use
import console
(width, height) = console.getTerminalSize()
print "Your terminal's width is: %d" % width
EDIT: oh, I'm sorry. That's not a python standard lib one, here's the source of console.py (I don't know where it's from).
The module seems to work like that: It checks if termcap
is available, when yes. It uses that; if no it checks whether the terminal supports a special ioctl
call and that does not work, too, it checks for the environment variables some shells export for that.
This will probably work on UNIX only.
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])
pipe
your Python program output to, say, less
. You would get the exception: IOError: [Errno 25] Inappropriate ioctl for device
–
Postmillennialism not sys.stdout.isatty()
–
Postmillennialism env=os.environ
line beforehand should fix that. Also, the os import is out of scope... –
Crystallize module 'console' has no attribute 'getTerminalSize'
–
Correctitude Code above didn't return correct result on my linux because winsize-struct has 4 unsigned shorts, not 2 signed shorts:
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 and hp should contain pixel width and height, but don't.
fcntl.ioctl(sys.stdin.fileno(), ...
–
Voluptuous stdout
or stderr
instead of stdin
, though. stdin
might very well be a pipe. You might also want to add a line such as if not os.isatty(0): return float("inf")
. –
Dachau 0
with a call to os.ctermid()
, which returns the name of the terminal device for the current process – like e.g. /dev/tty
–
Bluster It's either:
import os
columns, rows = os.get_terminal_size(0)
# or
import shutil
columns, rows = shutil.get_terminal_size()
The shutil
function is just a wrapper around os
one that catches some errors and set up a fallback, however it has one huge caveat - it breaks when piping!, which is a pretty huge deal.
To get terminal size when piping use os.get_terminal_size(0)
instead.
First argument 0
is an argument indicating that stdin file descriptor should be used instead of default stdout. We want to use stdin because stdout detaches itself when it is being piped which in this case raises an error.
I've tried to figure out when would it makes sense to use stdout instead of stdin argument and have no idea why it's a default here.
os.get_terminal_size()
was introduced in Python 3.3 –
Swaim os.get_terminal_size(0)
will crash if you pipe to stdin. Try: echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
–
Tortuosity I searched around and found a solution for windows at :
http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
and a solution for linux here.
So here is a version which works both on linux, os x and 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: https://mcmap.net/q/86025/-how-do-i-find-the-width-amp-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
Starting at Python 3.3 it is straight forward: 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
shutil.get_terminal_size() is the high-level function which should normally be used, os.get_terminal_size is the low-level implementation.
–
Heilner os.get_terminal_size()
not be used? –
Lucila It looks like there are some problems with that code, Johannes:
getTerminalSize
needs to import os
env
? looks like os.environ
.Also, why switch lines
and cols
before returning? If TIOCGWINSZ
and stty
both say lines
then cols
, I say leave it that way. This confused me for a good 10 minutes before I noticed the inconsistency.
Sridhar, I didn't get that error when I piped output. I'm pretty sure it's being caught properly in the try-except.
pascal, "HHHH"
doesn't work on my machine, but "hh"
does. I had trouble finding documentation for that function. It looks like it's platform dependent.
chochem, incorporated.
Here's my version:
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)
env
too, and it is indeed env = os.environ
, from accepted answer. –
Holotype Many of the Python 2 implementations here will fail if there is no controlling terminal when you call this script. You can check sys.stdout.isatty() to determine if this is in fact a terminal, but that will exclude a bunch of cases, so I believe the most pythonic way to figure out the terminal size is to use the builtin curses package.
import curses
w = curses.initscr()
height, width = w.getmaxyx()
Try "blessings"
I was looking for the very same thing. It is very easy to use and offers tools for coloring, styling and positioning in the terminal. What you need is as easy as:
from blessings import Terminal
t = Terminal()
w = t.width
h = t.height
Works like a charm in Linux. (I'm not sure about MacOSX and Windows)
Download and documentation here
or you can install it with pip:
pip install blessings
I was trying the solution from here that calls out to stty size
:
columns = int(subprocess.check_output(['stty', 'size']).split()[1])
However this failed for me because I was working on a script that expects redirected input on stdin, and stty
would complain that "stdin isn't a terminal" in that case.
I was able to make it work like this:
with open('/dev/tty') as tty:
height, width = subprocess.check_output(['stty', 'size'], stdin=tty).split()
If you're using Python 3.3 or above, I'd recommend the built-in get_terminal_size()
as already recommended. However if you are stuck with an older version and want a simple, cross-platform way of doing this, you could use asciimatics. This package supports versions of Python back to 2.7 and uses similar options to those suggested above to get the current terminal/console size.
Simply construct your Screen
class and use the dimensions
property to get the height and width. This has been proven to work on Linux, OSX and Windows.
Oh - and full disclosure here: I am the author, so please feel free to open a new issue if you have any problems getting this to work.
size = os.get_terminal_size()
-> OSError: [Errno 25] Inappropriate ioctl for device
–
Tobi Use subprocess
, it is the most convenient way of doing it:
Import:
import subprocess
Example of Use:
print(subprocess.check_output(['stty', 'size']).split())
Note: This function returns bytes but you can cast it to Integer with the
int()
function.
Note: this function returns an array, being:
array[0]
=rows andarray[1]
=columns.
Output:
[b'46', b'188']
For instance if you need to compare whether the width of your console is larger than W
, you can do something like this:
if int(subprocess.check_output(['stty', 'size']).split()[1]) > W:
...
@reannual's answer works well, but there's an issue with it: os.popen
is now deprecated. The subprocess
module should be used instead, so here's a version of @reannual's code that uses subprocess
and directly answers the question (by giving the column width directly as an int
:
import subprocess
columns = int(subprocess.check_output(['stty', 'size']).split()[1])
Tested on OS X 10.9
Here is an version that should be Linux and Solaris compatible. Based on the posts and commments from madchine. Requires the subprocess module.
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')
© 2022 - 2024 — McMap. All rights reserved.
os.popen
which is deprecated. The top-voted answer showingshutil
is the best way. – Dulsea