关于Windows:如何创建在后台运行并响应键盘输入的Python脚本?

How can I create a Python script that runs in the background and reacts to keyboard input?

此刻,我正在使用AutoHotKey通过键盘快捷键来触发脚本。我更喜欢用Python编程,而不是处理AutoHotKey,并且每次我触摸AutoHotKey脚本时,我都希望可以简单地编写干净的AutoHotkey代码。

让我们看一下简单的AutoHotKey脚本,该脚本在我按插入键时在任何窗口中显示hello world

1
2
3
4
foo(){
    send,"hello world"
}
Insert:: foo()

如何在Windows上的Python3中执行相同的操作?


您必须将其插入窗the才能实现。您可能必须通过ctypes或CFFI模块来执行此操作,因为pywin32中似乎不存在必需的API。

根据此页面,您将需要使用三个Windows API调用:

  • SetWindowsHookEx,使用idHook WH_KEYBOARD_LL设置键盘挂钩;偷看键盘事件的功能。
  • UnhookWindowsHookEx最终删除该挂钩。
  • 如果您对特定的按键不感兴趣,请致电NextNextHookEx将其传递给下一个挂钩。

您可以结合使用StackOverflow的两个答案来(几乎)解决此问题。

  • 使用此答案(由tehvan编写)创建类似getch()的方法,无需\
    即可从用户读取一个字符。 (以下从答案中重复)
  • 使用此答案的Python3版本(由Barafu Albino提供)在单独的过程中调用先前定义的_Getch()类。
  • 请注意,以下代码仅适用于Python3,并使用任何键来停止进程,而不仅仅是插入键。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    # This code is a combination of two StackOverflow answers
    # (links given in the answer)

    # ------- Answer 1 by tehvan -----------------------------------
    class _Getch:
       """Gets a single character from standard input.  
           Does not echo to the screen."""

        def __init__(self):
            try:
                self.impl = _GetchWindows()
            except ImportError:
                self.impl = _GetchUnix()

        def __call__(self): return self.impl()

    class _GetchUnix:
        def __init__(self):
            import tty, sys

        def __call__(self):
            import sys, tty, termios
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(sys.stdin.fileno())
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch

    class _GetchWindows:
        def __init__(self):
            import msvcrt

        def __call__(self):
            import msvcrt
            return msvcrt.getch()

    getch = _Getch()

    # -------- Answer 2 by Barafu Albino (modified) -------
    import _thread

    def input_thread(a_list):
        _Getch().__call__()
        a_list.append(True)

    def do_stuff():
        a_list = []
        _thread.start_new_thread(input_thread, (a_list,))
        print('Press any key to stop.')
        while not a_list:
            pass  
            # This is where you can put the stuff
            # you want to do until the key is pressed
        print('Stopped.')

    do_stuff()