是否可以修改输入功能以回显大写字母?

huangapple go评论59阅读模式
英文:

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

没有,没有实用的方法可以做到这一点。

也许可以在源代码的深处编辑函数,但那样会很混乱。最好编写一个自定义输入函数,使用类似curseskeyboard之类的东西来处理输入输出。

英文:

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.stdoutpynput 的版本。

在我看来,更有用的是具有可配置输出大写、小写、首字母大写或任何其他字符串方法的函数。还可以通过正则表达式来限制输入。

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 &quot;keep&quot; 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) -&gt; str: 
return &quot;&quot;.join(self.input)
@property
def xput(self) -&gt; list[str]: 
return (self.input or [None]*(self.caret+1))
def i(self, func:callable, d:int=0) -&gt; int:
return (-1, self.caret+d)[func(self.caret,len(self.input))]
&quot;&quot;&quot; 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 &amp; pause, do nothing
&quot;&quot;&quot;
def input2(prompt:str=&#39;&#39;, fmt:callable=None, expr:str=None, flags:int=0, req:str=None) -&gt; str:
#default
fmt = fmt or (lambda c: c)
#for matching `fmt` results
match = re.compile(fr&#39;{expr or r&quot;.&quot;}|[\b\n]$&#39;, flags).match
#for matching final results, upon submission
req = False if not req else re.compile(fr&#39;{req}&#39;, flags).match
#print prompt
sys.stdout.write(prompt)
sys.stdout.flush()
#input storage, caret position
d = _data()
#keypress handler
def emulator(key) -&gt; 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 &gt;= len(tmp): tmp.append(c)
else                  : tmp[d.caret] = c
#format
tmp = fmt(&#39;&#39;.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==&#39;\t&#39;) 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&#39;\r{blank}\r&#39;)         #erase line
sys.stdout.write(f&#39;{prompt}{d.output}&#39;)  #reprint line
sys.stdout.write(&#39;\b&#39;*(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 = (&#39;\n&#39;, tmp or &#39;&#39;)[(not e) &amp; 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 &lt; d.caret &lt;= (len(d.input)-r):
tmp = d.xput[d.i(operator.lt)]
tmp = (&#39;\b&#39;,tmp or &#39;&#39;)[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__ == &#39;__main__&#39;:
strawman = input2(&#39;Full Name: &#39;, str.upper, r&#39;^[a-z ]+$&#39;, re.I, r&#39;^[a-z]{2,} [a-z]{2,}$&#39;)
print(strawman)

repository: input2 (github)

答案4

得分: 1

作为对TheTridentGuy的回答的回应,这是一个(稍微)不太实用的方法:

如果你限制自己在UNIX上操作,那么你可以使用内置的termiostty模块将终端设置为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&#39;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=&#39;&#39;)
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 = &#39;&#39;
while 1:
if isData():
c = sys.stdin.read(1)
print(c.upper(), end=&#39;&#39;)
sys.stdout.flush()
if c == &#39;\n&#39;:
break
s += c.upper()
finally:
# restore the terminal settings
restoreSettings()
atexit.unregister(restoreSettings)
return s
inp = upper_input(&#39;&gt; &#39;)
print(f&#39;Hi, {inp}&#39;)

You could probably port this to Windows using something similar, but I don't have a Windows machine.

huangapple
  • 本文由 发表于 2023年5月29日 00:39:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76352527.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定