Quickstart

This page assumes you already have AutoHotkey.py installed. If you do not, head over to the Installation section. If you need a refresher on Python, check out The Python Tutorial.

Command Line Interface

When invoking AutoHotkey.py, you may specify any of these options:

ahkpy [-h] [-V] [-q] [--no-tray] [-c CMD | -m MOD | FILE | -] [args]

The most common use case is, of course, a simple invocation of a script:

ahkpy myscript.py

The REPL interface works best when invoked through the python executable, because it enables the arrow keys to traverse the command history.

python -m ahkpy

To start AutoHotkey.py without the terminal window, use pyw:

pyw -m ahkpy myscript.py

The AutoHotkey.py interface resembles that of the Python interpreter. For more information on the interface options refer to Python documentation.

The following CLI options are specific to AutoHotkey.py:

-q

Suppress message boxes with errors. If this option is not specified, AutoHotkey.py will show unhandled Python errors in message boxes.

--no-tray

Don’t show the AutoHotkey icon in the system tray.

When started, AutoHotkey.py searches for the AutoHotkey executable in the following sequence:

  1. Check the AUTOHOTKEY environment variable. If the variable is set, treat it as the AutoHotkey executable path.

  2. Find the program associated with the *.ahk files. If the program name starts with autohotkey (case-insensitive), treat it as the AutoHotkey executable path.

  3. Use the default C:\Program Files\AutoHotkey\AutoHotkey.exe path.

Note

In contrast with AutoHotkey, the whole script is executed once it’s loaded. That is, there are no separate auto-execute and hotkey/hotstring sections. Hotkeys are registered as the script is executed line by line.

Hotkeys

Hotkeys in AutoHotkey.py are registered with the hotkey() function. In the following example, the hotkey Win + N is configured to launch Notepad. The pound sign # stands for the Win key, which is known as a modifier:

import subprocess
import ahkpy

@ahkpy.hotkey("#n"):
def run_notepad():
    subprocess.Popen(["notepad"])

If you want to bind an existing function to a hotkey, pass it as an argument to hotkey():

ahkpy.hotkey("#n", subprocess.Popen, ["notepad"])

In the example above, the subprocess.Popen object will be created with the ["notepad"] argument when the user presses Win + N.

To disable a key or a combination of keys for the entire system, use the lambda: None function. For example, this disables the right-side Win key:

ahkpy.hotkey("RWin", lambda: None)

The methods Windows.active_window_context(), Windows.window_context(), and the HotkeyContext class can be used to make a hotkey perform a different action (or none at all) depending on a specific condition. For example:

notepad_ctx = ahkpy.windows.active_window_context(class_name="Notepad")
notepad_ctx.hotkey(
    "^a", ahkpy.message_box,
    "You pressed Ctrl-A while Notepad is active. Pressing Ctrl-A in any "
    "other window will pass the Ctrl-A keystroke to that window.",
)
notepad_ctx.hotkey(
    "#c", ahkpy.message_box, "You pressed Win-C while Notepad is active.",
)

ctx = ahkpy.windows.active_window_context()
ctx.hotkey(
    "#c", ahkpy.message_box,
    "You pressed Win-C while any window except Notepad is active.",
)
def is_mouse_over_taskbar():
    win = ahkpy.get_window_under_mouse()
    return win.class_name == "Shell_TrayWnd"

# Wheel over taskbar: increase/decrease volume.
taskbar_ctx = ahkpy.HotkeyContext(is_mouse_over_taskbar)
taskbar_ctx.hotkey("WheelUp", ahkpy.send, "{Volume_Up}")
taskbar_ctx.hotkey("WheelDown", ahkpy.send, "{Volume_Down}")

The same handler can be assigned to multiple hotkeys:

import os
import re
import subprocess

import ahkpy

def open_explorer(mode):
    """
    Ctrl+Shift+O to open containing folder in Explorer.
    Ctrl+Shift+E to open folder with current file selected.
    Supports SciTE and Notepad++.
    """
    path = ahkpy.windows.get_active().title
    if not path:
        return

    mo = re.match(r"\*?((.*)\\[^\\]+)(?= [-*] )", path)
    if not mo:
        return

    file = mo.group(1)
    folder = mo.group(2)
    if mode == "folder" and os.path.exists(folder):
        subprocess.Popen(["explorer.exe", f'/select,"{folder}"')
    else:
        subprocess.Popen(["explorer.exe", f'"{file}"')

ahkpy.hotkey("^+o", open_explorer, "file")
ahkpy.hotkey("^+e", open_explorer, "folder")

For more examples see the original Hotkeys usage.

Window Management

AutoHotkey.py provides the Windows class and its default instances: windows and all_windows. The Windows class is the interface to query open windows by multiple criteria, like title and window class. To query the windows, set the criteria with the filter() method. For example, this prepares a query of all windows with a class named ConsoleWindowClass:

>>> console_windows = ahkpy.windows.filter(class_name="ConsoleWindowClass")

The filter() call doesn’t retrieve any windows by itself, it instructs the subsequent operation:

>>> console_windows
Windows(class_name='ConsoleWindowClass', hidden_windows=False, hidden_text=True, title_mode='startswith', text_mode='fast')
>>> len(console_windows)  # Check how many console windows there are now.
3
>>> if console_windows:
...     print("yes")  # Executed if there's at least one console window.
...
yes
>>> list(console_windows)  # Retrieve the list of window instances.
[Window(id=39784856), Window(id=29757762), Window(id=262780)]
>>> [win.title for win in console_windows]
['Command Prompt', 'Windows PowerShell', 'C:\\Windows\\py.exe']

Specifying multiple criteria in the filter() call narrows down the search to the windows where all criteria match. In the following example, the script waits for a window whose title contains My File.txt and whose class is Notepad:

ahkpy.windows.filter("My File.txt", class_name="Notepad").wait()
# Filter chaining gives the same result.
ahkpy.windows.filter("My File.txt").filter(class_name="Notepad").wait()

Calling filter() is useful when you want to create and reuse a selection of windows. However, all Windows methods receive the search criteria, so the wait() example above can be shortened to the following:

ahkpy.windows.wait("My File.txt", class_name="Notepad")

The exclude() method is a companion to filter() that excludes the windows from the search:

non_cmd_windows = ahkpy.windows.exclude(title="Command Prompt")

For more fine-grained window filtering, use list comprehensions:

>>> # Get all tool windows of paint.net.
>>> [
...     win.title
...     for win in ahkpy.windows.filter(exe="PaintDotNet.exe")
...     if ahkpy.ExWindowStyle.TOOLWINDOW in win.ex_style
... ]
['Colors', 'Layers', 'History', 'Tools']

To get the currently active window, use the get_active() method:

# Press Win+↑ to maximize the active window.
ahkpy.hotkey("#Up", lambda: ahkpy.windows.get_active().maximize())

To get first (top-most) window from a query, use the first() method:

>>> ahkpy.windows.first(class_name="Notepad")
Window(id=6426410)

The first(), last(), get_active(), wait() methods return a Window instance. If there are no matching windows, Window(None) is returned. This object is falsy and returns None for most of its properties:

>>> win = ahkpy.windows.first(class_name="there's no such window")
>>> win
Window(id=None)
>>> win.exists
False
>>> if win:
...     print("window exists")  # Will not be printed.
...
>>> win.is_visible
False
>>> win.show()  # Does nothing.
>>> win.class_name is None
True

Also, a window that existed at some point in time but was closed acts the same as Window(None). Thus, be sure to check property values for None before working with them:

>>> win = ahkpy.windows.first(class_name="Notepad")
>>> win
Window(id=6819626)
>>> win.close()
>>> win.exists
False
>>> bool(win)
False
>>> win.class_name is None
True

DLL Calls

Use ctypes to call DLL functions:

>>> from ctypes import windll
>>> windll.user32.MessageBoxW(0, "Press Yes or No", "Title of box", 4)
6

Structure example #11:

>>> import subprocess
>>> from ctypes import byref, windll
>>> from ctypes.wintypes import RECT
>>>
>>> subprocess.Popen(["notepad"])
>>> notepad = ahkpy.windows.wait("Untitled - Notepad")
>>> rect = RECT()
>>> windll.user32.GetWindowRect(notepad.id, byref(rect))
1
>>> (rect.left, rect.top, rect.right, rect.bottom)
(1063, 145, 1667, 824)

Structure example #12:

>>> from ctypes import byref, windll
>>> from ctypes.wintypes import HANDLE, RECT
>>>
>>> screen_width = windll.user32.GetSystemMetrics(0)
>>> screen_height = windll.user32.GetSystemMetrics(1)
>>> rect = RECT(0, 0, screen_width//2, screen_height//2)
>>> # Pass zero to get the desktop's device context.
>>> dc = windll.user32.GetDC(0)
>>> # Create a red brush (0x0000FF is in BGR format).
>>> brush = windll.gdi32.CreateSolidBrush(0x0000FF)
>>> # Fill the specified rectangle using the brush above.
>>> windll.user32.FillRect(dc, byref(rect), brush)
>>> windll.gdi32.DeleteObject(brush)  # Clean-up.
>>> windll.user32.ReleaseDC(0, HANDLE(dc))  # Clean-up.

Settings

A callback is a function called by timer, window message, by changing clipboard, or by triggering a hotkey or a hotstring.

In the original AutoHotkey, a hotkey callback executes with the copy of the global settings. In contrast, in AutoHotkey.py, the callback gets a reference to the current Settings object, set by the set_settings() call. Meaning that, changing the individual settings in the Python callback changes them everywhere. Sometimes, you’ll want to avoid doing so, in which case you should use the local_settings() function. Other times, the implementation will come in handy, like when you want to create a hotkey that changes the global AHK settings:

ahkpy.default_settings.win_delay = 0.1

# The callback stores only the reference to
# ahkpy.default_settings, not the actual settings values.
ahkpy.hotkey("F1", lambda: print(ahkpy.get_settings().win_delay))

@ahkpy.hotkey("F2")
def change_defaults():
    ahkpy.default_settings.win_delay = 0.2
    assert ahkpy.get_settings() is ahkpy.default_settings

If you press F1, you will see 0.1 printed, which is the current win_delay. Press F2 and then F1 and you will see 0.2 printed. Also, the settings object that the F2 hotkey callback gets with the get_settings() call is the same exact settings object that the F1 hotkey gets.

Debugging

AutoHotkey.py supports pdb, the built-in Python debugger. Just put the breakpoint() invocation in your code where you want to enter the debugger and run the program. It works both during the main section and in the callbacks:

x = 0

@ahkpy.hotkey("F1")
def cb():
    global x
    x += 1
    breakpoint()  # Breakpoint in a callback

breakpoint()  # Breakpoint in the main section

The Visual Studio Code debugger can be configured to work with AutoHotkey.py. Follow the Python debug configurations in Visual Studio Code guide to create your launch.json. Once created, change the Python interpreter in the launch.json to ahkpy.exe, for example:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            // Add the following settings:
            "python": "ahkpy.exe",
            "pythonArgs": ["--no-tray"]
        }
    ]
}

Now you can set the breakpoints in Visual Studio Code and inspect the AutoHotkey.py program as you would do with a regular Python program.