【问题标题】:Capturing user input at arbitrary times in python在python中的任意时间捕获用户输入
【发布时间】:2011-04-13 11:59:41
【问题描述】:

当用户在控制台输入内容时,有没有办法向 python 模块发送中断?例如,如果我正在运行一个无限 while 循环,我可以用 try/except 包围它,然后在 except 块中执行我需要执行的操作。

有没有办法用任意输入复制这个功能?是控制序列还是标准字符?

编辑:对不起,这是在 linux 上

【问题讨论】:

  • 什么操作系统?我认为没有通用的方法。
  • 抱歉,是 linux。我想我只是假设每个程序员都运行 linux >_
  • 不用担心。事实上,这就是我将 Linux 示例放在我的答案中的原因 :)

标签: python linux input interrupt


【解决方案1】:

取决于操作系统和可用的库,有不同的方法可以实现这一点。 This answer 提供了其中的一些。

这是从那里复制的 Linux/OS X 部分,无限循环使用转义字符终止。对于 Windows 解决方案,您可以自行检查答案。

import sys
import select
import tty
import termios

from curses import ascii

def isData():
    return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])

old_settings = termios.tcgetattr(sys.stdin)
try:
    tty.setcbreak(sys.stdin.fileno())

    i = 0
    while 1:
        print i
        i += 1

        if isData():
            c = sys.stdin.read(1)
            if c == chr(ascii.ESC):
                break

finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)

编辑:将字符检测更改为使用 curses.ascii 定义的字符,这要感谢 Daenyth 对我分享的魔法值的不满。

【讨论】:

  • 这对我不起作用(在 Mac OS X 中但可能无关紧要),直到我切换回魔法值 '\x1b'。 ascii.ESC = 27 和 chr(27) = '\x1b'...所以也许你只需要 chr(ascii.ESC)。我确认此更改对我有用,并相应地编辑了您的代码,如果我遗漏了什么,请随时恢复!
  • 或者更确切地说,任何拥有适当代表/访问权限的人,请随时查看我的修复并批准它!由于上述原因,我很确定该代码自 2010 年以来已被破坏。
  • 所以一个人批准了我的编辑,但 3 人拒绝了它,并且无法向他们发送消息 - 再次,我很确定这段代码已被破坏并且已经存在 3 年了。恕我直言,python中的键盘输入是一件非常重要的事情。 Andrew,soon 或 vals 能否回复告诉我我的修复有什么问题,或者他们是否进行了实际测试?我将尝试重新提交编辑。谢谢!
  • 我刚刚在 linux 上进行了测试,可以确认 murftown 所说的内容。只有将ascii.ESC 替换为chr(ascii.ESC) 时,代码才能按原样运行。
  • 谢谢@murftown。没有尝试过,但从查看我的编辑来看,很明显这是我自 2010 年以来的编辑引入的一个错误,正如你所说。好收获!
【解决方案2】:

您需要一个单独的进程(或可能是一个线程)来读取终端并通过某种形式的进程间通信 (IPC) 将其发送到感兴趣的进程(线程间通信可能更难——基本上是唯一的您要做的就是将KeyboardInterrupt 从辅助线程发送到主线程)。既然您说“我希望有一种简单的方法可以将用户输入注入到循环中,而不仅仅是 ^C”,我很遗憾让您失望,但这是事实:有一些方法可以满足您的要求,但是简单它们不是。

【讨论】:

    【解决方案3】:

    KeyboardInterrupt 的特殊之处在于它可以被捕获(即在具有相应 POSIX 支持的操作系统下的 SIGINT,Windows 上的 SetConsoleCtrlHandler)并进行相应的处理。如果您想在处理用户输入的同时在另一个阻塞循环中工作,请查看threadingsubprocess 模块,了解如何在两个不同的线程/进程之间交换数据。还要确保了解与并行计算密切相关的问题(即竞争条件、线程安全/同步等)

    【讨论】:

    • 是的,我对线程和所有这些都很熟悉,我现在只是在做一个测试功能,我希望有一种简单的方法可以将用户输入注入到循环中,而不仅仅是^C
    • 让我们知道与用户交互从来没有简单!
    【解决方案4】:

    我不确定这是否是最佳解决方案,但您可以创建一个执行 while True: sys.stdin.read(1) 的线程

    这样您总是可以读取所有输入,但速度会很慢,而且您必须自己组合字符串。

    例子:

    import os
    import sys
    import time
    import threading
    
    class StdinReader(threading.Thread):
        def run(self):
            while True:
                print repr(sys.stdin.read(1))
    
    os.system('stty raw')
    
    stdin_reader = StdinReader()
    stdin_reader.start()
    
    while True:
        time.sleep(1)
    
    os.system('stty sane')
    

    stty raw 的事情取决于你在哪里运行它。它不会在任何地方都有效。

    【讨论】:

      【解决方案5】:

      这就是我实现我想要的功能的方式,但这并不是我的问题所在。所以我会发布这个,以防有人正在寻找相同的概念,但接受更直接的答案之一。

      
      while True:
              try:
                  time.sleep( 1 )
                  #do stuff
              except KeyboardInterrupt:
                  inp = raw_input( '>' )
                  if inp == 'i':
                      obj.integrate()
                  elif inp == 'r':
                      obj.reset()
      

      【讨论】:

        【解决方案6】:

        编辑:Guido 为我对接受的答案进行了编辑,因此不再需要此答案。

        由于 Muhammed 所做的更改,已接受的答案不再有效。我已尝试提交更正,但一直被拒绝,因此我将其作为单独的答案发布。我的代码几乎和他的一样,只有 1 个微小的变化:

        import sys
        import select
        import tty
        import termios
        
        from curses import ascii
        
        def isData():
            return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
        
        old_settings = termios.tcgetattr(sys.stdin)
        try:
            tty.setcbreak(sys.stdin.fileno())
        
            i = 0
            while 1:
                print i
                i += 1
        
                if isData():
                    c = sys.stdin.read(1)
                    if c == chr(ascii.ESC):
                        break
        
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
        

        唯一的区别是我将它改为“c == chr(ascii.ESC)”而不是“c == ascii.ESC”。我和其他 1 位开发人员都已测试并确认此更改是必要的,否则程序将无法正常运行。

        该程序应该显示越来越大的数字,直到您按 ESC,然后退出。但是如果 ascii.ESC 周围没有 chr(),它就不会检测到您的 ESC 按键。

        【讨论】:

          猜你喜欢
          • 2022-07-01
          • 2023-03-29
          • 2019-04-29
          • 1970-01-01
          • 2023-03-11
          • 2018-05-03
          • 1970-01-01
          • 1970-01-01
          • 2010-11-28
          相关资源
          最近更新 更多