Or, there’s another snazzy way to do this, which we use in the new Shotgun integration.
Basically you define a base class in C++, with some functions that are marked as “BlueprintImplementableEvent”.
#include "Engine.h"
#include "PythonBridge.generated.h"
UCLASS(Blueprintable)
class UPythonBridge : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = Python)
static UPythonBridge* Get();
UFUNCTION(BlueprintImplementableEvent, Category = Python)
void FunctionImplementedInPython() const;
};
Then you derive a new class from it in Python, in an init_unreal.py script so it gets created at init time, and override those functions with some Python implementation.
import unreal
@unreal.uclass()
class PythonBridgeImplementation(unreal.PythonBridge):
@unreal.ufunction(override=True)
def function_implemented_in_python(self):
unreal.log_warning("Wow! This is the BEST")
You give the base class a Get function that returns its first inherited child.
#include "PythonBridge.h"
UPythonBridge* UPythonBridge::Get()
{
TArray<UClass*> PythonBridgeClasses;
GetDerivedClasses(UPythonBridge::StaticClass(), PythonBridgeClasses);
int32 NumClasses = PythonBridgeClasses.Num();
if (NumClasses > 0)
{
return Cast<UPythonBridge>(PythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
}
return nullptr;
};
Then, when you want to call the Python code, say when a menu item is clicked, you can just get the instance and call the function. Behind the scenes, the implementation in Python gets called automatically.
void FTestPythonPlugin::MenuCallback2() {
UPythonBridge* bridge = UPythonBridge::Get();
bridge->FunctionImplementedInPython();
}