Bootstrap Chameleon Logo

Need more python APIs

One begets two, two begets three, and three begets everything.

In the previous tutorial on adding menu items to the Unreal editor, we used unreal.PythonBPLib.get_selected_folder() to obtain the folder selected by the user in the Content Browser. This unreal.PythonBPLib.get_selected_folder is one of over 200 extended editor APIs built into TAPython. These APIs supplement the Unreal editor with numerous additional features. You can find their API descriptions here, or see the 100 most commonly used editor APIs for the most frequently used ones.

How to Find Existing APIs

When we want to use (call) an editor feature, we recommend searching in the following order:

  1. If there is a Blueprint node capable of completing the desired function, then the corresponding Python function should be available for the Blueprint node.
  2. In an editor with Auto Complete For TApython set up, try using auto-completion to search for available APIs.
  3. Search for Unreal stub files to quickly find the required API. In Auto Complete For TApython, you can find information on splitting unreal.py into smaller files by class, making searching and navigation lighter and faster.
  4. Check whether TAPython's extended APIs offer similar functionality, for example.
  5. Search for cmd commands in the editor's cmd console that can be called.

TIP
Entering help in Cmd displays all available debugging commands for the current editor. 'Help' text in Unreal Engine Editor Cmd window

  1. Search for relevant Editor Library and EditorSubsystem code in Visual Studio.

Extra
Epic is continuously improving Python editor APIs, which is why some of TAPython's extended APIs are redundant with UE built-in APIs.

If none of the above steps yield a usable API, you can consider adding an additional editor API through C++ for Python to use. Follow these steps:

Adding Steps

1. C++ Project Type

First, you need a C++ Unreal project.

2. Add a BlueprintLibrary Type Unreal Plugin

Open the Plugin window, click the Add button in the top left corner.

Snapshot showing 'Add plugin button'

Choose a Blueprint Library type plugin, fill in the plugin name, author, and other information, then click the Create Plugin button. This will create a BlueprintLibrary type plugin.

New plugin dialog, highlighting Blueprint Library category

After compiling the project in Visual Studio, you can see the plugin in the Python command line.

Blueprint library info in Unreal Engine Output window

TIP
In the image above, Python (REPL) mode is used. In non-REPL mode, you need to use the print() function to output.

By default, the plugin will add a MagicBoxSampleFunction instance function with a float parameter that returns -1.0f.

MagicBoxBPLibrary.cpp

float UMagicBoxBPLibrary::MagicBoxSampleFunction(float Param)
{
    return -1;
}

This function's Python API is unreal.MagicBoxBPLibrary.magic_box_sample_function. Note that the function name is different from that in C++.

help(unreal.MagicBoxBPLibrary.magic_box_sample_function)

# magic_box_sample_function(...) method of builtins.type instance
#     X.magic_box_sample_function(param) -> float
#     Magic Box Sample Function
#    
#     Args:
#         param (float): 
#    
#     Returns:
#         float:

A snapshot showing the added function call and its result

3. Add blueprint-callable functions

Open the C++ project in Visual Studio, find the MagicBoxBPLibrary plugin, and add the declarations and definitions for the Python-callable functions in MagicBoxBPLibrary.h and MagicBoxBPLibrary.cpp, respectively. Then, compile the project. Once it is done, restart the editor, and you can use the new Python API in the editor.

TIP
TAPython provides additional editor APIs, and you can also use the editor APIs you create in TAPython.

4. Test in Python after compilation

Function conversion rules

The C++ function names will have case changes in Python. The main rules for UE are as follows:

Namespace

  • All new BPLibraries are in the unreal namespace
  • BThe BPLibrary name is the same as the C++ name, without case changes, such as MagicBoxBPLibrary in the example above.

Function name case conversion

SetStaticMeshLODMaterialID is converted to set_static_mesh_lod_material_id

  • The C++ function names are converted to lowercase letters, with uppercase letters connected by an underscore (_). When multiple uppercase letters appear in a row, such as LOD, they are treated as a whole.
  • In boolean parameters, if the first letter is a lowercase b, followed by an uppercase letter, the b will be removed in Python.

For example, the C++ parameter bool bOverride; corresponds to the override in Python.

  • The parameter names in the function also follow this rule.

For example:

MessageDialog is converted to message_dialog

SetStaticMeshLODMaterialID is converted to set_static_mesh_lod_material_id

Parameter Types

  • C++ bool, FString, int32, and float correspond to Python bool, str, integer, and float, respectively.
  • FName corresponds to unreal.Name in Python.

NOTE
In Python, you don't have to be overly concerned with distinguishing between FName, FString, and even FText. They have subtle differences in Python, but in practice, you can use str directly, and UE will automatically convert the types.

  • TArray, TMap correspond to unreal.Array, unreal.Map in Python.
  • Blueprint-visible UObject types have the same name in the unreal namespace in Python.

Non-const references

  • Non-const reference types in parameters are treated as return values in Python, not input parameters.

For example:

In the following function, FIntPoint& OutSizeXY is a non-const reference type:

UFUNCTION(BlueprintCallable, meta = (Keywords = "Python Editor"), Category = "PythonEditor")
static TArray<FColor> GetViewportPixels(FIntPoint& OutSizeXY);

Its corresponding Python function signature is:

unreal.PythonBPLib.get_viewport_pixels() -> (Array[Color], out_size_xy=IntPoint)

The return value is a tuple, with the first element being Array[Color] and the second element being an IntPoint type out_size_xy.

Another example: In the following example, the InGuidStr variable type is a const FString reference. Because of the const, it is an input value; if the const is removed, it will also become a return value.

UFUNCTION(BlueprintCallable, meta = (Keywords = "Python Editor"), Category = "PythonEditor")
static FGuid GuidFromString(const FString& InGuidStr);

Its corresponding Python function signature is:

unreal.PythonBPLib.guid_from_string(guid_str:str) -> Guid

Array

  • Parameters/return values in TArray can only be 1-dimensional TArray, and do not support 2D Array or nesting.

Therefore, for 2D arrays, such as pixels in an image, you need to flatten the pixels before inputting or outputting them.

For example:

UFUNCTION(BlueprintCallable, Category = Scripting)
void SetImagePixels(FName AkaName, TArray<FLinearColor> PixelColors, int32 Width, int32 Height);

The input value pixel_colors is a 1D Linear array.

set_image_pixels(...)
    x.set_image_pixels(aka_name, pixel_colors, width, height) -> None
    Set SImage's Image content with Linear Colors.

    Args:
        aka_name (Name): The aka name of the widget
        pixel_colors (Array[LinearColor]): The pixel list of image, len(PixelColors) == Height * Width
        width (int32): Width of Image.
        height (int32): Height of Image.

Multiple return values

Using the non-const reference parameters introduced above, we can return multiple values together from a C++ function to Python.

The return value of the C++ function itself is always the first element of the tuple with multiple return values, and other parameters follow in order.

Custom structs

We can define custom structs in C++ and then use them in Python.

For example, in the following case, we define an FStaticSwitchInfo struct. In UStruct, we can declare that this struct is Blueprint/Python-visible using USTRUCT(Blueprintable, BlueprintType):

USTRUCT(Blueprintable, BlueprintType)
struct FStaticSwitchInfo
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PythonEditor)
    FName Name;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PythonEditor)
    bool Value;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PythonEditor)
    bool bOverride;

};

After compilation, we can use this struct in Python:

class StaticSwitchInfo(StructBase):
    r"""
    Static Switch Info

    **C++ Source:**

    - **Plugin**: TAPython
    - **Module**: TAPython
    - **File**: PythonMaterialLib.h

    **Editor Properties:** (see get_editor_property/set_editor_property)

    - ``name`` (Name):  [Read-Write] Name
    - ``override`` (bool):  [Read-Write] Override
    - ``value`` (bool):  [Read-Write] Value
    """

or custom structs defined in C++ for use in Python, based on my experience, if the purpose is just to make it easier to pass arguments, I would not recommend using them. The reason is that subsequent modifications to the struct require recompiling the plugin, which loses the fast and agile nature of Python during development. TIP
Some tips and Tricks

Reference

Learning Unreal Engine with Python