Editor scripting & Python
Everything in this skill is editor-only: it lives in editor modules or behind
WITH_EDITOR and is stripped from packaged games. The two main paths are: the
Python Script Plugin (unreal module) for quick automation and pipeline scripts, and
Blutility (Editor Utility Widgets/Blueprints) for in-editor UI tools callable by
artists and designers.
When to use this skill
- Batch operations on assets — rename, set properties, reimport, generate LODs.
- Custom in-editor UMG panels (Editor Utility Widgets) for artists.
- Context-menu scripted actions triggered from the Content Browser or Level.
- Headless commandlet runs in CI without the full editor UI.
- Exposing a C++ function so it appears in Python/Blueprints with the correct name.
- Obtaining a reference to an editor subsystem from C++ or Python.
Approach comparison
| Approach | When to choose |
|---|---|
| Python (PythonScriptPlugin) | Pipeline automation, batch asset ops, CI jobs, quick scripts |
| Editor Utility Widget (Blutility) | Artist-facing tool panels with UMG UI in the editor |
| Editor Utility Blueprint | No-UI scripted actions on assets or actors |
| C++ editor module + UEditorSubsystem | Robust reusable tools, Slate/menu extensions |
| Commandlet | Headless batch processing, -run=pythonscript in CI |
Python (unreal module)
Enable the Python Editor Script Plugin (Plugins > Scripting). The plugin bundles
Python 3.11.8; no separate install is needed. The unreal module is generated at startup
from whatever is reflected to Blueprints — any BlueprintCallable function or class
exposed by any enabled plugin is automatically available.
Naming convention: C++ class UEditorAssetSubsystem → Python class
unreal.EditorAssetSubsystem. Function GetAllLevelActors() → get_all_level_actors().
Enum values become UPPER_SNAKE_CASE. The U/A/F/T prefix is dropped.
Reading/setting properties:
import unreal
actor = unreal.EditorActorSubsystem().get_actor_reference("PersistentLevel.MyActor")
# BlueprintReadWrite → direct attribute access
val = actor.some_property
# EditAnywhere → use get/set_editor_property for pre/post-edit notifications
actor.set_editor_property("hidden_in_game", True)
Transactions (undo/redo support):
with unreal.ScopedEditorTransaction("Batch rename"):
for asset in assets:
subsystem.rename_asset(asset.get_path_name(), new_path)
Progress feedback for long jobs:
with unreal.ScopedSlowTask(len(assets), "Processing...") as task:
task.make_dialog(True)
for asset in assets:
if task.should_cancel():
break
task.enter_progress_frame(1, f"Processing {asset.get_name()}")
# ... do work
Startup scripts — add paths under Project Settings → Plugins → Python → Startup
Scripts (stored in UPythonScriptPluginSettings::StartupScripts,
Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PythonScriptPluginSettings.h:67).
Auto-detected init_unreal.py in any Content/Python folder also runs on startup.
Commandlet (headless): UPythonScriptCommandlet runs a script without the full UI:
UnrealEditor-Cmd.exe MyProject.uproject -run=pythonscript -script="my_script.py"
Source: Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PythonScriptCommandlet.h:10.
See references/python-api.md for the C++↔Python mapping,
get_editor_subsystem, type coercion, logging, and subsystem access patterns.
Editor Utility Widgets & Blueprints (Blutility)
Blutility classes live in Editor/Blutility/. The module is Blutility; add it to your
editor module's PrivateDependencyModuleNames.
Key classes
| Class | Base | Purpose |
|---|---|---|
UEditorUtilityWidget |
UUserWidget |
Dockable UMG panel running in the editor |
UEditorUtilityWidgetBlueprint |
UWidgetBlueprint |
Asset that generates UEditorUtilityWidget |
UEditorUtilityObject |
UObject |
Editor-only utility with no UI; runs on demand |
UAssetActionUtility |
UEditorUtilityObject |
Right-click scripted actions in the Content Browser |
UEditorUtilityTask |
UObject |
Background task managed by UEditorUtilitySubsystem |
UEditorUtilityWidget (Editor/Blutility/Classes/EditorUtilityWidget.h:27):
inherits UUserWidget; runs entirely in editor. Run() is a BlueprintImplementableEvent
called when the widget is auto-run. Use TabDisplayName to set the panel name.
UEditorUtilityObject (Editor/Blutility/Classes/EditorUtilityObject.h:20):
pure-logic utility, no UI. Set bRunEditorUtilityOnStartup = true to auto-run after
asset discovery.
UAssetActionUtility (Editor/Blutility/Classes/AssetActionUtility.h:60):
any UFUNCTION(BlueprintCallable) on a subclass appears as a right-click option in the
Content Browser. Populate SupportedClasses (class defaults) to filter which asset types
show the action. GetSupportedClass() is deprecated since UE 5.2.
UEditorUtilitySubsystem manages widget tabs and utility tasks
(Editor/Blutility/Public/EditorUtilitySubsystem.h:47):
// Open an Editor Utility Widget tab from C++:
UEditorUtilitySubsystem* EUS = GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>();
EUS->SpawnAndRegisterTab(MyWidgetBlueprint); // line 88
// From Python:
// eus = unreal.get_editor_subsystem(unreal.EditorUtilitySubsystem)
// eus.spawn_and_register_tab(widget_bp)
See references/editor-utility-widgets.md for
Widget setup, tab lifecycle, UEditorUtilityTask, and scripted-actions detail.
Editor scripting subsystems
All subsystems inherit UEditorSubsystem. Access them via
GEditor->GetEditorSubsystem<T>() in C++ or unreal.get_editor_subsystem(T) in Python.
UEditorActorSubsystem (Editor/UnrealEd/Public/Subsystems/EditorActorSubsystem.h:49)
Actor-level operations in the current level editor world:
UEditorActorSubsystem* AS = GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
TArray<AActor*> All = AS->GetAllLevelActors(); // :166
TArray<AActor*> Sel = AS->GetSelectedLevelActors(); // :181
AActor* Placed = AS->SpawnActorFromClass(MyClass, Location); // :228
AS->DestroyActor(ActorToRemove); // :236
Python: unreal.get_editor_subsystem(unreal.EditorActorSubsystem).get_all_level_actors()
UEditorAssetSubsystem (Editor/UnrealEd/Public/Subsystems/EditorAssetSubsystem.h:38)
Content-browser asset operations (the preferred replacement for UEditorAssetLibrary):
UEditorAssetSubsystem* EAS = GEditor->GetEditorSubsystem<UEditorAssetSubsystem>();
UObject* Loaded = EAS->LoadAsset("/Game/MyFolder/MyAsset"); // :54
EAS->SaveAsset("/Game/MyFolder/MyAsset"); // :296
EAS->DuplicateAsset("/Game/Src", "/Game/Dst"); // :185
EAS->RenameAsset("/Game/Src", "/Game/Dst"); // :215
EAS->DeleteAsset("/Game/MyFolder/MyAsset"); // :155
ULevelEditorSubsystem (Editor/LevelEditor/Public/LevelEditorSubsystem.h:38)
Level-file and viewport operations:
ULevelEditorSubsystem* LS = GEditor->GetEditorSubsystem<ULevelEditorSubsystem>();
LS->NewLevel("/Game/Maps/MyLevel"); // :147
LS->LoadLevel("/Game/Maps/Existing"); // :167
LS->SaveCurrentLevel(); // :174
LS->EditorRequestBeginPlay(); // :78
UAssetEditorSubsystem (Editor/UnrealEd/Public/Subsystems/AssetEditorSubsystem.h:112)
Open assets in their specialized asset editors programmatically.
UUnrealEditorSubsystem (Editor/UnrealEd/Public/Subsystems/UnrealEditorSubsystem.h:17)
Viewport camera query/set (GetLevelViewportCameraInfo, SetLevelViewportCameraInfo)
and GetEditorWorld().
See references/editor-subsystems.md for all subsystem methods and Python equivalents.
Exposing C++ to editor scripting
UFUNCTION(BlueprintCallable) — surfaces to both Blueprints and Python
Any BlueprintCallable function on a UCLASS is reflected to Python automatically. In
Python the class name loses its prefix and the function name becomes snake_case.
UFUNCTION(meta = (CallInEditor = "true")) — Details panel button
Marks a function to appear as a button in the actor Details panel when an instance is
selected in the editor. Works on AActor and UActorComponent subclasses.
Source: Runtime/CoreUObject/Public/UObject/ObjectMacros.h:1047.
UFUNCTION(BlueprintCallable, Category = "Validation", meta = (CallInEditor = "true"))
void ValidateSetup();
meta = (ScriptMethod) — hoist a static function as an instance method in Python
A static BlueprintFunctionLibrary function that takes a struct or object as its first
parameter can be re-surfaced in Python as a method on that type:
UFUNCTION(BlueprintCallable, meta = (ScriptMethod))
static FVector ScaleVector(const FVector& V, float Factor);
// Python: v.scale_vector(2.0) instead of unreal.MyLib.scale_vector(v, 2.0)
Source: Runtime/CoreUObject/Public/UObject/ObjectMacros.h:1723. Related specifiers:
ScriptMethodSelfReturn (:1726), ScriptMethodMutable (:1729).
meta = (ScriptName = "PythonName") — override the Python/scripting name
UFUNCTION(BlueprintCallable, meta = (ScriptName = "load_texture"))
static UTexture* LoadTexture_Internal(const FString& Path);
// Python: unreal.MyLib.load_texture(path)
Source: Runtime/CoreUObject/Public/UObject/ObjectMacros.h:1285.
Version notes
UEditorAssetLibrary/UEditorLevelLibrary(EditorScriptingUtilities plugin) were deprecated in UE 5.0.UEditorLevelLibraryfunctions carryUE_DEPRECATED(5.0, "... Use the function in Editor Actor Utilities Subsystem"). PreferUEditorAssetSubsystemandUEditorActorSubsystemin all new code.UAssetActionUtility::GetSupportedClass()deprecated UE 5.2; use theSupportedClassesarray in class defaults instead.- Python 3.11.8 is the embedded version for UE 5.8 (VFX Reference Platform CY2024).
Set
UE_PYTHON_DIRto embed a different CPython build (requires source rebuild).
Gotchas
- Editor-only code in a runtime module → packaging failure. Put editor code behind
WITH_EDITORor in a module with typeEditor. - Python does not ship with the game — it is a tooling/editor-only plugin.
- Direct attribute set vs
set_editor_property— direct set bypasses pre/post-edit callbacks;set_editor_propertytriggers them (same as changing in Details panel). Useset_editor_propertyforEditAnywhereproperties. - Long Python jobs block the editor UI — wrap with
unreal.ScopedSlowTaskand yieldenter_progress_frameto keep the editor responsive and allow cancellation. - Forgetting to save — call
EAS->SaveAsset()or the Python equivalent; unsaved changes to assets are lost when the editor closes. - Hardcoding asset paths — use the Asset Registry to discover paths dynamically; see
ue-asset-management. SpawnActorFromClassvsSpawnActor—SpawnActorFromClassonUEditorActorSubsystemplaces into the editor world and notifies the editor; use it instead ofUWorld::SpawnActorfor editor-placed actors.
References & source material
Engine source (UE 5.8):
Blutility (Engine/Source/Editor/Blutility/):
Classes/EditorUtilityWidget.h—UEditorUtilityWidget:27,Run():34,TabDisplayName:74Classes/EditorUtilityObject.h—UEditorUtilityObject:20,bRunEditorUtilityOnStartup:41Classes/AssetActionUtility.h—UAssetActionUtility:60,SupportedClasses, deprecatedGetSupportedClass():71Classes/EditorUtilityTask.h—UEditorUtilityTask:32,FinishExecutingTask():56Public/EditorUtilitySubsystem.h—UEditorUtilitySubsystem:47,SpawnAndRegisterTab:88,RegisterAndExecuteTask:140,TryRun:76
Editor subsystems (Engine/Source/Editor/UnrealEd/Public/Subsystems/):
EditorActorSubsystem.h—UEditorActorSubsystem:49,GetAllLevelActors:166,SpawnActorFromClass:228,DestroyActor:236EditorAssetSubsystem.h—UEditorAssetSubsystem:38,LoadAsset:54,SaveAsset:296,DuplicateAsset:185,RenameAsset:215,DeleteAsset:155AssetEditorSubsystem.h—UAssetEditorSubsystem:112UnrealEditorSubsystem.h—UUnrealEditorSubsystem:17,GetLevelViewportCameraInfo:32
Level editor (Engine/Source/Editor/LevelEditor/Public/):
LevelEditorSubsystem.h—ULevelEditorSubsystem:38,NewLevel:147,LoadLevel:167,SaveCurrentLevel:174,EditorRequestBeginPlay:78
Python plugin (Engine/Plugins/Experimental/PythonScriptPlugin/):
Source/PythonScriptPlugin/Public/IPythonScriptPlugin.h—IPythonScriptPlugin:11,ExecPythonCommand:54Source/PythonScriptPlugin/Private/PythonScriptCommandlet.h—UPythonScriptCommandlet:10Source/PythonScriptPlugin/Private/PythonScriptPluginSettings.h—StartupScripts:67,AdditionalPaths:71
Reflection/meta specifiers (Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectMacros.h):
CallInEditor:1005,ScriptName:1243,ScriptMethod:1671,ScriptMethodSelfReturn:1674,ScriptMethodMutable:1677
Deprecated (EditorScriptingUtilities plugin, prefer subsystems):
Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorAssetLibrary.hEngine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h(deprecated UE 5.0)
Official docs (UE 5.8):
- Scripting and Automating the Unreal Editor — https://dev.epicgames.com/documentation/unreal-engine/scripting-and-automating-the-unreal-editor
- Scripting the Unreal Editor Using Python — https://dev.epicgames.com/documentation/unreal-engine/scripting-the-unreal-editor-using-python
- Scripting the Unreal Editor Using Blueprints — https://dev.epicgames.com/documentation/unreal-engine/scripting-the-unreal-editor-using-blueprints
- Python API Reference — https://dev.epicgames.com/documentation/unreal-engine/PythonAPI
Deep-dive references in this skill:
- references/editor-subsystems.md — all subsystem classes, their methods, and Python equivalents.
- references/editor-utility-widgets.md — Blutility
class hierarchy, tab lifecycle,
UEditorUtilityTask, scripted actions. - references/python-api.md — C++↔Python naming rules, type
mapping,
get_editor_subsystem, transactions, logging, commandlet invocation.
Related skills: ue-subsystems, ue-plugins-and-modules, ue-asset-management.