2015-12-21 38 views
1

我正在寻找向Python脚本发送按键的方式。在这种情况下,我试图检测脚本是否按下任意键,而不仅仅是中断信号(ctrl + c,ctrl + d,...)。如何处理与Python的异步击键?

我检查了信号python模块。但它似乎只准备处理中断信号,而不是如果我按“K”或“Space”例如。我已经在模块的官方文档看到了这一点:

import signal 
import os 
import time 

def receive_signal(signum, stack): 
    print 'Received:', signum 

signal.signal(signal.SIGUSR1, receive_signal) 
signal.signal(signal.SIGUSR2, receive_signal) 

print 'My PID is:', os.getpid() 

while True: 
    print 'Waiting...' 
    time.sleep(3) 

他们说:

要发送信号给正在运行的程序,我用命令行程序杀。为了产生下面的输出,我在一个窗口中运行了signal_signal.py,然后在另一个窗口中运行了kill -USR1 $pid, kill -USR2 $pid, and kill -INT $pid

我很肯定这个模块不是解决方案。你知道一些模块或什么可以帮助我异步发送按键到我的python脚本吗?

非常感谢!

+0

为了澄清,你想发送任意击键到一个类Unix的OS上运行的Python脚本?确切地说, – Aya

+0

!如果有什么不好的书面或语法错误,请编辑! –

+0

好吧,你似乎很多地使用了“信号”这个词,并且信号与按键不同。您想要接收击键的程序是否可以通过您想用来发送它们的脚本启动? – Aya

回答

2

我希望用户可以在任何时刻按任意键跳过一天,一个月或一台机器。

啊。现在它是有道理的。

不太确定这是可能的。

任何可能的。对于一个真正的异步解决方案来说,它可能相当复杂。

我能想到做到这一点的唯一方法就是避免使用轮询方式,这个过程是让fork(2)这个进程,让父进程监听按键,并将信号发送给子进程,这实际上完成了这项工作。

事情是这样的......

#!/usr/bin/env python 

import sys, os, time, termios, tty, signal 


# Define some custom exceptions we can raise in signal handlers 
class SkipYear(Exception): 
    pass 

class SkipMonth(Exception): 
    pass 


# Process one month 
def process_month(year, month): 

    # Fake up whatever the processing actually is 
    print 'Processing %04d-%02d' % (year, month) 
    time.sleep(1) 


# Process one year 
def process_year(year): 

    # Iterate months 1-12 
    for month in range(1, 13): 

     try: 
      process_month(year, month) 
     except SkipMonth: 
      print 'Skipping month %d' % month 


# Do all processing 
def process_all(args): 

    # Help 
    print 'Started processing - args = %r' % args 

    try: 

     # Iterate years 2010-2015 
     for year in range(2010, 2016): 

      try: 
       process_year(year) 
      except SkipYear: 
       print 'Skipping year %d' % year 

    # Handle SIGINT from parent process 
    except KeyboardInterrupt: 
     print 'Child caught SIGINT' 

    # Return success 
    print 'Child terminated normally' 
    return 0 


# Main entry point 
def main(args): 

    # Help 
    print 'Press Y to skip current year, M to skip current month, or CTRL-C to abort' 

    # Get file descriptor for stdin. This is almost always zero. 
    stdin_fd = sys.stdin.fileno() 

    # Fork here 
    pid = os.fork() 

    # If we're the child 
    if not pid: 

     # Detach child from controlling TTY, so it can't be the foreground 
     # process, and therefore can't get any signals from the TTY. 
     os.setsid() 

     # Define signal handler for SIGUSR1 and SIGUSR2 
     def on_signal(signum, frame): 
      if signum == signal.SIGUSR1: 
       raise SkipYear 
      elif signum == signal.SIGUSR2: 
       raise SkipMonth 

     # We want to catch SIGUSR1 and SIGUSR2 
     signal.signal(signal.SIGUSR1, on_signal) 
     signal.signal(signal.SIGUSR2, on_signal) 

     # Now do the thing 
     return process_all(args[1:]) 

    # If we get this far, we're the parent 

    # Define a signal handler for when the child terminates 
    def on_sigchld(signum, frame): 
     assert signum == signal.SIGCHLD 
     print 'Child terminated - terminating parent' 
     sys.exit(0) 

    # We want to catch SIGCHLD 
    signal.signal(signal.SIGCHLD, on_sigchld) 

    # Remember the original terminal attributes 
    stdin_attrs = termios.tcgetattr(stdin_fd) 

    # Change to cbreak mode, so we can detect single keypresses 
    tty.setcbreak(stdin_fd) 

    try: 

     # Loop until we get a signal. Typically one of... 
     # 
     # a) SIGCHLD, when the child process terminates 
     # b) SIGINT, when the user presses CTRL-C 
     while 1: 

      # Wait for a keypress 
      char = os.read(stdin_fd, 1) 

      # If it was 'Y', send SIGUSR1 to the child 
      if char.lower() == 'y': 
       os.kill(pid, signal.SIGUSR1) 

      # If it was 'M', send SIGUSR2 to the child 
      if char.lower() == 'm': 
       os.kill(pid, signal.SIGUSR2) 

    # Parent caught SIGINT - send SIGINT to child process 
    except KeyboardInterrupt: 
     print 'Forwarding SIGINT to child process' 
     os.kill(pid, signal.SIGINT) 

    # Catch system exit 
    except SystemExit: 
     print 'Caught SystemExit' 

    # Ensure we reset terminal attributes to original settings 
    finally: 
     termios.tcsetattr(stdin_fd, termios.TCSADRAIN, stdin_attrs) 

    # Return success 
    print 'Parent terminated normally' 
    return 0 


# Stub 
if __name__ == '__main__': 
    sys.exit(main(sys.argv)) 

...应该做的伎俩,虽然你会被你可以发送不同的信号的数量是有限的。

+0

真棒!非常感谢你的努力! –