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:

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:
