Skip to content

Windows Context Menus

Basic example

convert_image_menu.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "windows_context_menus",
# ]
#
# [tool.uv.sources]
# windows_context_menus = { path = "../../" }
# ///
"""Adds a context menu for converting images from and to formats."""

from windows_context_menus import CommandPlaceholder, Condition, ContextMenu, ContextMenuBinding, ExplorerItemType, RegistryHandler

CONVERT_IMAGE_COMMAND = f'cmd.exe /c magick "{CommandPlaceholder.FIRST_SELECTED}" "{CommandPlaceholder.FIRST_SELECTED}".{{}}'

convert_to_png_entry = ContextMenu(
    display_text="Convert to PNG",
    command=CONVERT_IMAGE_COMMAND.format("png"),
    condition=Condition.model_validate(
        {"extension": {"ne": ".png"}},
    ),
)

convert_to_jpeg_entry = ContextMenu(
    display_text=("Convert to JPEG"),
    command=CONVERT_IMAGE_COMMAND.format("jpeg"),
    condition=Condition.model_validate({"extension": {"ne": ".jpeg"}}),
)

convert_to_ico_entry = ContextMenu(
    display_text=("Convert to ICO"),
    command=CONVERT_IMAGE_COMMAND.format("ico"),
    condition=Condition.model_validate({"extension": {"ne": ".ico"}}),
)

convert_to_bmp_entry = ContextMenu(
    display_text=("Convert to BMP"),
    command=CONVERT_IMAGE_COMMAND.format("bmp"),
    condition=Condition.model_validate({"extension": {"ne": ".bmp"}}),
)

main: ContextMenu = ContextMenu(
    display_text=("Convert to..."),
    selection_limit=1,  # This menu supports only 1 file at a time.
    condition=Condition.model_validate(
        {
            "extension": {
                "in": [
                    ".png",
                    ".jpeg",
                    ".jpg",
                    ".bmp",
                    ".ico",
                    ".webp",
                    ".avif",
                    ".jfif",
                ],
            },
        },
    ),
    submenus=[convert_to_png_entry, convert_to_ico_entry, convert_to_jpeg_entry, convert_to_bmp_entry],
)

if __name__ == "__main__":
    RegistryHandler().apply_context_menu(
        main,
        bindings=[
            ContextMenuBinding(
                ExplorerItemType.ALL_FILES,
            ),
        ],
    )

Looks like:

convert_image.png

More complicated example

resolution_menu.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "windows_context_menus",
# ]
#
# [tool.uv.sources]
# windows_context_menus = { path = "../../" }
# ///

from enum import StrEnum

from examples.convert_video.volume_menu import WindowsIcon
from windows_context_menus import CommandPlaceholder, ContextMenu, ContextMenuBinding, ExplorerItemType, RegistryHandler


class Resolution(StrEnum):
    HD = "720p"
    FHD = "1080p"
    QHD = "1440p"


class Encoding(StrEnum):
    AVC = "libx264"
    HEVC = "hevc"


BASE_COMMAND = (
    f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -vf "scale=-1:{{res}}" -c:v libx264 -preset fast -c:a copy {{output_name}}'
)

resolution_submenus: list[ContextMenu] = []

for resolution in (Resolution.HD, Resolution.FHD):
    resolution_submenus.append(
        ContextMenu(
            display_text=(f"Convert to {resolution}"),
            command=BASE_COMMAND.format(
                res=resolution.rstrip("p"),
                output_name=f'"%1"-{resolution}.mp4',
            ),
        ),
    )


resolution_menu = ContextMenu(
    display_text=("Convert resolution..."),
    icon=WindowsIcon.PICTURE_IN_PICTURE,
    submenus=resolution_submenus,
)

if __name__ == "__main__":
    RegistryHandler().apply_context_menu(
        resolution_menu,
        bindings=[
            ContextMenuBinding(
                ExplorerItemType.ALL_FILES,
            ),
        ],
    )
speed_menu.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "windows_context_menus",
# ]
#
# [tool.uv.sources]
# windows_context_menus = { path = "../../" }
# ///
from windows_context_menus import CommandPlaceholder, ContextMenu, ContextMenuBinding, ExplorerItemType, RegistryHandler

ONE_AND_HALF_SPEED_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -filter:v "setpts=PTS/1.5" -filter:a "atempo=1.5" "{CommandPlaceholder.FIRST_SELECTED}"-1.5x.mp4'
FIVE_QUARTERS_SPEED_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -filter:v "setpts=PTS/1.25" -filter:a "atempo=1.25" "{CommandPlaceholder.FIRST_SELECTED}"-1.25x.mp4'
THREE_QUARTERS_SPEED_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -filter:v "setpts=PTS/0.75" -filter:a "atempo=0.75" "{CommandPlaceholder.FIRST_SELECTED}"-0.75x.mp4'
HALF_SPEED_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -filter:v "setpts=PTS/0.5" -filter:a "atempo=0.5" "{CommandPlaceholder.FIRST_SELECTED}"-0.5x.mp4'
QUARTER_SPEED_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -filter:v "setpts=PTS/0.25" -filter:a "atempo=0.5,atempo=0.5" "{CommandPlaceholder.FIRST_SELECTED}"-0.25x.mp4'

speed_submenus = [
    ContextMenu(
        display_text="Speed up by 1.5x",
        command=ONE_AND_HALF_SPEED_COMMAND,
    ),
    ContextMenu(
        display_text="Speed up by 1.25x",
        command=FIVE_QUARTERS_SPEED_COMMAND,
    ),
    ContextMenu(
        display_text="Slow down by 0.75x",
        command=THREE_QUARTERS_SPEED_COMMAND,
    ),
    ContextMenu(
        display_text="Slow down by 0.5x",
        command=HALF_SPEED_COMMAND,
    ),
    ContextMenu(
        display_text="Slow down by 0.25x",
        command=QUARTER_SPEED_COMMAND,
    ),
]


speed_menu = ContextMenu(
    display_text=("Change speed..."),
    icon="wmploc.dll,-29608",
    submenus=speed_submenus,
)

if __name__ == "__main__":
    RegistryHandler().apply_context_menu(
        speed_menu,
        bindings=[
            ContextMenuBinding(
                ExplorerItemType.ALL_FILES,
            ),
        ],
    )
volume_menu.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "windows_context_menus",
# ]
#
# [tool.uv.sources]
# windows_context_menus = { path = "../../" }
# ///
from windows_context_menus import CommandPlaceholder, ContextMenu, ContextMenuBinding, ExplorerItemType, RegistryHandler
from windows_context_menus.models.icons import WindowsIcon

MUTE_COMMAND = (
    f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -c:v copy -an "{CommandPlaceholder.FIRST_SELECTED}"-no_audio.mp4'
)
DOUBLE_VOLUME_COMMAND = f'cmd.exe /c ffmpeg -i "{CommandPlaceholder.FIRST_SELECTED}" -c:v copy -af "volume=2.0" "{CommandPlaceholder.FIRST_SELECTED}"-double_volume.mp4'

volume_submenus = [
    ContextMenu(
        display_text="Mute Audio",
        command=MUTE_COMMAND,
        icon=WindowsIcon.MUTE,
    ),
    ContextMenu(
        display_text="Double the volume",
        command=DOUBLE_VOLUME_COMMAND,
    ),
]


volume_menu = ContextMenu(
    display_text=("Volume options..."),
    icon=WindowsIcon.SPEAKER,
    submenus=volume_submenus,
)

if __name__ == "__main__":
    RegistryHandler().apply_context_menu(
        volume_menu,
        bindings=[
            ContextMenuBinding(
                ExplorerItemType.ALL_FILES,
            ),
        ],
    )

Then for the finalized menu:

video_menu.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "windows_context_menus",
# ]
#
# [tool.uv.sources]
# windows_context_menus = { path = "../../" }
# ///
from resolution_menu import resolution_menu
from speed_menu import speed_menu
from volume_menu import volume_menu

from windows_context_menus import Condition, ContextMenu, ContextMenuBinding, ExplorerItemType, RegistryHandler

menu = ContextMenu(
    display_text="Convert mp4...",
    icon="wmploc.dll,-610",
    condition=Condition.model_validate(
        {"extension": {"eq": ".mp4"}},
    ),
    submenus=[speed_menu, resolution_menu, volume_menu],
)

if __name__ == "__main__":
    RegistryHandler().apply_context_menu(
        menu,
        bindings=[
            ContextMenuBinding(
                ExplorerItemType.ALL_FILES,
            ),
        ],
    )

Renders as:

change_speed.png convert_resolution.png volume_options