【问题标题】:Python: interrupt the builtin input() to set a new prompt (before user input)Python:中断内置 input() 以设置新提示(在用户输入之前)
【发布时间】:2019-12-19 21:05:43
【问题描述】:

我有一个函数,它有一个连续循环,使用 python 的内置输入(提示)询问用户的输入。我也有一个单独的线程在做一些工作,当该线程中满足某个条件时,输入提示应该改变。

所以,假设在启动时提示是“输入:”,但是在线程工作的中间,满足了一个条件,所以提示应该切换到“继续,输入:”。现在,如果用户在第一个提示符处没有输入任何内容,但线程到达了切换提示符的位置,那么我们仍然停留在第一个阻塞输入调用上,该调用具有“输入:”,因为它是提示符。

# 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

这很混乱,但是当它超时并调用 new input() 时,当前行缓冲区被打印但光标在它的前面。因此,如果我输入“aaa”,那么它会重新打印“aaa”,但光标不在该缓冲区的末尾,而是在开头。

有什么建议吗?

更新:我当然可以尝试更多地使用信号选项。到目前为止,这似乎是我最好的选择。如果用户已经开始输入内容,我似乎无法让光标移动到输入缓冲区的末尾。我不希望用户知道呼叫超时并再次被呼叫。

【问题讨论】:

  • 而不是input,您可能希望使用curses 之类的东西与终端交互,而不是使用input(它只是将提示写入一个流并从另一个流中读取)。
  • curses 是否与 python 的 readline 一起使用以完成制表符?我没有使用过 curses,但快速查看文档似乎并没有提供一种简单的方法来实现它。
  • 不,它们是分开的。

标签: python input command-prompt stdin python-multithreading


【解决方案1】:

好的,我想通了。这比使用信号警报超时要好得多。

如果将来有人偶然发现这个特定问题,我基本上就是这样做的:

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)

如果您使用的是制表符完成(我没有在这里显示),您可能会注意到,如果您在第一次提示切换后尝试制表符完成(不接受来自第一个提示的任何输入),然后制表符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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2014-06-08
    • 1970-01-01
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多