You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 4071915)
#lockdown Nick.Penwarden #rb none ============================ MAJOR FEATURES & CHANGES ============================ Change 4060527 by Anousack.Kitisa Added support for importing FBX user properties as metadata on StaticMesh when importing FBX. Added support for exporting StaticMesh metadata as FBX user properties when exporting StaticMesh to FBX. #jira UESP-567 Change 4060835 by Jamie.Dale Added assign method to Python exposed structs This lets you assign the value of one struct instance onto another instance (rather than copy the pointer in Python). It also accepts anything that casts to the destination struct. Change 4060838 by Jamie.Dale Include unary operator function tooltips in doc string Change 4060843 by Jamie.Dale Fixed PythonizeValue including deprecated properties in the init function for a struct Change 4060908 by Jamie.Dale Fixed some name conflicts in generated Python glue Change 4061065 by Jamie.Dale Stubbed struct return values are now default constructed Change 4061205 by David.Hibbitts Added blueprint functions to create a message bus source, for use in projects where the Editor UI is not available or is impractical. Added a blueprint function to get available subject names for the LiveLink Client Added a RemoveSource method to ILiveLinkClient Added a GetSubjectNames method to ILiveLinkClient Fixed a crash when RequestShutdown was called on a MessageBusSource after the HeartbeatManager had already been shut down. Change 4061421 by Patrick.Boutot [AJA] Warn the user if he requested the key and the backbuffer is not setup properly. #jira UE-58614 Change 4061620 by Jamie.Dale Made the Sphinx config a template so we can inject the current engine version into it Change 4062578 by Jamie.Dale Optimized Python stub and doc gen file writes - Files are now only written when they've changed. - We now only remove files that are stale. - No requests to generate stub and doc files are queued before the first Tick. Change 4062634 by Jamie.Dale No longer export FDateTime defaults to struct __init__ as they can be non-deterministic Change 4064275 by Jamie.Dale Added callbacks for when Python is initialized and shutdown so that external modules can hook-in appropriately Change 4064613 by James.McNatton Change to initialization for FVirtualCameraWaypoint and FVirtualCameraSettingsPreset to remove non-deterministic constructors and a few resulting cleanup items Change 4064878 by Patrick.Boutot Add timecode provider plugin to capture from the Audio jack. Change 4064910 by Patrick.Boutot [AJA] Add AjaTimecodeProvider that provider the timecode from a SDI input source. Change 4067451 by Jamie.Dale Added command line options to enable all plugins, optionally excluding certain plugins Change 4067489 by Simon.Tourangeau Support for DX12 quad buffer stereo rendering Change 4068640 by Patrick.Boutot Add a state to CustomTimeStep. Show the state of the CustomTimeStep in "stat fps". Change 4069147 by Patrick.Boutot Move Mediasmith console to Engine. Renamed to TimecodeSynchronizer. Change 4071727 by Matt.Hoffman Initial pass at exposing Sequencer's Render to Movie functionality to Python. All settings that can be adjusted via the UI can be set from Python and renders can be invoked for both in-editor capture as well as new process capture. A basic API is provided which enables querying if a render in progress and canceling an in progress one. #jira UESP-541 [CL 4071957 by JeanMichel Dignard in Main branch]
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "PyFileWriter.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
|
||||
#include "PyGenUtil.h"
|
||||
|
||||
#if WITH_PYTHON
|
||||
|
||||
|
||||
FPyFileWriter::FPyFileWriter()
|
||||
: Indentation(0)
|
||||
{
|
||||
@@ -88,8 +86,7 @@ void FPyFileWriter::DecreaseIndent(const int32 InCount)
|
||||
|
||||
bool FPyFileWriter::SaveFile(const TCHAR* InFilename)
|
||||
{
|
||||
return FFileHelper::SaveStringToFile(FileContents, InFilename, FFileHelper::EEncodingOptions::ForceUTF8);
|
||||
return PyGenUtil::SaveGeneratedTextFile(InFilename, FileContents);
|
||||
}
|
||||
|
||||
|
||||
#endif // WITH_PYTHON
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "PyWrapperStruct.h"
|
||||
#include "Internationalization/BreakIterator.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "UObject/Class.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "UObject/EnumProperty.h"
|
||||
@@ -1807,17 +1808,21 @@ FString PythonizeTooltip(const FString& InTooltip, const FPythonizeTooltipContex
|
||||
return PythonizedTooltip;
|
||||
}
|
||||
|
||||
void PythonizeStructValueImpl(const UScriptStruct* InStruct, const void* InStructValue, const bool InIncludeUnrealNamespace, const bool InUseStrictTyping, FString& OutPythonDefaultValue);
|
||||
void PythonizeStructValueImpl(const UScriptStruct* InStruct, const void* InStructValue, const uint32 InFlags, FString& OutPythonDefaultValue);
|
||||
|
||||
void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const bool InIncludeUnrealNamespace, const bool InUseStrictTyping, FString& OutPythonDefaultValue)
|
||||
void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const uint32 InFlags, FString& OutPythonDefaultValue)
|
||||
{
|
||||
static const bool bIsForDocString = false;
|
||||
const TCHAR* UnrealNamespace = InIncludeUnrealNamespace ? TEXT("unreal.") : TEXT("");
|
||||
|
||||
const bool bIncludeUnrealNamespace = !!(InFlags & EPythonizeValueFlags::IncludeUnrealNamespace);
|
||||
const bool bUseStrictTyping = !!(InFlags & EPythonizeValueFlags::UseStrictTyping);
|
||||
|
||||
const TCHAR* UnrealNamespace = bIncludeUnrealNamespace ? TEXT("unreal.") : TEXT("");
|
||||
|
||||
if (InProp->ArrayDim > 1)
|
||||
{
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
? FString::Printf(TEXT("%sFixedArray.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(InProp, InIncludeUnrealNamespace, bIsForDocString))
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sFixedArray.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(InProp, bIncludeUnrealNamespace, bIsForDocString))
|
||||
: TEXT("[");
|
||||
}
|
||||
for (int32 ArrIndex = 0; ArrIndex < InProp->ArrayDim; ++ArrIndex)
|
||||
@@ -1855,7 +1860,7 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
else if (const UNameProperty* NameProp = Cast<const UNameProperty>(InProp))
|
||||
{
|
||||
const FString NameStrValue = NameProp->GetPropertyValue(PropArrValue).ToString();
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sName(\"%s\")"), UnrealNamespace, *NameStrValue)
|
||||
: FString::Printf(TEXT("\"%s\""), *NameStrValue);
|
||||
}
|
||||
@@ -1863,7 +1868,7 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
{
|
||||
const FString* TextStrValue = FTextInspector::GetSourceString(TextProp->GetPropertyValue(PropArrValue));
|
||||
check(TextStrValue);
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sText(\"%s\")"), UnrealNamespace, **TextStrValue)
|
||||
: FString::Printf(TEXT("\"%s\""), **TextStrValue);
|
||||
}
|
||||
@@ -1877,7 +1882,7 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
}
|
||||
else if (const UStructProperty* StructProp = Cast<const UStructProperty>(InProp))
|
||||
{
|
||||
PythonizeStructValueImpl(StructProp->Struct, PropArrValue, InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PythonizeStructValueImpl(StructProp->Struct, PropArrValue, InFlags, OutPythonDefaultValue);
|
||||
}
|
||||
else if (const UDelegateProperty* DelegateProp = Cast<const UDelegateProperty>(InProp))
|
||||
{
|
||||
@@ -1889,8 +1894,8 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
}
|
||||
else if (const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(InProp))
|
||||
{
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
? FString::Printf(TEXT("%sArray.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(ArrayProperty->Inner, InIncludeUnrealNamespace, bIsForDocString))
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sArray.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(ArrayProperty->Inner, bIncludeUnrealNamespace, bIsForDocString))
|
||||
: TEXT("[");
|
||||
{
|
||||
FScriptArrayHelper ScriptArrayHelper(ArrayProperty, PropArrValue);
|
||||
@@ -1901,17 +1906,17 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
{
|
||||
OutPythonDefaultValue += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(ArrayProperty->Inner, ScriptArrayHelper.GetRawPtr(ElementIndex), InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PythonizeValueImpl(ArrayProperty->Inner, ScriptArrayHelper.GetRawPtr(ElementIndex), InFlags, OutPythonDefaultValue);
|
||||
}
|
||||
}
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? TEXT("])")
|
||||
: TEXT("]");
|
||||
}
|
||||
else if (const USetProperty* SetProperty = Cast<const USetProperty>(InProp))
|
||||
{
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
? FString::Printf(TEXT("%sSet.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(SetProperty->ElementProp, InIncludeUnrealNamespace, bIsForDocString))
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sSet.cast(%s, ["), UnrealNamespace, *GetPropertyTypePythonName(SetProperty->ElementProp, bIncludeUnrealNamespace, bIsForDocString))
|
||||
: TEXT("[");
|
||||
{
|
||||
FScriptSetHelper ScriptSetHelper(SetProperty, PropArrValue);
|
||||
@@ -1923,18 +1928,18 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
{
|
||||
OutPythonDefaultValue += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(ScriptSetHelper.GetElementProperty(), ScriptSetHelper.GetElementPtr(SparseElementIndex), InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PythonizeValueImpl(ScriptSetHelper.GetElementProperty(), ScriptSetHelper.GetElementPtr(SparseElementIndex), InFlags, OutPythonDefaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? TEXT("])")
|
||||
: TEXT("]");
|
||||
}
|
||||
else if (const UMapProperty* MapProperty = Cast<const UMapProperty>(InProp))
|
||||
{
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
? FString::Printf(TEXT("%sMap.cast(%s, %s, {"), UnrealNamespace, *GetPropertyTypePythonName(MapProperty->KeyProp, InIncludeUnrealNamespace, bIsForDocString), *GetPropertyTypePythonName(MapProperty->ValueProp, InIncludeUnrealNamespace, bIsForDocString))
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%sMap.cast(%s, %s, {"), UnrealNamespace, *GetPropertyTypePythonName(MapProperty->KeyProp, bIncludeUnrealNamespace, bIsForDocString), *GetPropertyTypePythonName(MapProperty->ValueProp, bIncludeUnrealNamespace, bIsForDocString))
|
||||
: TEXT("{");
|
||||
{
|
||||
FScriptMapHelper ScriptMapHelper(MapProperty, PropArrValue);
|
||||
@@ -1946,13 +1951,13 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
{
|
||||
OutPythonDefaultValue += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(ScriptMapHelper.GetKeyProperty(), ScriptMapHelper.GetKeyPtr(SparseElementIndex), InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PythonizeValueImpl(ScriptMapHelper.GetKeyProperty(), ScriptMapHelper.GetKeyPtr(SparseElementIndex), InFlags, OutPythonDefaultValue);
|
||||
OutPythonDefaultValue += TEXT(": ");
|
||||
PythonizeValueImpl(ScriptMapHelper.GetValueProperty(), ScriptMapHelper.GetValuePtr(SparseElementIndex), InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PythonizeValueImpl(ScriptMapHelper.GetValueProperty(), ScriptMapHelper.GetValuePtr(SparseElementIndex), InFlags, OutPythonDefaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? TEXT("})")
|
||||
: TEXT("}");
|
||||
}
|
||||
@@ -1964,15 +1969,20 @@ void PythonizeValueImpl(const UProperty* InProp, const void* InPropValue, const
|
||||
}
|
||||
if (InProp->ArrayDim > 1)
|
||||
{
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? TEXT("])")
|
||||
: TEXT("]");
|
||||
}
|
||||
}
|
||||
|
||||
void PythonizeStructValueImpl(const UScriptStruct* InStruct, const void* InStructValue, const bool InIncludeUnrealNamespace, const bool InUseStrictTyping, FString& OutPythonDefaultValue)
|
||||
void PythonizeStructValueImpl(const UScriptStruct* InStruct, const void* InStructValue, const uint32 InFlags, FString& OutPythonDefaultValue)
|
||||
{
|
||||
const TCHAR* UnrealNamespace = InIncludeUnrealNamespace ? TEXT("unreal.") : TEXT("");
|
||||
const bool bIncludeUnrealNamespace = !!(InFlags & EPythonizeValueFlags::IncludeUnrealNamespace);
|
||||
const bool bUseStrictTyping = !!(InFlags & EPythonizeValueFlags::UseStrictTyping);
|
||||
const bool bDefaultConstructStructs = !!(InFlags & EPythonizeValueFlags::DefaultConstructStructs);
|
||||
const bool bDefaultConstructDateTime = !!(InFlags & EPythonizeValueFlags::DefaultConstructDateTime);
|
||||
|
||||
const TCHAR* UnrealNamespace = bIncludeUnrealNamespace ? TEXT("unreal.") : TEXT("");
|
||||
|
||||
// Note: We deliberately don't use any FPyWrapperStruct functionality here as this function may be called as part of generating the wrapped type
|
||||
|
||||
@@ -1986,88 +1996,91 @@ void PythonizeStructValueImpl(const UScriptStruct* InStruct, const void* InStruc
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const UFunction* MakeFunc = FindMakeBreakFunction(HasNativeMakeMetaDataKey);
|
||||
const UFunction* BreakFunc = FindMakeBreakFunction(HasNativeBreakMetaDataKey);
|
||||
|
||||
// If the struct has a make function, we assume the output of the break function matches the input of the make function
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? FString::Printf(TEXT("%s%s("), UnrealNamespace, *GetStructPythonName(InStruct))
|
||||
: TEXT("[");
|
||||
if (MakeFunc && BreakFunc)
|
||||
if (!bDefaultConstructStructs && (!bDefaultConstructDateTime || !InStruct->IsChildOf(TBaseStructure<FDateTime>::Get())))
|
||||
{
|
||||
UClass* Class = BreakFunc->GetOwnerClass();
|
||||
UObject* Obj = Class->GetDefaultObject();
|
||||
const UFunction* MakeFunc = FindMakeBreakFunction(HasNativeMakeMetaDataKey);
|
||||
const UFunction* BreakFunc = FindMakeBreakFunction(HasNativeBreakMetaDataKey);
|
||||
|
||||
FGeneratedWrappedFunction BreakFuncDef;
|
||||
BreakFuncDef.SetFunction(BreakFunc, FGeneratedWrappedFunction::SFF_ExtractParameters);
|
||||
|
||||
// Python can only support 255 parameters, so if we have more than that for this struct just use the default constructor
|
||||
if (BreakFuncDef.OutputParams.Num() <= 255)
|
||||
if (MakeFunc && BreakFunc)
|
||||
{
|
||||
// Call the break function using the instance we were given
|
||||
FStructOnScope FuncParams(BreakFuncDef.Func);
|
||||
if (BreakFuncDef.InputParams.Num() == 1 && Cast<UStructProperty>(BreakFuncDef.InputParams[0].ParamProp) && InStruct->IsChildOf(CastChecked<UStructProperty>(BreakFuncDef.InputParams[0].ParamProp)->Struct))
|
||||
{
|
||||
// Copy the given instance as the 'self' argument
|
||||
const FGeneratedWrappedMethodParameter& SelfParam = BreakFuncDef.InputParams[0];
|
||||
void* SelfArgInstance = SelfParam.ParamProp->ContainerPtrToValuePtr<void>(FuncParams.GetStructMemory());
|
||||
CastChecked<UStructProperty>(SelfParam.ParamProp)->Struct->CopyScriptStruct(SelfArgInstance, InStructValue);
|
||||
}
|
||||
PyUtil::InvokeFunctionCall(Obj, BreakFuncDef.Func, FuncParams.GetStructMemory(), TEXT("pythonize default struct value"));
|
||||
PyErr_Clear(); // Clear any errors in case InvokeFunctionCall failed
|
||||
UClass* Class = BreakFunc->GetOwnerClass();
|
||||
UObject* Obj = Class->GetDefaultObject();
|
||||
|
||||
// Extract the output argument values as defaults for the struct
|
||||
for (int32 OuputParamIndex = 0; OuputParamIndex < BreakFuncDef.OutputParams.Num(); ++OuputParamIndex)
|
||||
FGeneratedWrappedFunction BreakFuncDef;
|
||||
BreakFuncDef.SetFunction(BreakFunc, FGeneratedWrappedFunction::SFF_ExtractParameters);
|
||||
|
||||
// Python can only support 255 parameters, so if we have more than that for this struct just use the default constructor
|
||||
if (BreakFuncDef.OutputParams.Num() <= 255)
|
||||
{
|
||||
const FGeneratedWrappedMethodParameter& OutputParam = BreakFuncDef.OutputParams[OuputParamIndex];
|
||||
if (OuputParamIndex > 0)
|
||||
// Call the break function using the instance we were given
|
||||
FStructOnScope FuncParams(BreakFuncDef.Func);
|
||||
if (BreakFuncDef.InputParams.Num() == 1 && Cast<UStructProperty>(BreakFuncDef.InputParams[0].ParamProp) && InStruct->IsChildOf(CastChecked<UStructProperty>(BreakFuncDef.InputParams[0].ParamProp)->Struct))
|
||||
{
|
||||
OutPythonDefaultValue += TEXT(", ");
|
||||
// Copy the given instance as the 'self' argument
|
||||
const FGeneratedWrappedMethodParameter& SelfParam = BreakFuncDef.InputParams[0];
|
||||
void* SelfArgInstance = SelfParam.ParamProp->ContainerPtrToValuePtr<void>(FuncParams.GetStructMemory());
|
||||
CastChecked<UStructProperty>(SelfParam.ParamProp)->Struct->CopyScriptStruct(SelfArgInstance, InStructValue);
|
||||
}
|
||||
PythonizeValueImpl(OutputParam.ParamProp, OutputParam.ParamProp->ContainerPtrToValuePtr<void>(FuncParams.GetStructMemory()), InIncludeUnrealNamespace, InUseStrictTyping, OutPythonDefaultValue);
|
||||
PyUtil::InvokeFunctionCall(Obj, BreakFuncDef.Func, FuncParams.GetStructMemory(), TEXT("pythonize default struct value"));
|
||||
PyErr_Clear(); // Clear any errors in case InvokeFunctionCall failed
|
||||
|
||||
// Extract the output argument values as defaults for the struct
|
||||
for (int32 OuputParamIndex = 0; OuputParamIndex < BreakFuncDef.OutputParams.Num(); ++OuputParamIndex)
|
||||
{
|
||||
const FGeneratedWrappedMethodParameter& OutputParam = BreakFuncDef.OutputParams[OuputParamIndex];
|
||||
if (OuputParamIndex > 0)
|
||||
{
|
||||
OutPythonDefaultValue += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(OutputParam.ParamProp, OutputParam.ParamProp->ContainerPtrToValuePtr<void>(FuncParams.GetStructMemory()), InFlags, OutPythonDefaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 ExportedPropertyCount = 0;
|
||||
FString StructInitParamsStr;
|
||||
for (TFieldIterator<const UProperty> PropIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt)
|
||||
{
|
||||
const UProperty* Prop = *PropIt;
|
||||
if (ShouldExportProperty(Prop) && !IsDeprecatedProperty(Prop))
|
||||
{
|
||||
if (ExportedPropertyCount++ > 0)
|
||||
{
|
||||
StructInitParamsStr += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(Prop, Prop->ContainerPtrToValuePtr<void>(InStructValue), InFlags, StructInitParamsStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Python can only support 255 parameters, so if we have more than that for this struct just use the default constructor
|
||||
if (ExportedPropertyCount <= 255)
|
||||
{
|
||||
OutPythonDefaultValue += StructInitParamsStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 ExportedPropertyCount = 0;
|
||||
FString StructInitParamsStr;
|
||||
for (TFieldIterator<const UProperty> PropIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt)
|
||||
{
|
||||
const UProperty* Prop = *PropIt;
|
||||
if (ShouldExportProperty(Prop))
|
||||
{
|
||||
if (ExportedPropertyCount++ > 0)
|
||||
{
|
||||
StructInitParamsStr += TEXT(", ");
|
||||
}
|
||||
PythonizeValueImpl(Prop, Prop->ContainerPtrToValuePtr<void>(InStructValue), InIncludeUnrealNamespace, InUseStrictTyping, StructInitParamsStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Python can only support 255 parameters, so if we have more than that for this struct just use the default constructor
|
||||
if (ExportedPropertyCount <= 255)
|
||||
{
|
||||
OutPythonDefaultValue += StructInitParamsStr;
|
||||
}
|
||||
}
|
||||
OutPythonDefaultValue += InUseStrictTyping
|
||||
OutPythonDefaultValue += bUseStrictTyping
|
||||
? TEXT(")")
|
||||
: TEXT("]");
|
||||
}
|
||||
|
||||
FString PythonizeValue(const UProperty* InProp, const void* InPropValue, const bool InIncludeUnrealNamespace, const bool InUseStrictTyping)
|
||||
FString PythonizeValue(const UProperty* InProp, const void* InPropValue, const uint32 InFlags)
|
||||
{
|
||||
FString PythonValue;
|
||||
PythonizeValueImpl(InProp, InPropValue, InIncludeUnrealNamespace, InUseStrictTyping, PythonValue);
|
||||
PythonizeValueImpl(InProp, InPropValue, InFlags, PythonValue);
|
||||
return PythonValue;
|
||||
}
|
||||
|
||||
FString PythonizeDefaultValue(const UProperty* InProp, const FString& InDefaultValue, const bool InIncludeUnrealNamespace, const bool InUseStrictTyping)
|
||||
FString PythonizeDefaultValue(const UProperty* InProp, const FString& InDefaultValue, const uint32 InFlags)
|
||||
{
|
||||
PyUtil::FPropValueOnScope PropValue(InProp);
|
||||
PyUtil::ImportDefaultValue(InProp, PropValue.GetValue(), InDefaultValue);
|
||||
return PythonizeValue(InProp, PropValue.GetValue(), InIncludeUnrealNamespace, InUseStrictTyping);
|
||||
return PythonizeValue(InProp, PropValue.GetValue(), InFlags);
|
||||
}
|
||||
|
||||
FString GetFieldModule(const UField* InField)
|
||||
@@ -2581,6 +2594,34 @@ void AppendCppSourceInformationDocString(const UField* InOwnerType, FString& Out
|
||||
OutStr += LINE_TERMINATOR;
|
||||
}
|
||||
|
||||
bool SaveGeneratedTextFile(const TCHAR* InFilename, const FString& InFileContents, const bool InForceWrite)
|
||||
{
|
||||
bool bWriteFile = InForceWrite;
|
||||
|
||||
if (!bWriteFile)
|
||||
{
|
||||
FString CurrentFileContents;
|
||||
if (FFileHelper::LoadFileToString(CurrentFileContents, InFilename))
|
||||
{
|
||||
// Only write the file if the contents differ
|
||||
bWriteFile = !InFileContents.Equals(CurrentFileContents, ESearchCase::CaseSensitive);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to load the file, assume it's missing so write it
|
||||
bWriteFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bWriteFile)
|
||||
{
|
||||
return FFileHelper::SaveStringToFile(InFileContents, InFilename, FFileHelper::EEncodingOptions::ForceUTF8);
|
||||
}
|
||||
|
||||
// File up-to-date, return success
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // WITH_PYTHON
|
||||
|
||||
@@ -840,6 +840,19 @@ namespace PyGenUtil
|
||||
Upper,
|
||||
};
|
||||
|
||||
/** Flags controlling the behavior of PythonizeValue */
|
||||
namespace EPythonizeValueFlags
|
||||
{
|
||||
enum EFlags
|
||||
{
|
||||
None = 0,
|
||||
IncludeUnrealNamespace = 1<<0,
|
||||
UseStrictTyping = 1<<1,
|
||||
DefaultConstructStructs = 1<<2,
|
||||
DefaultConstructDateTime = 1<<3,
|
||||
};
|
||||
};
|
||||
|
||||
/** Context information passed to PythonizeTooltip */
|
||||
struct FPythonizeTooltipContext
|
||||
{
|
||||
@@ -958,10 +971,10 @@ namespace PyGenUtil
|
||||
FString PythonizeTooltip(const FString& InTooltip, const FPythonizeTooltipContext& InContext = FPythonizeTooltipContext());
|
||||
|
||||
/** Given a property and its value, convert it into something that could be used in a Python script */
|
||||
FString PythonizeValue(const UProperty* InProp, const void* InPropValue, const bool InIncludeUnrealNamespace = false, const bool InUseStrictTyping = false);
|
||||
FString PythonizeValue(const UProperty* InProp, const void* InPropValue, const uint32 InFlags = EPythonizeValueFlags::None);
|
||||
|
||||
/** Given a property and its default value, convert it into something that could be used in a Python script */
|
||||
FString PythonizeDefaultValue(const UProperty* InProp, const FString& InDefaultValue, const bool InIncludeUnrealNamespace = false, const bool InUseStrictTyping = false);
|
||||
FString PythonizeDefaultValue(const UProperty* InProp, const FString& InDefaultValue, const uint32 InFlags = EPythonizeValueFlags::None);
|
||||
|
||||
/** Get the native module the given field belongs to */
|
||||
FString GetFieldModule(const UField* InField);
|
||||
@@ -1040,6 +1053,9 @@ namespace PyGenUtil
|
||||
|
||||
/** Append the doc string for the C++ source information of the given type to the given string */
|
||||
void AppendCppSourceInformationDocString(const UField* InOwnerType, FString& OutStr);
|
||||
|
||||
/** Save a generated text file to disk as UTF-8 (only writes the file if the contents differs, unless forced) */
|
||||
bool SaveGeneratedTextFile(const TCHAR* InFilename, const FString& InFileContents, const bool InForceWrite = false);
|
||||
}
|
||||
|
||||
#endif // WITH_PYTHON
|
||||
|
||||
@@ -2,13 +2,44 @@
|
||||
|
||||
#include "PyOnlineDocsWriter.h"
|
||||
#include "PyUtil.h"
|
||||
#include "PyGenUtil.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Logging/MessageLog.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
#if WITH_PYTHON
|
||||
|
||||
class FPyDeleteUnreferencedFilesVisitor : public IPlatformFile::FDirectoryVisitor
|
||||
{
|
||||
public:
|
||||
void ReferenceFile(const FString& InFilename)
|
||||
{
|
||||
ReferencedFiles.Add(FPaths::ConvertRelativePathToFull(InFilename));
|
||||
}
|
||||
|
||||
bool IsReferencedFile(const FString& InFilename) const
|
||||
{
|
||||
return ReferencedFiles.Contains(FPaths::ConvertRelativePathToFull(InFilename));
|
||||
}
|
||||
|
||||
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
|
||||
{
|
||||
if (!bIsDirectory && !IsReferencedFile(FilenameOrDirectory))
|
||||
{
|
||||
IFileManager::Get().Delete(FilenameOrDirectory, false, true, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/** Set of referenced files (absolute paths) */
|
||||
TSet<FString> ReferencedFiles;
|
||||
};
|
||||
|
||||
|
||||
void FPyOnlineDocsModule::AccumulateFunction(const TCHAR* InFunctionName)
|
||||
{
|
||||
FunctionNames.Add(InFunctionName);
|
||||
@@ -46,6 +77,29 @@ FString FPyOnlineDocsWriter::GetTemplatePath() const
|
||||
return GetSourcePath() / TEXT("_templates");
|
||||
}
|
||||
|
||||
void FPyOnlineDocsWriter::GenerateConfigFile()
|
||||
{
|
||||
// Load up conf.py template
|
||||
const FString ConfigTemplatePath = GetTemplatePath() / TEXT("conf.py");
|
||||
FString ConfigTemplate;
|
||||
if (!FFileHelper::LoadFileToString(ConfigTemplate, *ConfigTemplatePath))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' failed to load!"), *ConfigTemplatePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace {{Version}} with the actual version number
|
||||
FString ConfigText = ConfigTemplate;
|
||||
ConfigText.ReplaceInline(TEXT("{{Version}}"), VERSION_STRINGIFY(ENGINE_MAJOR_VERSION) TEXT(".") VERSION_STRINGIFY(ENGINE_MINOR_VERSION), ESearchCase::CaseSensitive);
|
||||
|
||||
// Save out config file
|
||||
const FString ConfigPath = GetSourcePath() / TEXT("conf.py");
|
||||
if (!PyGenUtil::SaveGeneratedTextFile(*ConfigPath, ConfigText))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to write!"), *ConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
void FPyOnlineDocsWriter::GenerateIndexFile()
|
||||
{
|
||||
// Load up index.rst template
|
||||
@@ -53,7 +107,7 @@ void FPyOnlineDocsWriter::GenerateIndexFile()
|
||||
FString IndexTemplate;
|
||||
if (!FFileHelper::LoadFileToString(IndexTemplate, *IndexTemplatePath))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' was not found!"), *IndexTemplatePath);
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' failed to load!"), *IndexTemplatePath);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,25 +163,28 @@ void FPyOnlineDocsWriter::GenerateIndexFile()
|
||||
|
||||
// Save out index file
|
||||
const FString IndexPath = GetSourcePath() / TEXT("index.rst");
|
||||
FFileHelper::SaveStringToFile(IndexText, *IndexPath, FFileHelper::EEncodingOptions::ForceUTF8);
|
||||
if (!PyGenUtil::SaveGeneratedTextFile(*IndexPath, IndexText))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to write!"), *IndexPath);
|
||||
}
|
||||
}
|
||||
|
||||
void FPyOnlineDocsWriter::GenerateModuleFiles()
|
||||
{
|
||||
// Erase any previous files
|
||||
const FString ModuleSourcePath = GetSourcePath() / TEXT("module");
|
||||
IFileManager::Get().DeleteDirectory(*ModuleSourcePath, false, true);
|
||||
|
||||
// Load up Module.rst template
|
||||
const FString ModuleTemplatePath = GetTemplatePath() / TEXT("Module.rst");
|
||||
FString ModuleTemplate;
|
||||
if (!FFileHelper::LoadFileToString(ModuleTemplate, *ModuleTemplatePath))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' was not found!"), *ModuleTemplatePath);
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' failed to load!"), *ModuleTemplatePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep track of referenced files so we can delete any stale ones
|
||||
FPyDeleteUnreferencedFilesVisitor DeleteUnreferencedFilesVisitor;
|
||||
|
||||
// Create page for each module
|
||||
const FString ModuleSourcePath = GetSourcePath() / TEXT("module");
|
||||
for (const TSharedRef<FPyOnlineDocsModule>& Module : Modules)
|
||||
{
|
||||
FString ModuleFunctions;
|
||||
@@ -144,26 +201,36 @@ void FPyOnlineDocsWriter::GenerateModuleFiles()
|
||||
|
||||
// Write out module file
|
||||
const FString ModulePath = ModuleSourcePath / Module->Name + TEXT(".rst");
|
||||
FFileHelper::SaveStringToFile(ModuleText, *ModulePath, FFileHelper::EEncodingOptions::ForceUTF8);
|
||||
if (PyGenUtil::SaveGeneratedTextFile(*ModulePath, ModuleText))
|
||||
{
|
||||
DeleteUnreferencedFilesVisitor.ReferenceFile(ModulePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to write!"), *ModulePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any stale files
|
||||
IFileManager::Get().IterateDirectory(*ModuleSourcePath, DeleteUnreferencedFilesVisitor);
|
||||
}
|
||||
|
||||
void FPyOnlineDocsWriter::GenerateClassFiles()
|
||||
{
|
||||
// Erase any previous files
|
||||
const FString ClassSourcePath = GetSourcePath() / TEXT("class");
|
||||
IFileManager::Get().DeleteDirectory(*ClassSourcePath, false, true);
|
||||
|
||||
// Load up Class.rst template
|
||||
const FString ClassTemplatePath = GetTemplatePath() / TEXT("Class.rst");
|
||||
FString ClassTemplate;
|
||||
if (!FFileHelper::LoadFileToString(ClassTemplate, *ClassTemplatePath))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' was not found!"), *ClassTemplatePath);
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation template file '%s' failed to load!"), *ClassTemplatePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep track of referenced files so we can delete any stale ones
|
||||
FPyDeleteUnreferencedFilesVisitor DeleteUnreferencedFilesVisitor;
|
||||
|
||||
// Create page for each class in each section
|
||||
const FString ClassSourcePath = GetSourcePath() / TEXT("class");
|
||||
for (const TSharedRef<FPyOnlineDocsSection>& Section : Sections)
|
||||
{
|
||||
for (const FString& TypeName : Section->TypeNames)
|
||||
@@ -174,9 +241,19 @@ void FPyOnlineDocsWriter::GenerateClassFiles()
|
||||
|
||||
// Write out class file
|
||||
const FString ClassPath = ClassSourcePath / TypeName + TEXT(".rst");
|
||||
FFileHelper::SaveStringToFile(ClassText, *ClassPath, FFileHelper::EEncodingOptions::ForceUTF8);
|
||||
if (PyGenUtil::SaveGeneratedTextFile(*ClassPath, ClassText))
|
||||
{
|
||||
DeleteUnreferencedFilesVisitor.ReferenceFile(*ClassPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to write!"), *ClassPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any stale files
|
||||
IFileManager::Get().IterateDirectory(*ClassSourcePath, DeleteUnreferencedFilesVisitor);
|
||||
}
|
||||
|
||||
void FPyOnlineDocsWriter::GenerateFiles(const FString& InPythonStubPath)
|
||||
@@ -184,9 +261,24 @@ void FPyOnlineDocsWriter::GenerateFiles(const FString& InPythonStubPath)
|
||||
UE_LOG(LogPython, Log, TEXT("Generating Python API online docs used by Sphinx to generate static HTML..."));
|
||||
|
||||
// Copy generated unreal module stub file to PythonScriptPlugin/SphinxDocs/modules
|
||||
const FString MoldulesPath = GetSphinxDocsPath() / TEXT("modules") / FPaths::GetCleanFilename(InPythonStubPath);
|
||||
IFileManager::Get().Copy(*MoldulesPath, *InPythonStubPath);
|
||||
{
|
||||
const FString PythonStubDestPath = GetSphinxDocsPath() / TEXT("modules") / FPaths::GetCleanFilename(InPythonStubPath);
|
||||
|
||||
FString SourceFileContents;
|
||||
if (FFileHelper::LoadFileToString(SourceFileContents, *InPythonStubPath))
|
||||
{
|
||||
if (!PyGenUtil::SaveGeneratedTextFile(*PythonStubDestPath, SourceFileContents))
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to write!"), *PythonStubDestPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogPython, Warning, TEXT("Documentation generation file '%s' failed to load!"), *InPythonStubPath);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateConfigFile();
|
||||
GenerateIndexFile();
|
||||
GenerateModuleFiles();
|
||||
GenerateClassFiles();
|
||||
|
||||
@@ -79,6 +79,9 @@ public:
|
||||
/** Get the directory for the Sphinx template files. */
|
||||
FString GetTemplatePath() const;
|
||||
|
||||
/** Create Python config for Sphinx based on template. */
|
||||
void GenerateConfigFile();
|
||||
|
||||
/** Create index reStructuredText file for Sphinx based on template. */
|
||||
void GenerateIndexFile();
|
||||
|
||||
|
||||
@@ -954,6 +954,35 @@ PyTypeObject InitializePyWrapperStructType()
|
||||
return (PyObject*)FPyWrapperStructFactory::Get().CreateInstance(InSelf->ScriptStruct, InSelf->StructInstance, FPyWrapperOwnerContext(), EPyConversionMethod::Copy);
|
||||
}
|
||||
|
||||
static PyObject* Assign(FPyWrapperStruct* InSelf, PyObject* InArgs)
|
||||
{
|
||||
if (!FPyWrapperStruct::ValidateInternalState(InSelf))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* PyObj = nullptr;
|
||||
if (!PyArg_ParseTuple(InArgs, "O:assign", &PyObj))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
check(PyObj);
|
||||
|
||||
FPyWrapperStructPtr PyStruct = FPyWrapperStructPtr::StealReference(FPyWrapperStruct::CastPyObject(PyObj, Py_TYPE(InSelf)));
|
||||
if (!PyStruct)
|
||||
{
|
||||
PyUtil::SetPythonError(PyExc_TypeError, InSelf, *FString::Printf(TEXT("Cannot cast type '%s' to '%s'"), *PyUtil::GetFriendlyTypename(PyObj), *PyUtil::GetFriendlyTypename(InSelf)));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyStruct && ensureAlways(PyStruct->ScriptStruct->IsChildOf(InSelf->ScriptStruct)))
|
||||
{
|
||||
InSelf->ScriptStruct->CopyScriptStruct(InSelf->StructInstance, PyStruct->StructInstance);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* ToTuple(FPyWrapperStruct* InSelf)
|
||||
{
|
||||
return FPyWrapperStruct::BreakStruct(InSelf);
|
||||
@@ -1079,6 +1108,7 @@ PyTypeObject InitializePyWrapperStructType()
|
||||
{ "static_struct", PyCFunctionCast(&FMethods::StaticStruct), METH_NOARGS | METH_CLASS, "X.static_struct() -> Struct -- get the Unreal struct of this type" },
|
||||
{ "__copy__", PyCFunctionCast(&FMethods::Copy), METH_NOARGS, "x.__copy__() -> struct -- copy this Unreal struct" },
|
||||
{ "copy", PyCFunctionCast(&FMethods::Copy), METH_NOARGS, "x.copy() -> struct -- copy this Unreal struct" },
|
||||
{ "assign", PyCFunctionCast(&FMethods::Assign), METH_VARARGS, "x.assign(object) -> None -- assign the value of this Unreal struct to value of the given object" },
|
||||
{ "to_tuple", PyCFunctionCast(&FMethods::ToTuple), METH_NOARGS, "x.to_tuple() -> tuple -- break this Unreal struct into a tuple of its properties" },
|
||||
{ "get_editor_property", PyCFunctionCast(&FMethods::GetEditorProperty), METH_VARARGS | METH_KEYWORDS, "x.get_editor_property(name) -> object -- get the value of any property visible to the editor" },
|
||||
{ "set_editor_property", PyCFunctionCast(&FMethods::SetEditorProperty), METH_VARARGS | METH_KEYWORDS, "x.set_editor_property(name, value) -> None -- set the value of any property visible to the editor, ensuring that the pre/post change notifications are called" },
|
||||
|
||||
@@ -1936,8 +1936,6 @@ void FPyWrapperTypeRegistry::GatherWrappedTypesForPropertyReferences(const UProp
|
||||
|
||||
void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedTypes() const
|
||||
{
|
||||
// todo: delete the PythonStub directory?
|
||||
|
||||
FPyFileWriter PythonScript;
|
||||
|
||||
TUniquePtr<FPyOnlineDocsWriter> OnlineDocsWriter;
|
||||
@@ -2122,8 +2120,8 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
return TEXT("None");
|
||||
}
|
||||
|
||||
// We use strict typing for return values to aid auto-complete
|
||||
static const bool bUseStrictTyping = true;
|
||||
// We use strict typing for return values to aid auto-complete (we also only care about the type and not the value, so structs can be default constructed)
|
||||
static const uint32 PythonizeValueFlags = PyGenUtil::EPythonizeValueFlags::UseStrictTyping | PyGenUtil::EPythonizeValueFlags::DefaultConstructStructs;
|
||||
|
||||
// If we have multiple return values and the main return value is a bool, skip it (to mimic PyGenUtils::PackReturnValues)
|
||||
int32 ReturnPropIndex = 0;
|
||||
@@ -2137,7 +2135,7 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
if (NumPropertiesToPack == 1)
|
||||
{
|
||||
const PyGenUtil::FGeneratedWrappedMethodParameter& ReturnParam = InOutputParams[ReturnPropIndex];
|
||||
return PyGenUtil::PythonizeValue(ReturnParam.ParamProp, ReturnParam.ParamProp->ContainerPtrToValuePtr<void>(InBaseParamsAddr), /*IncludeUnrealNamespace*/false, bUseStrictTyping);
|
||||
return PyGenUtil::PythonizeValue(ReturnParam.ParamProp, ReturnParam.ParamProp->ContainerPtrToValuePtr<void>(InBaseParamsAddr), PythonizeValueFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2149,7 +2147,7 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
FunctionReturnStr += TEXT(", ");
|
||||
}
|
||||
const PyGenUtil::FGeneratedWrappedMethodParameter& ReturnParam = InOutputParams[ReturnPropIndex];
|
||||
FunctionReturnStr += PyGenUtil::PythonizeValue(ReturnParam.ParamProp, ReturnParam.ParamProp->ContainerPtrToValuePtr<void>(InBaseParamsAddr), /*IncludeUnrealNamespace*/false, bUseStrictTyping);
|
||||
FunctionReturnStr += PyGenUtil::PythonizeValue(ReturnParam.ParamProp, ReturnParam.ParamProp->ContainerPtrToValuePtr<void>(InBaseParamsAddr), PythonizeValueFlags);
|
||||
}
|
||||
FunctionReturnStr += TEXT(")");
|
||||
return FunctionReturnStr;
|
||||
@@ -2266,15 +2264,35 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
|
||||
auto ExportGeneratedGetSet = [&ExportGetSet](const PyGenUtil::FGeneratedWrappedGetSet& InGetSet)
|
||||
{
|
||||
// We use strict typing for return values to aid auto-complete
|
||||
static const bool bUseStrictTyping = true;
|
||||
const FString GetReturnValue = PyGenUtil::PythonizeDefaultValue(InGetSet.Prop.Prop, FString(), /*IncludeUnrealNamespace*/false, bUseStrictTyping);
|
||||
// We use strict typing for return values to aid auto-complete (we also only care about the type and not the value, so structs can be default constructed)
|
||||
static const uint32 PythonizeValueFlags = PyGenUtil::EPythonizeValueFlags::UseStrictTyping | PyGenUtil::EPythonizeValueFlags::DefaultConstructStructs;
|
||||
const FString GetReturnValue = PyGenUtil::PythonizeDefaultValue(InGetSet.Prop.Prop, FString(), PythonizeValueFlags);
|
||||
const bool bIsReadOnly = InGetSet.Prop.Prop->HasAnyPropertyFlags(CPF_BlueprintReadOnly | CPF_EditConst);
|
||||
ExportGetSet(UTF8_TO_TCHAR(InGetSet.GetSetName.GetData()), UTF8_TO_TCHAR(InGetSet.GetSetDoc.GetData()), *GetReturnValue, bIsReadOnly);
|
||||
};
|
||||
|
||||
auto ExportGeneratedOperator = [&OutPythonScript](const PyGenUtil::FGeneratedWrappedOperatorStack& InOpStack, const PyGenUtil::FGeneratedWrappedOperatorSignature& InOpSignature)
|
||||
{
|
||||
auto AppendFunctionTooltip = [](const UFunction* InFunc, const TCHAR* InIdentation, FString& OutStr)
|
||||
{
|
||||
const FString FuncTooltip = PyGenUtil::GetFieldTooltip(InFunc);
|
||||
TArray<FString> FuncTooltipLines;
|
||||
FuncTooltip.ParseIntoArrayLines(FuncTooltipLines, /*bCullEmpty*/false);
|
||||
|
||||
bool bMultipleLines = false;
|
||||
for (const FString& FuncTooltipLine : FuncTooltipLines)
|
||||
{
|
||||
if (bMultipleLines)
|
||||
{
|
||||
OutStr += LINE_TERMINATOR;
|
||||
OutStr += InIdentation;
|
||||
}
|
||||
bMultipleLines = true;
|
||||
|
||||
OutStr += FuncTooltipLine;
|
||||
}
|
||||
};
|
||||
|
||||
FString OpDocString;
|
||||
if (InOpSignature.OtherType != PyGenUtil::FGeneratedWrappedOperatorSignature::EType::None)
|
||||
{
|
||||
@@ -2283,28 +2301,17 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
{
|
||||
if (OpFunc.OtherParam.ParamProp)
|
||||
{
|
||||
const FString FuncTooltip = PyGenUtil::GetFieldTooltip(OpFunc.Func);
|
||||
TArray<FString> FuncTooltipLines;
|
||||
FuncTooltip.ParseIntoArrayLines(FuncTooltipLines, /*bCullEmpty*/false);
|
||||
|
||||
OpDocString += LINE_TERMINATOR TEXT("- ``"); // add as a list and code style
|
||||
OpDocString += PyGenUtil::GetPropertyTypePythonName(OpFunc.OtherParam.ParamProp);
|
||||
OpDocString += TEXT("`` ");
|
||||
|
||||
bool bMultipleLines = false;
|
||||
for (const FString& FuncTooltipLine : FuncTooltipLines)
|
||||
{
|
||||
if (bMultipleLines)
|
||||
{
|
||||
OpDocString += LINE_TERMINATOR TEXT(" ");
|
||||
}
|
||||
bMultipleLines = true;
|
||||
|
||||
OpDocString += FuncTooltipLine;
|
||||
}
|
||||
AppendFunctionTooltip(OpFunc.Func, TEXT(" "), OpDocString);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (InOpStack.Funcs.Num() > 0)
|
||||
{
|
||||
AppendFunctionTooltip(InOpStack.Funcs[0].Func, TEXT(""), OpDocString);
|
||||
}
|
||||
|
||||
OutPythonScript.WriteLine(FString::Printf(TEXT("def %s(self%s):"), InOpSignature.PyFuncName, (InOpSignature.OtherType == PyGenUtil::FGeneratedWrappedOperatorSignature::EType::None ? TEXT("") : TEXT(", other"))));
|
||||
OutPythonScript.IncreaseIndent();
|
||||
@@ -2338,6 +2345,9 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
TSharedPtr<const FPyWrapperStructMetaData> StructMetaData = StaticCastSharedPtr<FPyWrapperStructMetaData>(GeneratedTypeData->MetaData);
|
||||
check(StructMetaData.IsValid());
|
||||
|
||||
// Don't export FDateTime values for struct __init__ as they can be non-deterministic
|
||||
static const uint32 PythonizeValueFlags = PyGenUtil::EPythonizeValueFlags::DefaultConstructDateTime;
|
||||
|
||||
// Python can only support 255 parameters, so if we have more than that for this struct just use the generic __init__ function
|
||||
FString InitParamsStr;
|
||||
if (StructMetaData->MakeFunc.Func)
|
||||
@@ -2351,7 +2361,7 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
if (InitParam.ParamDefaultValue.IsSet())
|
||||
{
|
||||
InitParamsStr += TEXT("=");
|
||||
InitParamsStr += PyGenUtil::PythonizeDefaultValue(InitParam.ParamProp, InitParam.ParamDefaultValue.GetValue());
|
||||
InitParamsStr += PyGenUtil::PythonizeDefaultValue(InitParam.ParamProp, InitParam.ParamDefaultValue.GetValue(), PythonizeValueFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2367,7 +2377,7 @@ void FPyWrapperTypeRegistry::GenerateStubCodeForWrappedType(PyTypeObject* PyType
|
||||
if (InitParam.ParamDefaultValue.IsSet())
|
||||
{
|
||||
InitParamsStr += TEXT("=");
|
||||
InitParamsStr += PyGenUtil::PythonizeValue(InitParam.ParamProp, InitParam.ParamProp->ContainerPtrToValuePtr<void>(StructData.GetStructMemory()));
|
||||
InitParamsStr += PyGenUtil::PythonizeValue(InitParam.ParamProp, InitParam.ParamProp->ContainerPtrToValuePtr<void>(StructData.GetStructMemory()), PythonizeValueFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,6 +361,16 @@ bool FPythonScriptPlugin::ExecPythonCommand(const TCHAR* InPythonCommand)
|
||||
#endif // WITH_PYTHON
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FPythonScriptPlugin::OnPythonInitialized()
|
||||
{
|
||||
return OnPythonInitializedDelegate;
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FPythonScriptPlugin::OnPythonShutdown()
|
||||
{
|
||||
return OnPythonShutdownDelegate;
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::StartupModule()
|
||||
{
|
||||
#if WITH_PYTHON
|
||||
@@ -517,11 +527,6 @@ void FPythonScriptPlugin::InitializePython()
|
||||
// Initialize the wrapped types
|
||||
FPyWrapperTypeRegistry::Get().GenerateWrappedTypes();
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Register to generate stub code on first tick after all modules loaded.
|
||||
ModulesChangedDelayedNotify();
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
// Initialize the tick handler
|
||||
TickHandle = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([this](float DeltaTime)
|
||||
{
|
||||
@@ -529,6 +534,9 @@ void FPythonScriptPlugin::InitializePython()
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
// Notify any external listeners
|
||||
OnPythonInitializedDelegate.Broadcast();
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::ShutdownPython()
|
||||
@@ -538,10 +546,13 @@ void FPythonScriptPlugin::ShutdownPython()
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify any external listeners
|
||||
OnPythonShutdownDelegate.Broadcast();
|
||||
|
||||
FTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
||||
if (ModuleDelayedHandle.IsValid())
|
||||
{
|
||||
FTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
||||
FTicker::GetCoreTicker().RemoveTicker(ModuleDelayedHandle);
|
||||
}
|
||||
|
||||
FPyWrapperTypeRegistry::Get().OnModuleDirtied().RemoveAll(this);
|
||||
@@ -568,31 +579,22 @@ void FPythonScriptPlugin::ShutdownPython()
|
||||
bHasTicked = false;
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::ModulesChangedDelayedNotify()
|
||||
void FPythonScriptPlugin::RequestStubCodeGeneration()
|
||||
{
|
||||
// Delay 3 seconds before notifying.
|
||||
float Delay = 3.0f;
|
||||
|
||||
// Ignore requests made before the fist Tick
|
||||
if (!bHasTicked)
|
||||
{
|
||||
// Waiting for first notify yet?
|
||||
if (ModuleDelayedHandle.IsValid())
|
||||
{
|
||||
// First notify is set up and has not yet happened
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify on first tick
|
||||
Delay = 0.0f;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// Delay 2 seconds before generating as this may be triggered by loading several modules at once
|
||||
static const float Delay = 2.0f;
|
||||
|
||||
// If there is an existing pending notification, remove it so that it can be reset
|
||||
if (ModuleDelayedHandle.IsValid())
|
||||
{
|
||||
// If there is an existing pending notification, remove it so that it can be reset
|
||||
if (ModuleDelayedHandle.IsValid())
|
||||
{
|
||||
FTicker::GetCoreTicker().RemoveTicker(ModuleDelayedHandle);
|
||||
ModuleDelayedHandle.Reset();
|
||||
}
|
||||
FTicker::GetCoreTicker().RemoveTicker(ModuleDelayedHandle);
|
||||
ModuleDelayedHandle.Reset();
|
||||
}
|
||||
|
||||
// Set new tick
|
||||
@@ -603,7 +605,7 @@ void FPythonScriptPlugin::ModulesChangedDelayedNotify()
|
||||
ModuleDelayedHandle.Reset();
|
||||
|
||||
// Call the event now that the delay has passed.
|
||||
OnModulesChangedDelayedNotify();
|
||||
GenerateStubCode();
|
||||
|
||||
// Don't reschedule to run again.
|
||||
return false;
|
||||
@@ -611,6 +613,15 @@ void FPythonScriptPlugin::ModulesChangedDelayedNotify()
|
||||
Delay);
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::GenerateStubCode()
|
||||
{
|
||||
if (GetDefault<UPythonScriptPluginSettings>()->bDeveloperMode)
|
||||
{
|
||||
// Generate stub code if developer mode enabled
|
||||
FPyWrapperTypeRegistry::Get().GenerateStubCodeForWrappedTypes();
|
||||
}
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::Tick(const float InDeltaTime)
|
||||
{
|
||||
// If this is our first Tick, handle any post-init logic that should happen once the engine is fully initialized
|
||||
@@ -636,6 +647,11 @@ void FPythonScriptPlugin::Tick(const float InDeltaTime)
|
||||
{
|
||||
HandlePythonExecCommand(*StartupScript);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Register to generate stub code after a short delay
|
||||
RequestStubCodeGeneration();
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
FPyWrapperTypeReinstancer::Get().ProcessPending();
|
||||
@@ -877,16 +893,16 @@ void FPythonScriptPlugin::OnModulesChanged(FName InModuleName, EModuleChangeReas
|
||||
case EModuleChangeReason::ModuleLoaded:
|
||||
FPyWrapperTypeRegistry::Get().GenerateWrappedTypesForModule(InModuleName);
|
||||
#if WITH_EDITOR
|
||||
// Register to generate stub code after delay or reset delay.
|
||||
ModulesChangedDelayedNotify();
|
||||
// Register to generate stub code after a short delay
|
||||
RequestStubCodeGeneration();
|
||||
#endif // WITH_EDITOR
|
||||
break;
|
||||
|
||||
case EModuleChangeReason::ModuleUnloaded:
|
||||
FPyWrapperTypeRegistry::Get().OrphanWrappedTypesForModule(InModuleName);
|
||||
#if WITH_EDITOR
|
||||
// Register to generate stub code after delay or reset delay.
|
||||
ModulesChangedDelayedNotify();
|
||||
// Register to generate stub code after a short delay
|
||||
RequestStubCodeGeneration();
|
||||
#endif // WITH_EDITOR
|
||||
break;
|
||||
|
||||
@@ -895,15 +911,6 @@ void FPythonScriptPlugin::OnModulesChanged(FName InModuleName, EModuleChangeReas
|
||||
}
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::OnModulesChangedDelayedNotify()
|
||||
{
|
||||
if (GetDefault<UPythonScriptPluginSettings>()->bDeveloperMode)
|
||||
{
|
||||
// Generate stub code if developer mode enabled
|
||||
FPyWrapperTypeRegistry::Get().GenerateStubCodeForWrappedTypes();
|
||||
}
|
||||
}
|
||||
|
||||
void FPythonScriptPlugin::OnContentPathMounted(const FString& InAssetPath, const FString& InFilesystemPath)
|
||||
{
|
||||
FPyScopedGIL GIL;
|
||||
|
||||
@@ -64,6 +64,8 @@ public:
|
||||
//~ IPythonScriptPlugin interface
|
||||
virtual bool IsPythonAvailable() const override;
|
||||
virtual bool ExecPythonCommand(const TCHAR* InPythonCommand) override;
|
||||
virtual FSimpleMulticastDelegate& OnPythonInitialized() override;
|
||||
virtual FSimpleMulticastDelegate& OnPythonShutdown() override;
|
||||
|
||||
//~ IModuleInterface interface
|
||||
virtual void StartupModule() override;
|
||||
@@ -95,7 +97,9 @@ private:
|
||||
|
||||
void ShutdownPython();
|
||||
|
||||
void ModulesChangedDelayedNotify();
|
||||
void RequestStubCodeGeneration();
|
||||
|
||||
void GenerateStubCode();
|
||||
|
||||
void Tick(const float InDeltaTime);
|
||||
|
||||
@@ -103,8 +107,6 @@ private:
|
||||
|
||||
void OnModulesChanged(FName InModuleName, EModuleChangeReason InModuleChangeReason);
|
||||
|
||||
void OnModulesChangedDelayedNotify();
|
||||
|
||||
void OnContentPathMounted(const FString& InAssetPath, const FString& InFilesystemPath);
|
||||
|
||||
void OnContentPathDismounted(const FString& InAssetPath, const FString& InFilesystemPath);
|
||||
@@ -129,5 +131,6 @@ private:
|
||||
bool bHasTicked;
|
||||
#endif // WITH_PYTHON
|
||||
|
||||
|
||||
FSimpleMulticastDelegate OnPythonInitializedDelegate;
|
||||
FSimpleMulticastDelegate OnPythonShutdownDelegate;
|
||||
};
|
||||
|
||||
@@ -27,4 +27,14 @@ public:
|
||||
* @return true if the command ran successfully, false if there were errors (the output log will show the errors).
|
||||
*/
|
||||
virtual bool ExecPythonCommand(const TCHAR* InPythonCommand) = 0;
|
||||
|
||||
/**
|
||||
* Delegate called after Python has been initialized.
|
||||
*/
|
||||
virtual FSimpleMulticastDelegate& OnPythonInitialized() = 0;
|
||||
|
||||
/**
|
||||
* Delegate called before Python is shutdown.
|
||||
*/
|
||||
virtual FSimpleMulticastDelegate& OnPythonShutdown() = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user