跳转到主要内容

A Python封装的AHK

项目描述

ahk

完全类型的Python封装AutoHotkey。

Docs Build version pyversion Coverage Downloads

安装

pip install ahk

需要Python 3.8+

支持AutoHotkey v1和v2。另请参阅: 非Python依赖项

用法

from ahk import AHK

ahk = AHK()

ahk.mouse_move(x=100, y=100, blocking=True)  # Blocks until mouse finishes moving (the default)
ahk.mouse_move(x=150, y=150, speed=10, blocking=True) # Moves the mouse to x, y taking 'speed' seconds to move
print(ahk.mouse_position)  #  (150, 150)

ahk

示例

本包中可用的一些函数的非穷尽性示例。请参阅 完整文档 以获取完整的API参考和附加功能。

热键

可以将热键配置为运行Python函数作为回调。

例如

from ahk import AHK

def my_callback():
    print('Hello callback!')

ahk = AHK()
# when WIN + n is pressed, fire `my_callback`
ahk.add_hotkey('#n', callback=my_callback)
ahk.start_hotkeys()  # start the hotkey process thread
ahk.block_forever()  # not strictly needed in all scripts -- stops the script from exiting; sleep forever

现在每当您按下 Windows Key + n,将在后台线程中调用 my_callback 回调函数。

您还可以为您的回调添加异常处理器

from ahk import AHK
ahk = AHK()

def go_boom():
    raise Exception('boom!')

def my_ex_handler(hotkey: str, exception: Exception):
    print('exception with callback for hotkey', hotkey, 'Here was the error:', exception)

ahk.add_hotkey('#n', callback=go_boom, ex_handler=my_ex_handler)

还有用于删除热键的方法

# ...
ahk.remove_hotkey('#n') # remove a hotkey by its keyname
ahk.clear_hotkeys()  # remove all hotkeys

注意以下内容

  • 热键在单独的进程中运行,必须手动启动(使用 ahk.start_hotkeys()
  • 可以使用 ahk.stop_hotkeys() 停止热键(不会停止正在运行的回调函数)
  • 热字符串(以下讨论)与热键共享相同的进程,并以相同的方式启动/停止
  • 如果在进程运行时添加或删除热键或热字符串,则自动重新启动底层的AHK进程

请参阅相关的AHK文档

热字符串

热字符串也可以添加到热键进程线程。

除了热字符串支持正常的AHK字符串替换外,您还可以提供Python回调(带有可选的异常处理程序)以响应热字符串触发。

from ahk import AHK
ahk = AHK()

def my_callback():
    print('hello callback!')

ahk.add_hotstring('btw', 'by the way')  # string replacements
ahk.add_hotstring('btw', my_callback) # call python function in response to the hotstring

您还可以删除热字符串

ahk.remove_hotstring('btw')  # remove a hotstring by its trigger sequence
ahk.clear_hotstrings()  # remove all registered hotstrings

鼠标

from ahk import AHK

ahk = AHK()

ahk.mouse_position  # Returns a tuple of mouse coordinates (x, y) (relative to active window)
ahk.get_mouse_position(coord_mode='Screen') # get coordinates relative to the screen
ahk.mouse_move(100, 100, speed=10, relative=True)  # Moves the mouse reletave to the current position
ahk.mouse_position = (100, 100)  # Moves the mouse instantly to absolute screen position
ahk.click()  # Click the primary mouse button
ahk.click(200, 200)  # Moves the mouse to a particular position and clicks (relative to active window)
ahk.click(100, 200, coord_mode='Screen') # click relative to the screen instead of active window
ahk.click(button='R', click_count=2) # Clicks the right mouse button twice
ahk.right_click() # Clicks the secondary mouse button
ahk.mouse_drag(100, 100, relative=True) # Holds down primary button and moves the mouse

键盘

from ahk import AHK

ahk = AHK()

ahk.type('hello, world!')  # Send keys, as if typed (performs string escapes for you)
ahk.send_input('Hello, {U+1F30E}{!}')  # Like AHK SendInput
                                   # Unlike `type`, control sequences must be escaped manually.
                                   # For example the characters `!^+#=` and braces (`{` `}`) must be escaped manually.
ahk.key_state('Control')  # Return True or False based on whether Control key is pressed down
ahk.key_state('CapsLock', mode='T')  # Check toggle state of a key (like for NumLock, CapsLock, etc)
ahk.key_press('a')  # Press and release a key
ahk.key_down('Control')  # Press down (but do not release) Control key
ahk.key_up('Control')  # Release the key
ahk.set_capslock_state("On")  # Turn CapsLock on
if ahk.key_wait('x', timeout=3):  # wait for a key to be pressed; returns a boolean
    print('X was pressed within 3 seconds')
else:
    print('X was not pressed within 3 seconds')

窗口

您也可以对窗口进行操作。

获取窗口

from ahk import AHK

ahk = AHK()

win = ahk.active_window                        # Get the active window
win = ahk.win_get(title='Untitled - Notepad')  # by title
all_windows = ahk.list_windows()               # list of all windows
win = ahk.win_get_from_mouse_position()        # the window under the mouse cursor
win = ahk.win_get(title='ahk_pid 20366')       # get window from pid

# Wait for a window
try:
    # wait up to 5 seconds for notepad
    win = ahk.win_wait(title='Untitled - Notepad', timeout=5)
    # see also: win_wait_active, win_wait_not_active
except TimeoutError:
    print('Notepad was not found!')

处理窗口

from ahk import AHK

ahk = AHK()

ahk.run_script('Run Notepad') # Open notepad
win = ahk.find_window(title='Untitled - Notepad') # Find the opened window; returns a `Window` object

# Window object methods
win.send('hello', control='Edit1')  # Send keys directly to the window (does not need focus!)
# OR ahk.control_send(title='Untitled - Notepad', control='Edit1')
win.move(x=200, y=300, width=500, height=800)

win.activate()                  # Give the window focus
win.close()                     # Close the window
win.hide()                      # Hide the window
win.kill()                      # Kill the window
win.maximize()                  # Maximize the window
win.minimize()                  # Minimize the window
win.restore()                   # Restore the window
win.show()                      # Show the window
win.disable()                   # Make the window non-interactable
win.enable()                    # Enable it again
win.to_top()                    # Move the window on top of other windows
win.to_bottom()                 # Move the window to the bottom of the other windows
win.get_class()                 # Get the class name of the window
win.get_minmax()                # Get the min/max status
win.get_process_name()          # Get the process name (e.g., "notepad.exe")
win.process_name                # Property; same as `.get_process_name()` above
win.is_always_on_top()          # Whether the window has the 'always on top' style applied
win.list_controls()             # Get a list of controls (list of `Control` objects)
win.redraw()                    # Redraw the window
win.set_style("-0xC00000")      # Set a style on the window (in this case, removing the title bar)
win.set_ex_style("^0x80")       # Set an ExStyle on the window (in this case, removes the window from alt-tab list)
win.set_region("")              # See: https://www.autohotkey.com/docs/v2/lib/WinSetRegion.htm
win.set_trans_color("White")    # Makes all pixels of the chosen color invisible inside the specified window.
win.set_transparent(155)        # Makes the specified window semi-transparent (or "Off" to turn off transparency)


win.always_on_top = 'On' # Make the window always on top
# or
win.set_always_on_top('On')

for window in ahk.list_windows():  # list all (non-hidden) windows -- ``detect_hidden_windows=True`` to include hidden
    print(window.title)

    # Some more attributes
    print(window.text)           # window text -- or .get_text()
    print(window.get_position()) # (x, y, width, height)
    print(window.id)             # the ahk_id of the window
    print(window.pid)            # process ID -- or .get_pid()
    print(window.process_path)   # or .get_process_path()


if win.active:        # or win.is_active()
    ...

if win.exist:         # or win.exists()
    ...

# Controls

edit_control = win.list_controls()[0]  # get the first control for the window, in this case "Edit1" for Notepad
edit_control.get_text()      # get the text in Notepad
edit_control.get_position()  # returns a `Postion` namedtuple: e.g. Position(x=6, y=49, width=2381, height=1013)

可以直接调用各种窗口方法,而无需先创建一个 Window 对象。例如,可以使用底层的 win_* 方法在 AHK 类上直接调用,而不是使用上面的 win.close(),可以调用 ahk.win_close(title='Untitled - Notepad')

屏幕

from ahk import AHK

ahk = AHK()

ahk.image_search('C:\\path\\to\\image.jpg')  # Find an image on screen

# Find an image within a boundary on screen
ahk.image_search('C:\\path\\to\\image.jpg', upper_bound=(100, 100),  # upper-left corner of search area
                                            lower_bound=(400, 400))  # lower-right corner of search area
ahk.pixel_get_color(100, 100)  # Get color of pixel located at coords (100, 100)
ahk.pixel_search(color='0x9d6346', search_region_start=(0, 0), search_region_end=(500, 500))  # Get coords of the first pixel with specified color

剪贴板

获取/设置 Clipboard 数据

from ahk import AHK
ahk = AHK()

ahk.set_clipboard('hello \N{EARTH GLOBE AMERICAS}')  # set clipboard text contents
ahk.get_clipboard() # get clipboard text contents
# 'hello 🌎'
ahk.set_clipboard("")  # Clear the clipboard

ahk.clip_wait(timeout=3)  # Wait for clipboard contents to change (with text or file(s))
ahk.clip_wait(timeout=3, wait_for_any_data=True)  # wait for _any_ clipboard contents

您也可以获取/设置 ClipboardAll -- 然而,您不应该尝试使用除恰好get_clipboard_all 返回的数据以外的任何数据调用 set_clipboard_all,否则可能会发生意外问题。

from ahk import AHK
ahk = AHK()

# save all clipboard contents in all formats
saved_clipboard = ahk.get_clipboard_all()
ahk.set_clipboard('something else')
...
ahk.set_clipboard_all(saved_clipboard)  # restore saved content from earlier

您还可以设置一个回调,当剪贴板内容更改时执行。与上面提到的Hotkey方法一样,您也可以设置异常处理程序。与热键一样,on_clipboard_change 回调也要求调用 .start_hotkeys() 才能生效。

回调函数必须接受一个位置参数,该参数是一个整数,表示剪贴板的数据类型。

from ahk import AHK
ahk = AHK()
def my_clipboard_callback(change_type: int):
    if change_type == 0:
        print('Clipboard is now empty')
    elif change_type == 1:
        print('Clipboard has text contents')
    elif change_type == 2:
        print('Clipboard has non-text contents')

ahk.on_clipboard_change(my_clipboard_callback)
ahk.start_hotkeys()  # like with hotkeys, must be called at least once for listening to start
# ...
ahk.set_clipboard("hello") # will cause the message "Clipboard has text contents" to be printed by the callback
ahk.set_clipboard("") # Clears the clipboard, causing the message "Clipboard is now empty" to be printed by the callback

声音

from ahk import AHK

ahk = AHK()

ahk.sound_play('C:\\path\\to\\sound.wav')  # Play an audio file
ahk.sound_beep(frequency=440, duration=1000)  # Play a beep for 1 second (duration in microseconds)
ahk.get_volume(device_number=1)  # Get volume of a device
ahk.set_volume(50, device_number=1)  # Set volume of a device
ahk.sound_get(device_number=1, component_type='MASTER', control_type='VOLUME') # Get sound device property
ahk.sound_set(50, device_number=1, component_type='MASTER', control_type='VOLUME') # Set sound device property

GUI

工具提示/托盘提示

import time
from ahk import AHK

ahk = AHK()
ahk.show_tooltip("hello4", x=10, y=10)
time.sleep(2)
ahk.hide_tooltip() # hide the tooltip
ahk.show_info_traytip("Info", "It's also info", silent=False, blocking=True)  # Default info traytip
ahk.show_warning_traytip("Warning", "It's a warning")                           # Warning traytip
ahk.show_error_traytip("Error", "It's an error")                                 # Error trytip

对话框

from ahk import AHK, MsgBoxButtons
ahk = AHK()

ahk.msg_box(text='Do you like message boxes?', title='My Title', buttons=MsgBoxButtons.YES_NO)
ahk.input_box(prompt='Password', title='Enter your password', hide=True)
ahk.file_select_box(title='Select one or more mp3 files', multi=True, filter='*.mp3', file_must_exist=True)
ahk.folder_select_box(prompt='Select a folder')

全局状态更改

您可以更改各种全局状态,如 CoordModeDetectHiddenWindows 等,这样您就不必直接将这些参数传递给函数调用。

from ahk import AHK

ahk = AHK()

ahk.set_coord_mode('Mouse', 'Screen')  # set default Mouse CoordMode to be relative to Screen
ahk.set_detect_hidden_windows(True) # Turn on detect hidden windows by default
ahk.set_send_level(5)  # Change send https://www.autohotkey.com/docs/v1/lib/SendLevel.htm

ahk.set_title_match_mode('Slow') # change title match speed and/or mode
ahk.set_title_match_mode('RegEx')
ahk.set_title_match_mode(('RegEx', 'Slow'))  # or both at the same time
ahk.set_send_mode('Event')  # change the default SendMode

添加指令

您可以将指令添加到所有生成的脚本中。例如,为了防止AHK托盘图标出现,可以添加NoTrayIcon指令。

from ahk import AHK
from ahk.directives import NoTrayIcon

ahk = AHK(directives=[NoTrayIcon])

默认情况下,会自动添加一些指令以确保功能,并将与任何用户提供的指令合并。

默认情况下,指令不应用于处理热键和热字符串的AHK进程(以下讨论)。要使用关键字参数 apply_to_hotkeys_process=True 将指令应用于热键进程

from ahk import AHK
from ahk.directives import NoTrayIcon

directives = [
    NoTrayIcon(apply_to_hotkeys_process=True)
]

ahk = AHK(directives=directives)

托盘菜单图标

如上所述,如果您愿意,可以隐藏托盘图标。此外,还有一些可用于自定义托盘图标的方法。

from ahk import AHK
ahk = AHK()

# change the tray icon (in this case, using a builtin system icon)
ahk.menu_tray_icon('Shell32.dll', 174)
# revert it back to the original:
ahk.menu_tray_icon()

# change the tooltip that shows up when hovering the mouse over the tray icon
ahk.menu_tray_tooltip('My Program Name')

# Hide the tray icon
ahk.menu_tray_icon_hide()

# Show the tray icon that was previously hidden by ``NoTrayIcon`` or ``menu_tray_icon_hide``
ahk.menu_tray_icon_show()

注册表方法

您可以读取/写入/删除注册表键

from ahk import AHK
ahk = AHK()

ahk.reg_write('REG_SZ', r'HKEY_CURRENT_USER\SOFTWARE\my-software', value='test')
ahk.reg_write('REG_SZ', r'HKEY_CURRENT_USER\SOFTWARE\my-software', value_name='foo', value='bar')
ahk.reg_read(r'HKEY_CURRENT_USER\SOFTWARE\my-software')  # 'test'
ahk.reg_delete(r'HKEY_CURRENT_USER\SOFTWARE\my-software')

如果键不存在或发生其他问题,将引发异常。

非阻塞模式

此库中的大多数方法都提供了非阻塞接口,因此您的Python脚本可以在AHK脚本运行的同时继续执行。

默认情况下,所有调用都是 阻塞的 -- 每个函数将在下一个函数运行之前完全执行。

但是,有时您可能希望在AHK执行某些代码时运行其他代码。当提供带有 Falseblocking 关键字参数时,函数调用将立即返回,而AHK函数将在后台执行。

例如,您可以慢慢移动鼠标,并在其移动时报告其位置

import time

from ahk import AHK

ahk = AHK()

ahk.mouse_position = (200, 200)  # Moves the mouse instantly to the start position
start = time.time()

# move the mouse very slowly
ahk.mouse_move(x=100, y=100, speed=30, blocking=False)

# This code begins executing right away, even though the mouse is still moving
while True:
    t = round(time.time() - start, 4)
    position = ahk.mouse_position
    print(t, position) #  report mouse position while it moves
    if position == (100, 100):
        break

当您指定 blocking=False 时,您将始终接收到一个特殊的 FutureResult 对象(或者在异步API中讨论的 AsyncFutureResult 对象),它允许您等待函数完成并通过 get_result 函数检索返回值。即使函数通常返回 None,这也很有用,以确保AHK已完成函数的执行。

非阻塞调用

  • 将在新的AHK进程中隔离,完成调用后终止
  • 总是立即启动
  • 不会继承之前的全局状态更改(例如,来自set_coord_mode调用或类似的)-- 这可能在未来的版本中发生变化。
  • 不会阻止其他调用启动
  • 将始终返回一个特殊的FutureResult对象(或在异步API中讨论的AsyncFutureResult对象),这允许您等待函数完成并通过result函数检索返回值。即使函数通常返回None,这也可以确保AHK已执行函数。
from ahk import AHK
ahk = AHK()
future_result = ahk.mouse_move(100, 100, speed=40, blocking=False)
...
# wait on the mouse_move to finish
future_result.result(timeout=10) # timeout keyword is optional

异步API(asyncio)

提供了一个异步API,以便可以使用async/await调用函数。异步API中提供了与同步API中相同的所有方法。

from ahk import AsyncAHK
import asyncio
ahk = AsyncAHK()

async def main():
    await ahk.mouse_move(100, 100)
    x, y = await ahk.get_mouse_position()
    print(x, y)

asyncio.run(main())

异步API与正常API相同,有一些明显的区别

  • 虽然属性(如窗口的.mouse_position.title)可以被await,但已添加了一些额外的方法(如get_mouse_position()get_title()),以提供更直观的API,并且建议使用这些方法而不是属性。
  • 异步API中不允许使用属性设置器(例如,ahk.mouse_position = (200, 200))(将引发RunTimeError)。属性设置器在同步API中仍然可用。
  • AsyncFutureResult对象(在指定blocking=False时返回)与同步API中的FutureResult对象工作相同,但result方法不支持timeout关键字)。

注意以下事项:

  • 默认情况下,单个AsyncAHK实例上的等待任务不会并发运行。您必须使用blocking=False,如同步API中所示,或者使用多个AsyncAHK实例。
  • 在异步与同步API中处理热键(及其回调)没有区别。

类型提示和mypy

此库已完全类型提示,允许您利用像mypy这样的工具来帮助验证代码的类型正确性。实现类型检查功能的IDE也能够利用类型提示来帮助确保代码的安全性。

运行任意AutoHotkey脚本

您还可以以.ahk脚本文件或包含AHK代码的字符串形式运行任意AutoHotkey代码。

from ahk import AHK
ahk = AHK()
my_script = '''\
MouseMove, 100, 100
; etc...
'''

ahk.run_script(my_script)
from ahk import AHK
ahk = AHK()
script_path = r'C:\Path\To\myscript.ahk'
ahk.run_script(script_path)

非Python依赖项

要使用此包,您需要AutoHotkey可执行文件(例如,AutoHotkey.exe)。它默认应位于PATH上或默认安装位置(v1的C:\Program Files\AutoHotkey\AutoHotkey.exe或v2的C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe)。

AutoHotkey v1和v2都完全支持,尽管使用哪个版本可能会出现一些行为差异。请参见以下说明。

为v1和v2提供AutoHotkey二进制文件的建议方法是为此包安装binary额外内容。这将提供必要的可执行文件,并有助于确保它们正确放置在PATH上。

pip install "ahk[binary]"

或者,您可以在代码中提供路径

from ahk import AHK

ahk = AHK(executable_path='C:\\path\\to\\AutoHotkey.exe')

您还可以使用AHK_PATH环境变量来指定可执行文件的位置。

set AHK_PATH=C:\Path\To\AutoHotkey.exe
python myscript.py

使用AHK v2

默认情况下,如果没有设置executable_path参数(或AHK_PATH环境变量),则仅在PATH上或默认安装位置搜索AutoHotkey v1的二进制文件名。此行为可能在未来的版本中更改,以允许默认使用v2。

要使用AutoHotkey版本2,您可以执行以下任何一项操作

  1. 提供包含AutoHotkey v2二进制文件位置的executable_path关键字参数
  2. 使用包含AutoHotkey v2二进制文件位置的AHK_PATH环境变量
  3. 提供包含值v2version关键字参数,这将启用使用AutoHotkey v2二进制文件名和默认安装位置查找可执行文件。

例如

from ahk import AHK


ahk = AHK(executable_path=r'C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe')
# OR
ahk = AHK(version='v2')

当您提供version关键字参数(可以是"v1""v2")时,将执行检查以确保提供的(或发现的)二进制文件与请求的版本匹配。当省略version关键字时,版本将自动从提供的(或发现的)可执行二进制文件中确定。

使用AutoHotkey v1与v2的区别

本项目的API最初是为AutoHotkey v1设计的,即使在使用AutoHotkey v2时,函数签名也相同。虽然大多数行为保持不变,但与v1相比,使用v2时,某些行为会发生改变。这主要是由于两个版本之间的底层差异造成的。

在使用此库与AutoHotkey v2一起使用时,您可能会遇到以下一些显著的差异:

  1. 查找并返回窗口的函数通常会引发异常,而不是返回None(在AutoHotkey v2中,当窗口或控件无法找到时,通常会抛出TargetError)
  2. control参数未指定时,ControlSendahk.control_sendWindow.sendControl.send)在AutoHotkey v2中的行为与v1不同。在v1中,按键发送到最顶层的控件,这通常是正确的行为。在v2中,按键直接发送到窗口。这意味着在许多情况下,使用V2时需要明确指定控件。
  3. 在v2中不支持某些功能——特别是:对于TrayTipahk.show_traytip)的secondstowait参数已从v2中删除。在Python包装器中指定此参数将发出警告,并且参数将被忽略。
  4. 在v1中存在但在v2中尚未实现的功能——这预计将在未来的版本中改变。特别是:一些声音函数尚未实现。
  5. v2中的默认SendMode更改为Input,而不是v1中的Event(例如,作为结果,例如,mouse_movemouse_drag的鼠标速度参数将在V2中忽略,除非更改发送模式)
  6. AutoHotkey v2中的默认TitleMatchMode2。在AutoHotkey v1中为1。使用title_match_mode关键字参数到win_get和其他接受此关键字参数的方法来控制此行为,或者使用set_title_match_mode来更改默认行为(非阻塞调用在单独的进程中运行,不受set_title_match_mode的影响)

扩展:添加您的自己的AutoHotkey代码(测试版)

您可以为扩展ahk的功能开发扩展——即:编写自己的AutoHotkey代码,并为AHK类添加额外的函数。有关更多信息,请参阅扩展文档

贡献

所有贡献都受到欢迎并受到赞赏。

请随时为反馈、想法、功能请求或问题打开GitHub问题或PR。

类似的项目

以下是一些常用的自动化Python的项目。

  • Pyautogui - Al Sweigart的跨平台自动化创作
  • Pywinauto - 使用Python在Windows平台上的自动化。
  • keyboard - 纯Python跨平台键盘钩子/控制和热键!
  • mouse - 来自keyboard的创建者,纯Python mouse 控制!
  • pynput - 键盘和鼠标控制

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适合您平台的应用程序文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分发

ahk-1.8.0.tar.gz (132.7 kB 查看散列)

上传时间

构建分发

ahk-1.8.0-py3-none-any.whl (123.8 kB 查看散列)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面