Cách lấy chiều rộng cửa sổ bảng điều khiển Linux bằng Python


264

Có cách nào trong python để lập trình xác định chiều rộng của giao diện điều khiển không? Tôi có nghĩa là số lượng ký tự phù hợp trong một dòng mà không gói, không phải chiều rộng pixel của cửa sổ.

Biên tập

Tìm kiếm một giải pháp hoạt động trên Linux


Hãy xem câu trả lời này để có giải pháp rộng hơn để có cơ chế in "phụ thuộc cột". stackoverflow.com/questions/44129613/
Mạnh

Câu trả lời:


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

sử dụng lệnh 'stty size' mà theo một luồng trong danh sách gửi thư python là phổ biến hợp lý trên linux. Nó mở lệnh 'stty size' dưới dạng tệp, 'đọc' từ nó và sử dụng một chuỗi tách đơn giản để tách tọa độ.

Không giống như giá trị os.envir ["COLUMNS"] (mà tôi không thể truy cập mặc dù sử dụng bash làm vỏ tiêu chuẩn của mình), dữ liệu cũng sẽ được cập nhật trong khi tôi tin rằng os.envir ["COLUMNS"] giá trị sẽ chỉ có hiệu lực trong thời gian khởi chạy trình thông dịch python (giả sử người dùng đã thay đổi kích thước cửa sổ kể từ đó).

(Xem câu trả lời của @GringoSuave về cách thực hiện việc này trên python 3.3+)


1
Bạn cũng có thể làm điều này để hoạt động trên Solaris nếu thay vì "kích thước" bạn vượt qua "-a". Sẽ có "hàng = Y; cột = X" trong đầu ra được phân cách bằng dấu chấm phẩy.
Joseph Garvin

5
COLUMNS không được xuất theo mặc định trong Bash, đó là lý do tại sao os.envir ["COLUMNS"] không hoạt động.
Kjell Andreassen

38
rows, columns = subprocess.check_output(['stty', 'size']).split()ngắn hơn một chút, cộng với quy trình con là tương lai
cdosborn 17/03/2015

7
Tput tốt hơn stty, vì stty không thể hoạt động với PIPE.
liuyang1

5
rows, columns = subprocess.check_output(['stty', 'size']).decode().split()Nếu bạn muốn các chuỗi unicode cho khả năng tương thích py2 / 3
Bryce Guinta

264

Không chắc chắn tại sao nó nằm trong mô-đun shutil, nhưng nó đã hạ cánh ở đó trong Python 3.3, Truy vấn kích thước của thiết bị đầu cuối đầu ra :

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

Một triển khai cấp thấp là trong mô-đun os. Cũng hoạt động trong Windows.

Một backport hiện có sẵn cho Python 3.2 trở xuống:


25
Đó là bởi vì bạn không nên sử dụng 2.7 nữa, hãy tăng lên 3.x thật đáng giá.
anthonyryan1

5
@osirisgothra Nhiều nhà cung cấp dịch vụ lưu trữ chưa hỗ trợ python3, vì vậy một số người trong chúng ta buộc phải sử dụng python2 để phát triển back end. Mặc dù điều đó không có gì để làm với kích thước thiết bị đầu cuối ...
whitebeard

4
@osirisgothra Bởi vì có rất nhiều mã Python 2 sẽ mất quá nhiều công việc để chuyển. Ngoài ra, Pypy vẫn có hỗ trợ Python 3 khá kém.
Antimon

2
@whitebeard Có lý do nào người đăng ký không thể cài đặt Python 3 trên VPS không? Hoặc vào năm 2016, mọi người vẫn đang sử dụng lưu trữ chia sẻ mà quản trị viên không muốn cài đặt Python 3? Ví dụ: lưu trữ chia sẻ WebFaction đã python3.5được cài đặt.
Damian Yerrick

2
+1 cho một giải pháp cũng hoạt động khi đầu vào tiêu chuẩn đã được chuyển hướng từ một tệp! Với các giải pháp khác, tôi đã nhận được Inappropriate ioctl for devicelỗi / cảnh báo hoặc nhận giá trị dự phòng được xác định là 80.
pawamoy

65

sử dụng

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

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

EDIT : oh, tôi xin lỗi. Đó không phải là một lib python tiêu chuẩn, đây là nguồn của console.py (Tôi không biết nó đến từ đâu).

Các mô-đun dường như hoạt động như thế: Nó kiểm tra nếu termcapcó sẵn, khi có. Nó sử dụng điều đó; nếu không, nó sẽ kiểm tra xem thiết bị đầu cuối có hỗ trợ một ioctlcuộc gọi đặc biệt hay không và nó cũng không hoạt động, nó sẽ kiểm tra các biến môi trường mà một số shell xuất cho điều đó. Điều này có lẽ sẽ chỉ hoạt động trên 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])

5
Cảm ơn đã trả lời nhanh, nhưng ở đây ( effbot.org/zone/console-handbook.htmlm ) nó nói rằng "Mô-đun Console hiện chỉ khả dụng cho Windows 95, 98, NT và 2000." Tôi đang tìm kiếm một giải pháp hoạt động trên Linux. Nó có thể không rõ ràng từ thẻ, tôi sẽ chỉnh sửa câu hỏi cho phù hợp.
Serge Golovov

2
vì mô-đun "bàn điều khiển" mà bạn đang sử dụng không có trong thư viện python tiêu chuẩn, bạn nên cung cấp mã nguồn của nó hoặc ít nhất là một liên kết đến nó.
nosklo

Tôi rất xin lỗi về việc đó. Trong thực tế, tôi đã không biết mô-đun đó. Tôi đã thử nhập bảng điều khiển và nó hoạt động, tôi đã sử dụng bảng điều khiển. <Tab> <tab> và getTerminalSize () xuất hiện. Thay vì tìm kiếm nó từ đâu tôi đã đăng một câu trả lời vì tôi rất may mắn vì sự đơn giản g
Julian Weiss

Tôi có thể đang xem xét một mô-đun "bàn điều khiển" khác, bạn có thể vui lòng cung cấp một liên kết cho cái bạn có không?
Serge Golovov

4
Ồ, và không để chồng mã, nhưng "cr" là một tên khó hiểu bởi vì nó ngụ ý bộ dữ liệu là (cols, hàng). Trong thực tế, nó là đảo ngược.
Paul Du Bois

57

Mã ở trên không trả về kết quả chính xác trên linux của tôi vì winize-struct có 4 quần short không dấu, không phải 2 quần short có chữ ký:

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 và hp nên chứa chiều rộng và chiều cao pixel, nhưng không.


4
Đây là cách nó nên được thực hiện; lưu ý rằng nếu bạn có ý định in tới thiết bị đầu cuối, bạn nên sử dụng '1' làm mô tả tệp (đối số đầu tiên của ioctl), vì stdin có thể là một đường ống hoặc một số tty khác nhau.
mic_e

1
Có lẽ nên thay 0 bằngfcntl.ioctl(sys.stdin.fileno(), ...
raylu

4
đây là câu trả lời tốt nhất - người dùng của bạn sẽ rất vui khi không có quá trình con bất ngờ xảy ra chỉ để có được độ rộng hạn
zzzeek

4
Đây thực sự là câu trả lời sạch nhất. Tôi nghĩ rằng bạn nên sử dụng stdouthoặc stderrthay vì stdin, mặc dù. stdinrất có thể là một đường ống. Bạn cũng có thể muốn thêm một dòng như if not os.isatty(0): return float("inf").
mic_e

cái này bằng cách nào đó hoạt động trên một thiết bị đầu cuối chromebook mà không có chức năng. +1
HyperNeutrino

39

Tôi đã tìm kiếm xung quanh và tìm thấy một giải pháp cho các cửa sổ tại:

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

và một giải pháp cho linux ở đây.

Vì vậy, đây là phiên bản hoạt động cả trên linux, os x và 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

Bạn đã tiết kiệm cho tôi thời gian làm điều này bản thân mình. Hoạt động trên Linux. Nên làm việc trên Windows là tốt. Cảm ơn!
Steve V.

30

Đó cũng là:

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

Các shutil chức năng chỉ là một wrapper xung quanh osmột mà bắt một số lỗi và thiết lập một dự phòng, tuy nhiên nó có một ngoại khổng lồ - nó phá vỡ khi đường ống! , đó là một thỏa thuận khá lớn.
Để có được kích thước thiết bị đầu cuối khi sử dụng đường ống os.get_terminal_size(0)thay thế.

Đối số đầu tiên 0là một đối số chỉ ra rằng bộ mô tả tệp stdin nên được sử dụng thay vì thiết bị xuất chuẩn mặc định. Chúng tôi muốn sử dụng stdin vì thiết bị xuất chuẩn sẽ tự tách ra khi nó đang được xử lý trong trường hợp này gây ra lỗi.

Tôi đã cố gắng tìm ra khi nào nên sử dụng thiết bị xuất chuẩn thay vì đối số stdin và không biết tại sao nó lại mặc định ở đây.


3
os.get_terminal_size()đã được giới thiệu trong Python 3.3
Villapx

Tôi đã thử tắt máy lúc đầu, và nó đã hoạt động ngay lần thử đầu tiên. Tôi đang sử dụng Python 3.6+.
Dan

Sử dụng os.get_terminal_size(0)sẽ sụp đổ nếu bạn ống để stdin. Hãy thử:echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
ehabkost

19

Bắt đầu từ Python 3.3, nó rất đơn giản: https://docs.python.org/3/l Library / os.html # querying-the-size-of-a-termminal

>>> import os
>>> ts = os.get_terminal_size()
>>> ts.lines
24
>>> ts.columns
80

16
shutil.get_terminal_size() is the high-level function which should normally be used, os.get_terminal_size is the low-level implementation.
mid_kid

1
Đây là một bản sao gần của một câu trả lời cũ hơn được đưa ra ở trên.
Gringo Suave

6

Có vẻ như có một số vấn đề với mã đó, Johannes:

  • getTerminalSize cần phải import os
  • envgì vẻ thích os.environ.

Ngoài ra, tại sao chuyển đổi linescolstrước khi trở về? Nếu TIOCGWINSZsttycả hai nói linesthìcols , tôi nói hãy để nó như vậy. Điều này làm tôi bối rối trong 10 phút trước khi tôi nhận thấy sự không nhất quán.

Sridhar, tôi đã không nhận được lỗi đó khi tôi đầu ra. Tôi khá chắc chắn rằng nó đã bị bắt đúng cách trong thử - ngoại trừ.

pascal, "HHHH"không hoạt động trên máy của tôi, nhưng"hh" không. Tôi gặp khó khăn khi tìm tài liệu cho chức năng đó. Có vẻ như nó phụ thuộc vào nền tảng.

choool, kết hợp.

Đây là phiên bản của tôi:

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)

Tôi đã đi lang thang về envquá, và nó thực sự env = os.environ, từ câu trả lời được chấp nhận .
sdaau

6

Nhiều triển khai Python 2 ở đây sẽ thất bại nếu không có thiết bị đầu cuối kiểm soát khi bạn gọi tập lệnh này. Bạn có thể kiểm tra sys.stdout.isatty () để xác định xem đây có thực sự là một thiết bị đầu cuối hay không, nhưng điều đó sẽ loại trừ một loạt các trường hợp, vì vậy tôi tin rằng cách pythonic nhất để tìm ra kích thước thiết bị đầu cuối là sử dụng gói lời nguyền dựng sẵn.

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

2

Tôi đã thử giải pháp từ đây mà gọi stty size:

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

Tuy nhiên điều này không thành công đối với tôi vì tôi đang làm việc với một kịch bản mong đợi đầu vào được chuyển hướng trên stdin và stty sẽ phàn nàn rằng "stdin không phải là thiết bị đầu cuối" trong trường hợp đó.

Tôi đã có thể làm cho nó hoạt động như thế này:

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

2

Hãy thử "phước lành"

Tôi đã tìm kiếm điều tương tự. Nó rất dễ sử dụng và cung cấp các công cụ để tô màu, tạo kiểu và định vị trong thiết bị đầu cuối. Những gì bạn cần là dễ dàng như:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Hoạt động như một cơ duyên trong Linux. (Tôi không chắc chắn về MacOSX và Windows)

Tải về và tài liệu tại đây

hoặc bạn có thể cài đặt nó với pip:

pip install blessings

Ngày nay, tôi muốn nói thử "phước", một ngã ba hiện đang được duy trì (và nâng cao) của "phước lành".
zezoche

1

Câu trả lời của @ reannual hoạt động tốt, nhưng có một vấn đề với nó: os.popen hiện không được chấp nhận . Các subprocessmô-đun nên được sử dụng thay vào đó, vì vậy đây là một phiên bản của mã @ reannual rằng sử dụng subprocessvà trực tiếp trả lời câu hỏi (bằng cách cho chiều rộng cột trực tiếp như một int:

import subprocess

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

Đã thử nghiệm trên OS X 10.9


1

Nếu bạn đang sử dụng Python 3.3 trở lên, tôi khuyên bạn nên tích hợp sẵn get_terminal_size()như đã đề xuất. Tuy nhiên, nếu bạn bị mắc kẹt với một phiên bản cũ hơn và muốn một cách đơn giản, đa nền tảng để làm điều này, bạn có thể sử dụng asciimatics . Gói này hỗ trợ các phiên bản Python trở lại 2.7 và sử dụng các tùy chọn tương tự như các tùy chọn được đề xuất ở trên để có được kích thước thiết bị đầu cuối / bảng điều khiển hiện tại.

Đơn giản chỉ cần xây dựng của bạn Screen lớp và sử dụng thuộc dimensionstính để có được chiều cao và chiều rộng. Điều này đã được chứng minh là hoạt động trên Linux, OSX và Windows.

Ồ - và tiết lộ đầy đủ ở đây: Tôi là tác giả, vì vậy xin vui lòng mở một vấn đề mới nếu bạn có bất kỳ vấn đề nào để làm việc này.


0

Đây là một phiên bản nên tương thích với Linux và Solaris. Dựa trên các bài viết và cam kết từ madchine . Yêu cầu mô đun quy trình con.

def thuật ngữ ():
    nhập shlex, quy trình con, tái
    đầu ra = sub process.checkDefput (shlex.split ('/ bin / stty -a'))
    m = re.search ('hàng \ D + (? P \ d +); cột \ D + (? P \ d +);', đầu ra)
    nếu m:
        trả về m.group ('hàng'), m.group ('cột')
    nâng OSError ('Phản hồi xấu:% s'% (đầu ra))
>>> điều khoản ()
('40 ',' 100 ')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.