在python中捕获任意时间的用户输入

时间:2010-09-16 23:41:04

标签: python linux input interrupt

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

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

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

6 个答案:

答案 0 :(得分:2)

您需要一个单独的进程(或可能是一个线程)来读取终端并通过某种形式的进程间通信(IPC)将其发送到感兴趣的进程(线程间通信可能更难 - 基本上是唯一的你要做的是从一个辅助线程向主线程发送一个KeyboardInterrupt。既然你说“我希望会有截获用户输入的不仅仅是^ C其他环路的简单方法”,我很伤心让你失望,但是这是事实:有方法可以做到你要求什么,但简单他们不是。

答案 1 :(得分:2)

取决于操作系统和可用的库,有不同的方法可以实现这一点。 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对我所分享的魔法价值的不满。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

我不确定它是否是最佳解决方案,但您可以创建一个执行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事情取决于你在哪里运行它。它无处不在。

答案 4 :(得分:0)

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


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

答案 5 :(得分:0)

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

由于穆罕默德所做的改变,已接受的答案已不再有效。我试图提交更正,但它一直被拒绝,所以我会将其作为一个单独的答案发布。我的代码几乎与他的代码相同,只有一个很小的变化:

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 == ascii.ESC”,而是将其更改为“c == chr(ascii.ESC)。我和其他1位开发人员都测试并确认此更改是必要的,否则该程序将无法正常工作。

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