Download

Running a Python script with c++

Hey,
I am just getting to a point where my Python scripts start working quite well. Now, I want them to embed as a button or something similar, so artists can easily click something and not have to copy and paste the code.
The best solution seems to create a plugin (with a button) and try to write to the command line from c++.
I tried something like this in combination with the single button plugin template.

[FONT=courier new]#include “CommandLine.h”
#include “UnrealString.h”
FString MyCommand = “py D:\ekremer\projects\rnd\import_export_unreal\work\import_fbx_unreal_4-20.py”;
FCommandLine::Append(*MyCommand);

It compiles successfully but it does not work in the engine.
Are there some other solutions for this problem or is there a problem with my code?
Furthermore it would be interesting if you can push variables from c++ to a Python script.

Best Regards

Hey,

Try using this code in your button or menu callback to launch the script:

GEngine->Exec(NULL, TEXT(“py D:\ekremer\projects\rnd\import_export_unreal\work\import_fbx_unreal_4-20.py”));

I think that appending to the command line won’t do what you’re expecting, since the command line would already have been processed by the time your plugin code runs. (Also, the way to launch a Python script from the command line doesn’t use the py console command, it uses either ExecutePythonScript or the -run=pythonscript commandlet.)

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();
}

Hey,
Thank you for your reply! The writing to the command line works now.
Your second solution seems to be even better. But I have a problem with implementing it. I could implement the first part and the build succeeded but if I try to use this function on my plugin, it fails to build just after adding #include “PythonBridge.h”. Is it important where the code/script is placed? I just created it by right-clicking and then choosing New C++ Class. Can you explain how to create a base-class?

Is it also possible with your second approach to feed variables to python as you can do to regular c++ functions?

Best regards

What error messages do you get when you build?

I just put my .h and .cpp in the same Private folder as my plugin’s main module file. I expect that they need to be in one of your project’s include paths. I think I created them just doing right-click > New C++ Class, but I set the path so it would go next to my plugin’s other code.

It is possible to pass variables to Python. They’ll get converted automatically wherever possible to Python types so that you can get the values in your Python script.

For example, the Shotgun integration defines a function that accepts a string parameter, like:


    UFUNCTION(BlueprintImplementableEvent, Category = Python)
    void ExecuteCommand(const FString& CommandName) const;


Then, the Python override is defined in the derived class as:


    @unreal.ufunction(override=True)
    def execute_command(self, command_name):


and that command_name variable is just treated like a string in the function body.

Also, I should mention that our engineers are working on exposing menu creation to Python scripts in a simpler way that doesn’t require the C++ plugin. It’s a little overkill for what should be a relatively simple editor extension.
I don’t have a firm estimate of when that is going to be available though, so if you need something working right away the plugin is the only way to go.

Hi, i know it would probably requires you to adapt the import script but the UnrealEnginePython plugin has full support for extenders (both menus and button):

Thank you! I do not know why it didn’t work but after some manually rebuilding and placing it worked now. Thank you!

Hello,

I tried this Shotgun approach and works nice (after couple of days spent for ubuntu+python integration) until i try to use BlueprintImplementableEvent with arrays (struct with array doesn’t work as well)




// PythonBridge.h
UPythonBridge ... {
  ...
  UFUNCTION(BlueprintImplementableEvent)
  void Function(const TArray<FString>& lines);

}

// pythonbridge.py

  ..
  @unreal.ufunction(override=True)
  def function(self, lines):
    # useful code that works
    # no return


i call this python-overridden function using pythonbridge reflection trick and it works and does all the stuff it should but then it crashes inside DoCall lambda (didn’t use debug symbols but it looks like it gets read access violation when it tries to do some output for a function without output did you encounter such problem? i use a workaround with class member variables that works without crash but it looks ugly

I think I’m seeing the same issue where overridden function executes correctly but then I hit a crash in the DoCall lambda. @m.chernykh I’m curious about your workaround that avoids the crash, could you please share?


**Assertion failed:** ArrayIndex < ArrayDim [File:D:\Build\++UE4\Sync\Engine\Source\Runtime\CoreUObject\Public\UObject/UnrealType.h] [Line: 335]

Callstack:


> [Inline Frame] UE4Editor-PythonScriptPlugin.dll!UProperty::ContainerVoidPtrToValuePtrInternal(void *) Line 335 C++
[Inline Frame] UE4Editor-PythonScriptPlugin.dll!UProperty::ContainerPtrToValuePtr(void * ContainerPtr, int) Line 391 C++
**UE4Editor-PythonScriptPlugin.dll!UPythonGeneratedClass::CallPythonFunction::__l2::<lambda>() Line 1851 C++**
UE4Editor-PythonScriptPlugin.dll!UPythonGeneratedClass::CallPythonFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 1862 C++
UE4Editor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 4861 C++
UE4Editor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) Line 1480 C++
UE4Editor-ProjectAcousticsBakeUI.dll!UAcousticsPythonBridge::EstimateProcessingTime(const FString & costsheet, const int & frequency, const int & probeCount, const float & probeSpacing, const float & receiverSpacing, const float & simulationVolume, const FString & vmSize, const int & dedicatedNodes, const int & lowpriNodes) Line 37 C++
UE4Editor-ProjectAcousticsBakeUI.dll!SAcousticsEdit::OnCheckStateChanged_Landscape(ECheckBoxState state) Line 816 C++
[Inline Frame] UE4Editor-ProjectAcousticsBakeUI.dll!TMemberFunctionCaller<SAcousticsEdit,void (__cdecl SAcousticsEdit::*)(enum ECheckBoxState)>::operator()(ECheckBoxState &) Line 156 C++
[Inline Frame] UE4Editor-ProjectAcousticsBakeUI.dll!UE4Tuple_Private::TTupleImpl<TIntegerSequence<unsigned int> >::ApplyAfter(TMemberFunctionCaller<SAcousticsEdit,void (__cdecl SAcousticsEdit::*)(enum ECheckBoxState)> &&) Line 498 C++
[Inline Frame] UE4Editor-ProjectAcousticsBakeUI.dll!TBaseSPMethodDelegateInstance<0,SAcousticsEdit,0,TTypeWrapper<void> __cdecl(enum ECheckBoxState)>::Execute(ECheckBoxState) Line 279 C++
UE4Editor-ProjectAcousticsBakeUI.dll!TBaseSPMethodDelegateInstance<0,SAcousticsEdit,0,void __cdecl(enum ECheckBoxState)>::ExecuteIfSafe(ECheckBoxState <Params_0>) Line 355 C++
UE4Editor-Slate.dll!SCheckBox::OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MouseEvent) Line 191 C++
[Inline Frame] UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent::__l8::<lambda_1002768c627006711ef2f351a87ec0e7>::operator()(const FArrangedWidget &) Line 5539 C++
UE4Editor-Slate.dll!FEventRouter::Route<FReply,FEventRouter::FToLeafmostPolicy,FPointerEvent,<lambda_1002768c627006711ef2f351a87ec0e7> >(FSlateApplication * ThisApplication, FEventRouter::FToLeafmostPolicy RoutingPolicy, FPointerEvent EventCopy, const FSlateApplication::RoutePointerUpEvent::__l8::<lambda_1002768c627006711ef2f351a87ec0e7> & Lambda) Line 270 C++
UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent(FWidgetPath & WidgetsUnderPointer, FPointerEvent & PointerEvent) Line 5528 C++
UE4Editor-Slate.dll!FSlateApplication::ProcessMouseButtonUpEvent(FPointerEvent & MouseEvent) Line 6087 C++
UE4Editor-Slate.dll!FSlateApplication::OnMouseUp(const EMouseButtons::Type Button, const FVector2D CursorPos) Line 6060 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage & DeferredMessage) Line 1831 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::DeferMessage(TSharedPtr<FWindowsWindow,0> & NativeWindow, HWND__ * InHWnd, unsigned int InMessage, unsigned __int64 InWParam, __int64 InLParam, int MouseX, int MouseY, unsigned int RawInputFlags) Line 2281 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessMessage(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 929 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::AppWndProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 766 C++
user32.dll!UserCallWinProcCheckWow() Unknown
user32.dll!DispatchMessageWorker() Unknown
[Inline Frame] UE4Editor-ApplicationCore.dll!WinPumpMessages() Line 107 C++
UE4Editor-ApplicationCore.dll!FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop) Line 130 C++
UE4Editor.exe!FEngineLoop::Tick() Line 3615 C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 174 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 262 C++
[Inline Frame] UE4Editor.exe!invoke_main() Line 102 C++
UE4Editor.exe!__scrt_common_main_seh() Line 283 C++
kernel32.dll!BaseThreadInitThunk() Unknown
ntdll.dll!00007ff89c2bfbf1() Unknown

Simple functions work good. Uproperty reflection works good. So i just use class variables to pass arguments



// use bridge in cpp code
  auto* python_bridge = UPythonBridgeMaps::Get();
  python_bridge->filenames.Empty();
  // fill python_bridge->filenames
  python_bridge->DownloadByTestFilenames();

// python bridge, .h
UCLASS(Blueprintable)
class UPythonBridgeMaps : public UPythonBridge {
  GENERATED_BODY()

 public:
  UFUNCTION(BlueprintCallable, Category = Python)
  static UPythonBridgeMaps* Get() {
    return UPythonBridge::Get<UPythonBridgeMaps>();
  }

  ...
  // HACK: provide arguments using class member because proper way causes editor crash.
  // it looks like input params are treated as output params as well
  // for void(const TArray<FString>&) BlueprintImplementableEvent function
  UPROPERTY(EditAnywhere, BlueprintReadWrite)
  TArray<FString> filenames;
  UFUNCTION(BlueprintImplementableEvent, Category = Python)
  void DownloadByTestFilenames() const;
  ...
}

// python implementer, .py
...
@unreal.uclass()
class PythonBridgeMapsImplementation(unreal.PythonBridgeMaps):
  ...
  @unreal.ufunction(override=True)
  def download_by_test_filenames(self):
    loader = TestLoader()
    selected_simulator_tests = loader.get_simulator_tests_among_files(self.filenames)
    ...



Ah yes, that makes sense. Thanks for sharing!

hey @Ro-Su- in the same way we can override ufunctions there is a way to create uproperties inside a custom class? like:



@unreal.uclass()
class MyClass(Object):

      @unreal.uproperty()
      MyVar = 20



I can’t find a proper way :stuck_out_tongue:

@ZkarmaKun you use the unreal.uproperty() function, and pass the type. Like this


@unreal.uclass()
class MyPyObject(Object):
    prop_from_python = unreal.uproperty(int)


And then what do you do with that VALUE?

I have no idea how to apply it to my component

thanks