Package: ahk
ahkA fully typed Python wrapper around AutoHotkey.Installationpip install ahkRequires Python 3.8+Supports AutoHotkey v1 and v2. See also:Non-Python dependenciesUsagefromahkimportAHKahk=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 moveprint(ahk.mouse_position)#  (150, 150)ExamplesNon-exhaustive examples of some functions available with this package. See thefull documentationfor complete API references and additional features.HotkeysHotkeys can be configured to run python functions as callbacks.For example:fromahkimportAHKdefmy_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 threadahk.block_forever()# not strictly needed in all scripts -- stops the script from exiting; sleep foreverNow whenever you press+n, themy_callbackcallback function will be called in a background thread.You can also add an exception handler for your callback:fromahkimportAHKahk=AHK()defgo_boom():raiseException('boom!')defmy_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)There are also methods for removing hotkeys:# ...ahk.remove_hotkey('#n')# remove a hotkey by its keynameahk.clear_hotkeys()# remove all hotkeysNote that:Hotkeys run in a separate process that must be started manually (withahk.start_hotkeys())Hotkeys can be stopped withahk.stop_hotkeys()(will not stop actively running callbacks)Hotstrings (discussed below) share the same process with hotkeys and are started/stopped in the same mannerIf hotkeys or hotstrings are added or removed while the process is running, the underlying AHK process is restarted automaticallySee also therelevant AHK documentationHotstringsHotstringscan also be added to the hotkey process thread.In addition to Hotstrings supporting normal AHK string replacements, you can also provide Python callbacks (with optional exception handlers) in response to hotstrings triggering.fromahkimportAHKahk=AHK()defmy_callback():print('hello callback!')ahk.add_hotstring('btw','by the way')# string replacementsahk.add_hotstring('btw',my_callback)# call python function in response to the hotstringYou can also remove hotstrings:ahk.remove_hotstring('btw')# remove a hotstring by its trigger sequenceahk.clear_hotstrings()# remove all registered hotstringsMousefromahkimportAHKahk=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 screenahk.mouse_move(100,100,speed=10,relative=True)# Moves the mouse reletave to the current positionahk.mouse_position=(100,100)# Moves the mouse instantly to absolute screen positionahk.click()# Click the primary mouse buttonahk.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 windowahk.click(button='R',click_count=2)# Clicks the right mouse button twiceahk.right_click()# Clicks the secondary mouse buttonahk.mouse_drag(100,100,relative=True)# Holds down primary button and moves the mouseKeyboardfromahkimportAHKahk=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 downahk.key_state('CapsLock',mode='T')# Check toggle state of a key (like for NumLock, CapsLock, etc)ahk.key_press('a')# Press and release a keyahk.key_down('Control')# Press down (but do not release) Control keyahk.key_up('Control')# Release the keyahk.set_capslock_state("On")# Turn CapsLock onahk.key_wait('a',timeout=3)# Wait up to 3 seconds for the "a" key to be pressed. NOTE: This throws# a TimeoutError if the key isn't pressed within the timeout windowWindowsYou can do stuff with windows, too.Getting windowsfromahkimportAHKahk=AHK()win=ahk.active_window# Get the active windowwin=ahk.win_get(title='Untitled - Notepad')# by titleall_windows=ahk.list_windows()# list of all windowswin=ahk.win_get_from_mouse_position()# the window under the mouse cursorwin=ahk.win_get(title='ahk_pid 20366')# get window from pid# Wait for a windowtry:# wait up to 5 seconds for notepadwin=ahk.win_wait(title='Untitled - Notepad',timeout=5)# see also: win_wait_active, win_wait_not_activeexceptTimeoutError:print('Notepad was not found!')Working with windowsfromahkimportAHKahk=AHK()ahk.run_script('Run Notepad')# Open notepadwin=ahk.find_window(title='Untitled - Notepad')# Find the opened windowwin.send('hello')# Send keys directly to the window (does not need focus!)win.move(x=200,y=300,width=500,height=800)win.activate()# Give the window focuswin.close()# Close the windowwin.hide()# Hide the windwowwin.kill()# Kill the windowwin.maximize()# Maximize the windowwin.minimize()# Minimize the windowwin.restore()# Restore the windowwin.show()# Show the windowwin.disable()# Make the window non-interactablewin.enable()# Enable it againwin.to_top()# Move the window on top of other windowswin.to_bottom()# Move the window to the bottom of the other windowswin.always_on_top='On'# Make the window always on top# orwin.set_always_on_top('On')forwindowinahk.list_windows():print(window.title)# Some more attributesprint(window.text)# window text -- or .get_text()print(window.get_position())# (x, y, width, height)print(window.id)# the ahk_id of the windowprint(window.pid)# process ID -- or .get_pid()print(window.process_path)# or .get_process_path()ifwin.active:# or win.is_active()...ifwin.exist:# or win.exists()...ScreenfromahkimportAHKahk=AHK()ahk.image_search('C:\\path\\to\\image.jpg')# Find an image on screen# Find an image within a boundary on screenahk.image_search('C:\\path\\to\\image.jpg',upper_bound=(100,100),# upper-left corner of search arealower_bound=(400,400))# lower-right corner of search areaahk.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 colorClipboardGet/setClipboarddatafromahkimportAHKahk=AHK()ahk.set_clipboard('hello\N{EARTH GLOBE AMERICAS}')# set clipboard text contentsahk.get_clipboard()# get clipboard text contents# 'hello 🌎'You may also get/setClipboardAll-- however, you should never try to callset_clipboard_allwith any other
data than asexactlyas returned byget_clipboard_allor unexpected problems may occur.fromahkimportAHKahk=AHK()# save all clipboard contents in all formatssaved_clipboard=ahk.get_clipboard_all()ahk.set_clipboard('something else')...ahk.set_clipboard_all(saved_clipboard)# restore saved content from earlierSoundfromahkimportAHKahk=AHK()ahk.sound_play('C:\\path\\to\\sound.wav')# Play an audio fileahk.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 deviceahk.set_volume(50,device_number=1)# Set volume of a deviceahk.sound_get(device_number=1,component_type='MASTER',control_type='VOLUME')# Get sound device propertyahk.sound_set(50,device_number=1,component_type='MASTER',control_type='VOLUME')# Set sound device propertyGUITooltips/traytipsimporttimefromahkimportAHKahk=AHK()ahk.show_tooltip("hello4",x=10,y=10)time.sleep(2)ahk.hide_tooltip()# hide the tooltipahk.show_info_traytip("Info","It's also info",silent=False,blocking=True)# Default info traytipahk.show_warning_traytip("Warning","It's a warning")# Warning traytipahk.show_error_traytip("Error","It's an error")# Error trytipDialog boxesfromahkimportAHK,MsgBoxButtonsahk=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')Global state changesYou can change various global states such asCoordMode,DetectHiddenWindows, etc. so you don't have to pass
these parameters directly to function callsfromahkimportAHKahk=AHK()ahk.set_coord_mode('Mouse','Screen')# set default Mouse CoordMode to be relative to Screenahk.set_detect_hidden_windows(True)# Turn on detect hidden windows by defaultahk.set_send_level(5)# Change send https://www.autohotkey.com/docs/v1/lib/SendLevel.htmahk.set_title_match_mode('Slow')# change title match speed and/or modeahk.set_title_match_mode('RegEx')ahk.set_title_match_mode(('RegEx','Slow'))# or both at the same timeAdd directivesYou can add directives that will be added to all generated scripts.
For example, to prevent the AHK trayicon from appearing, you can add the NoTrayIcon directive.fromahkimportAHKfromahk.directivesimportNoTrayIconahk=AHK(directives=[NoTrayIcon])By default, some directives are automatically added to ensure functionality and are merged with any user-provided directives.Directives are not applied for the AHK process used for handling hotkeys and hotstrings (discussed below) by default. To apply a directive
to the hotkeys process using the keyword argumentapply_to_hotkeys_process=True:fromahkimportAHKfromahk.directivesimportNoTrayIcondirectives=[NoTrayIcon(apply_to_hotkeys_process=True)]ahk=AHK(directives=directives)Menu tray iconAs discussed above, you can hide the tray icon if you wish. Additionally, there are some methods available for
customizing the tray icon.fromahkimportAHKahk=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 iconahk.menu_tray_tooltip('My Program Name')# Show the tray icon that was previously hidden by ``NoTrayIcon``ahk.menu_tray_icon_show()Registry methodsYou can read/write/delete registry keys:fromahkimportAHKahk=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')If a key does not exist or some other problem occurs, an exception is raised.non-blocking modesMost methods in this library supply a non-blocking interface, so your Python scripts can continue executing while
your AHK scripts run.By default, all calls areblocking-- each function will execute completely before the next function is ran.However, sometimes you may want to run other code while AHK executes some code.  When theblockingkeyword
argument is supplied withFalse, function calls will return immediately while the AHK function is carried out
in the background.As an example, you can move the mouse slowly and report its position as it moves:importtimefromahkimportAHKahk=AHK()ahk.mouse_position=(200,200)# Moves the mouse instantly to the start positionstart=time.time()# move the mouse very slowlyahk.mouse_move(x=100,y=100,speed=30,blocking=False)# This code begins executing right away, even though the mouse is still movingwhileTrue:t=round(time.time()-start,4)position=ahk.mouse_positionprint(t,position)#  report mouse position while it movesifposition==(100,100):breakWhen you specifyblocking=Falseyou will always receive a specialFutureResultobject (orAsyncFutureResultobject in the async API, discussed below)
which allows you to wait on the function to complete and retrieve return value through aget_resultfunction. Even
when a function normally returnsNone, this can be useful to ensure AHK has finished executing the function.nonblocking calls:Are isolated in a new AHK process that will terminate after the call is completeAlways start immediatelyDo not inherit previous global state changes (e.g., fromset_coord_modecalls or similar) -- this may change in a future version.will not block other calls from startingwill always return a specialFutureResultobject (orAsyncFutureResultobject in the async API, discussed below)
which allows you to wait on the function to complete and retrieve return value through theresultfunction. Even
when a function normally returnsNone, this can be useful to ensure AHK has finished executing the function.fromahkimportAHKahk=AHK()future_result=ahk.mouse_move(100,100,speed=40,blocking=False)...# wait on the mouse_move to finishfuture_result.result(timeout=10)# timeout keyword is optionalAsync API (asyncio)An async API is provided so functions can be called usingasync/await.
All the same methods from the synchronous API are available in the async API.fromahkimportAsyncAHKimportasyncioahk=AsyncAHK()asyncdefmain():awaitahk.mouse_move(100,100)x,y=awaitahk.get_mouse_position()print(x,y)asyncio.run(main())The async API is identical to that of the normal API, with a few notable differences:While properties (like.mouse_positionor.titlefor windows) can beawaited,
additional methods (likeget_mouse_position()andget_title()) have been added for a more intuitive API and
are recommended over the use of properties.Propertysetters(e.g.,ahk.mouse_postion = (200, 200)) are not allowed in the async API (a RunTimeError is raised).
Property setters remain available in the sync API.AsyncFutureResultobjects (returned when specifyingblocking=False) work the same as theFutureResultobjects in the sync API, except thetimeoutkeyword is not supported for theresultmethod).Note also that:by default, awaited tasks on a singleAsyncAHKinstance will not run concurrently. You must either
useblocking=False, as in the sync API, or use multiple instances ofAsyncAHK.There is no difference in working with hotkeys (and their callbacks) in the async vs sync API.type-hints and mypyThis library is fully type-hinted, allowing you to leverage tools likemypyto help validate the type-correctness
of your code. IDEs that implement type-checking features are also able to leverage type hints to help ensure your
code is safe.Run arbitrary AutoHotkey scriptsYou can also run arbitrary AutoHotkey code either as a.ahkscript file or as a string containing AHK code.fromahkimportAHKahk=AHK()my_script='''\MouseMove, 100, 100; etc...'''ahk.run_script(my_script)fromahkimportAHKahk=AHK()script_path=r'C:\Path\To\myscript.ahk'ahk.run_script(script_path)Non-Python dependenciesTo use this package, you need theAutoHotkey executable(e.g.,AutoHotkey.exe).
It's expected to be on PATH by default OR in a default installation location (C:\Program Files\AutoHotkey\AutoHotkey.exefor v1 orC:\Program Files\AutoHotkey\v2\AutoHotkey64.exefor v2)AutoHotkey v1 is fully supported. AutoHotkey v2 support is available, but is considered to be in beta status.The recommended way to supply the AutoHotkey binary (for both v1 and v2) is to install thebinaryextra for this package. This will
provide the necessary executables and help ensure they are correctly placed on PATH.pip install "ahk[binary]"Alternatively, you may provide the path in code:fromahkimportAHKahk=AHK(executable_path='C:\\path\\to\\AutoHotkey.exe')You can also use theAHK_PATHenvironment variable to specify the executable location.set AHK_PATH=C:\Path\To\AutoHotkey.exepython myscript.pyUsing AHK v2By default, when noexecutable_pathparameter (orAHK_PATHenvironment variable) is set, only AutoHotkey v1 binary names
are searched for on PATH or default install locations. This behavior may change in future versions to allow v2 to be used by default.To use AutoHotkey version 2, you can do any of the following things:provide theexecutable_pathkeyword argument with the location of the AutoHotkey v2 binaryset theAHK_PATHenvironment variable with the location of an AutoHotkey v2 binaryProvide theversionkeyword argument with the valuev2which enables finding the executable using AutoHotkey v2 binary names and default install locations.For example:fromahkimportAHKahk=AHK(executable_path=r'C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe')# ORahk=AHK(version='v2')When you provide theversionkeyword argument (with either"v1"or"v2") a check is performed to ensure the provided (or discovered) binary matches the requested version. When
theversionkeyword is omitted, the version is determined automatically from the provided (or discovered) executable binary.Differences when using AutoHotkey v1 vs AutoHotkey v2The API of this project is originally designed against AutoHotkey v1 and function signatures are the same, even when using AutoHotkey v2.
While most of the behavior remains the same, some behavior does change when using AutoHotkey v2 compared to v1. This is mostly due to
underlying differences between the two versions.Some of the differences that you will experience when using AutoHotkey v2 include:Functions that find and return windows will often raise an exception rather than returningNone(as in AutoHotkey v2, a TargetError is thrown in most cases where the window or control cannot be found)The behavior ofControlSend(ahk.control_sendorWindow.sendorControl.send) differs in AutoHotkey v2 when thecontrolparameter is not specified. In v1, keys are sent to the topmost controls, which is usually the correct behavior. In v2, keys are sent directly to the window. This means in many cases, you need to specify the control explicitly when using V2.Some functionality is not supported in v2 -- specifically: thesecondstowaitparamater forTrayTip(ahk.show_traytip) was removed in v2. Specifying this parameter in the Python wrapper will cause a warning to be emitted and the parameter is ignored.Some functionality that is present in v1 is not yet implemented in v2 -- this is expected to change in future versions. Specifically: somesound functionsare not implemented.ContributingAll contributions are welcomed and appreciated.Please feel free to open a GitHub issue or PR for feedback, ideas, feature requests or questions.Similar projectsThese are some similar projects that are commonly used for automation with Python.Pyautogui- Al Sweigart's creation for cross-platform automationPywinauto- Automation on Windows platforms with Python.keyboard- Pure Python cross-platform keyboard hooks/control and hotkeys!mouse- From the creators ofkeyboard, Pure Pythonmousecontrol!pynput- Keyboard and mouse control