Editor utilitty widget menus in unreal

*************************************************** UPDATE ***************************************

This is how I managed in the end to execute EUW that make use of BP functions written in Python from a menu: no C++ involved.
The problem is that EUWs with python BP functions need to be closed and not docked at startup, or they’ll be initialized before the functions, ending in not workin EUWs (unless one opens them and refresh nodes by hand)

I created a Content only UE4 plugin with a Python folder under its Content one.
By default the engine will add the Python folder to path and run at startup files named init_unreal.py.
with this name if found in a Python folder.
The same folder also contains a custom module called py_bp_functions.py where I have a bunch of python functions and @uclass decorated wrappers.
The file contains function definition pairs like this:

def foo(message):
    print(message)
    return(True)
    
@ue.uclass()
    class PythonBPFunctionLibrary(ue.BlueprintFunctionLibrary):

        @ue.ufunction(static=True, params=[str], ret=str, meta=dict(Category="Python functions"))
        def bp_foo():
            return foo()

Under plugin content I also have a folder named EditorUtilityWidgets that contains EUW that I want to execute.

init_unreal.py file is:

import os    
import unreal as ue
import py_bp_functions.py  # this is just needed to make Unreal creating BP functions for the current session


""" Thanks to Travis Evashkevich (TooL) and Andreas Rånman for the Menu creation """


def create_euw_menu(widgets_list):
    menus = ue.ToolMenus.get()
    # Find the Main menu, this should not fail,
    # but if we're looking for a menu we're unsure about 'if not'
    # works as nullptr check,
    main_menu = menus.find_menu("LevelEditor.MainMenu")
    if not main_menu:
        print("Failed to find the Main menu")

    my_menu = main_menu.add_sub_menu(main_menu.get_name(), "MySection", "MenuName", "MenuLabel", tool_tip="A menu for execution of EUWs")
    for widget in widgets_list:
        name = os.path.basename(widget).replace("EUW_", "")  # derive label from blueprint name removing EOW prefix
        entry = ue.ToolMenuEntry(
            name=name,
            type=ue.MultiBlockType.MENU_ENTRY,  # If you pass a type that is not supported Unreal will let you know,
        )
        entry.set_label(name)
        command_string = "import unreal; unreal.EditorUtilitySubsystem().spawn_and_register_tab(unreal.EditorAssetLibrary.load_asset('" + widget + "'))"  # construct the command string here
        entry.set_string_command(ue.ToolMenuStringCommandType.PYTHON, ue.Name(""), string=command_string)  # here an empy Name/string is needed for the custom_type argument
        my_menu.add_menu_entry("Items", entry)
    menus.refresh_all_widgets()


_EUW_PATH = "/PluginName/EditorUtilityWidgets/"  # Path to plugin folders containing EditorUtilityWidgets
_EUW_LIST = ("EUW_MyTool, EUW_MyOtherTool",)  # List of EditorUtilityWidgets that need to be listed in Menu

if __name__ == "__main__":
euw_list = [_UEUW_PATH+euw for euw in _EUW_LIST]
create_euw_menu(euw_list)

Doing this, all BP functions are ready for usage at startup. The only caveats so far are:

  1. EUWs should be closed and undocked before closing the engine, or at the next run they’ll be there and probably not working

  2. When you first open an EUW you’ll get a linker error in the log (only first time): nothing that will break the blueprints but probably annoying once at package time. But EUW should not be packaged and the fact that all this stuff is a plugin shoul easily allow to exclude them.

But it seems to work, it’s easily redistributable, it doesn’t require to add strange startup stuff to the project and give artists a shortcut for executing custom tools + the advantage of the nice python library :smiley:

2 Likes