英文:
Is it possible to modify input function to echo uppercase letters?
问题
如果我有一个input("> ")
,如果你尝试输入小写的"Hello, world!",它会变成这样:
> HELLO WORLD!
英文:
Let's say that I have an input("> ")
, and if you try to input a lowercase "Hello, world!" it will look like this:
> HELLO WORLD!
答案1
得分: 5
没有,没有实用的方法可以做到这一点。
也许可以在源代码的深处编辑函数,但那样会很混乱。最好编写一个自定义输入函数,使用类似curses或keyboard之类的东西来处理输入输出。
英文:
No, there isn't a practical way to do this
It might be possible to edit the function somewhere deep in the source code, but that would be messy. You'd be best off writing a custom input function, using something like curses or keyboard to deal with the IO.
答案2
得分: 3
你可以使用 pyautogui
来模拟这个操作。
import pyautogui, ctypes
def upper_input(msg:str='输入一些内容:'):
if not ctypes.WinDLL("User32.dll").GetKeyState(0x14):
pyautogui.press('capslock')
input(msg)
pyautogui.press('capslock')
upper_input()
英文:
You could somewhat emulate this with pyautogui
.
import pyautogui, ctypes
def upper_input(msg:str='write something: '):
if not ctypes.WinDLL("User32.dll").GetKeyState(0x14):
pyautogui.press('capslock')
input(msg)
pyautogui.press('capslock')
upper_input()
答案3
得分: 1
这是一个使用 sys.stdout
和 pynput
的版本。
在我看来,更有用的是具有可配置输出大写、小写、首字母大写或任何其他字符串方法的函数。还可以通过正则表达式来限制输入。
import sys, re, operator
from pynput import keyboard
from dataclasses import dataclass, field
#commands / print only
BACKSPACE = 8
ENTER = 13
L_ARROW = 37
R_ARROW = 39
#ranges to "keep" from
ALL = tuple((9, *range(32, 127))) #all accepted characters range
ALL_AE = (9,32) #AttributeError accepted characters range (tab, space, np_dot)
ALL_TE = tuple((46, *range(48,58))) #TypeError accepted characters range (0-9)
#numpad vk range
NPVK = range(96,106)
NPVK_OFS = 48
#numpad decimal
DCVK = 110
DCVK_OFS = 64
@dataclass
class _data:
input:list[str] = field(default_factory=list[str])
caret:int = 0
#conveniences
@property
def output(self) -> str:
return "".join(self.input)
@property
def xput(self) -> list[str]:
return (self.input or [None]*(self.caret+1))
def i(self, func:callable, d:int=0) -> int:
return (-1, self.caret+d)[func(self.caret,len(self.input))]
""" INPUT 2
*all arguments are optional
@prompt - the user prompt
@fmt - str.lower, str.upper, str.title... or any similar function reference
@expr/flags - `re.compile(@expr, @flags).match(@fmt(input_data))`
@req - `re.compile(@req, @flags).match(final_data)`
supports:
* backspace
* numpad
* arrow keys (locked to span of input)
! home, end, pgup, pgdn, ins, del, prt sc & pause, do nothing
"""
def input2(prompt:str='', fmt:callable=None, expr:str=None, flags:int=0, req:str=None) -> str:
#default
fmt = fmt or (lambda c: c)
#for matching `fmt` results
match = re.compile(fr'{expr or r"."}|[\b\n]$', flags).match
#for matching final results, upon submission
req = False if not req else re.compile(fr'{req}', flags).match
#print prompt
sys.stdout.write(prompt)
sys.stdout.flush()
#input storage, caret position
d = _data()
#keypress handler
def emulator(key) -> bool:
#get and test ordinal, one way or another
try:
i = ord(key.char)
ok = i in ALL
except AttributeError:
i = key.value.vk
ok = i in ALL_AE + (BACKSPACE, ENTER)
except TypeError:
i = key.vk
#reformat numpad vk to number ord
i -= NPVK_OFS * (i in NPVK)
#reformat numpad decimal vk to dot ord
i -= DCVK_OFS * (i == DCVK)
ok = i in ALL_TE
if ok:
#get character
c = chr(i)
e = False #assume enter is not being pressed
#character filtering / storage
if t := (i in ALL):
#copy
tmp = d.input[:]
#modify
if d.caret >= len(tmp): tmp.append(c)
else : tmp[d.caret] = c
#format
tmp = fmt(''.join(tmp))
#check
if not match(tmp): return True
#update
d.input = list(tmp)
#backspace
elif i==BACKSPACE:
#accomodate for maximum tab space and/or character to be popped
n = sum(3*(c=='\t') for c in d.input)+1
#adjust console if d.input was modified
if d.xput.pop(d.i(operator.le, -1)):
L = len(d.input)
#blank line
blank = chr(0)*(len(prompt)+L+n)
#update caret
d.caret -= 1
sys.stdout.write(f'\r{blank}\r') #erase line
sys.stdout.write(f'{prompt}{d.output}') #reprint line
sys.stdout.write('\b'*(L-d.caret)) #place caret
sys.stdout.flush()
return True
#enter with required
elif (e := (i==ENTER)) and req:
if not req(d.output): return True
#decide the proper character to print
tmp = d.xput[d.i(operator.lt)]
tmp = ('\n', tmp or '')[not e & t]
#update caret
d.caret += 1
#print character
sys.stdout.write(tmp)
sys.stdout.flush()
#quit on enter
return not e
#arrow keys
elif i in (L_ARROW, R_ARROW):
r = i==R_ARROW
if -r < d.caret <= (len(d.input)-r):
tmp = d.xput[d.i(operator.lt)]
tmp = ('\b',tmp or '')[r]
d.caret += (-1,1)[r]
sys.stdout.write(tmp)
sys.stdout.flush()
return True
#listen for keys
with keyboard.Listener(on_press=emulator) as listener:
listener.join()
return d.output
if __name__ == '__main__':
strawman = input2('Full Name: ', str.upper, r'^[a-z ]+$', re.I, r^[a-z]{2,} [a-z]{2,}$')
print(strawman)
repository: input2 (github)
英文:
Here is a version that uses sys.stdout
and pynput
.
IMO, it's more useful to have a function that can be configured to output uppercase, as-well-as lowercase, capitalized, title or any other string method. It may also be useful to pass everything through a regex
, to restrict input.
import sys, re, operator
from pynput import keyboard
from dataclasses import dataclass, field
#commands / print only
BACKSPACE = 8
ENTER = 13
L_ARROW = 37
R_ARROW = 39
#ranges to "keep" from
ALL = tuple((9, *range(32, 127))) #all accepted characters range
ALL_AE = (9,32) #AttributeError accepted characters range (tab,space,np_dot)
ALL_TE = tuple((46, *range(48,58))) #TypeError accepted characters range (0-9)
#numpad vk range
NPVK = range(96,106)
NPVK_OFS = 48
#numpad decimal
DCVK = 110
DCVK_OFS = 64
@dataclass
class _data:
input:list[str] = field(default_factory=list[str])
caret:int = 0
#conveniences
@property
def output(self) -> str:
return "".join(self.input)
@property
def xput(self) -> list[str]:
return (self.input or [None]*(self.caret+1))
def i(self, func:callable, d:int=0) -> int:
return (-1, self.caret+d)[func(self.caret,len(self.input))]
""" INPUT 2
*all arguments are optional
@prompt - the user prompt
@fmt - str.lower, str.upper, str.title... or any similar function reference
@expr/flags - `re.compile(@expr, @flags).match(@fmt(input_data))`
@req - `re.compile(@req, @flags).match(final_data)`
supports:
* backspace
* numpad
* arrow keys (locked to span of input)
! home, end, pgup, pgdn, ins, del, prt sc & pause, do nothing
"""
def input2(prompt:str='', fmt:callable=None, expr:str=None, flags:int=0, req:str=None) -> str:
#default
fmt = fmt or (lambda c: c)
#for matching `fmt` results
match = re.compile(fr'{expr or r"."}|[\b\n]$', flags).match
#for matching final results, upon submission
req = False if not req else re.compile(fr'{req}', flags).match
#print prompt
sys.stdout.write(prompt)
sys.stdout.flush()
#input storage, caret position
d = _data()
#keypress handler
def emulator(key) -> bool:
#get and test ordinal, one way or another
try:
i = ord(key.char)
ok = i in ALL
except AttributeError:
i = key.value.vk
ok = i in ALL_AE + (BACKSPACE, ENTER)
except TypeError:
i = key.vk
#reformat numpad vk to number ord
i -= NPVK_OFS * (i in NPVK)
#reformat numpad decimal vk to dot ord
i -= DCVK_OFS * (i == DCVK)
ok = i in ALL_TE
if ok:
#get character
c = chr(i)
e = False #assume enter is not being pressed
#character filtering / storage
if t := (i in ALL):
#copy
tmp = d.input[:]
#modify
if d.caret >= len(tmp): tmp.append(c)
else : tmp[d.caret] = c
#format
tmp = fmt(''.join(tmp))
#check
if not match(tmp): return True
#update
d.input = list(tmp)
#backspace
elif i==BACKSPACE:
#accomodate for maximum tab space and/or character to be popped
n = sum(3*(c=='\t') for c in d.input)+1
#adjust console if d.input was modified
if d.xput.pop(d.i(operator.le, -1)):
L = len(d.input)
#blank line
blank = chr(0)*(len(prompt)+L+n)
#update caret
d.caret -= 1
sys.stdout.write(f'\r{blank}\r') #erase line
sys.stdout.write(f'{prompt}{d.output}') #reprint line
sys.stdout.write('\b'*(L-d.caret)) #place caret
sys.stdout.flush()
return True
#enter with required
elif (e := (i==ENTER)) and req:
if not req(d.output): return True
#decide the proper character to print
tmp = d.xput[d.i(operator.lt)]
tmp = ('\n', tmp or '')[(not e) & t]
#update caret
d.caret += 1
#print character
sys.stdout.write(tmp)
sys.stdout.flush()
#quit on enter
return not e
#arrow keys
elif i in (L_ARROW, R_ARROW):
r = i==R_ARROW
if -r < d.caret <= (len(d.input)-r):
tmp = d.xput[d.i(operator.lt)]
tmp = ('\b',tmp or '')[r]
d.caret += (-1,1)[r]
sys.stdout.write(tmp)
sys.stdout.flush()
return True
#listen for keys
with keyboard.Listener(on_press=emulator) as listener:
listener.join()
return d.output
if __name__ == '__main__':
strawman = input2('Full Name: ', str.upper, r'^[a-z ]+$', re.I, r'^[a-z]{2,} [a-z]{2,}$')
print(strawman)
repository: input2 (github)
答案4
得分: 1
作为对TheTridentGuy的回答的回应,这是一个(稍微)不太实用的方法:
如果你限制自己在UNIX上操作,那么你可以使用内置的termios
和tty
模块将终端设置为cbreak模式。从那里,你可以将接收到的每个字符转换为大写,并使用print()
和sys.stdout.flush()
来输出。你还需要使用atexit
注册一个函数,在函数结束时恢复终端设置,以确保终端设置始终被还原(如果不这样做,它会使终端处于奇怪的模式,导致程序的其他部分无法正常工作)。这个想法的一部分受到了this answer的启发:
import atexit
import sys
import select
import tty
import termios
def upper_input(prompt):
# 获取当前终端设置
old_settings = termios.tcgetattr(sys.stdin)
# 如果有数据供我们读取,则返回true
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
# 恢复终端设置为原始状态
def restoreSettings():
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
# 输出提示
print(prompt, end='')
sys.stdout.flush()
try:
# 更改终端设置
tty.setcbreak(sys.stdin.fileno())
atexit.register(restoreSettings)
# 循环直到收到换行符,将每个字符添加到一个字符串中,并将接收到的每个字符以大写形式打印出来
s = ''
while 1:
if isData():
c = sys.stdin.read(1)
print(c.upper(), end='')
sys.stdout.flush()
if c == '\n':
break
s += c.upper()
finally:
# 恢复终端设置
restoreSettings()
atexit.unregister(restoreSettings)
return s
inp = upper_input('> ')
print(f'Hi, {inp}')
你可能可以使用类似的方法将这个代码移植到Windows上,但我没有Windows机器。
英文:
In response to TheTridentGuy's answer, here is that (slightly) impractical way:
If you're restricting yourself to UNIX, then you can use the built-in termios
and tty
modules to put the terminal in cbreak mode. From there, you can uppercase each individual character you get and use print()
and sys.stdout.flush()
. You'll also want to register a function with atexit
to restore the terminal then unregister at the end of your function, just to make sure the terminal settings are always restored (if you don't do this, it'll leave your terminal in a weird mode and make other parts of your program not work). Part of this idea was inspired by this answer:
import atexit
import sys
import select
import tty
import termios
def upper_input(prompt):
# get current terminal settings
old_settings = termios.tcgetattr(sys.stdin)
# Returns true if there's data for us to read using select
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
# restores the terminal settings to what they were
def restoreSettings():
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
# print the prompt
print(prompt, end='')
sys.stdout.flush()
try:
# change the terminal settings
tty.setcbreak(sys.stdin.fileno())
atexit.register(restoreSettings)
# keep looping until we get a newline, adding each character to a
# growing string and printing out each character we get as uppercase
s = ''
while 1:
if isData():
c = sys.stdin.read(1)
print(c.upper(), end='')
sys.stdout.flush()
if c == '\n':
break
s += c.upper()
finally:
# restore the terminal settings
restoreSettings()
atexit.unregister(restoreSettings)
return s
inp = upper_input('> ')
print(f'Hi, {inp}')
You could probably port this to Windows using something similar, but I don't have a Windows machine.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论