Comment obtenir la largeur de fenêtre de la console Linux en Python

y a-t-il un moyen en python de déterminer programmatiquement la largeur de la console? Je veux dire le nombre de caractères qui s'inscrit dans une ligne sans emballage, pas la largeur en pixels de la fenêtre.

Modifier

à la recherche d'une solution qui fonctionne sur Linux

208
demandé sur anatoly techtonik 2009-02-19 22:17:19

14 réponses

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

utilise la commande "stty size" qui selon un thread sur la liste de diffusion python est raisonnablement universel sur linux. Il ouvre la commande' stty size 'en tant que Fichier,' reads ' à partir de celle-ci, et utilise une simple fente de chaîne de caractères pour séparer les coordonnées.

contrairement à l'os.environ ["colonnes"] valeur (à laquelle je ne peux pas accéder malgré l'utilisation de bash comme shell standard) les données seront également à jour alors que je crois que le OS.environ["COLONNES"] valeur ne serait valide que pour le moment du lancement de l'interpréteur python (supposons que l'utilisateur redimensionné la fenêtre depuis).

226
répondu brokkr 2009-06-03 10:06:54

Je ne sais pas pourquoi il est dans le module shutil , mais il a atterri là en Python 3.3, Querying la taille du terminal de sortie :

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

une implémentation de bas niveau se trouve dans le module os.

un backport est maintenant disponible pour Python 3.2 et inférieur:

188
répondu Gringo Suave 2017-03-30 17:45:29

utiliser

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

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

MODIFIER : oh, je suis désolé. Ce n'est pas un Python standard lib one, voici la source de console.py (Je ne sais pas d'où ça vient).

le module semble fonctionner comme cela: il vérifie si termcap est disponible, quand Oui. Il utilise cela; si non il vérifie si le terminal supporte un appel spécial ioctl et cela ne fonctionne pas, aussi, il vérifie pour les variables d'environnement quelques shells exportez pour ça. Cela ne fonctionnera probablement que sur 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])
65
répondu Johannes Weiss 2012-11-14 19:38:02

Code ci-dessus n'a pas retourné le résultat correct sur mon linux parce que winsize-struct A 4 shorts non signés, pas 2 shorts signés:

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 et hp doivent contenir la largeur et la hauteur des pixels, mais ne le font pas.

49
répondu pascal 2010-06-09 22:36:38

j'ai cherché partout et j'ai trouvé une solution pour windows à:

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

et une solution pour linux ici.

voici donc une version qui fonctionne à la fois sur linux, os x et 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://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
35
répondu Harco Kuppens 2011-07-05 09:22:23

à partir de Python 3.3 c'est tout droit: 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
15
répondu Bob Enohp 2014-04-27 23:24:31

on dirait Qu'il y a des problèmes avec ce code, Johannes:

  • getTerminalSize doit import os
  • qu'est-ce que env ? on dirait os.environ .

de plus, pourquoi changer lines et cols avant de revenir? Si TIOCGWINSZ et stty disent tous les deux lines puis cols , je dis de laisser tomber. Cela m'a embrouillé pendant une bonne 10 minutes avant que je remarqué l'incohérence.

Sridhar, Je n'ai pas eu cette erreur quand j'ai pipé la sortie. Je suis presque sûr qu'il est pris correctement dans l'essai-sauf.

pascal, "HHHH" ne fonctionne pas sur ma machine, mais "hh" le fait. J'ai eu du mal à trouver de la documentation pour cette fonction. On dirait que ça dépend de la plateforme.

chochem, incorporated.

voici ma 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)
6
répondu thejoshwolfe 2010-06-16 07:23:51

beaucoup d'implémentations de Python 2 échoueront s'il n'y a pas de terminal de contrôle lorsque vous appelez ce script. Vous pouvez vérifier sys.la sortie standard stdout.isatty () pour déterminer si c'est en fait un terminal, mais cela exclura un tas de cas, donc je crois que le moyen le plus pythonique pour comprendre la taille du terminal est d'utiliser le paquet de malédictions intégré.

import curses
w = curses.initscr()
height, width = w.getmaxyx()
6
répondu wonton 2016-05-31 22:26:04

c'est soit:

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

la fonction shutil n'est qu'un enveloppeur autour de os qui capte quelques erreurs et met en place un repli, cependant il a une énorme mise en garde - il casse lorsque la tuyauterie! , ce qui est assez énorme.

Pour obtenir la taille du terminal lorsque la tuyauterie utiliser os.get_terminal_size(0) à la place.

Premier argument 0 est un argument indiquant que le fichier stdin le descripteur doit être utilisé à la place du stdout par défaut. Nous voulons utiliser stdin parce que stdout se détache quand il est pipé qui soulève dans ce cas soulève une erreur..

J'ai essayé de comprendre quand il serait logique d'utiliser stdout à la place de l'argument stdin et n'ont aucune idée de la raison pour laquelle c'est un défaut ici.

5
répondu Granitosaurus 2018-05-03 04:51:39

la réponse de @reannual fonctionne bien, mais il y a un problème avec elle: os.popen est maintenant déprécié . Le module subprocess devrait être utilisé à la place, donc voici une version du code de @reannual qui utilise subprocess et répond directement à la question (en donnant la largeur de la colonne directement comme un int :

import subprocess

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

testé sur OS X 10.9

1
répondu rickcnagy 2014-07-22 18:30:16

j'ai essayé la solution d'ici qui appelle à stty size :

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

cependant cela a échoué pour moi parce que je travaillais sur un script qui attend des entrées redirigées sur stdin, et stty se plaindrait que "stdin n'est pas un terminal" dans ce cas.

j'ai été capable de le faire fonctionner comme ceci:

with open('/dev/tty') as tty:
    height, width = subprocess.check_output(['stty', 'size'], stdin=tty).split()
1
répondu Marc Liyanage 2014-09-08 18:36:32

Essayer "bénédictions"

je cherchais la même chose. Il est très facile à utiliser et offre des outils pour la coloration, le style et le positionnement dans le terminal. Ce dont vous avez besoin est aussi simple que:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

fonctionne comme un charme sous Linux. (Je ne suis pas sûr de MacOSX et Windows)

Téléchargement et documentation ici

ou vous pouvez l'installer avec pip:

pip install blessings
1
répondu Iman Akbari 2014-12-23 12:00:41

si vous utilisez Python 3.3 ou plus, je recommande l'intégration get_terminal_size() comme déjà recommandé. Cependant, si vous êtes coincé avec une version plus ancienne et que vous voulez un moyen simple et multiplate-forme de le faire, vous pouvez utiliser asciimatics . Ce paquet prend en charge les versions de Python de retour à 2.7 et utilise des options similaires à celles suggérées ci-dessus pour obtenir la taille du terminal/console actuelle.

il vous suffit de construire votre classe Screen et d'utiliser le dimensions propriété pour obtenir la hauteur et la largeur. Il a été prouvé que cela fonctionne sous Linux, OSX et Windows.

Oh - et la divulgation complète: je suis l'auteur, n'hésitez pas à ouvrir un nouveau problème si vous avez des problèmes pour obtenir que cela fonctionne.

1
répondu Peter Brittain 2015-12-24 09:54:50

Voici une version qui devrait être compatible Linux et Solaris. D'après les billets et les engagements de madchine . Nécessite le module de sous-processus.

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')
0
répondu Derrick Petzold 2017-05-23 11:55:19