Extending Unreal Editor Menus with Python

Extending Unreal Editor Menus with Python

January 15, 20258 min readBy Sergi Carrion
PythonUnreal Engine 5Tools

If you make tools and utilities for your team in Unreal, the time will come when you'll want to integrate them into the Editor. This short guide covers an example of how to extend Unreal's menu system with Python.

What we'll build

  • A simple startup hook (init_unreal.py) to load your tools automatically.
  • A clean, organized menu structure with sections and submenus.
  • Custom menu entries that can launch Editor Utility Widgets or open external documentation.
  • We'll take a look at the registration system that prevents duplicate menus when reloading scripts.

Prerequisites

  • Unreal Editor 4.26+ with Python Editor Script Plugin enabled
  • Basic understanding of Python
  • (Optional) Editor Utility Widget assets for your tools

1) Startup hook

The init_unreal.py file serves as your entry point. Unreal Engine automatically looks for and executes this file when the editor starts up, making it the perfect place to hook into the startup process. For more details on how this works, check out the official documentation on Scripting the Unreal Editor using Python. Here's an example of how to implement it:

""" Unreal Editor startup hook for your Python content. This module is executed by Unreal on editor startup. """ import unreal import your_menus as _your_menus def initialize_editor_startup() -> None: """Register your Tools menu on Unreal Editor startup.""" _your_menus.register_your_menus() # Execute immediately on import initialize_editor_startup()

This file is just the trigger. All the actual menu logic lives in your_menus.py to keep things organized.

2) Idempotent menu registration with proper structure

The menu registration function demonstrates several production-ready patterns:

def register_your_tools_menu() -> unreal.ToolMenu: """Register your Tool's menu and its entries. If the menu already exists, returns it without re-registering. """ menus = unreal.ToolMenus.get() # If the menu exists, return it existing_menu = menus.find_menu("YourTools") if existing_menu: return existing_menu main_menu = menus.find_menu("LevelEditor.MainMenu") custom_menu = main_menu.add_sub_menu( "MainMenu", "YourTools", "YourTools", "Your Tools", "Tools for Your Project" )

Discovering extension points

Pro tip: Enable Editor Settings → Display UI Extension Points to overlay extension point names directly in the UI. This visual debugging feature shows you exactly which owner/menu/section IDs to target.

Alternatively, use the ToolsMenu.Edit command in Unreal's command console to inspect and browse available ToolMenus programmatically. This is invaluable when you need to extend existing menus or toolbars.

3) Creating menu entries

The ToolMenuEntryScript class is your foundation for executable menu items. Here's an example of how to implement it:

@unreal.uclass() class RFUtilsMenuEntry(unreal.ToolMenuEntryScript): """Launches the Actor Tag Manager Editor Utility Widget in a new tab.""" @unreal.ufunction(override=True) def execute(self, context): try: widget_path = "/Game/Tools/Utilities/MyEditorUtilityWidget" editor_utility_subsystem = unreal.get_editor_subsystem(unreal.EditorUtilitySubsystem) editor_utility_subsystem.spawn_and_register_tab(unreal.load_asset(widget_path)) except Exception as e: unreal.log_error(f"Failed to launch MyEditorUtilityWidget: {str(e)}")

4) Organizing menus with sections and submenus

Proper organization prevents menu bloat and improves discoverability which is extremely important if you want users to find your tools. Here's how to structure your menu hierarchy:

# Add Tools section (sections automatically create headers in Unreal) custom_menu.add_section("MainToolsSection", "Tools") # Create Tag Utilities submenu in the Tools section tag_utilities_menu = custom_menu.add_sub_menu( custom_menu.menu_name, # owner "MainToolsSection", # section_name "MyEditorUtilityTool", # name "MyEditor Utility Tool", # label "MyEditor Utility Tool to handle whatever logic you need" # tool_tip ) # Add Actor Tag Manager entry to the Tag Utilities submenu rf_utils_script = RFUtilsMenuEntry() rf_utils_script.init_entry( owner_name=tag_utilities_menu.menu_name, menu=tag_utilities_menu.menu_name, section="", name="MyEditorUtilityTool", label="MyEditor Utility Tool", tool_tip="MyEditor Utility Tool to handle whatever logic you need" ) rf_utils_script.register_menu_entry()

A flat list of 20 scripts is a nightmare to navigate. If a user can't find your tool in 5 seconds, they won't use it. Grouping related commands into sections and submenus makes the interface discoverable and intuitive. It’s often harder to design a clean menu structure than to write the tool itself, but it pays off in the long run.

5) Linking to external resources

Sometimes the best tool is just a link to the right page. Whether it's your team's wiki or official documentation, having it one click away saves time.

@unreal.uclass() class DocumentationMenuEntry(unreal.ToolMenuEntryScript): """Opens the Blueprint documentation in the default web browser.""" @unreal.ufunction(override=True) def execute(self, context): try: import webbrowser # Example: Linking to official docs, but this could be your internal Notion/Confluence webbrowser.open("https://dev.epicgames.com/documentation/en-us/unreal-engine/introduction-to-blueprints-visual-scripting-in-unreal-engine") except Exception as e: unreal.log_error(f"Failed to open documentation: {str(e)}")

6) Finalizing menu registration

Finally, we register the last few entries and refresh the UI. Calling refresh_all_widgets() is the magic command that makes your new menu appear instantly without needing to restart the editor.

# Add About section custom_menu.add_section("YourToolsAboutSection", "About") # Add Documentation menu entry doc_script = DocumentationMenuEntry() doc_script.init_entry( owner_name=custom_menu.menu_name, menu=custom_menu.menu_name, section="YourToolsAboutSection", name="Documentation", label="Documentation", tool_tip="Open the documentation" ) doc_script.register_menu_entry() # Refresh all widgets to show the new menu immediately menus.refresh_all_widgets() return custom_menu

7) Going further: Dynamic Menus

Dynamic menu population: Consider populating menus dynamically. Instead of manually registering every single tool, you can iterate over a list, a data table, a JSON config file, or even scan a directory of assets.

def populate_from_config(menu, tools_list): """ Example: Generate menu entries from a list of dictionaries. Realistically, this list would come from a .json file or a Data Table. """ for tool in tools_list: entry = MyGenericMenuEntry() entry.init_entry( owner_name=menu.menu_name, menu=menu.menu_name, section="Tools", name=tool['name'], label=tool['label'], tool_tip=tool['tooltip'] ) entry.register_menu_entry()

This allows you to add new tools to your pipeline without ever touching the python code again—just update your config file.

Conclusion

And that's it. You now have a menu system where you can hook documentation, editor utility widgets, and any other custom logic you need. It’s quick to set up and it makes the difference between a tool that feels like a hack (having to right click and "Run Editor Utility Widget" from the context menu) and one that feels like part of the engine.

Extending Unreal Editor Menus with Python | Sergi Carrion