Quickstart

This page assumes you’ve already installed AutoHotkey.py. If you haven’t, 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, of course, simply invokes a script:

ahkpy myscript.py

The REPL interface works best when you invoke it with the python executable, which allows you to explore your command history with the up and down arrow keys.

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. To learn more about your interface options, refer to the Python documentation.

The following CLI options are specific to AutoHotkey.py:

-q

Suppress message boxes with errors. If you don’t specify this option, AutoHotkey.py shows unhandled Python errors in message boxes.

--no-tray

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

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

  1. First, checks whether the AUTOHOTKEY environment variable is set. If it is, AutoHotkey.py uses the environment variable as the AutoHotkey executable path.

  2. If not, checks the Windows Registry to find whether a program’s set to run when a user opens an AHK file. If the program name starts with autohotkey (case-insensitive), AutoHotkey.py uses it as the AutoHotkey executable path.

  3. Otherwise, defaults to the path C:\Program Files\AutoHotkey\AutoHotkey.exe.

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

The hotkey() function registers Hotkeys. In the following example, the hotkey Win + N is configured to launch Notepad. The pound sign # stands for the Win key, one of the hotkey modifiers:

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 preceding example, when a user presses Win + N, they create a subprocess.Popen object with the argument ["notepad"].

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)

If you want to specify certain conditions when a hotkey performs a different action (or no action at all), you can use the methods Windows.active_window_context() and Windows.window_context(), or the class HotkeyContext. 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. With the Windows class, you can query windows through multiple search parameters, 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 of the class ConsoleWindowClass:

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

The only role of the filter() method is to pack the query parameters. Once you’ve set the filters you want, you can perform a real operation, like get the count of matching windows:

>>> 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 for filter() narrows the search down to only 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 a window from a 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, if a window existed at some point in time but was closed, it 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 with a regular Python program.