web-dev-qa-db-fra.com

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

Existe-t-il un moyen dans python de déterminer par programme la largeur de la console? Je veux dire le nombre de caractères pouvant être insérés dans une ligne sans le retour à la ligne, pas la largeur en pixels de la fenêtre.

Modifier

Vous recherchez une solution qui fonctionne sous Linux

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

utilise la commande 'stty size' qui, selon n fil de la liste de diffusion python = est raisonnablement universelle sur linux. Elle ouvre la commande 'stty size' sous forme de fichier , "lit", et utilise une simple scission de chaîne pour séparer les coordonnées.

Contrairement à la valeur os.environ ["COLUMNS"] (à laquelle je ne peux pas accéder malgré l'utilisation de bash comme shell standard), les données sont également à jour, alors que je crois que os.environ ["COLUMNS"] valeur ne serait valide que pour le moment du lancement de l’interprète python (supposons que l’utilisateur redimensionne la fenêtre depuis lors).

240
brokkr

Vous ne savez pas pourquoi il est dans le module shutil, mais il a atterri dans Python 3.3, Interrogation de 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:

223
Gringo Suave

utilisation

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

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

ÉDITER : oh, je suis désolé. Ce n'est pas un python lib standard, voici la source de console.py (je ne sais pas d'où il vient).

Le module semble fonctionner comme ça: il vérifie si termcap est disponible, si oui. Il utilise ça si non, il vérifie si le terminal prend en charge un appel spécial ioctl et si cela ne fonctionne pas, il vérifie également les variables d'environnement exportées par certains shells. Cela ne fonctionnera probablement que sous 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
Johannes Weiss

Le code ci-dessus n'a pas retourné le résultat correct sur mon linux car 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 une largeur et une hauteur en pixels, mais ne le faites pas.

54
pascal

J'ai cherché autour et 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 __== "__main__":
    sizex,sizey=getTerminalSize()
    print  'width =',sizex,'height =',sizey
38
Harco Kuppens

À partir de Python 3.3, c’est simple: 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
16
Bob Enohp

C'est soit:

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

La fonction shutil est juste un wrapper autour de os qui détecte certaines erreurs et met en place un repli, mais elle a un énorme inconvénient - elle se casse lors du piping!, ce qui est une très grosse affaire.
Pour obtenir la taille du terminal lors de la tuyauterie, utilisez os.get_terminal_size(0) à la place.

Le premier argument 0 Est un argument indiquant que le descripteur de fichier stdin doit être utilisé à la place de la commande standard stdout. Nous voulons utiliser stdin parce que stdout se détache quand il est en cours de transmission, ce qui soulève dans ce cas une erreur.
J'ai essayé de comprendre quand il serait judicieux d'utiliser stdout au lieu d'argument stdin et je ne sais pas pourquoi c'est un défaut ici.

14
Granitosaurus

Il semble y avoir quelques problèmes avec ce code, Johannes:

  • getTerminalSize a besoin de import os
  • qu'est-ce que env? ressemble à os.environ.

Aussi, pourquoi passer lines et cols avant de revenir? Si TIOCGWINSZ et stty disent tous deux lines alors cols, je dis qu'il en soit ainsi. Cela m'a confondu pendant une bonne dizaine de minutes avant que je remarque l'incohérence.

Sridhar, je n'ai pas eu cette erreur quand j'ai transmis la sortie. Je suis à peu près sûr que ça se passe correctement dans l'essai, sauf.

Pascal, "HHHH" ne fonctionne pas sur ma machine, mais "hh" Est-ce que. J'ai eu du mal à trouver de la documentation pour cette fonction. Il semble que cela dépende de la plate-forme.

chochem, incorporé.

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
thejoshwolfe

De nombreuses implémentations de Python 2) ici échoueront s'il n'y a pas de terminal de contrôle lorsque vous appelez ce script. Vous pouvez vérifier sys.stdout.isatty () pour déterminer s'il s'agit bien d'un terminal, mais cela exclura un grand nombre de cas, je pense donc que le moyen le plus pythonique de déterminer la taille du terminal consiste à utiliser le paquetage curses intégré.

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

J'essayais la solution d'ici qui appelle à stty size:

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

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

J'ai pu le faire fonctionner comme ceci:

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

Si vous utilisez Python 3.3 ou supérieur, je recommanderais le get_terminal_size() intégré] comme déjà recommandé. Cependant, si vous êtes coincé avec une version plus ancienne et souhaitez un Pour cela, vous pouvez utiliser asciimatics . Ce paquet supporte les versions de Python de retour à la 2.7 et utilise les mêmes options que celles suggérées ci-dessus pour obtenir la taille actuelle du terminal/de la console.

Construisez simplement votre classe Screen et utilisez la propriété dimensions pour obtenir la hauteur et la largeur. Il a été prouvé que cela fonctionnait sous Linux, OSX et Windows.

Oh - et divulgation complète ici: je suis l’auteur, alors n'hésitez pas à ouvrir un nouveau numéro si vous avez des problèmes à le faire fonctionner.

1
Peter Brittain

La réponse de @ reannual fonctionne bien, mais elle pose un problème: os.popenest maintenant obsolète . Le module subprocess devrait être utilisé à la place. Voici donc une version du code de @ reannual qui utilise subprocess et répond directement à la question (en donnant directement la largeur de la colonne sous forme de int:

import subprocess

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

Testé sur OS X 10.9

1
rickcnagy

Essayez les "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
Iman Akbari

Voici une version qui devrait être compatible avec Linux et Solaris. Basé sur les messages et les commentaires de madchine . Requiert le module de sous-processus.

 def termsize (): 
 importer shlex, sous-processus, soit 
 sortie = sous-processus.check_output (shlex.split ('/ bin/stty -a')) 
 m = re.search ('lignes\D + (? P\d +); colonnes\D + (? P\d +);', sortie) 
 si m: 
 renvoie m.group ( 'lignes'), m.group ('colonnes') 
 soulève OSError ('Mauvaise réponse:% s'% (sortie)) 
 >>> termsize () 
 ('40', '100') 
0
Derrick Petzold