2015-12-21 15 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

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