如何从 Windows 终端中的 Python 获取当前文本光标位置?
How to get the current text cursor position from Python in a Windows Terminal?
通过使用 Windows API ctypes
库,获取 Python 中的当前鼠标指针位置很简单。但是,似乎从鼠标指针的屏幕位置位置 (x,y) 开始,以获取当前 text cursor 的位置当前终端window,似乎难度很大
此外,程序员社区不断混淆鼠标 指针 位置与文本 光标 位置,这让情况变得更糟。 Historically 从来没有鼠标 “光标” 所以当人们说“光标”时,他们应该 指的是文本光标,而不是另一种方式。由于这个错误,Whosebug 充满了与“光标”相关的问题和答案,但似乎 none 与获取终端 shell 的当前 字符位置 . [被诅咒的光标!]
获取相对鼠标指针位置:
from ctypes import windll, wintypes, byref
def get_cursor_pos():
cursor = wintypes.POINT()
windll.user32.GetCursorPos(byref(cursor))
return (cursor.x, cursor.y)
while(1): print('{}\t\t\r'.format(get_cursor_pos()), end='')
我想要一个函数,根据 character row 给我最后一个位置
和 列 。也许是这样的:
def cpos():
xy = here_be_magic()
return xy
# Clear screen and start from top:
print('\x1b[H', end='');
print('12345', end='', flush=True); xy=cpos(); print('( {},{})'.format(xy[0],xy[1]),end='', flush=True)
# 12345 (1,5) # written on top of blank screen
如何在我的终端中获取 text
光标位置(行,列)?
(不做任何假设,不必编写我自己的 window 经理?)
最终我希望用它来找到任何终端中的最后一个光标位置window,(并且可能被任何程序使用?)
可能相关(但无用)SO 问题:
- get current key cursors position text[C#]
- How to get the text cursor position in Windows?[鼠标?]
- How to get the current cursor position in python readline[鼠标?]
- Python and ctypes: how to correctly pass "pointer-to-pointer" into DLL?
更新 (2022-01-17)
查看 MS 文档,我现在确信应该可以从(较旧的、非基于 VT 的)API 调用中获取此信息,GetConsoleScreenBufferInfo
是这样给出的.
BOOL WINAPI GetConsoleScreenBufferInfo(
_In_ HANDLE hConsoleOutput, # A handle to the console screen buffer. The handle must have the GENERIC_READ access right.
_Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo # A pointer to a CONSOLE_SCREEN_BUFFER_INFO structure that receives the console screen buffer information.
);
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize; # contains the size of the console screen buffer, in character columns and rows.
COORD dwCursorPosition; # contains the column and row coordinates of the cursor in the console screen buffer.
WORD wAttributes; # Character attributes (divided into two classes: color and DBCS)
SMALL_RECT srWindow; # A SMALL_RECT structure that contains the console screen buffer coordinates of the upper-left and lower-right corners of the display window.
COORD dwMaximumWindowSize; # A COORD structure that contains the maximum size of the console window, in character columns and rows, given the current screen buffer size and font and the screen size.
} CONSOLE_SCREEN_BUFFER_INFO; #
# Defines the coordinates of a character cell in a console screen buffer.
# The origin of the coordinate system (0,0) is at the top, left cell of the buffer.
typedef struct _COORD {
SHORT X; # The horizontal coordinate or column value. The units depend on the function call.
SHORT Y; # The vertical coordinate or row value. The units depend on the function call.
} COORD, *PCOORD;
typedef struct _SMALL_RECT {
SHORT Left;
SHORT Top;
SHORT Right;
SHORT Bottom;
} SMALL_RECT;
因此,鉴于此,我认为以下方法可行。
cls='\x1b[H'
from ctypes import windll, wintypes, byref
def cpos():
cursor = wintypes._COORD(ctypes.c_short)
windll.kernel32.GetConsoleScreenBufferInfo(byref(cursor))
return (cursor.X, cursor.Y)
cpos()
# TypeError: '_ctypes.PyCSimpleType' object cannot be interpreted as an integer
问题是找到各种结构定义。经过大量实验后,我得到了以下可行的解决方案。
#!/usr/bin/env python -u
# -*- coding: UTF-8 -*-
#------------------------------------------------------------------------------
from ctypes import windll, wintypes, Structure, c_short, c_ushort, byref, c_ulong
from readline import console
#------------------------------------------------
# Win32 API
#------------------------------------------------
SHORT = c_short
WORD = c_ushort
DWORD = c_ulong
STD_OUTPUT_HANDLE = DWORD(-11) # $CONOUT
# These are already defined, so no need to redefine.
COORD = wintypes._COORD
SMALL_RECT = wintypes.SMALL_RECT
CONSOLE_SCREEN_BUFFER_INFO = console.CONSOLE_SCREEN_BUFFER_INFO
#------------------------------------------------
# Main
#------------------------------------------------
wk32 = windll.kernel32
hSo = wk32.GetStdHandle(STD_OUTPUT_HANDLE)
GetCSBI = wk32.GetConsoleScreenBufferInfo
def cpos():
csbi = CONSOLE_SCREEN_BUFFER_INFO()
GetCSBI(hSo, byref(csbi))
xy = csbi.dwCursorPosition
return '({},{})'.format(xy.X,xy.Y)
cls='\x1b[H'
print('\n'*61)
print(cls+'12345', end='', flush=True); print(' {}'.format(cpos()), flush=True)
# 12345 (5,503)
在 (my) Windows 环境中失败:
Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import readline
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'readline'
>>>
和pip install readline
returns error: this module is not meant to work on Windows
(截断)。
我找到了另一个纯 Windows 环境的解决方案(没有 GNU readline interface. Desired structure definitions borrowed at programtalk.com,请参阅第 1..91 行:
# from winbase.h
STDOUT = -11
STDERR = -12
from ctypes import (windll, byref, Structure, c_char, c_short, c_uint32,
c_ushort, ArgumentError, WinError)
handles = {
STDOUT: windll.kernel32.GetStdHandle(STDOUT),
STDERR: windll.kernel32.GetStdHandle(STDERR),
}
SHORT = c_short
WORD = c_ushort
DWORD = c_uint32
TCHAR = c_char
class COORD(Structure):
"""struct in wincon.h"""
_fields_ = [
('X', SHORT),
('Y', SHORT),
]
class SMALL_RECT(Structure):
"""struct in wincon.h."""
_fields_ = [
("Left", SHORT),
("Top", SHORT),
("Right", SHORT),
("Bottom", SHORT),
]
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
"""struct in wincon.h."""
_fields_ = [
("dwSize", COORD),
("dwCursorPosition", COORD),
("wAttributes", WORD),
("srWindow", SMALL_RECT),
("dwMaximumWindowSize", COORD),
]
def __str__(self):
"""Get string representation of console screen buffer info."""
return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
self.dwSize.Y, self.dwSize.X
, self.dwCursorPosition.Y, self.dwCursorPosition.X
, self.wAttributes
, self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
, self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
)
def GetConsoleScreenBufferInfo(stream_id=STDOUT):
"""Get console screen buffer info object."""
handle = handles[stream_id]
csbi = CONSOLE_SCREEN_BUFFER_INFO()
success = windll.kernel32.GetConsoleScreenBufferInfo(
handle, byref(csbi))
if not success:
raise WinError()
return csbi
### end of https://programtalk.com/vs4/python/14134/dosage/dosagelib/colorama.py/
clrcur = '\x1b[H' # move cursor to the top left corner
clrscr = '\x1b[2J' # clear entire screen (? moving cursor ?)
# '\x1b[H\x1b[2J'
from ctypes import windll, wintypes, byref
def get_cursor_pos():
cursor = wintypes.POINT()
aux = windll.user32.GetCursorPos(byref(cursor))
return (cursor.x, cursor.y)
mouse_pos = get_cursor_pos()
# print('mouse at {}'.format(mouse_pos))
def cpos():
csbi = GetConsoleScreenBufferInfo()
return '({},{})'.format(csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y)
print('12345', end='', flush=True)
print(' {}'.format(cpos()), flush=True)
# an attempt to resolve discrepancy between buffer and screen size
# in the following code snippet:
import sys
if len(sys.argv) > 1 and len(sys.argv[1]) > 0:
csbi = GetConsoleScreenBufferInfo()
keybd_pos = (csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y)
print('\nkbd buffer at {}'.format(keybd_pos))
import os
screensize = os.get_terminal_size()
keybd_poss = ( csbi.dwCursorPosition.X,
min( csbi.dwSize.Y,
csbi.dwCursorPosition.Y,
csbi.dwMaximumWindowSize.Y,
screensize.lines))
# screen line number is incorrectly computed if termial is scroll-forwarded
print('kbd screen at {} (disputable? derived from the following data:)'
.format(keybd_poss))
print( 'csbi.dwSize ', (csbi.dwSize.X, csbi.dwSize.Y))
print( 'terminal_size ', (screensize.columns, screensize.lines))
print( 'csbi.dwMaxSize', (csbi.dwMaximumWindowSize.X, csbi.dwMaximumWindowSize.Y))
print( 'csbi.dwCurPos ', (csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y))
输出:.\SO732748.py
12345 (5,526)
从第 113 行开始,尝试解决 差异 缓冲区和屏幕大小之间的差异(不成功,绝对屏幕行号计算不正确,至少如果终端是 scroll-forwarded). 差异不会出现在Windows终端,那里总是buffer height == window height
,所有这些计算都是不必要的...
示例:.\SO732748.py x
12345 (5,529)
kbd buffer at (0, 530)
kbd screen at (0, 36) (disputable? derived from the following data:)
csbi.dwSize (89, 1152)
terminal_size (88, 36)
csbi.dwMaxSize (89, 37)
csbi.dwCurPos (0, 530)
通过使用 Windows API ctypes
库,获取 Python 中的当前鼠标指针位置很简单。但是,似乎从鼠标指针的屏幕位置位置 (x,y) 开始,以获取当前 text cursor 的位置当前终端window,似乎难度很大
此外,程序员社区不断混淆鼠标 指针 位置与文本 光标 位置,这让情况变得更糟。 Historically 从来没有鼠标 “光标” 所以当人们说“光标”时,他们应该 指的是文本光标,而不是另一种方式。由于这个错误,Whosebug 充满了与“光标”相关的问题和答案,但似乎 none 与获取终端 shell 的当前 字符位置 . [被诅咒的光标!]
获取相对鼠标指针位置:
from ctypes import windll, wintypes, byref
def get_cursor_pos():
cursor = wintypes.POINT()
windll.user32.GetCursorPos(byref(cursor))
return (cursor.x, cursor.y)
while(1): print('{}\t\t\r'.format(get_cursor_pos()), end='')
我想要一个函数,根据 character row 给我最后一个位置 和 列 。也许是这样的:
def cpos():
xy = here_be_magic()
return xy
# Clear screen and start from top:
print('\x1b[H', end='');
print('12345', end='', flush=True); xy=cpos(); print('( {},{})'.format(xy[0],xy[1]),end='', flush=True)
# 12345 (1,5) # written on top of blank screen
如何在我的终端中获取 text
光标位置(行,列)?
(不做任何假设,不必编写我自己的 window 经理?)
最终我希望用它来找到任何终端中的最后一个光标位置window,(并且可能被任何程序使用?)
可能相关(但无用)SO 问题:
- get current key cursors position text[C#]
- How to get the text cursor position in Windows?[鼠标?]
- How to get the current cursor position in python readline[鼠标?]
- Python and ctypes: how to correctly pass "pointer-to-pointer" into DLL?
更新 (2022-01-17)
查看 MS 文档,我现在确信应该可以从(较旧的、非基于 VT 的)API 调用中获取此信息,GetConsoleScreenBufferInfo
是这样给出的.
BOOL WINAPI GetConsoleScreenBufferInfo(
_In_ HANDLE hConsoleOutput, # A handle to the console screen buffer. The handle must have the GENERIC_READ access right.
_Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo # A pointer to a CONSOLE_SCREEN_BUFFER_INFO structure that receives the console screen buffer information.
);
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize; # contains the size of the console screen buffer, in character columns and rows.
COORD dwCursorPosition; # contains the column and row coordinates of the cursor in the console screen buffer.
WORD wAttributes; # Character attributes (divided into two classes: color and DBCS)
SMALL_RECT srWindow; # A SMALL_RECT structure that contains the console screen buffer coordinates of the upper-left and lower-right corners of the display window.
COORD dwMaximumWindowSize; # A COORD structure that contains the maximum size of the console window, in character columns and rows, given the current screen buffer size and font and the screen size.
} CONSOLE_SCREEN_BUFFER_INFO; #
# Defines the coordinates of a character cell in a console screen buffer.
# The origin of the coordinate system (0,0) is at the top, left cell of the buffer.
typedef struct _COORD {
SHORT X; # The horizontal coordinate or column value. The units depend on the function call.
SHORT Y; # The vertical coordinate or row value. The units depend on the function call.
} COORD, *PCOORD;
typedef struct _SMALL_RECT {
SHORT Left;
SHORT Top;
SHORT Right;
SHORT Bottom;
} SMALL_RECT;
因此,鉴于此,我认为以下方法可行。
cls='\x1b[H'
from ctypes import windll, wintypes, byref
def cpos():
cursor = wintypes._COORD(ctypes.c_short)
windll.kernel32.GetConsoleScreenBufferInfo(byref(cursor))
return (cursor.X, cursor.Y)
cpos()
# TypeError: '_ctypes.PyCSimpleType' object cannot be interpreted as an integer
问题是找到各种结构定义。经过大量实验后,我得到了以下可行的解决方案。
#!/usr/bin/env python -u
# -*- coding: UTF-8 -*-
#------------------------------------------------------------------------------
from ctypes import windll, wintypes, Structure, c_short, c_ushort, byref, c_ulong
from readline import console
#------------------------------------------------
# Win32 API
#------------------------------------------------
SHORT = c_short
WORD = c_ushort
DWORD = c_ulong
STD_OUTPUT_HANDLE = DWORD(-11) # $CONOUT
# These are already defined, so no need to redefine.
COORD = wintypes._COORD
SMALL_RECT = wintypes.SMALL_RECT
CONSOLE_SCREEN_BUFFER_INFO = console.CONSOLE_SCREEN_BUFFER_INFO
#------------------------------------------------
# Main
#------------------------------------------------
wk32 = windll.kernel32
hSo = wk32.GetStdHandle(STD_OUTPUT_HANDLE)
GetCSBI = wk32.GetConsoleScreenBufferInfo
def cpos():
csbi = CONSOLE_SCREEN_BUFFER_INFO()
GetCSBI(hSo, byref(csbi))
xy = csbi.dwCursorPosition
return '({},{})'.format(xy.X,xy.Y)
cls='\x1b[H'
print('\n'*61)
print(cls+'12345', end='', flush=True); print(' {}'.format(cpos()), flush=True)
# 12345 (5,503)
Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import readline Traceback (most recent call last): File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named 'readline' >>>
和pip install readline
returns error: this module is not meant to work on Windows
(截断)。
我找到了另一个纯 Windows 环境的解决方案(没有 GNU readline interface. Desired structure definitions borrowed at programtalk.com,请参阅第 1..91 行:
# from winbase.h
STDOUT = -11
STDERR = -12
from ctypes import (windll, byref, Structure, c_char, c_short, c_uint32,
c_ushort, ArgumentError, WinError)
handles = {
STDOUT: windll.kernel32.GetStdHandle(STDOUT),
STDERR: windll.kernel32.GetStdHandle(STDERR),
}
SHORT = c_short
WORD = c_ushort
DWORD = c_uint32
TCHAR = c_char
class COORD(Structure):
"""struct in wincon.h"""
_fields_ = [
('X', SHORT),
('Y', SHORT),
]
class SMALL_RECT(Structure):
"""struct in wincon.h."""
_fields_ = [
("Left", SHORT),
("Top", SHORT),
("Right", SHORT),
("Bottom", SHORT),
]
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
"""struct in wincon.h."""
_fields_ = [
("dwSize", COORD),
("dwCursorPosition", COORD),
("wAttributes", WORD),
("srWindow", SMALL_RECT),
("dwMaximumWindowSize", COORD),
]
def __str__(self):
"""Get string representation of console screen buffer info."""
return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
self.dwSize.Y, self.dwSize.X
, self.dwCursorPosition.Y, self.dwCursorPosition.X
, self.wAttributes
, self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
, self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
)
def GetConsoleScreenBufferInfo(stream_id=STDOUT):
"""Get console screen buffer info object."""
handle = handles[stream_id]
csbi = CONSOLE_SCREEN_BUFFER_INFO()
success = windll.kernel32.GetConsoleScreenBufferInfo(
handle, byref(csbi))
if not success:
raise WinError()
return csbi
### end of https://programtalk.com/vs4/python/14134/dosage/dosagelib/colorama.py/
clrcur = '\x1b[H' # move cursor to the top left corner
clrscr = '\x1b[2J' # clear entire screen (? moving cursor ?)
# '\x1b[H\x1b[2J'
from ctypes import windll, wintypes, byref
def get_cursor_pos():
cursor = wintypes.POINT()
aux = windll.user32.GetCursorPos(byref(cursor))
return (cursor.x, cursor.y)
mouse_pos = get_cursor_pos()
# print('mouse at {}'.format(mouse_pos))
def cpos():
csbi = GetConsoleScreenBufferInfo()
return '({},{})'.format(csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y)
print('12345', end='', flush=True)
print(' {}'.format(cpos()), flush=True)
# an attempt to resolve discrepancy between buffer and screen size
# in the following code snippet:
import sys
if len(sys.argv) > 1 and len(sys.argv[1]) > 0:
csbi = GetConsoleScreenBufferInfo()
keybd_pos = (csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y)
print('\nkbd buffer at {}'.format(keybd_pos))
import os
screensize = os.get_terminal_size()
keybd_poss = ( csbi.dwCursorPosition.X,
min( csbi.dwSize.Y,
csbi.dwCursorPosition.Y,
csbi.dwMaximumWindowSize.Y,
screensize.lines))
# screen line number is incorrectly computed if termial is scroll-forwarded
print('kbd screen at {} (disputable? derived from the following data:)'
.format(keybd_poss))
print( 'csbi.dwSize ', (csbi.dwSize.X, csbi.dwSize.Y))
print( 'terminal_size ', (screensize.columns, screensize.lines))
print( 'csbi.dwMaxSize', (csbi.dwMaximumWindowSize.X, csbi.dwMaximumWindowSize.Y))
print( 'csbi.dwCurPos ', (csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y))
输出:.\SO732748.py
12345 (5,526)
从第 113 行开始,尝试解决 差异 缓冲区和屏幕大小之间的差异(不成功,绝对屏幕行号计算不正确,至少如果终端是 scroll-forwarded). 差异不会出现在Windows终端,那里总是buffer height == window height
,所有这些计算都是不必要的...
示例:.\SO732748.py x
12345 (5,529)
kbd buffer at (0, 530)
kbd screen at (0, 36) (disputable? derived from the following data:)
csbi.dwSize (89, 1152)
terminal_size (88, 36)
csbi.dwMaxSize (89, 37)
csbi.dwCurPos (0, 530)