windows 是否可以在Python中使用ANSI转义码获得光标位置?

lawou6xi  于 2023-03-19  发布在  Windows
关注(0)|答案(2)|浏览(139)

(Note:适用于Windows 10和Python 3+)
我已经读过几篇关于如何使用ANSI转义码获得光标位置的文章,但是它们没有提供解决方案,至少对于Python没有。Wikipedia(https://en.wikipedia.org/wiki/ANSI_escape_code)包含以下ANSI seq:在其游标管理列表中:

ESC 6 n:DSR(设备状态报告)向应用程序报告光标位置(CPR)(如同在键盘上键入)ESC[n;mR,其中n是行,m是列。)

因此,如果您使用print("\033[6n")应用此命令,则响应将被“推”到键盘,并且只有在脚本终止后,您才会在MS DOS提示符下得到以下内容:^[[7; 11 R(数字取决于具体情况)。
现在,我真的不明白这有什么用!所以,我的问题是"Can we 'finally' get information about the cursor position using the **mentioned conditions** (Windows, ANSI codes and Python)?"...这样我就可以一劳永逸地结束这一章了!

v8wbuo2f

v8wbuo2f1#

我找到了一个解决方案,你必须使用msvcrt,它是Python标准库的一部分。
我已经在Windows 10 2004版上使用Python 3.8.7进行了尝试。
这个方法不会将光标位置输出到键盘输入缓冲区。相反,它允许你将光标位置保存到一个变量中,以便进一步操作。

import msvcrt

# Added "\033[A" to move cursor up one line since 'print("\033[6n")' outputs a blank line
print("\033[A\033[6n")
buff = ""

keep_going = True
while keep_going:

    # getch() reads the keypress one character at a time,
    # since ANSI ESC[6n sequence fills the keyboard input buffer
    # 'decode("utf-8")' also works.
    buff += msvcrt.getch().decode("ASCII")

    # kbhit() returns True if a keypress is waiting to be read.
    # Once all the characters in the keyboard input buffer are read, the while loop exits
    keep_going = msvcrt.kbhit()

# Printing 'buff' variable outputs a blank line and no cursor position.
# By removing "\x1b" from the string in 'buff' variable,
# it will allow you to save the remaining string,
# containing the cursor position, to a variable for later use
newbuff = buff.replace("\x1b[", "")
print(newbuff)

结果:

1;1R
tquggr8v

tquggr8v2#

我知道我来晚了。但是我在搜索类似的东西时偶然发现了这个。它在Scheme中--我不知道Python。如果你仍然感兴趣,下面是我的做法。

;; Return a list obtained by reading characters from the input up to and
  ;; including an instance of the terminating character. The result does not
  ;; include the termination character. If the termination character is not
  ;; read before the `eof` object, returns a list composed of all of the
  ;; characters read to that point.
  (define (read-to-terminating-char terminator input-port)
    (let loop ()
      (let ([c (read-char input-port)])
        (if (or (eof-object? c) (char=? terminator c))
            '()
            (cons c (loop))))))

  ;; Return a version of the list without the first `n` elements.
  (define (drop lst n)
    (let loop ([my-lst lst]
               [num n])
      (if (zero? num)
          my-lst
          (loop (cdr my-lst) (- num 1)))))

  ;; Return a list containing all of the digits at the start of
  ;; the input.
  (define (get-leading-digits lst)
    (let loop ([my-lst lst])
      (if (or (null? my-lst) (not (char-numeric? (car my-lst))))
          '()
          (cons (car my-lst) (loop (cdr my-lst))))))

  ;; Request the position of the cursor in the terminal. Return a list
  ;; containing the cursor row and column positions (integers).
  (define (request-cursor-position)
    (display "\x1b;[6n")
    ;; The response will be of the form "\esc[rr;cccR" where there are
    ;; typically 1 or 2 digits representing the row and 1 to 3 digits
    ;; representing the column.
    (let* ([resp (read-to-terminating-char #\R (current-input-port))]
           [no-prefix (cddr resp)] ;; Remove the "\esc[" from the response.
           [row-lst (get-leading-digits no-prefix)]
           [row (string->number (list->string row-lst))]
           [last-part (drop no-prefix (+ 1 (length row-lst)))]
           [cols-digits (get-leading-digits last-part)]
           [col (string->number (list->string cols-digits))])
      (list row col)))

request-cursor-position过程使用的ANSI命令与您尝试使用的命令相同(\x1b;是Scheme编写ESCAPE字符的方式。)终端以您看到的格式返回包含光标位置的行和列的响应。这就是read-to-terminating-character过程所做的。在本例中,响应由R字符终止。如你所见。
从这里开始,只需要解析响应中的行号和列号,我的过程将结果返回到一个包含行和列的列表中,并按照行和列的顺序返回。
对于我来说,要让这些命令可靠地工作,我必须将终端设置为“原始模式”。标准的“熟制模式”只会被控制字符串搞砸。
这里有一个小测试程序,展示了如何使用这个过程.

(import
 (scheme)
 (srfi s27 random-bits)
 (ansi-terminal))

(define (random-in-range min max)
  (+ min (random-integer (+ 1 (- max min)))))

(define (test-one-position row col)
  (move-cursor-to row col)
  (let ([pos-list (request-cursor-position)])
    (when (or (not (= row (car pos-list)))
              (not (= col (cadr pos-list))))
      (display "\rError: output: ") (display pos-list)
      (display " not equal to input ")
      (display (list row col)) (display "\r") (newline)
      (exit 0))))

(define (test-random-positions rows cols)
  (let loop ([num-tests 100]
             [test-row (random-in-range 1 rows)]
             [test-col (random-in-range 1 cols)])
    (when (not (zero? num-tests))
      (test-one-position test-row test-col)
      (loop (- num-tests 1)
            (random-in-range 1 rows)
            (random-in-range 1 cols)))))

(define (run-tests)
  (dynamic-wind
    (lambda ()
      (enable-raw-mode))
    (lambda ()
      (display "Testing...")
      (random-source-randomize! default-random-source)
      (let ([term-size (window-size)]
            [pos (request-cursor-position)])
        (test-random-positions (car term-size) (cadr term-size))
        (move-cursor-to (+ 1 (car pos)) 1)
        (display "All tests successful!")))
    (lambda ()
      (disable-raw-mode))))

运行该程序会产生如下结果:

on branch: (main) ! chez
Chez Scheme Version 9.5.8
Copyright 1984-2022 Cisco Systems, Inc.

> (load "test-ansi-pos.ss")
> (run-tests)
Testing...
All tests successful!
>

希望这能有所帮助,尽管它不是Python语言。

相关问题