Python: 中断内置 input() 以设置新提示(在用户输入之前)
Python: interrupt the builtin input() to set a new prompt (before user input)
我有一个函数,它有一个连续循环,要求用户使用 python 的内置输入(提示)进行输入。我还有一个单独的线程在做一些工作,当那个线程满足某个条件时,输入提示应该改变。
所以,假设在启动时提示是 "Input: ",但是在线程工作的中间,满足了一个条件,所以提示应该切换到 "Go Ahead, Type: "。现在,如果用户在第一个提示符下没有输入任何内容,但线程到达了提示符切换的位置,那么我们仍然停留在第一个具有 "Input: " 提示符的阻塞输入调用上。
# Start the thread that does some work and eventually changes the prompt
myThread.start()
#Start looping to ask for user input, get the prompt from the thread
while True:
userInput = input(myThread.get_prompt())
<do something with input>
我知道我可以使用 select([stdin],[],[],0.0) 完成此操作,在执行 stdin.readline() 之前轮询标准输入,然后再次打印提示如果 1) 我们得到用户输入或 2) 如果提示改变。
但是,我想找到一个使用 Python 的内置 input() 函数的解决方案,以便我可以使用 python 的 readline 模块设置制表符补全。
我试着玩弄一个基本上每隔几秒就会中断 input() 的信号。有了这个,我需要让它看起来无缝,即在不重新打印提示的情况下进行了新的 input() 调用。所以像:
myThread.start()
while True:
userInput = None
signal(SIGALRM, handler)
signal.alarm(3)
try:
userInput = input("\r" + myThread.get_prompt())
except TimeoutError:
pass
finally:
signal.alarm(0)
<do something with input>
def handler(signum, frame):
raise TimeoutError
这很乱,但是当它超时并调用新的 input() 时,打印了当前行缓冲区,但光标位于它的前面。因此,如果我键入 "aaa" 然后它会重新打印 "aaa" 但光标不在该缓冲区的末尾,而是在开头。
有什么建议吗?
更新:我当然可以尝试更多地使用信号选项。到目前为止,这似乎是我最好的选择。如果用户已经开始输入内容,我似乎无法让光标移动到输入缓冲区的末尾。我不希望用户知道呼叫超时和再次呼叫。
好的,我明白了。而且它比使用信号警报超时要好得多。
以防将来有人遇到这个特定问题,这基本上是我所做的:
import threading
import time
import readline
import sys
class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__()
# Set the initial prompt
self.prompt = "Input: "
def run(self):
time.sleep(6)
# Now we want to change the prompt so that next time input loop asks for prompt it is correct
self.set_prompt("Go ahead, type: ")
# Now overwrite what was there in the prompt
sys.stdout.write("\r" + self.get_prompt())
sys.stdout.flush()
# Get the current line buffer and reprint it, in case some input had started to be entered when the prompt was switched
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()
def get_prompt(self):
return self.prompt
def set_prompt(self, new_prompt):
self.prompt = new_prompt
# Create and start thread
myThread = MyThread()
myThread.start()
# Input loop
while True:
userInput = input(myThread.get_prompt())
print("Got: " + userInput)
如果您正在使用制表符补全(我没有在此处显示),您可能会注意到,如果您在第一次提示切换后尝试制表符补全(不接受来自第一个提示的任何输入),然后 tab complete 仍然认为我们正在阅读第一个提示并将重新打印该提示。在这种情况下,您需要执行以下操作:
readline.set_completion_display_matches_hook(display)
其中 display 是一个函数,它做的事情类似于我们的线程必须做的事情来重写正确的提示:
def display(substitution, matches, longest_match_length):
# Print the matches
print('')
buffer = ''
for match in matches:
buffer += match + " "
print(buffer)
# Rewrite the NEW prompt
sys.stdout.write(<prompt>)
sys.stdout.flush()
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()
我有一个函数,它有一个连续循环,要求用户使用 python 的内置输入(提示)进行输入。我还有一个单独的线程在做一些工作,当那个线程满足某个条件时,输入提示应该改变。
所以,假设在启动时提示是 "Input: ",但是在线程工作的中间,满足了一个条件,所以提示应该切换到 "Go Ahead, Type: "。现在,如果用户在第一个提示符下没有输入任何内容,但线程到达了提示符切换的位置,那么我们仍然停留在第一个具有 "Input: " 提示符的阻塞输入调用上。
# Start the thread that does some work and eventually changes the prompt
myThread.start()
#Start looping to ask for user input, get the prompt from the thread
while True:
userInput = input(myThread.get_prompt())
<do something with input>
我知道我可以使用 select([stdin],[],[],0.0) 完成此操作,在执行 stdin.readline() 之前轮询标准输入,然后再次打印提示如果 1) 我们得到用户输入或 2) 如果提示改变。
但是,我想找到一个使用 Python 的内置 input() 函数的解决方案,以便我可以使用 python 的 readline 模块设置制表符补全。
我试着玩弄一个基本上每隔几秒就会中断 input() 的信号。有了这个,我需要让它看起来无缝,即在不重新打印提示的情况下进行了新的 input() 调用。所以像:
myThread.start()
while True:
userInput = None
signal(SIGALRM, handler)
signal.alarm(3)
try:
userInput = input("\r" + myThread.get_prompt())
except TimeoutError:
pass
finally:
signal.alarm(0)
<do something with input>
def handler(signum, frame):
raise TimeoutError
这很乱,但是当它超时并调用新的 input() 时,打印了当前行缓冲区,但光标位于它的前面。因此,如果我键入 "aaa" 然后它会重新打印 "aaa" 但光标不在该缓冲区的末尾,而是在开头。
有什么建议吗?
更新:我当然可以尝试更多地使用信号选项。到目前为止,这似乎是我最好的选择。如果用户已经开始输入内容,我似乎无法让光标移动到输入缓冲区的末尾。我不希望用户知道呼叫超时和再次呼叫。
好的,我明白了。而且它比使用信号警报超时要好得多。
以防将来有人遇到这个特定问题,这基本上是我所做的:
import threading
import time
import readline
import sys
class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__()
# Set the initial prompt
self.prompt = "Input: "
def run(self):
time.sleep(6)
# Now we want to change the prompt so that next time input loop asks for prompt it is correct
self.set_prompt("Go ahead, type: ")
# Now overwrite what was there in the prompt
sys.stdout.write("\r" + self.get_prompt())
sys.stdout.flush()
# Get the current line buffer and reprint it, in case some input had started to be entered when the prompt was switched
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()
def get_prompt(self):
return self.prompt
def set_prompt(self, new_prompt):
self.prompt = new_prompt
# Create and start thread
myThread = MyThread()
myThread.start()
# Input loop
while True:
userInput = input(myThread.get_prompt())
print("Got: " + userInput)
如果您正在使用制表符补全(我没有在此处显示),您可能会注意到,如果您在第一次提示切换后尝试制表符补全(不接受来自第一个提示的任何输入),然后 tab complete 仍然认为我们正在阅读第一个提示并将重新打印该提示。在这种情况下,您需要执行以下操作:
readline.set_completion_display_matches_hook(display)
其中 display 是一个函数,它做的事情类似于我们的线程必须做的事情来重写正确的提示:
def display(substitution, matches, longest_match_length):
# Print the matches
print('')
buffer = ''
for match in matches:
buffer += match + " "
print(buffer)
# Rewrite the NEW prompt
sys.stdout.write(<prompt>)
sys.stdout.flush()
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()