Skip to content

Exec python in cpp async ​

In this example we want get SG context using SGTK in python in cpp.

Custom Interface for python Task object ​

We can create an custom interface inherited from IQueuedWork to execute custom python command asynchronously.

cpp
/**
 * require: Builtin PythonScriptPlugin module in <YOUR_MODULE>.Build.cs
*/
#pragma once

#include "CoreMinimal.h"
#include "PythonScriptTypes.h"


class IPythonAutoDeleteAsyncTask : private IQueuedWork
{
	DECLARE_DELEGATE_OneParam(FOnPythonTaskComplete, FString&);

public:
	/**
	 * Create and initialize a new instance
	 *
	 * @param pythonCommand Python command container
	 */
	IPythonAutoDeleteAsyncTask(const FPythonCommandEx& pythonCommand);
	
public:
	/**
	 * Override Methods
	 */

	/** todo Abandon behaviour implementation */
	virtual void Abandon() override {};
	virtual void DoThreadedWork() override;

	virtual void DoWork() = 0;
	virtual void Start();

public:
	/**
	 * Public fields
	 */

	FOnPythonTaskComplete OnPythonTaskComplete;
	FPythonCommandEx PythonCommand;
};

/** Inline Methods */

FORCEINLINE IPythonAutoDeleteAsyncTask::IPythonAutoDeleteAsyncTask(const FPythonCommandEx& pythonCommand):
	PythonCommand(pythonCommand)
{}

inline void IPythonAutoDeleteAsyncTask::Start()
{
	GThreadPool->AddQueuedWork(this);
}

inline void IPythonAutoDeleteAsyncTask::DoThreadedWork()
{
	DoWork();
	delete this;
}

Example of implementation ​

cpp
#pragma once
#include "Interfaces/IPythonAutoDeleteAsyncTask.h"

class FPythonTask final : public IPythonAutoDeleteAsyncTask
{
public:
	explicit FPythonTask(const FPythonCommandEx& pythonCommand)
		: IPythonAutoDeleteAsyncTask(pythonCommand)
	{
	}

	virtual void DoWork() override;
};

// .cpp
void FPythonTask::DoWork()
{
	IPythonScriptPlugin::Get()->ExecPythonCommandEx(PythonCommand);

	if (PythonCommand.CommandResult.IsEmpty())
		return;
	OnPythonTaskComplete.ExecuteIfBound(PythonCommand.CommandResult);
}

Example of usage ​

cpp

FPythonCommandEx ccc;
ccc.Command = FString("current_context.project.get('id', '')");
ccc.ExecutionMode = EPythonCommandExecutionMode::EvaluateStatement;
ISWOPythonAutoDeleteAsyncTask* c = new FSWOPythonTask(ccc);
c->OnPythonTaskComplete.BindLambda([this](const FString& result)
    {
        UE_LOG(LogTemp, Warning, TEXT("Result: %s"), *result);
    });
c->Start();

// Add this module in module.cs :
PythonScriptPlugin // builtin engine plugin

// [Warning] Due to async behaviour execution order cannot be ensure.

FPythonCommandEx setContextCommand;
setContextCommand.Command = FString("import sgtk;current_engine = sgtk.platform.current_engine();current_context = current_engine.context");
setContextCommand.ExecutionMode = EPythonCommandExecutionMode::ExecuteStatement;
ISWOPythonAutoDeleteAsyncTask* setContextTask = new FSWOPythonTask(setContextCommand);
setContextTask->Start();

FPythonCommandEx getProjectIDCommand;
getProjectIDCommand.Command = FString("current_context.project.get('id', '')");
getProjectIDCommand.ExecutionMode = EPythonCommandExecutionMode::EvaluateStatement;
ISWOPythonAutoDeleteAsyncTask* getProjectIDTask = new FSWOPythonTask(getProjectIDCommand);
getProjectIDTask->OnPythonTaskComplete.BindLambda([this](const FString& result)
    {
        UE_LOG(LogTemp, Warning, TEXT("Result: %s"), *result);
    });
getProjectIDTask->Start();