From 079929153a9d8f707ac79af75d33dc49a72a2a39 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 13 Jun 2017 07:39:14 +0200 Subject: [PATCH 01/94] use Editor in bebylon --- UnrealEnginePython.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnrealEnginePython.uplugin b/UnrealEnginePython.uplugin index 8ce9d4932..6d16a645c 100644 --- a/UnrealEnginePython.uplugin +++ b/UnrealEnginePython.uplugin @@ -17,7 +17,7 @@ "Modules": [ { "Name": "UnrealEnginePython", - "Type": "Runtime", + "Type": "Editor", "LoadingPhase": "PreDefault" }, { From a215099de535d5dbb5277d92371e7868629b4154 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 12 Jun 2017 23:42:53 -0700 Subject: [PATCH 02/94] Adding get_game_saved_dir --- Source/UnrealEnginePython/Private/UEPyEngine.cpp | 4 ++++ Source/UnrealEnginePython/Private/UEPyEngine.h | 1 + Source/UnrealEnginePython/Private/UEPyModule.cpp | 1 + 3 files changed, 6 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index b1ee97fdf..8b00a02e6 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -135,6 +135,10 @@ PyObject *py_unreal_engine_get_content_dir(PyObject * self, PyObject * args) { return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameContentDir())); } +PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) { + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); +} + PyObject *py_unreal_engine_convert_relative_path_to_full(PyObject * self, PyObject * args) { char *path; if (!PyArg_ParseTuple(args, "s:convert_relative_path_to_full", &path)) { diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index 3232fe0da..8c32a644e 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -14,6 +14,7 @@ PyObject *py_unreal_engine_get_right_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_up_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_content_dir(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_game_saved_dir(PyObject *, PyObject *); PyObject *py_unreal_engine_find_object(PyObject *, PyObject *); PyObject *py_unreal_engine_find_class(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index c5da15a30..7d30eeaeb 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -148,6 +148,7 @@ static PyMethodDef unreal_engine_methods[] = { { "get_right_vector", py_unreal_engine_get_right_vector, METH_VARARGS, "" }, { "get_content_dir", py_unreal_engine_get_content_dir, METH_VARARGS, "" }, + { "get_game_saved_dir", py_unreal_engine_get_game_saved_dir, METH_VARARGS, "" }, { "convert_relative_path_to_full", py_unreal_engine_convert_relative_path_to_full, METH_VARARGS, "" }, { "compress_image_array", py_unreal_engine_compress_image_array, METH_VARARGS, "" }, From c2bb6520a60362b350e4ea4e7d8b5dc5bf9028ad Mon Sep 17 00:00:00 2001 From: ikrima Date: Thu, 15 Jun 2017 12:55:23 -0700 Subject: [PATCH 03/94] Re-adding Delta parameter to SVectorInputBoxType --- .../Private/Slate/UEPySVectorInputBox.cpp | 6 ++++-- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp index 384df56fa..7e6242fe3 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp @@ -43,8 +43,10 @@ PyTypeObject ue_PySVectorInputBoxType = { static int ue_py_svector_input_box_init(ue_PySVectorInputBox *self, PyObject *args, PyObject *kwargs) { ue_py_slate_setup_farguments(SVectorInputBox); - //ue_py_slate_farguments_optional_float("delta", Delta); -#if ENGINE_MINOR_VERSION > 15 +#if WITH_KNL_PYEXT + ue_py_slate_farguments_optional_float("delta", Delta); +#endif +#if ENGINE_MINOR_VERSION > 15 || WITH_KNL_PYEXT ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); #endif ue_py_slate_farguments_optional_bool("allow_responsive_layout", AllowResponsiveLayout); diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index a7ac55f67..d7621995b 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -19,6 +19,7 @@ public class UnrealEnginePython : ModuleRules private string[] windowsKnownPaths = { + "C:/knl/python3", "C:/Program Files/Python36", "C:/Program Files/Python35", "C:/Python27", @@ -72,6 +73,7 @@ public class UnrealEnginePython : ModuleRules public UnrealEnginePython(TargetInfo Target) { + Definitions.Add("WITH_KNL_PYEXT=1"); PublicIncludePaths.AddRange( new string[] { From 3a9389c383e19df1a3df9565a0f76244af869f38 Mon Sep 17 00:00:00 2001 From: ikrima Date: Sat, 17 Jun 2017 05:59:51 -0700 Subject: [PATCH 04/94] Add get_game_user_developer_dir --- Source/UnrealEnginePython/Private/UEPyEngine.cpp | 4 ++++ Source/UnrealEnginePython/Private/UEPyEngine.h | 1 + Source/UnrealEnginePython/Private/UEPyModule.cpp | 1 + 3 files changed, 6 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 8b00a02e6..ab46997c1 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -139,6 +139,10 @@ PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); } +PyObject * py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *) { + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameUserDeveloperDir())); +} + PyObject *py_unreal_engine_convert_relative_path_to_full(PyObject * self, PyObject * args) { char *path; if (!PyArg_ParseTuple(args, "s:convert_relative_path_to_full", &path)) { diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index 8c32a644e..871328c0c 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -15,6 +15,7 @@ PyObject *py_unreal_engine_get_up_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_content_dir(PyObject *, PyObject *); PyObject *py_unreal_engine_get_game_saved_dir(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *); PyObject *py_unreal_engine_find_object(PyObject *, PyObject *); PyObject *py_unreal_engine_find_class(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 0088593a9..d1a24f7b9 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -149,6 +149,7 @@ static PyMethodDef unreal_engine_methods[] = { { "get_content_dir", py_unreal_engine_get_content_dir, METH_VARARGS, "" }, { "get_game_saved_dir", py_unreal_engine_get_game_saved_dir, METH_VARARGS, "" }, + { "get_game_user_developer_dir", py_unreal_engine_get_game_user_developer_dir, METH_VARARGS, "" }, { "convert_relative_path_to_full", py_unreal_engine_convert_relative_path_to_full, METH_VARARGS, "" }, { "compress_image_array", py_unreal_engine_compress_image_array, METH_VARARGS, "" }, From c1392fb988d5e900c894ad945951d8353815e9fc Mon Sep 17 00:00:00 2001 From: ikrima Date: Thu, 6 Jul 2017 20:42:22 -0700 Subject: [PATCH 05/94] Ryan changes to make Python plugin independent of system installation --- .../Private/UnrealEnginePython.cpp | 62 ++++++++++++++++--- .../UnrealEnginePython.Build.cs | 42 +++++++++++-- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 0d2cf22b6..8bd9ba31d 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -147,26 +147,31 @@ FAutoConsoleCommand ExecPythonScriptCommand( void FUnrealEnginePythonModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - FString IniValue; - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), IniValue, GEngineIni)) { + FString PythonHome; + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) { #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); Py_SetPythonHome(home); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), IniValue, GEngineIni)) { - IniValue = FPaths::Combine(*FPaths::GameContentDir(), *IniValue); + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), PythonHome, GEngineIni)) { + PythonHome = FPaths::Combine(*FPaths::GameContentDir(), *PythonHome); + FPaths::NormalizeFilename(PythonHome); + PythonHome = FPaths::ConvertRelativePathToFull(PythonHome); #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif + Py_SetPythonHome(home); } + FString IniValue; if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) { #if PY_MAJOR_VERSION >= 3 wchar_t *program_name = (wchar_t *)*IniValue; @@ -178,6 +183,8 @@ void FUnrealEnginePythonModule::StartupModule() if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeProgramName"), IniValue, GEngineIni)) { IniValue = FPaths::Combine(*FPaths::GameContentDir(), *IniValue); + FPaths::NormalizeFilename(IniValue); + IniValue = FPaths::ConvertRelativePathToFull(IniValue); #if PY_MAJOR_VERSION >= 3 wchar_t *program_name = (wchar_t *)*IniValue; #else @@ -218,6 +225,45 @@ void FUnrealEnginePythonModule::StartupModule() ZipPath = FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("ue_python.zip")); } + // To ensure there are no path conflicts, if we have a valid python home at this point, + // we override the current environment entirely with the environment we want to use, + // removing any paths to other python environments we aren't using. + if (PythonHome.Len() > 0) + { + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); + + const int32 MaxPathVarLen = 32768; + FString OrigPathVar = FString::ChrN(MaxPathVarLen, TEXT('\0')); + FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"), OrigPathVar.GetCharArray().GetData(), MaxPathVarLen); + + // Get the current path and remove elements with python in them, we don't want any conflicts + const TCHAR* PathDelimiter = FPlatformMisc::GetPathVarDelimiter(); + TArray PathVars; + OrigPathVar.ParseIntoArray(PathVars, PathDelimiter, true); + for (int32 PathRemoveIndex = PathVars.Num() - 1; PathRemoveIndex >= 0; --PathRemoveIndex) + { + if (PathVars[PathRemoveIndex].Contains(TEXT("python"), ESearchCase::IgnoreCase)) + { + UE_LOG(LogPython, Verbose, TEXT("Removing other python Path: '%s'"), *PathVars[PathRemoveIndex]); + PathVars.RemoveAt(PathRemoveIndex); + } + } + + // Setup our own paths for PYTHONPATH + TArray OurPythonPaths = { + PythonHome, + FPaths::Combine(PythonHome, TEXT("Lib")), + FPaths::Combine(PythonHome, TEXT("Lib/site-packages")), + }; + FString PythonPathVars = FString::Join(OurPythonPaths, PathDelimiter); + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONPATH"), *PythonPathVars); + + // Also add our paths to PATH, just so any searching will find our local python + PathVars.Append(OurPythonPaths); + FString ModifiedPath = FString::Join(PathVars, PathDelimiter); + FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); + } + Py_Initialize(); PyEval_InitThreads(); diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index e816a2952..f72bb95f0 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -19,7 +19,7 @@ public class UnrealEnginePython : ModuleRules private string[] windowsKnownPaths = { - "C:/knl/python3", + "../../../../../ThirdParty/Python3", "C:/Program Files/Python36", "C:/Program Files/Python35", "C:/Python27", @@ -171,6 +171,19 @@ public UnrealEnginePython(TargetInfo Target) string libPath = GetWindowsPythonLibFile(pythonHome); PublicLibraryPaths.Add(Path.GetDirectoryName(libPath)); PublicAdditionalLibraries.Add(libPath); + + // copy the dlls into the plugins dlls folder, so they don't have to be on the path + string dllsDir = Path.Combine(ModuleDirectory, "../../Binaries", Target.Platform == UnrealTargetPlatform.Win32 ? "Win32" : "Win64"); + try + { + File.Copy(Path.Combine(pythonHome, "python3.dll"), Path.Combine(dllsDir, "python3.dll"), true); + File.Copy(Path.Combine(pythonHome, "python36.dll"), Path.Combine(dllsDir, "python36.dll"), true); + } + catch(System.IO.IOException) { } + catch(System.UnauthorizedAccessException) + { + System.Console.WriteLine("WARNING: Unable to copy python dlls, they are probably in use..."); + } } else if (Target.Platform == UnrealTargetPlatform.Mac) { @@ -217,6 +230,15 @@ public UnrealEnginePython(TargetInfo Target) } + private bool IsPathRelative(string Path) + { + bool IsRooted = Path.StartsWith("\\", System.StringComparison.Ordinal) || // Root of the current directory on Windows. Also covers "\\" for UNC or "network" paths. + Path.StartsWith("/", System.StringComparison.Ordinal) || // Root of the current directory on Windows, root on UNIX-likes. + // Also covers "\\", considering normalization replaces "\\" with "//". + (Path.Length >= 2 && char.IsLetter(Path[0]) && Path[1] == ':'); // Starts with ":" + return !IsRooted; + } + private string DiscoverPythonPath(string[] knownPaths) { // insert the PYTHONHOME content as the first known path @@ -227,16 +249,23 @@ private string DiscoverPythonPath(string[] knownPaths) foreach (string path in paths) { - string headerFile = Path.Combine(path, "include", "Python.h"); + string actualPath = path; + + if (IsPathRelative(actualPath)) + { + actualPath = Path.GetFullPath(Path.Combine(ModuleDirectory, actualPath)); + } + + string headerFile = Path.Combine(actualPath, "include", "Python.h"); if (File.Exists(headerFile)) { - return path; + return actualPath; } // this is mainly useful for OSX - headerFile = Path.Combine(path, "Headers", "Python.h"); + headerFile = Path.Combine(actualPath, "Headers", "Python.h"); if (File.Exists(headerFile)) { - return path; + return actualPath; } } return ""; @@ -327,7 +356,8 @@ private string GetWindowsPythonLibFile(string basePath) } if (!found) { - System.Console.WriteLine("[WARNING] Your Python installation is not in the system PATH environment variable, very probably the plugin will fail to load"); + System.Console.WriteLine("[WARNING] Your Python installation is not in the system PATH environment variable."); + System.Console.WriteLine("[WARNING] Ensure your python paths are set in GlobalConfig (DefaultEngine.ini) so the path can be corrected at runtime."); } // first try with python3 for (int i = 9; i >= 0; i--) From 080faa94f9c97ddbb1185001c9603d37d4811525 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 17 Jul 2017 18:24:48 -0700 Subject: [PATCH 06/94] BUG: Editor only animation montage composite functions need to be compiled out in non-editor builds --- .../UnrealEnginePython/Private/UEPyModule.cpp | 3 +- .../Private/UObject/UEPyAnimSequence.cpp | 33 ++++++++++--------- .../Private/UObject/UEPyAnimSequence.h | 4 +-- .../UnrealEnginePython.Build.cs | 18 ++++++++-- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index aaf60b657..415caa7cd 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -466,12 +466,11 @@ static PyMethodDef ue_PyUObject_methods[] = { { "anim_get_skeleton", (PyCFunction)py_ue_anim_get_skeleton, METH_VARARGS, "" }, { "anim_set_skeleton", (PyCFunction)py_ue_anim_set_skeleton, METH_VARARGS, "" }, - { "add_anim_composite_section", (PyCFunction)py_ue_add_anim_composite_section, METH_VARARGS, "" }, - #if WITH_EDITOR { "get_raw_animation_data", (PyCFunction)py_ue_anim_sequence_get_raw_animation_data, METH_VARARGS, "" }, { "get_raw_animation_track", (PyCFunction)py_ue_anim_sequence_get_raw_animation_track, METH_VARARGS, "" }, { "add_new_raw_track", (PyCFunction)py_ue_anim_sequence_add_new_raw_track, METH_VARARGS, "" }, + { "add_anim_composite_section", (PyCFunction)py_ue_add_anim_composite_section, METH_VARARGS, "" }, #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp index 866b38a5e..18eb2b767 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp @@ -93,6 +93,22 @@ PyObject *py_ue_anim_sequence_add_new_raw_track(ue_PyUObject * self, PyObject * return PyLong_FromLong(index); } + +PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { + ue_py_check(self); + + char *name; + float time; + if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) + return nullptr; + + UAnimMontage *anim = ue_py_check_type(self); + if (!anim) + return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); + + return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); +} + #endif PyObject *py_ue_anim_set_skeleton(ue_PyUObject * self, PyObject * args) { @@ -113,19 +129,4 @@ PyObject *py_ue_anim_set_skeleton(ue_PyUObject * self, PyObject * args) { anim->SetSkeleton(skeleton); Py_RETURN_NONE; -} - -PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { - ue_py_check(self); - - char *name; - float time; - if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) - return nullptr; - - UAnimMontage *anim = ue_py_check_type(self); - if (!anim) - return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); - - return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); -} +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h index c3c8aaeb8..46fec4422 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h @@ -10,6 +10,6 @@ PyObject *py_ue_anim_get_skeleton(ue_PyUObject *, PyObject *); PyObject *py_ue_anim_sequence_get_raw_animation_data(ue_PyUObject *, PyObject *); PyObject *py_ue_anim_sequence_get_raw_animation_track(ue_PyUObject *, PyObject *); PyObject *py_ue_anim_sequence_add_new_raw_track(ue_PyUObject *, PyObject *); +PyObject *py_ue_add_anim_composite_section(ue_PyUObject *, PyObject *); #endif -PyObject *py_ue_anim_set_skeleton(ue_PyUObject *, PyObject *); -PyObject *py_ue_add_anim_composite_section(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_anim_set_skeleton(ue_PyUObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index f72bb95f0..c3b5ed6b6 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -176,8 +176,22 @@ public UnrealEnginePython(TargetInfo Target) string dllsDir = Path.Combine(ModuleDirectory, "../../Binaries", Target.Platform == UnrealTargetPlatform.Win32 ? "Win32" : "Win64"); try { - File.Copy(Path.Combine(pythonHome, "python3.dll"), Path.Combine(dllsDir, "python3.dll"), true); - File.Copy(Path.Combine(pythonHome, "python36.dll"), Path.Combine(dllsDir, "python36.dll"), true); + string[] dllsToCopy = + { + "python3.dll", + "python36.dll" + }; + foreach (string dllToCopy in dllsToCopy) + { + // If the dll exist, make sure to set attributes so they are actually accessible + if (File.Exists(Path.Combine(dllsDir, dllToCopy))) + { + File.SetAttributes(Path.Combine(dllsDir, dllToCopy), FileAttributes.Normal); + } + + File.Copy(Path.Combine(pythonHome, dllToCopy), Path.Combine(dllsDir, dllToCopy), true); + File.SetAttributes(Path.Combine(dllsDir, dllToCopy), FileAttributes.Normal); + } } catch(System.IO.IOException) { } catch(System.UnauthorizedAccessException) From db026777e2dee47cfb4ad26b0af7dd94aea8fb00 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 22 Aug 2017 17:56:02 +0200 Subject: [PATCH 07/94] build fixes --- .../UnrealEnginePython/Private/UEPyModule.cpp | 1 - .../Private/UObject/UEPyAnimSequence.cpp | 17 ----------------- .../Private/UnrealEnginePython.cpp | 2 +- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 75b8b3f13..4552a7ad9 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -506,7 +506,6 @@ static PyMethodDef ue_PyUObject_methods[] = { { "get_raw_animation_data", (PyCFunction)py_ue_anim_sequence_get_raw_animation_data, METH_VARARGS, "" }, { "get_raw_animation_track", (PyCFunction)py_ue_anim_sequence_get_raw_animation_track, METH_VARARGS, "" }, { "add_new_raw_track", (PyCFunction)py_ue_anim_sequence_add_new_raw_track, METH_VARARGS, "" }, - { "add_anim_composite_section", (PyCFunction)py_ue_add_anim_composite_section, METH_VARARGS, "" }, #endif { "add_anim_composite_section", (PyCFunction)py_ue_add_anim_composite_section, METH_VARARGS, "" }, #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp index 8c9588abd..0115058a6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp @@ -134,23 +134,6 @@ PyObject *py_ue_anim_set_skeleton(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -#if WITH_EDITOR -PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { - ue_py_check(self); - - char *name; - float time; - if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) - return nullptr; - - UAnimMontage *anim = ue_py_check_type(self); - if (!anim) - return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); - - return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); -} -#endif - PyObject *py_ue_get_blend_parameter(ue_PyUObject * self, PyObject * args) { ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index dd570be7f..6ecdaa907 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -234,6 +234,7 @@ void FUnrealEnginePythonModule::StartupModule() if (!FPaths::DirectoryExists(ScriptsPath)) { IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); PlatformFile.CreateDirectory(*ScriptsPath); + } // To ensure there are no path conflicts, if we have a valid python home at this point, // we override the current environment entirely with the environment we want to use, @@ -272,7 +273,6 @@ void FUnrealEnginePythonModule::StartupModule() PathVars.Append(OurPythonPaths); FString ModifiedPath = FString::Join(PathVars, PathDelimiter); FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); ->>>>>>> 080faa94f9c97ddbb1185001c9603d37d4811525 } Py_Initialize(); From 59ed31645931c9d79766c68919a1494bf065a364 Mon Sep 17 00:00:00 2001 From: ikrima Date: Fri, 8 Sep 2017 18:10:27 -0700 Subject: [PATCH 08/94] Fixing 4.17 compilation bugs & warnings -Minor issues with incremental compilation with missing headers --- Source/PythonConsole/Private/PythonConsoleModule.cpp | 2 +- Source/UnrealEnginePython/Private/PythonScript.cpp | 1 - .../UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp | 5 ++--- .../UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h | 3 ++- Source/UnrealEnginePython/Private/UEPyEditor.cpp | 4 ++++ Source/UnrealEnginePython/Public/PythonScript.h | 3 ++- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Source/PythonConsole/Private/PythonConsoleModule.cpp b/Source/PythonConsole/Private/PythonConsoleModule.cpp index 9f1c6bdb8..0569edec5 100644 --- a/Source/PythonConsole/Private/PythonConsoleModule.cpp +++ b/Source/PythonConsole/Private/PythonConsoleModule.cpp @@ -7,7 +7,7 @@ #include "SDockTab.h" #include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructure.h" -IMPLEMENT_MODULE( FPythonConsoleModule, PythonLog ); +IMPLEMENT_MODULE( FPythonConsoleModule, PythonConsole ); namespace PythonConsoleModule { diff --git a/Source/UnrealEnginePython/Private/PythonScript.cpp b/Source/UnrealEnginePython/Private/PythonScript.cpp index 3d3b2ece8..84f4e2fbd 100644 --- a/Source/UnrealEnginePython/Private/PythonScript.cpp +++ b/Source/UnrealEnginePython/Private/PythonScript.cpp @@ -1,5 +1,4 @@ #include "UnrealEnginePythonPrivatePCH.h" - #include "PythonScript.h" static void callback(void *arg) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp index 9683be4ec..66c131ab0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp @@ -1,9 +1,8 @@ -#if WITH_EDITOR -#if ENGINE_MINOR_VERSION > 13 #include "UnrealEnginePythonPrivatePCH.h" - #include "UEPySFilePathPicker.h" +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 13 #define sw_file_path_picker StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h index 2e649051a..b87bd4ac5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h @@ -2,9 +2,9 @@ #include "UnrealEnginePython.h" - #include "UEPySCompoundWidget.h" +#if WITH_EDITOR #if ENGINE_MINOR_VERSION > 13 #include "Developer/DesktopWidgets/Public/Widgets/Input/SFilePathPicker.h" @@ -17,3 +17,4 @@ typedef struct { void ue_python_init_sfile_path_picker(PyObject *); #endif +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 3685f8ca9..581541792 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -1718,7 +1718,11 @@ PyObject *py_unreal_engine_move_selected_actors_to_level(PyObject *self, PyObjec ULevel *level = (ULevel *)py_obj_level->ue_object; +#if ENGINE_MINOR_VERSION >= 17 + UEditorLevelUtils::MoveSelectedActorsToLevel(level); +#else GEditor->MoveSelectedActorsToLevel(level); +#endif Py_INCREF(Py_None); return Py_None; diff --git a/Source/UnrealEnginePython/Public/PythonScript.h b/Source/UnrealEnginePython/Public/PythonScript.h index cac7b554a..0c95a8253 100644 --- a/Source/UnrealEnginePython/Public/PythonScript.h +++ b/Source/UnrealEnginePython/Public/PythonScript.h @@ -1,6 +1,7 @@ #pragma once - +#include "CoreMinimal.h" +#include "UObject/Object.h" #include "PythonScript.generated.h" UCLASS(MinimalAPI) From a6c14eed402577a5ff4cf98a62d5e09a760224f8 Mon Sep 17 00:00:00 2001 From: ikrima Date: Fri, 15 Sep 2017 01:51:28 -0700 Subject: [PATCH 09/94] Fixing 4.17 compilation issues --- Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp | 3 +-- Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp | 5 ++--- Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h | 4 +++- Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp | 6 ++---- Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h | 3 ++- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp index eb3ea1ed1..96f003948 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp @@ -1,7 +1,6 @@ - -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#if ENGINE_MINOR_VERSION > 12 #if WITH_EDITOR #include "UEPyFbx.h" diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp index 15fa7d7a8..cf367203e 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp @@ -1,9 +1,8 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#include "UEPyFbx.h" #if WITH_EDITOR - -#include "UEPyFbx.h" +#if ENGINE_MINOR_VERSION > 12 static PyObject *py_ue_fbx_mesh_get_polygon_count(ue_PyFbxMesh *self, PyObject *args) { diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h index 370698a10..be525201f 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h @@ -2,6 +2,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 12 #include @@ -15,6 +16,7 @@ struct ue_PyFbxMesh void ue_python_init_fbx_mesh(PyObject *); -PyObject *py_ue_new_fbx_mesh(FbxNode *); +PyObject *py_ue_new_fbx_mesh(FbxMesh *); #endif +#endif diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp index 9f9f62871..15dfa0170 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp @@ -1,10 +1,8 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" - -#if WITH_EDITOR - #include "UEPyFbx.h" +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 12 static PyObject *py_ue_fbx_node_get_child_count(ue_PyFbxNode *self, PyObject *args) { return PyLong_FromLong(self->fbx_node->GetChildCount()); } diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h index 7e07f089f..2fd7d6f84 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h @@ -2,7 +2,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR - +#if ENGINE_MINOR_VERSION > 12 #include struct ue_PyFbxNode { @@ -19,3 +19,4 @@ PyObject *py_ue_new_fbx_node(FbxNode *); ue_PyFbxNode *py_ue_is_fbx_node(PyObject *); #endif +#endif \ No newline at end of file From 029577a37e30198aa1a604610113193e31739a26 Mon Sep 17 00:00:00 2001 From: ikrima Date: Sat, 18 Nov 2017 02:49:58 -0800 Subject: [PATCH 10/94] Synchronize to Changelist #3575 --- .../Private/PythonScriptFactory.cpp | 2 + .../ConsoleManager/UEPyIConsoleManager.cpp | 63 +++ .../ConsoleManager/UEPyIConsoleManager.h | 9 + .../Private/PyNativeWidgetHost.cpp | 20 + .../Private/PyUserWidget.cpp | 45 +- .../Private/Slate/UEPySPythonTreeView.cpp | 38 +- .../Private/Slate/UEPySPythonTreeView.h | 11 +- .../Private/Slate/UEPySTreeView.cpp | 18 +- .../Private/Slate/UEPySWidget.cpp | 114 ++-- .../UnrealEnginePython/Private/UEPyEngine.cpp | 485 +++++++++++++----- .../UnrealEnginePython/Private/UEPyEngine.h | 4 + .../UnrealEnginePython/Private/UEPyModule.cpp | 31 +- .../Private/UEPyUScriptStruct.cpp | 3 +- .../Private/UObject/UEPyWidgetComponent.cpp | 15 +- .../Public/PyNativeWidgetHost.h | 24 + .../UnrealEnginePython/Public/PyUserWidget.h | 17 +- UnrealEnginePython.uplugin | 13 +- 17 files changed, 727 insertions(+), 185 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp create mode 100644 Source/UnrealEnginePython/Public/PyNativeWidgetHost.h diff --git a/Source/PythonConsole/Private/PythonScriptFactory.cpp b/Source/PythonConsole/Private/PythonScriptFactory.cpp index 1b1828a7c..b5dba512f 100644 --- a/Source/PythonConsole/Private/PythonScriptFactory.cpp +++ b/Source/PythonConsole/Private/PythonScriptFactory.cpp @@ -5,7 +5,9 @@ UPythonScriptFactory::UPythonScriptFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { +#if !WITH_KNL_PYEXT Formats.Add(FString("py;Python Script")); +#endif bCreateNew = false; bEditAfterNew = true; diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp index 09d952df0..0bcce13ce 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp @@ -507,6 +507,68 @@ static PyObject *py_ue_iconsole_manager_register_variable_float(PyObject *cls, P Py_RETURN_NONE; } +void UPythonConsoleDelegate::OnConsoleCommand(const TArray < FString > & InArgs) +{ + FScopePythonGIL gil; + + PyObject *ret = nullptr; + if (InArgs.Num() == 0) + { + ret = PyObject_CallFunction(py_callable, nullptr); + } + else + { + PyObject *py_args = PyTuple_New(InArgs.Num()); + for (int32 i = 0; i < InArgs.Num(); i++) + { + PyTuple_SetItem(py_args, i, PyUnicode_FromString(TCHAR_TO_UTF8(*InArgs[i]))); + } + ret = PyObject_CallObject(py_callable, py_args); + Py_DECREF(py_args); + } + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +static PyObject *py_ue_iconsole_manager_register_command(PyObject *cls, PyObject * args) +{ + char *key; + PyObject *py_callable; + char *help = nullptr; + if (!PyArg_ParseTuple(args, "sO|s:register_command", &key, &py_callable, &help)) + { + return nullptr; + } + + if (!PyCallable_Check(py_callable)) + { + return PyErr_Format(PyExc_Exception, "argument is not callable"); + } + + IConsoleObject *c_object = IConsoleManager::Get().FindConsoleObject(UTF8_TO_TCHAR(key)); + if (c_object) + { + return PyErr_Format(PyExc_Exception, "console object \"%s\" already exists", key); + } + + UPythonConsoleDelegate *py_delegate = NewObject(); + py_delegate->SetPyCallable(py_callable); + py_delegate->AddToRoot(); + FConsoleCommandWithArgsDelegate console_delegate; + console_delegate.BindUObject(py_delegate, &UPythonConsoleDelegate::OnConsoleCommand); + + if (!IConsoleManager::Get().RegisterConsoleCommand(UTF8_TO_TCHAR(key), help ? UTF8_TO_TCHAR(help) : UTF8_TO_TCHAR(key), console_delegate, 0)) + { + return PyErr_Format(PyExc_Exception, "unable to register console command \"%s\"", key); + } + + Py_RETURN_NONE; +} + static PyMethodDef ue_PyIConsoleManager_methods[] = { { "get_history", (PyCFunction)py_ue_iconsole_manager_get_history, METH_VARARGS | METH_CLASS, "" }, { "add_history_entry", (PyCFunction)py_ue_iconsole_manager_add_history_entry, METH_VARARGS | METH_CLASS, "" }, @@ -533,6 +595,7 @@ static PyMethodDef ue_PyIConsoleManager_methods[] = { { "register_variable_string", (PyCFunction)py_ue_iconsole_manager_register_variable_string, METH_VARARGS | METH_CLASS, "" }, { "register_variable_int", (PyCFunction)py_ue_iconsole_manager_register_variable_int, METH_VARARGS | METH_CLASS, "" }, { "register_variable_float", (PyCFunction)py_ue_iconsole_manager_register_variable_float, METH_VARARGS | METH_CLASS, "" }, + { "register_command", (PyCFunction)py_ue_iconsole_manager_register_command, METH_VARARGS | METH_CLASS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h index b7846e5c8..b147fdf7e 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h @@ -3,6 +3,7 @@ #include "UnrealEnginePython.h" #include "Runtime/Core/Public/HAL/IConsoleManager.h" +#include "UEPyIConsoleManager.generated.h" typedef struct { @@ -10,5 +11,13 @@ typedef struct /* Type-specific fields go here. */ } ue_PyIConsoleManager; +UCLASS() +class UPythonConsoleDelegate : public UPythonDelegate +{ + GENERATED_BODY() + +public: + void OnConsoleCommand(const TArray < FString > &InArgs); +}; void ue_python_init_iconsole_manager(PyObject *); diff --git a/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp b/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp new file mode 100644 index 000000000..486887f6b --- /dev/null +++ b/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp @@ -0,0 +1,20 @@ +#include "UnrealEnginePythonPrivatePCH.h" +#include "PyNativeWidgetHost.h" + +#include "UnrealEnginePython.h" + + + + +UPyNativeWidgetHost::UPyNativeWidgetHost(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + bIsVariable = true; +} + +#if WITH_EDITOR +const FText UPyNativeWidgetHost::GetPaletteCategory() +{ + return NSLOCTEXT("Python", "Python", "Python"); +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index 33cc7bc28..a9dad7ffd 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -1,15 +1,27 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PyUserWidget.h" +#include "PyNativeWidgetHost.h" #include "PythonDelegate.h" #include "Slate/UEPyFGeometry.h" #include "Slate/UEPyFPaintContext.h" +#include "Widgets/Layout/SBox.h" +#include "UMGStyle.h" +#include "Runtime/UMG/Public/Blueprint/WidgetTree.h" + void UPyUserWidget::NativeConstruct() { Super::NativeConstruct(); + WidgetTree->ForEachWidget([&](UWidget* Widget) { + if (Widget->IsA()) + { + PyNativeWidgetHost = Cast(Widget); + } + }); + if (PythonModule.IsEmpty()) return; @@ -62,7 +74,7 @@ void UPyUserWidget::NativeConstruct() if (PythonPaintForceDisabled) bCanEverPaint = false; - + if (!PyObject_HasAttrString(py_user_widget_instance, (char *)"construct")) return; @@ -215,6 +227,33 @@ FReply UPyUserWidget::NativeOnKeyDown(const FGeometry & InGeometry, const FKeyEv return FReply::Unhandled(); } +#if WITH_EDITOR + +const FText UPyUserWidget::GetPaletteCategory() +{ + return NSLOCTEXT("Python", "Python", "Python"); +} +#endif + +void UPyUserWidget::SetSlateWidget(TSharedRef InContent) +{ + if (PyNativeWidgetHost.IsValid()) + { + PyNativeWidgetHost->SetContent(InContent); + } +} + + +void UPyUserWidget::ReleaseSlateResources(bool bReleaseChildren) +{ + Super::ReleaseSlateResources(bReleaseChildren); +} + +TSharedRef UPyUserWidget::RebuildWidget() +{ + return Super::RebuildWidget(); +} + FReply UPyUserWidget::NativeOnMouseWheel(const FGeometry & InGeometry, const FPointerEvent & InMouseEvent) { Super::NativeOnMouseWheel(InGeometry, InMouseEvent); @@ -308,6 +347,10 @@ void UPyUserWidget::NativePaint(FPaintContext & InContext) const Py_DECREF(ret); } +UPyUserWidget::UPyUserWidget(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{} + UPyUserWidget::~UPyUserWidget() { FScopePythonGIL gil; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp index 617e6c5bf..c1e4deaac 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp @@ -8,8 +8,31 @@ #define sw_python_tree_view StaticCastSharedRef(self->s_tree_view.s_list_view.s_table_view_base.s_compound_widget.s_widget.s_widget) +static PyObject *py_ue_spython_tree_view_set_item_expansion(ue_PySPythonTreeView *self, PyObject * args) +{ + PyObject *py_item; + PyObject *py_bool; + if (!PyArg_ParseTuple(args, "OO:set_item_expansion", &py_item, &py_bool)) + { + return nullptr; + } + sw_python_tree_view->SetPythonItemExpansion(py_item, PyObject_IsTrue(py_bool) ? true : false); + Py_RETURN_NONE; +} + +void SPythonTreeView::SetPythonItemExpansion(PyObject *item, bool InShouldExpandItem) +{ + for (TSharedPtr PythonItem : *ItemsSource) + { + if (PythonItem->py_object == item) + { + SetItemExpansion(PythonItem, InShouldExpandItem); + } + } +} static PyMethodDef ue_PySPythonTreeView_methods[] = { + { "set_item_expansion", (PyCFunction)py_ue_spython_tree_view_set_item_expansion, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -44,25 +67,29 @@ PyTypeObject ue_PySPythonTreeViewType = { ue_PySPythonTreeView_methods, /* tp_methods */ }; -static int ue_py_spython_tree_view_init(ue_PySPythonTreeView *self, PyObject *args, PyObject *kwargs) { +static int ue_py_spython_tree_view_init(ue_PySPythonTreeView *self, PyObject *args, PyObject *kwargs) +{ ue_py_slate_setup_farguments(SPythonTreeView); // first of all check for values PyObject *values = ue_py_dict_get_item(kwargs, "tree_items_source"); - if (!values) { + if (!values) + { PyErr_SetString(PyExc_Exception, "you must specify tree items"); return -1; } values = PyObject_GetIter(values); - if (!values) { + if (!values) + { PyErr_SetString(PyExc_Exception, "values field is not an iterable"); return -1; } TArray> *items = new TArray>(); - while (PyObject *item = PyIter_Next(values)) { + while (PyObject *item = PyIter_Next(values)) + { Py_INCREF(item); // keep track of items self->s_tree_view.s_list_view.s_table_view_base.s_compound_widget.s_widget.py_refs.Add(item); @@ -88,7 +115,8 @@ static int ue_py_spython_tree_view_init(ue_PySPythonTreeView *self, PyObject *ar return 0; } -void ue_python_init_spython_tree_view(PyObject *ue_module) { +void ue_python_init_spython_tree_view(PyObject *ue_module) +{ ue_PySPythonTreeViewType.tp_init = (initproc)ue_py_spython_tree_view_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.h index 911fd78d3..4c20d9e1c 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.h @@ -6,14 +6,19 @@ extern PyTypeObject ue_PySPythonTreeViewType; -class SPythonTreeView : public STreeView> { +class SPythonTreeView : public STreeView> +{ public: - ~SPythonTreeView() { + ~SPythonTreeView() + { delete(ItemsSource); } + + void SetPythonItemExpansion(PyObject *item, bool InShouldExpandItem); }; -typedef struct { +typedef struct +{ ue_PySTreeView s_tree_view; /* Type-specific fields go here. */ } ue_PySPythonTreeView; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTreeView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySTreeView.cpp index 6f5f93679..a89b178ed 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTreeView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTreeView.cpp @@ -3,8 +3,23 @@ #include "UEPySTreeView.h" +#define sw_tree_view StaticCastSharedRef>>(self->s_list_view.s_table_view_base.s_compound_widget.s_widget.s_widget) + +static PyObject *py_ue_stree_view_request_tree_refresh(ue_PySTreeView *self, PyObject * args) +{ + sw_tree_view->RequestTreeRefresh(); + Py_RETURN_NONE; +} + +static PyObject *py_ue_stree_view_clear_expanded_items(ue_PySTreeView *self, PyObject * args) +{ + sw_tree_view->ClearExpandedItems(); + Py_RETURN_NONE; +} static PyMethodDef ue_PySTreeView_methods[] = { + { "request_tree_refresh", (PyCFunction)py_ue_stree_view_request_tree_refresh, METH_VARARGS, "" }, + { "clear_expanded_items", (PyCFunction)py_ue_stree_view_clear_expanded_items, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -40,7 +55,8 @@ PyTypeObject ue_PySTreeViewType = { }; -void ue_python_init_stree_view(PyObject *ue_module) { +void ue_python_init_stree_view(PyObject *ue_module) +{ ue_PySTreeViewType.tp_base = &ue_PySListViewType; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 1a39e5954..76eb7c792 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -3,7 +3,8 @@ #include "UEPySWidget.h" -static PyObject *ue_PySWidget_str(ue_PySWidget *self) { +static PyObject *ue_PySWidget_str(ue_PySWidget *self) +{ #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromFormat("", TCHAR_TO_UTF8(*self->s_widget->GetTypeAsString()), &self->s_widget.Get(), self->s_widget.GetSharedReferenceCount(), self->ob_base.ob_refcnt); @@ -13,10 +14,12 @@ static PyObject *ue_PySWidget_str(ue_PySWidget *self) { #endif } -static PyObject *py_ue_swidget_get_children(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_get_children(ue_PySWidget *self, PyObject * args) +{ FChildren *children = self->s_widget->GetChildren(); PyObject *py_list = PyList_New(0); - for (int32 i = 0; i < children->Num(); i++) { + for (int32 i = 0; i < children->Num(); i++) + { TSharedRef widget = children->GetChildAt(i); PyObject *item = (PyObject *)ue_py_get_swidget(widget); PyList_Append(py_list, item); @@ -25,9 +28,11 @@ static PyObject *py_ue_swidget_get_children(ue_PySWidget *self, PyObject * args) return py_list; } -static PyObject *py_ue_swidget_set_tooltip_text(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_set_tooltip_text(ue_PySWidget *self, PyObject * args) +{ char *text; - if (!PyArg_ParseTuple(args, "s:set_tooltip_text", &text)) { + if (!PyArg_ParseTuple(args, "s:set_tooltip_text", &text)) + { return NULL; } @@ -37,9 +42,11 @@ static PyObject *py_ue_swidget_set_tooltip_text(ue_PySWidget *self, PyObject * a return (PyObject *)self; } -static PyObject *py_ue_swidget_set_cursor(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_set_cursor(ue_PySWidget *self, PyObject * args) +{ int cursor; - if (!PyArg_ParseTuple(args, "i:set_cursor", &cursor)) { + if (!PyArg_ParseTuple(args, "i:set_cursor", &cursor)) + { return NULL; } @@ -49,9 +56,11 @@ static PyObject *py_ue_swidget_set_cursor(ue_PySWidget *self, PyObject * args) { return (PyObject *)self; } -static PyObject *py_ue_swidget_set_enabled(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_set_enabled(ue_PySWidget *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_enabled", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_enabled", &py_bool)) + { return NULL; } @@ -62,13 +71,16 @@ static PyObject *py_ue_swidget_set_enabled(ue_PySWidget *self, PyObject * args) } #if ENGINE_MINOR_VERSION > 12 -static PyObject *py_ue_swidget_bind_on_mouse_button_down(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_bind_on_mouse_button_down(ue_PySWidget *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_down", &py_callable)) { + if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_down", &py_callable)) + { return NULL; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } @@ -84,13 +96,16 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_down(ue_PySWidget *self, PyO return (PyObject *)self; } -static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_up", &py_callable)) { + if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_up", &py_callable)) + { return NULL; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } @@ -106,13 +121,16 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObj return (PyObject *)self; } -static PyObject *py_ue_swidget_bind_on_mouse_double_click(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_bind_on_mouse_double_click(ue_PySWidget *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_mouse_double_click", &py_callable)) { + if (!PyArg_ParseTuple(args, "O:bind_on_mouse_double_click", &py_callable)) + { return NULL; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } @@ -128,13 +146,16 @@ static PyObject *py_ue_swidget_bind_on_mouse_double_click(ue_PySWidget *self, Py return (PyObject *)self; } -static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_mouse_move", &py_callable)) { + if (!PyArg_ParseTuple(args, "O:bind_on_mouse_move", &py_callable)) + { return NULL; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } @@ -152,9 +173,11 @@ static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * #endif -static PyObject *py_ue_swidget_has_keyboard_focus(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_has_keyboard_focus(ue_PySWidget *self, PyObject * args) +{ - if (self->s_widget->HasKeyboardFocus()) { + if (self->s_widget->HasKeyboardFocus()) + { Py_INCREF(Py_True); return Py_True; } @@ -163,14 +186,27 @@ static PyObject *py_ue_swidget_has_keyboard_focus(ue_PySWidget *self, PyObject * return Py_False; } -static PyObject *py_ue_swidget_get_type(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_get_type(ue_PySWidget *self, PyObject * args) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->s_widget->GetTypeAsString()))); } -static PyObject *py_ue_swidget_get_shared_reference_count(ue_PySWidget *self, PyObject * args) { +static PyObject *py_ue_swidget_get_shared_reference_count(ue_PySWidget *self, PyObject * args) +{ return PyLong_FromLong(self->s_widget.GetSharedReferenceCount()); } +static PyObject *py_ue_swidget_invalidate(ue_PySWidget *self, PyObject * args) +{ + int invalidate_mode = 0; + if (!PyArg_ParseTuple(args, "|i:invalidate", &invalidate_mode)) + { + return nullptr; + } + self->s_widget->Invalidate((EInvalidateWidget)invalidate_mode); + Py_RETURN_NONE; +} + static PyMethodDef ue_PySWidget_methods[] = { { "get_shared_reference_count", (PyCFunction)py_ue_swidget_get_shared_reference_count, METH_VARARGS, "" }, { "get_children", (PyCFunction)py_ue_swidget_get_children, METH_VARARGS, "" }, @@ -179,6 +215,7 @@ static PyMethodDef ue_PySWidget_methods[] = { { "set_cursor", (PyCFunction)py_ue_swidget_set_cursor, METH_VARARGS, "" }, { "set_enabled", (PyCFunction)py_ue_swidget_set_enabled, METH_VARARGS, "" }, { "has_keyboard_focus", (PyCFunction)py_ue_swidget_has_keyboard_focus, METH_VARARGS, "" }, + { "invalidate", (PyCFunction)py_ue_swidget_invalidate, METH_VARARGS, "" }, #if ENGINE_MINOR_VERSION > 12 { "bind_on_mouse_button_down", (PyCFunction)py_ue_swidget_bind_on_mouse_button_down, METH_VARARGS, "" }, { "bind_on_mouse_button_up", (PyCFunction)py_ue_swidget_bind_on_mouse_button_down, METH_VARARGS, "" }, @@ -188,30 +225,36 @@ static PyMethodDef ue_PySWidget_methods[] = { { NULL } /* Sentinel */ }; -static void ue_PySWidgett_dealloc(ue_PySWidget *self) { +static void ue_PySWidgett_dealloc(ue_PySWidget *self) +{ #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PySWidget %p mapped to %s %p (slate refcount: %d)"), self, *self->s_widget->GetTypeAsString(), &self->s_widget.Get(), self->s_widget.GetSharedReferenceCount()); #endif Py_DECREF(self->py_dict); - for (UPythonSlateDelegate *item : self->delegates) { + for (UPythonSlateDelegate *item : self->delegates) + { if (item->IsValidLowLevel() && item->IsRooted()) item->RemoveFromRoot(); } - for (ue_PySWidget *item : self->py_swidget_slots) { + for (ue_PySWidget *item : self->py_swidget_slots) + { Py_DECREF(item); } // decref content (if any) Py_XDECREF(self->py_swidget_content); - for (PyObject *item : self->py_refs) { + for (PyObject *item : self->py_refs) + { Py_DECREF(item); } ue_py_unregister_swidget(&self->s_widget.Get()); // decrement widget reference count // but only if python vm is still fully active (hack to avoid crashes on editor shutdown) - if (Py_IsInitialized()) { + if (Py_IsInitialized()) + { self->s_widget = SNullWidget::NullWidget; } - else { + else + { UE_LOG(LogPython, Warning, TEXT("Python VM is being destroyed, skipping ue_PySWidget destruction")); } Py_TYPE(self)->tp_free((PyObject *)self); @@ -255,14 +298,16 @@ ue_PySWidget_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ue_PySWidget *self; self = (ue_PySWidget *)type->tp_alloc(type, 0); - if (self != NULL) { + if (self != NULL) + { ue_py_setup_swidget(self); } return (PyObject *)self; } -void ue_python_init_swidget(PyObject *ue_module) { +void ue_python_init_swidget(PyObject *ue_module) +{ ue_PySWidgetType.tp_new = ue_PySWidget_new; ue_PySWidgetType.tp_getattro = PyObject_GenericGetAttr; @@ -276,7 +321,8 @@ void ue_python_init_swidget(PyObject *ue_module) { PyModule_AddObject(ue_module, "SWidget", (PyObject *)&ue_PySWidgetType); } -ue_PySWidget *py_ue_is_swidget(PyObject *obj) { +ue_PySWidget *py_ue_is_swidget(PyObject *obj) +{ if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySWidgetType)) return nullptr; return (ue_PySWidget *)obj; diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index ad0b98008..daa4cc51d 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -8,9 +8,11 @@ -PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) +{ PyObject *py_message; - if (!PyArg_ParseTuple(args, "O:log", &py_message)) { + if (!PyArg_ParseTuple(args, "O:log", &py_message)) + { return NULL; } @@ -25,9 +27,11 @@ PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) { return Py_None; } -PyObject *py_unreal_engine_log_warning(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_log_warning(PyObject * self, PyObject * args) +{ PyObject *py_message; - if (!PyArg_ParseTuple(args, "O:log_warning", &py_message)) { + if (!PyArg_ParseTuple(args, "O:log_warning", &py_message)) + { return NULL; } @@ -42,9 +46,11 @@ PyObject *py_unreal_engine_log_warning(PyObject * self, PyObject * args) { return Py_None; } -PyObject *py_unreal_engine_log_error(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_log_error(PyObject * self, PyObject * args) +{ PyObject *py_message; - if (!PyArg_ParseTuple(args, "O:log_error", &py_message)) { + if (!PyArg_ParseTuple(args, "O:log_error", &py_message)) + { return NULL; } @@ -59,15 +65,18 @@ PyObject *py_unreal_engine_log_error(PyObject * self, PyObject * args) { return Py_None; } -PyObject *py_unreal_engine_add_on_screen_debug_message(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_add_on_screen_debug_message(PyObject * self, PyObject * args) +{ int key; float time_to_display; PyObject *py_message; - if (!PyArg_ParseTuple(args, "ifO:add_on_screen_debug_message", &key, &time_to_display, &py_message)) { + if (!PyArg_ParseTuple(args, "ifO:add_on_screen_debug_message", &key, &time_to_display, &py_message)) + { return NULL; } - if (!GEngine) { + if (!GEngine) + { Py_INCREF(Py_None); return Py_None; } @@ -85,22 +94,26 @@ PyObject *py_unreal_engine_add_on_screen_debug_message(PyObject * self, PyObject return Py_None; } -PyObject *py_unreal_engine_print_string(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_print_string(PyObject * self, PyObject * args) +{ PyObject *py_message; float timeout = 2.0; PyObject *py_color = nullptr; - if (!PyArg_ParseTuple(args, "O|fO:print_string", &py_message, &timeout, &py_color)) { + if (!PyArg_ParseTuple(args, "O|fO:print_string", &py_message, &timeout, &py_color)) + { return NULL; } - if (!GEngine) { + if (!GEngine) + { Py_RETURN_NONE; } FColor color = FColor::Cyan; - if (py_color) { + if (py_color) + { ue_PyFColor *f_color = py_ue_is_fcolor(py_color); if (!f_color) return PyErr_Format(PyExc_Exception, "argument is not a FColor"); @@ -119,7 +132,8 @@ PyObject *py_unreal_engine_print_string(PyObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_unreal_engine_get_forward_vector(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_forward_vector(PyObject * self, PyObject * args) +{ FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; @@ -127,7 +141,8 @@ PyObject *py_unreal_engine_get_forward_vector(PyObject * self, PyObject * args) return py_ue_new_fvector(vec); } -PyObject *py_unreal_engine_get_right_vector(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_right_vector(PyObject * self, PyObject * args) +{ FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; @@ -135,7 +150,8 @@ PyObject *py_unreal_engine_get_right_vector(PyObject * self, PyObject * args) { return py_ue_new_fvector(vec); } -PyObject *py_unreal_engine_get_up_vector(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_up_vector(PyObject * self, PyObject * args) +{ FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; @@ -143,53 +159,66 @@ PyObject *py_unreal_engine_get_up_vector(PyObject * self, PyObject * args) { return py_ue_new_fvector(vec); } -PyObject *py_unreal_engine_get_content_dir(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_content_dir(PyObject * self, PyObject * args) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameContentDir())); } -PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) { - return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); +PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) +{ + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); } -PyObject * py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *) { - return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameUserDeveloperDir())); +PyObject * py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *) +{ + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameUserDeveloperDir())); } -PyObject *py_unreal_engine_convert_relative_path_to_full(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_convert_relative_path_to_full(PyObject * self, PyObject * args) +{ char *path; - if (!PyArg_ParseTuple(args, "s:convert_relative_path_to_full", &path)) { + if (!PyArg_ParseTuple(args, "s:convert_relative_path_to_full", &path)) + { return NULL; } return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(UTF8_TO_TCHAR(path)))); } -PyObject *py_unreal_engine_object_path_to_package_name(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_object_path_to_package_name(PyObject * self, PyObject * args) +{ char *path; - if (!PyArg_ParseTuple(args, "s:object_path_to_package_name", &path)) { + if (!PyArg_ParseTuple(args, "s:object_path_to_package_name", &path)) + { return NULL; } return PyUnicode_FromString(TCHAR_TO_UTF8(*FPackageName::ObjectPathToPackageName(UTF8_TO_TCHAR(path)))); } -PyObject *py_unreal_engine_get_path(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_path(PyObject * self, PyObject * args) +{ char *path; - if (!PyArg_ParseTuple(args, "s:get_path", &path)) { + if (!PyArg_ParseTuple(args, "s:get_path", &path)) + { return NULL; } return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GetPath(UTF8_TO_TCHAR(path)))); } -PyObject *py_unreal_engine_get_base_filename(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_base_filename(PyObject * self, PyObject * args) +{ char *path; - if (!PyArg_ParseTuple(args, "s:get_base_filename", &path)) { + if (!PyArg_ParseTuple(args, "s:get_base_filename", &path)) + { return NULL; } return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GetBaseFilename(UTF8_TO_TCHAR(path)))); } -PyObject *py_unreal_engine_create_world(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_create_world(PyObject * self, PyObject * args) +{ int world_type = 0; - if (!PyArg_ParseTuple(args, "|i:create_world", &world_type)) { + if (!PyArg_ParseTuple(args, "|i:create_world", &world_type)) + { return NULL; } @@ -202,9 +231,11 @@ PyObject *py_unreal_engine_create_world(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:find_class", &name)) { + if (!PyArg_ParseTuple(args, "s:find_class", &name)) + { return NULL; } @@ -220,9 +251,11 @@ PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:find_enum", &name)) { + if (!PyArg_ParseTuple(args, "s:find_enum", &name)) + { return NULL; } @@ -239,9 +272,11 @@ PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) { } -PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:load_package", &name)) { + if (!PyArg_ParseTuple(args, "s:load_package", &name)) + { return nullptr; } @@ -257,10 +292,12 @@ PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) +{ char *name; char *filename = nullptr; - if (!PyArg_ParseTuple(args, "s|s:load_class", &name, &filename)) { + if (!PyArg_ParseTuple(args, "s|s:load_class", &name, &filename)) + { return NULL; } @@ -280,10 +317,12 @@ PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) +{ char *name; char *filename = nullptr; - if (!PyArg_ParseTuple(args, "s|s:load_enum", &name, &filename)) { + if (!PyArg_ParseTuple(args, "s|s:load_enum", &name, &filename)) + { return NULL; } @@ -303,9 +342,11 @@ PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:find_struct", &name)) { + if (!PyArg_ParseTuple(args, "s:find_struct", &name)) + { return NULL; } @@ -322,10 +363,12 @@ PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) { } -PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) +{ char *name; char *filename = nullptr; - if (!PyArg_ParseTuple(args, "s|s:load_struct", &name, &filename)) { + if (!PyArg_ParseTuple(args, "s|s:load_struct", &name, &filename)) + { return NULL; } @@ -347,20 +390,24 @@ PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) { } -PyObject *py_unreal_engine_load_object(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_load_object(PyObject * self, PyObject * args) +{ PyObject *obj; char *name; char *filename = nullptr; - if (!PyArg_ParseTuple(args, "Os|s:load_object", &obj, &name, &filename)) { + if (!PyArg_ParseTuple(args, "Os|s:load_object", &obj, &name, &filename)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass"); } @@ -383,31 +430,37 @@ PyObject *py_unreal_engine_load_object(PyObject * self, PyObject * args) { } -PyObject *py_unreal_engine_string_to_guid(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_string_to_guid(PyObject * self, PyObject * args) +{ char *str; - if (!PyArg_ParseTuple(args, "s:string_to_guid", &str)) { + if (!PyArg_ParseTuple(args, "s:string_to_guid", &str)) + { return NULL; } FGuid guid; - if (FGuid::Parse(FString(str), guid)) { + if (FGuid::Parse(FString(str), guid)) + { return py_ue_new_uscriptstruct(FindObject(ANY_PACKAGE, UTF8_TO_TCHAR((char *)"Guid")), (uint8 *)&guid); } return PyErr_Format(PyExc_Exception, "unable to build FGuid"); } -PyObject *py_unreal_engine_slate_tick(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_slate_tick(PyObject * self, PyObject * args) +{ FSlateApplication::Get().PumpMessages(); FSlateApplication::Get().Tick(); Py_RETURN_NONE; } -PyObject *py_unreal_engine_engine_tick(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_engine_tick(PyObject * self, PyObject * args) +{ float delta_seconds = FApp::GetDeltaTime(); PyObject *py_bool = nullptr; - if (!PyArg_ParseTuple(args, "|fO:engine_tick", &delta_seconds, &py_bool)) { + if (!PyArg_ParseTuple(args, "|fO:engine_tick", &delta_seconds, &py_bool)) + { return NULL; } @@ -416,14 +469,17 @@ PyObject *py_unreal_engine_engine_tick(PyObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_unreal_engine_get_delta_time(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_delta_time(PyObject * self, PyObject * args) +{ return PyFloat_FromDouble(FApp::GetDeltaTime()); } -PyObject *py_unreal_engine_find_object(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_find_object(PyObject * self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:find_object", &name)) { + if (!PyArg_ParseTuple(args, "s:find_object", &name)) + { return NULL; } @@ -440,16 +496,19 @@ PyObject *py_unreal_engine_find_object(PyObject * self, PyObject * args) { } -PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) +{ PyObject *obj; PyObject *py_outer = NULL; char *name = nullptr; - if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj, &py_outer, &name)) { + if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj, &py_outer, &name)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -462,14 +521,17 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) { FName f_name = NAME_None; - if (name && strlen(name) > 0) { + if (name && strlen(name) > 0) + { f_name = FName(UTF8_TO_TCHAR(name)); } UObject *outer = GetTransientPackage(); - if (py_outer && py_outer != Py_None) { - if (!ue_is_pyuobject(py_outer)) { + if (py_outer && py_outer != Py_None) + { + if (!ue_is_pyuobject(py_outer)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -491,14 +553,17 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) +{ PyObject *obj; - if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj)) { + if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -521,18 +586,22 @@ PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) } -PyObject *py_unreal_engine_new_class(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_new_class(PyObject * self, PyObject * args) +{ PyObject *py_parent; char *name; - if (!PyArg_ParseTuple(args, "Os:new_class", &py_parent, &name)) { + if (!PyArg_ParseTuple(args, "Os:new_class", &py_parent, &name)) + { return NULL; } UClass *parent = nullptr; - if (py_parent != Py_None) { - if (!ue_is_pyuobject(py_parent)) { + if (py_parent != Py_None) + { + if (!ue_is_pyuobject(py_parent)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)py_parent; @@ -552,11 +621,28 @@ PyObject *py_unreal_engine_new_class(PyObject * self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) +{ + + PyObject *ret = PyList_New(0); + + for (TObjectIterator Itr; Itr; ++Itr) + { + ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + if (!py_obj) + continue; + PyList_Append(ret, (PyObject *)py_obj); + } + return ret; +} + +PyObject *py_unreal_engine_all_worlds(PyObject * self, PyObject * args) +{ PyObject *ret = PyList_New(0); - for (TObjectIterator Itr; Itr; ++Itr) { + for (TObjectIterator Itr; Itr; ++Itr) + { ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); if (!py_obj) continue; @@ -565,9 +651,40 @@ PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) { return ret; } -PyObject *py_unreal_engine_create_and_dispatch_when_ready(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_tobject_iterator(PyObject * self, PyObject * args) +{ + + PyObject *py_class; + if (!PyArg_ParseTuple(args, "O:tobject_iterator", &py_class)) + { + return NULL; + } + + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + { + return PyErr_Format(PyExc_TypeError, "argument is not a UClass"); + } + + PyObject *ret = PyList_New(0); + + for (TObjectIterator Itr; Itr; ++Itr) + { + if (!(*Itr)->IsA(u_class)) + continue; + ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + if (!py_obj) + continue; + PyList_Append(ret, (PyObject *)py_obj); + } + return ret; +} + +PyObject *py_unreal_engine_create_and_dispatch_when_ready(PyObject * self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:create_and_dispatch_when_ready", &py_callable)) { + if (!PyArg_ParseTuple(args, "O:create_and_dispatch_when_ready", &py_callable)) + { return NULL; } @@ -576,13 +693,16 @@ PyObject *py_unreal_engine_create_and_dispatch_when_ready(PyObject * self, PyObj Py_INCREF(py_callable); - FGraphEventRef task = FFunctionGraphTask::CreateAndDispatchWhenReady([&]() { + FGraphEventRef task = FFunctionGraphTask::CreateAndDispatchWhenReady([&]() + { FScopePythonGIL gil; PyObject *ret = PyObject_CallObject(py_callable, nullptr); - if (ret) { + if (ret) + { Py_DECREF(ret); } - else { + else + { unreal_engine_py_log_error(); } Py_DECREF(py_callable); @@ -596,9 +716,11 @@ PyObject *py_unreal_engine_create_and_dispatch_when_ready(PyObject * self, PyObj return Py_None; } -PyObject *py_unreal_engine_get_game_viewport_size(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_game_viewport_size(PyObject *self, PyObject * args) +{ - if (!GEngine->GameViewport) { + if (!GEngine->GameViewport) + { return PyErr_Format(PyExc_Exception, "unable to get GameViewport"); } @@ -609,20 +731,24 @@ PyObject *py_unreal_engine_get_game_viewport_size(PyObject *self, PyObject * arg } -PyObject *py_unreal_engine_get_resolution(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_resolution(PyObject *self, PyObject * args) +{ return Py_BuildValue("(ff)", GSystemResolution.ResX, GSystemResolution.ResY); } -PyObject *py_unreal_engine_get_viewport_screenshot(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_viewport_screenshot(PyObject *self, PyObject * args) +{ - if (!GEngine->GameViewport) { + if (!GEngine->GameViewport) + { Py_INCREF(Py_None); return Py_None; } PyObject *py_bool = nullptr; bool as_int_list = false; - if (!PyArg_ParseTuple(args, "|O:get_viewport_screenshot", &py_bool)) { + if (!PyArg_ParseTuple(args, "|O:get_viewport_screenshot", &py_bool)) + { return NULL; } @@ -634,14 +760,17 @@ PyObject *py_unreal_engine_get_viewport_screenshot(PyObject *self, PyObject * ar bool success = GetViewportScreenShot(viewport, bitmap); - if (!success) { + if (!success) + { Py_INCREF(Py_None); return Py_None; } - if (as_int_list) { + if (as_int_list) + { PyObject *bitmap_tuple = PyTuple_New(bitmap.Num() * 4); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i * 4, PyLong_FromLong(bitmap[i].R)); PyTuple_SetItem(bitmap_tuple, i * 4 + 1, PyLong_FromLong(bitmap[i].G)); PyTuple_SetItem(bitmap_tuple, i * 4 + 2, PyLong_FromLong(bitmap[i].B)); @@ -651,16 +780,19 @@ PyObject *py_unreal_engine_get_viewport_screenshot(PyObject *self, PyObject * ar } PyObject *bitmap_tuple = PyTuple_New(bitmap.Num()); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i, py_ue_new_fcolor(bitmap[i])); } return bitmap_tuple; } -PyObject *py_unreal_engine_get_viewport_size(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_viewport_size(PyObject *self, PyObject * args) +{ - if (!GEngine->GameViewport) { + if (!GEngine->GameViewport) + { Py_INCREF(Py_None); return Py_None; } @@ -676,17 +808,20 @@ PyObject *py_unreal_engine_get_viewport_size(PyObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_unreal_engine_editor_get_active_viewport_screenshot(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_editor_get_active_viewport_screenshot(PyObject *self, PyObject * args) +{ FViewport *viewport = GEditor->GetActiveViewport(); - if (!viewport) { + if (!viewport) + { Py_INCREF(Py_None); return Py_None; } PyObject *py_bool = nullptr; bool as_int_list = false; - if (!PyArg_ParseTuple(args, "|O:editor_get_active_viewport_screenshot", &py_bool)) { + if (!PyArg_ParseTuple(args, "|O:editor_get_active_viewport_screenshot", &py_bool)) + { return NULL; } @@ -697,14 +832,17 @@ PyObject *py_unreal_engine_editor_get_active_viewport_screenshot(PyObject *self, bool success = GetViewportScreenShot(viewport, bitmap); - if (!success) { + if (!success) + { Py_INCREF(Py_None); return Py_None; } - if (as_int_list) { + if (as_int_list) + { PyObject *bitmap_tuple = PyTuple_New(bitmap.Num() * 4); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i * 4, PyLong_FromLong(bitmap[i].R)); PyTuple_SetItem(bitmap_tuple, i * 4 + 1, PyLong_FromLong(bitmap[i].G)); PyTuple_SetItem(bitmap_tuple, i * 4 + 2, PyLong_FromLong(bitmap[i].B)); @@ -714,18 +852,21 @@ PyObject *py_unreal_engine_editor_get_active_viewport_screenshot(PyObject *self, } PyObject *bitmap_tuple = PyTuple_New(bitmap.Num()); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i, py_ue_new_fcolor(bitmap[i])); } return bitmap_tuple; } -PyObject *py_unreal_engine_editor_get_active_viewport_size(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_editor_get_active_viewport_size(PyObject *self, PyObject * args) +{ FViewport *viewport = GEditor->GetActiveViewport(); - if (!viewport) { + if (!viewport) + { Py_INCREF(Py_None); return Py_None; } @@ -740,17 +881,20 @@ PyObject *py_unreal_engine_editor_get_active_viewport_size(PyObject *self, PyObj return tuple_size; } -PyObject *py_unreal_engine_editor_get_pie_viewport_screenshot(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_editor_get_pie_viewport_screenshot(PyObject *self, PyObject * args) +{ FViewport *viewport = GEditor->GetPIEViewport(); - if (!viewport) { + if (!viewport) + { Py_INCREF(Py_None); return Py_None; } PyObject *py_bool = nullptr; bool as_int_list = false; - if (!PyArg_ParseTuple(args, "|O:editor_get_pie_viewport_screenshot", &py_bool)) { + if (!PyArg_ParseTuple(args, "|O:editor_get_pie_viewport_screenshot", &py_bool)) + { return NULL; } @@ -761,14 +905,17 @@ PyObject *py_unreal_engine_editor_get_pie_viewport_screenshot(PyObject *self, Py bool success = GetViewportScreenShot(viewport, bitmap); - if (!success) { + if (!success) + { Py_INCREF(Py_None); return Py_None; } - if (as_int_list) { + if (as_int_list) + { PyObject *bitmap_tuple = PyTuple_New(bitmap.Num() * 4); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i * 4, PyLong_FromLong(bitmap[i].R)); PyTuple_SetItem(bitmap_tuple, i * 4 + 1, PyLong_FromLong(bitmap[i].G)); PyTuple_SetItem(bitmap_tuple, i * 4 + 2, PyLong_FromLong(bitmap[i].B)); @@ -778,18 +925,21 @@ PyObject *py_unreal_engine_editor_get_pie_viewport_screenshot(PyObject *self, Py } PyObject *bitmap_tuple = PyTuple_New(bitmap.Num()); - for (int i = 0; i < bitmap.Num(); i++) { + for (int i = 0; i < bitmap.Num(); i++) + { PyTuple_SetItem(bitmap_tuple, i, py_ue_new_fcolor(bitmap[i])); } return bitmap_tuple; } -PyObject *py_unreal_engine_editor_get_pie_viewport_size(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_editor_get_pie_viewport_size(PyObject *self, PyObject * args) +{ FViewport *viewport = GEditor->GetPIEViewport(); - if (!viewport) { + if (!viewport) + { Py_INCREF(Py_None); return Py_None; } @@ -803,17 +953,20 @@ PyObject *py_unreal_engine_editor_get_pie_viewport_size(PyObject *self, PyObject } #endif -PyObject *py_unreal_engine_create_package(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_create_package(PyObject *self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:create_package", &name)) { + if (!PyArg_ParseTuple(args, "s:create_package", &name)) + { return nullptr; } UPackage *u_package = (UPackage *)StaticFindObject(nullptr, ANY_PACKAGE, UTF8_TO_TCHAR(name), true); // create a new package if it does not exist - if (u_package) { + if (u_package) + { return PyErr_Format(PyExc_Exception, "package %s already exists", TCHAR_TO_UTF8(*u_package->GetPathName())); } u_package = CreatePackage(nullptr, UTF8_TO_TCHAR(name)); @@ -831,17 +984,20 @@ PyObject *py_unreal_engine_create_package(PyObject *self, PyObject * args) { return (PyObject *)ret; } -PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args) +{ char *name; - if (!PyArg_ParseTuple(args, "s:get_or_create_package", &name)) { + if (!PyArg_ParseTuple(args, "s:get_or_create_package", &name)) + { return nullptr; } UPackage *u_package = (UPackage *)StaticFindObject(nullptr, ANY_PACKAGE, UTF8_TO_TCHAR(name), true); // create a new package if it does not exist - if (!u_package) { + if (!u_package) + { u_package = CreatePackage(nullptr, UTF8_TO_TCHAR(name)); if (!u_package) return PyErr_Format(PyExc_Exception, "unable to create package"); @@ -858,7 +1014,8 @@ PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args return (PyObject *)ret; } -PyObject *py_unreal_engine_get_transient_package(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_get_transient_package(PyObject *self, PyObject * args) +{ ue_PyUObject *ret = ue_get_python_wrapper(GetTransientPackage()); if (!ret) @@ -867,7 +1024,8 @@ PyObject *py_unreal_engine_get_transient_package(PyObject *self, PyObject * args return (PyObject *)ret; } -PyObject *py_unreal_engine_open_file_dialog(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_open_file_dialog(PyObject *self, PyObject * args) +{ char *title; char *default_path = (char *)""; char *default_file = (char *)""; @@ -895,18 +1053,21 @@ PyObject *py_unreal_engine_open_file_dialog(PyObject *self, PyObject * args) { FString(UTF8_TO_TCHAR(default_file)), FString(UTF8_TO_TCHAR(file_types)), (py_multiple && PyObject_IsTrue(py_multiple)) ? EFileDialogFlags::Multiple : EFileDialogFlags::None, - files)) { + files)) + { Py_RETURN_NONE; } PyObject *py_list = PyList_New(0); - for (FString file : files) { + for (FString file : files) + { PyList_Append(py_list, PyUnicode_FromString(TCHAR_TO_UTF8(*file))); } return py_list; } -PyObject *py_unreal_engine_open_directory_dialog(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_open_directory_dialog(PyObject *self, PyObject * args) +{ char *title; char *default_path = (char *)""; @@ -928,15 +1089,17 @@ PyObject *py_unreal_engine_open_directory_dialog(PyObject *self, PyObject * args if (!DesktopPlatform->OpenDirectoryDialog(ParentWindow->GetNativeWindow()->GetOSWindowHandle(), FString(UTF8_TO_TCHAR(title)), FString(UTF8_TO_TCHAR(default_path)), - choosen_dir)) { + choosen_dir)) + { Py_RETURN_NONE; } return PyUnicode_FromString(TCHAR_TO_UTF8(*choosen_dir)); } -PyObject *py_unreal_engine_open_font_dialog(PyObject *self, PyObject * args) { - +PyObject *py_unreal_engine_open_font_dialog(PyObject *self, PyObject * args) +{ + IDesktopPlatform *DesktopPlatform = FDesktopPlatformModule::Get(); if (!DesktopPlatform) return PyErr_Format(PyExc_Exception, "unable to get reference to DesktopPlatform module"); @@ -952,14 +1115,16 @@ PyObject *py_unreal_engine_open_font_dialog(PyObject *self, PyObject * args) { float height; EFontImportFlags flags; - if (!DesktopPlatform->OpenFontDialog(ParentWindow->GetNativeWindow()->GetOSWindowHandle(), font_name, height, flags)) { + if (!DesktopPlatform->OpenFontDialog(ParentWindow->GetNativeWindow()->GetOSWindowHandle(), font_name, height, flags)) + { Py_RETURN_NONE; } return Py_BuildValue((char*)"(sfi)", TCHAR_TO_UTF8(*font_name), height, flags); } -PyObject *py_unreal_engine_save_file_dialog(PyObject *self, PyObject * args) { +PyObject *py_unreal_engine_save_file_dialog(PyObject *self, PyObject * args) +{ char *title; char *default_path = (char *)""; char *default_file = (char *)""; @@ -987,13 +1152,81 @@ PyObject *py_unreal_engine_save_file_dialog(PyObject *self, PyObject * args) { FString(UTF8_TO_TCHAR(default_file)), FString(UTF8_TO_TCHAR(file_types)), (py_multiple && PyObject_IsTrue(py_multiple)) ? EFileDialogFlags::Multiple : EFileDialogFlags::None, - files)) { + files)) + { Py_RETURN_NONE; } PyObject *py_list = PyList_New(0); - for (FString file : files) { + for (FString file : files) + { PyList_Append(py_list, PyUnicode_FromString(TCHAR_TO_UTF8(*file))); } return py_list; +} + +PyObject *py_unreal_engine_copy_properties_for_unrelated_objects(PyObject * self, PyObject * args, PyObject *kwargs) +{ + + PyObject *old_py_object; + PyObject *new_py_object; + + PyObject *py_aggressive_default_subobject_replacement = nullptr; + PyObject *py_copy_deprecated_properties = nullptr; + PyObject *py_do_delta = nullptr; + PyObject *py_notify_object_replacement = nullptr; + PyObject *py_preserve_root_component = nullptr; + PyObject *py_replace_object_class_references = nullptr; + PyObject *py_skip_compiler_generated_defaults = nullptr; + + static char *kw_names[] = { + (char *)"old_object", + (char *)"new_object", + (char *)"aggressive_default_subobject_replacement", + (char *)"copy_deprecated_properties", + (char *)"do_delta", + (char *)"notify_object_replacement", + (char *)"preserve_root_component", + (char *)"replace_object_class_references", + (char *)"skip_compiler_generated_defaults", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOOOOOO:copy_properties_for_unrelated_objects", kw_names, + &old_py_object, + &new_py_object, + &py_aggressive_default_subobject_replacement, + &py_copy_deprecated_properties, + &py_do_delta, + &py_notify_object_replacement, + &py_preserve_root_component, + &py_replace_object_class_references, + &py_skip_compiler_generated_defaults)) + { + return nullptr; + } + + UObject *old_object = ue_py_check_type(old_py_object); + if (!old_object) + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + + UObject *new_object = ue_py_check_type(new_py_object); + if (!new_object) + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + + UEngine::FCopyPropertiesForUnrelatedObjectsParams params; + params.bAggressiveDefaultSubobjectReplacement = (py_aggressive_default_subobject_replacement && PyObject_IsTrue(py_aggressive_default_subobject_replacement)); + params.bCopyDeprecatedProperties = (py_copy_deprecated_properties && PyObject_IsTrue(py_copy_deprecated_properties)); + params.bDoDelta = (py_do_delta && PyObject_IsTrue(py_do_delta)); + params.bNotifyObjectReplacement = (py_notify_object_replacement && PyObject_IsTrue(py_notify_object_replacement)); + params.bPreserveRootComponent = (py_preserve_root_component && PyObject_IsTrue(py_preserve_root_component)); + params.bReplaceObjectClassReferences = (py_replace_object_class_references && PyObject_IsTrue(py_replace_object_class_references)); + params.bSkipCompilerGeneratedDefaults = (py_skip_compiler_generated_defaults && PyObject_IsTrue(py_skip_compiler_generated_defaults)); + + GEngine->CopyPropertiesForUnrelatedObjects( + old_object, + new_object, + params); + + Py_RETURN_NONE; } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index 5e90ff6a4..cc1da3025 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -38,6 +38,8 @@ PyObject *py_unreal_engine_slate_tick(PyObject *, PyObject *); PyObject *py_unreal_engine_get_delta_time(PyObject *, PyObject *); PyObject *py_unreal_engine_all_classes(PyObject *, PyObject *); +PyObject *py_unreal_engine_all_worlds(PyObject *, PyObject *); +PyObject *py_unreal_engine_tobject_iterator(PyObject *, PyObject *); PyObject *py_unreal_engine_new_object(PyObject *, PyObject *); @@ -67,6 +69,8 @@ PyObject *py_unreal_engine_open_font_dialog(PyObject *, PyObject *); PyObject *py_unreal_engine_open_directory_dialog(PyObject *, PyObject *); PyObject *py_unreal_engine_save_file_dialog(PyObject *, PyObject *); +PyObject *py_unreal_engine_copy_properties_for_unrelated_objects(PyObject *, PyObject *, PyObject *); + #if WITH_EDITOR PyObject *py_unreal_engine_editor_get_active_viewport_screenshot(PyObject *, PyObject *); PyObject *py_unreal_engine_editor_get_pie_viewport_screenshot(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 8fb3161e9..271bedbb4 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -168,8 +168,8 @@ static PyMethodDef unreal_engine_methods[] = { { "get_right_vector", py_unreal_engine_get_right_vector, METH_VARARGS, "" }, { "get_content_dir", py_unreal_engine_get_content_dir, METH_VARARGS, "" }, - { "get_game_saved_dir", py_unreal_engine_get_game_saved_dir, METH_VARARGS, "" }, - { "get_game_user_developer_dir", py_unreal_engine_get_game_user_developer_dir, METH_VARARGS, "" }, + { "get_game_saved_dir", py_unreal_engine_get_game_saved_dir, METH_VARARGS, "" }, + { "get_game_user_developer_dir", py_unreal_engine_get_game_user_developer_dir, METH_VARARGS, "" }, { "convert_relative_path_to_full", py_unreal_engine_convert_relative_path_to_full, METH_VARARGS, "" }, { "get_path", py_unreal_engine_get_path, METH_VARARGS, "" }, @@ -310,7 +310,8 @@ static PyMethodDef unreal_engine_methods[] = { { "get_mutable_default", py_unreal_engine_get_mutable_default, METH_VARARGS, "" }, { "all_classes", (PyCFunction)py_unreal_engine_all_classes, METH_VARARGS, "" }, - + { "all_worlds", (PyCFunction)py_unreal_engine_all_worlds, METH_VARARGS, "" }, + { "tobject_iterator", (PyCFunction)py_unreal_engine_tobject_iterator, METH_VARARGS, "" }, { "new_class", py_unreal_engine_new_class, METH_VARARGS, "" }, @@ -358,6 +359,8 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_take_high_res_screen_shots", py_unreal_engine_editor_take_high_res_screen_shots, METH_VARARGS, "" }, #endif +#pragma warning(suppress: 4191) + { "copy_properties_for_unrelated_objects", (PyCFunction)py_unreal_engine_copy_properties_for_unrelated_objects, METH_VARARGS | METH_KEYWORDS, "" }, { NULL, NULL }, }; @@ -2335,6 +2338,13 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer) Py_DECREF(py_long); return true; } + if (auto casted_prop = Cast(prop)) + { + PyObject *py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long)); + Py_DECREF(py_long); + return true; + } if (auto casted_prop = Cast(prop)) { PyObject *py_long = PyNumber_Long(py_obj); @@ -2342,6 +2352,13 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer) Py_DECREF(py_long); return true; } + if (auto casted_prop = Cast(prop)) + { + PyObject *py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLongLong(py_long)); + Py_DECREF(py_long); + return true; + } if (auto casted_prop = Cast(prop)) { PyObject *py_float = PyNumber_Float(py_obj); @@ -2861,6 +2878,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } + //TODO: rdeioris: Should assert warn if u_funciton->ParmsSize != u_function->PropertiesSize bc it overflowed uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); @@ -2869,7 +2887,11 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * for (; IArgs && (IArgs->PropertyFlags & CPF_Parm); ++IArgs) { UProperty *prop = *IArgs; - prop->InitializeValue_InContainer(buffer); + if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) + { + prop->InitializeValue_InContainer(buffer); + } + #if WITH_EDITOR FString default_key = FString("CPP_Default_") + prop->GetName(); FString default_key_value = u_function->GetMetaData(FName(*default_key)); @@ -2882,6 +2904,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * #endif } #endif + } Py_ssize_t tuple_len = PyTuple_Size(args); diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 596745400..cd3b880de 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -263,7 +263,8 @@ PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, uint8 *data) { ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); ret->u_struct = u_struct; uint8 *struct_data = (uint8*)FMemory::Malloc(u_struct->GetStructureSize()); - FMemory::Memcpy(struct_data, data, u_struct->GetStructureSize()); + ret->u_struct->InitializeStruct(struct_data); + ret->u_struct->CopyScriptStruct(struct_data, data); ret->data = struct_data; return (PyObject *)ret; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp index 285e0d2a6..758ce7163 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp @@ -2,6 +2,7 @@ #include "Runtime/UMG/Public/Components/WidgetComponent.h" +#include "PyUserWidget.h" PyObject *py_ue_set_slate_widget(ue_PyUObject * self, PyObject * args) @@ -17,14 +18,22 @@ PyObject *py_ue_set_slate_widget(ue_PyUObject * self, PyObject * args) } UWidgetComponent *widget_component = ue_py_check_type(self); - if (!widget_component) - return PyErr_Format(PyExc_Exception, "uobject is not a UWidgetComponent"); + UPyUserWidget *py_user_widget = ue_py_check_type(self); + if (!widget_component && !py_user_widget) + return PyErr_Format(PyExc_Exception, "uobject is not a UWidgetComponent or UPyUserWidget"); ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); if (!s_widget) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - widget_component->SetSlateWidget(s_widget->s_widget); + if (widget_component) + { + widget_component->SetSlateWidget(s_widget->s_widget); + } + else + { + py_user_widget->SetSlateWidget(s_widget->s_widget); + } Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h new file mode 100644 index 000000000..b80dcf93e --- /dev/null +++ b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h @@ -0,0 +1,24 @@ +// Copyright Kite & Lightning + +#pragma once + +#include "CoreMinimal.h" +#include "Components/NativeWidgetHost.h" +#include "PyNativeWidgetHost.generated.h" + +/** + * + */ +UCLASS() +class UNREALENGINEPYTHON_API UPyNativeWidgetHost : public UNativeWidgetHost +{ + GENERATED_BODY() + + + UPyNativeWidgetHost(const FObjectInitializer& ObjectInitializer); + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + +}; diff --git a/Source/UnrealEnginePython/Public/PyUserWidget.h b/Source/UnrealEnginePython/Public/PyUserWidget.h index 964f05cc7..3d42c3d17 100644 --- a/Source/UnrealEnginePython/Public/PyUserWidget.h +++ b/Source/UnrealEnginePython/Public/PyUserWidget.h @@ -12,7 +12,7 @@ class UPyUserWidget : public UUserWidget GENERATED_BODY() public: - + UPyUserWidget(const FObjectInitializer& ObjectInitializer); ~UPyUserWidget(); virtual void NativeConstruct() override; @@ -44,6 +44,21 @@ class UPyUserWidget : public UUserWidget UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite, meta = (ExposeOnSpawn = true)) bool PythonPaintForceDisabled; + UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite) + TWeakObjectPtr PyNativeWidgetHost; + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + + void SetSlateWidget(TSharedRef InContent); + virtual void ReleaseSlateResources(bool bReleaseChildren) override; + +protected: + // UWidget interface + virtual TSharedRef RebuildWidget() override; + // End of UWidget interface + private: PyObject *py_user_widget_instance; // mapped uobject, required for debug and advanced reflection diff --git a/UnrealEnginePython.uplugin b/UnrealEnginePython.uplugin index 6cbb9b4dc..5c3b2e07e 100644 --- a/UnrealEnginePython.uplugin +++ b/UnrealEnginePython.uplugin @@ -15,11 +15,11 @@ "IsBetaVersion": true, "Installed": false, "Modules": [ - { - "Name": "UnrealEnginePython", - "Type": "Editor", - "LoadingPhase": "Default" - }, + { + "Name": "UnrealEnginePython", + "Type": "Runtime", + "LoadingPhase": "Default" + }, { "Name": "PythonConsole", "Type": "Editor", @@ -36,5 +36,6 @@ "Name": "LevelSequenceEditor", "Enabled": true } - ] + ], + "BlacklistTargets": ["Server"] } From 5851564f595e48743ed27f241c3569a83cd300ca Mon Sep 17 00:00:00 2001 From: ikrima Date: Sat, 18 Nov 2017 05:00:27 -0800 Subject: [PATCH 11/94] FIX: Adding guards for 4.18 only code --- Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp index 5660715cc..3ea13d553 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp @@ -43,6 +43,7 @@ static PyObject *py_ue_fassetdata_get_thumbnail(ue_PyFAssetData *self, PyObject return py_ue_new_fobject_thumbnail(*thumbnail); } +#if ENGINE_MINOR_VERSION >= 18 static PyObject *py_ue_fassetdata_has_custom_thumbnail(ue_PyFAssetData *self, PyObject * args) { @@ -53,6 +54,7 @@ static PyObject *py_ue_fassetdata_has_custom_thumbnail(ue_PyFAssetData *self, Py Py_RETURN_TRUE; } +#endif static PyObject *py_ue_fassetdata_has_cached_thumbnail(ue_PyFAssetData *self, PyObject * args) { @@ -69,7 +71,9 @@ static PyMethodDef ue_PyFAssetData_methods[] = { { "get_asset", (PyCFunction)py_ue_fassetdata_get_asset, METH_VARARGS, "" }, { "is_asset_loaded", (PyCFunction)py_ue_fassetdata_is_asset_loaded, METH_VARARGS, "" }, { "get_thumbnail", (PyCFunction)py_ue_fassetdata_get_thumbnail, METH_VARARGS, "" }, +#if ENGINE_MINOR_VERSION >= 18 { "has_custom_thumbnail", (PyCFunction)py_ue_fassetdata_has_custom_thumbnail, METH_VARARGS, "" }, +#endif { "has_cached_thumbnail", (PyCFunction)py_ue_fassetdata_has_cached_thumbnail, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; From 369947f7769b437508eb166b740a5df2bb0646ce Mon Sep 17 00:00:00 2001 From: ikrima Date: Sat, 18 Nov 2017 05:01:26 -0800 Subject: [PATCH 12/94] Merge Fix: Adding back our custom logic for loading the plugin to guard against existing python installations --- .../Private/UObject/UEPyWidgetComponent.cpp | 10 ++---- .../Private/UnrealEnginePython.cpp | 32 +++++++++---------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp index 758ce7163..a548be8f6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp @@ -26,14 +26,8 @@ PyObject *py_ue_set_slate_widget(ue_PyUObject * self, PyObject * args) if (!s_widget) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - if (widget_component) - { - widget_component->SetSlateWidget(s_widget->s_widget); - } - else - { - py_user_widget->SetSlateWidget(s_widget->s_widget); - } + if (widget_component) { widget_component->SetSlateWidget(s_widget->s_widget); } + else { py_user_widget->SetSlateWidget(s_widget->s_widget); } Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index e2296bb99..29c52441a 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -159,25 +159,25 @@ void FUnrealEnginePythonModule::StartupModule() BrutalFinalize = false; // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - FString IniValue; - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), IniValue, GEngineIni)) { + FString PythonHome; + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) { #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif - FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *IniValue); + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); Py_SetPythonHome(home); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), IniValue, GEngineIni)) { - IniValue = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); - FPaths::NormalizeFilename(IniValue); - IniValue = FPaths::ConvertRelativePathToFull(IniValue); + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), PythonHome, GEngineIni)) { + PythonHome = FPaths::Combine(*PROJECT_CONTENT_DIR, *PythonHome); + FPaths::NormalizeFilename(PythonHome); + PythonHome = FPaths::ConvertRelativePathToFull(PythonHome); #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif Py_SetPythonHome(home); @@ -245,9 +245,9 @@ void FUnrealEnginePythonModule::StartupModule() // To ensure there are no path conflicts, if we have a valid python home at this point, // we override the current environment entirely with the environment we want to use, // removing any paths to other python environments we aren't using. - if (IniValue.Len() > 0) + if (PythonHome.Len() > 0) { - FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *IniValue); + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); const int32 MaxPathVarLen = 32768; FString OrigPathVar = FString::ChrN(MaxPathVarLen, TEXT('\0')); @@ -268,9 +268,9 @@ void FUnrealEnginePythonModule::StartupModule() // Setup our own paths for PYTHONPATH TArray OurPythonPaths = { - IniValue, - FPaths::Combine(IniValue, TEXT("Lib")), - FPaths::Combine(IniValue, TEXT("Lib/site-packages")), + PythonHome, + FPaths::Combine(PythonHome, TEXT("Lib")), + FPaths::Combine(PythonHome, TEXT("Lib/site-packages")), }; FString PythonPathVars = FString::Join(OurPythonPaths, PathDelimiter); FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONPATH"), *PythonPathVars); From caaedb55b157b831b9abceaf4658c3dde781a66f Mon Sep 17 00:00:00 2001 From: ikrima Date: Sat, 18 Nov 2017 06:13:13 -0800 Subject: [PATCH 13/94] FIX: Can't copy construct FToolBarBuilder because it has no default constructor. Need to placement new it to initialize it --- Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index 684ea57c6..9099097e5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -157,7 +157,7 @@ static PyTypeObject ue_PyFToolBarBuilderType = { }; static int ue_py_ftool_bar_builder_init(ue_PyFToolBarBuilder *self, PyObject *args, PyObject *kwargs) { - self->tool_bar_builder = FToolBarBuilder(nullptr, FMultiBoxCustomization::None); + new(&self->tool_bar_builder) FToolBarBuilder(TSharedPtr(), FMultiBoxCustomization::None); return 0; } From f794a91411c12f414fe97ce358aa1cd46e512e69 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 27 Nov 2017 17:17:50 -0800 Subject: [PATCH 14/94] Add sizzle reel for python plugin --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6b6a32bb9..685c97ecf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Teaser: https://twitter.com/KNLstudio/status/932657812466843648 + # UnrealEnginePython Embed Python in Unreal Engine 4 From b08942d4b6dcaff6b1e101f640beff7ad8c5828a Mon Sep 17 00:00:00 2001 From: ikrima Date: Wed, 6 Dec 2017 07:31:42 -0800 Subject: [PATCH 15/94] Extending SPythonWidget & SPythonListView for fully featured listviews in python -Add RequestListRefresh to PySlate SListView so we can update listviews without recreating them -Extending get display name to uclass objects -Adding ability to set content to spythonwidget -Adding clear_content ability as well so python defined slate widgets can manipulate their child content slots -Adding find slate style function & find icon for class so we can use built-in style sets -Helper macro to create swidgets with variadic arguments for widgets that need to be constructed with parameters (e.g. tablerow) -FIX: Adding calls to base OnPaint for SPythonWidget and returning the correct layer. Needed so child slot content can draw itself -Adding ability to Set Header Row on SPythonListView -Extending python object of SPythonListView to maintain a reference of items separate from its base .refs field We need to track these separately as we clear the item list on update -Adding generic fslate_style.get() that gets specific widget struct styles from slatestyle set (e.g. combostyle, hyperlink,editabletext,etc) -Also ability to get other style struct types such as slatecolor,fontstyle,slatebrush,etc -Adding parsing margin struct in horizontal box add_slot parsing -Adding get_icon() to fslate_icon struct -FIX: Making uy_PyFSlateIcon carry a value type of FSlateIcon and to use placement new to initialize it -FIX: Crash when OnSelectionChanged is called with null item -Add ability to generate multi column row widgets defined in python -Add ability to set idetails_view object source -Adding a ESlateEnums static class that houses all non-bp based slate enums -Adding options to detail view creation (update from selection, lockable, name_area_settings, etc -Adding EUserInterfaceAction option to menu builder -FIX: FMenuBuilder was not properly being initialized -FIX: SHeaderRow AddColumn was being passed a stack reference variable which was crashing after it went out of scope -Adding python object struct check based on IsChildOf --- .../Private/Slate/UEPyFMenuBuilder.cpp | 12 +- .../Private/Slate/UEPyFSlateIcon.cpp | 26 +++- .../Private/Slate/UEPyFSlateIcon.h | 3 +- .../Private/Slate/UEPyFSlateStyleSet.cpp | 121 ++++++++++++++- .../Private/Slate/UEPyFSlateStyleSet.h | 4 +- .../Private/Slate/UEPyFToolBarBuilder.cpp | 2 +- .../Private/Slate/UEPyIDetailsView.cpp | 96 ++++++++++++ .../Private/Slate/UEPyIDetailsView.h | 19 +++ .../Private/Slate/UEPySHeaderRow.cpp | 24 ++- .../Private/Slate/UEPySHeaderRow.h | 2 + .../Private/Slate/UEPySHorizontalBox.cpp | 4 + .../Private/Slate/UEPySListView.cpp | 2 + .../Private/Slate/UEPySPythonListView.cpp | 127 +++++++++++---- .../Private/Slate/UEPySPythonListView.h | 3 + .../Slate/UEPySPythonMultiColumnTableRow.cpp | 83 ++++++++++ .../Slate/UEPySPythonMultiColumnTableRow.h | 110 +++++++++++++ .../Private/Slate/UEPySPythonWidget.cpp | 37 ++++- .../Private/Slate/UEPySPythonWidget.h | 144 +++++++++++++++++- .../Private/Slate/UEPySTableViewBase.cpp | 15 ++ .../Private/Slate/UEPySTableViewBase.h | 4 +- .../Private/Slate/UEPySlate.cpp | 135 +++++++++++++--- .../Private/Slate/UEPySlate.h | 14 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 28 ++++ .../UnrealEnginePython/Private/UEPyModule.h | 15 +- .../Private/UEPyUScriptStruct.cpp | 2 +- .../Private/UEPyUScriptStruct.h | 2 +- .../Private/UObject/UEPyObject.cpp | 7 +- .../Private/UObject/UEPySequencer.cpp | 44 ++++++ .../Private/UObject/UEPySequencer.h | 1 + .../Private/UnrealEnginePythonPrivatePCH.h | 2 + .../Private/Wrappers/UEPyESlateEnums.cpp | 96 ++++++++++++ .../Private/Wrappers/UEPyESlateEnums.h | 15 ++ 32 files changed, 1106 insertions(+), 93 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h create mode 100644 Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp create mode 100644 Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 4519c46aa..f495a2db3 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -41,8 +41,9 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO char *label; char *tooltip; PyObject *py_callable; - PyObject *py_obj = nullptr; - if (!PyArg_ParseTuple(args, "ssO|O:add_menu_entry", &label, &tooltip, &py_callable, &py_obj)) + PyObject *py_obj = nullptr; + PyObject *py_uiaction_obj = nullptr; + if (!PyArg_ParseTuple(args, "ssO|OO:add_menu_entry", &label, &tooltip, &py_callable, &py_obj, &py_uiaction_obj)) return nullptr; if (!PyCallable_Check(py_callable)) @@ -50,6 +51,7 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO return PyErr_Format(PyExc_Exception, "argument is not callable"); } + FExecuteAction handler; UPythonSlateDelegate *py_delegate = NewObject(); py_delegate->SetPyCallable(py_callable); @@ -65,7 +67,9 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); } - self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler)); + ue_PyESlateEnums *py_uiaction_enum = py_uiaction_obj ? py_ue_is_eslate_enums(py_uiaction_obj) : nullptr; + self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler), NAME_None, + py_uiaction_enum ? (EUserInterfaceActionType::Type)(py_uiaction_enum->val) : EUserInterfaceActionType::Button); Py_RETURN_NONE; } @@ -193,7 +197,7 @@ static PyTypeObject ue_PyFMenuBuilderType = { static int ue_py_fmenu_builder_init(ue_PyFMenuBuilder *self, PyObject *args, PyObject *kwargs) { - self->menu_builder = FMenuBuilder(true, nullptr); + new(&self->menu_builder) FMenuBuilder(true, nullptr); return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp index bdd0662e2..ba079ac02 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp @@ -2,14 +2,21 @@ #include "UEPyFSlateIcon.h" +static PyObject *py_ue_fslate_icon_get_icon(ue_PyFSlateIcon *self, PyObject * args) +{ + PyObject *ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (const uint8*)self->icon.GetIcon()); + return ret; +} + static PyMethodDef ue_PyFSlateIcon_methods[] = { + { "get_icon", (PyCFunction)py_ue_fslate_icon_get_icon, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; static PyObject *ue_PyFSlateIcon_str(ue_PyFSlateIcon *self) { return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->icon->GetStyleName().ToString())); + TCHAR_TO_UTF8(*self->icon.GetStyleName().ToString())); } static PyTypeObject ue_PyFSlateIconType = { @@ -55,10 +62,10 @@ static int ue_py_fslate_icon_init(ue_PyFSlateIcon *self, PyObject *args, PyObjec PyErr_SetString(PyExc_ValueError, "you have not specified as style name"); return -1; } - self->icon = new FSlateIcon(FName(style_set), FName(style)); + new(&self->icon) FSlateIcon(FName(style_set), FName(style)); } else { - self->icon = new FSlateIcon(); + new(&self->icon) FSlateIcon(); } return 0; } @@ -75,8 +82,15 @@ void ue_python_init_fslate_icon(PyObject *ue_module) { PyModule_AddObject(ue_module, "FSlateIcon", (PyObject *)&ue_PyFSlateIconType); } +ue_PyFSlateIcon *py_ue_new_fslate_icon(const FSlateIcon slate_icon) +{ + ue_PyFSlateIcon *ret = (ue_PyFSlateIcon *)PyObject_New(ue_PyFSlateIcon, &ue_PyFSlateIconType); + ret->icon = slate_icon; + return ret; +} + ue_PyFSlateIcon *py_ue_is_fslate_icon(PyObject *obj) { - if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFSlateIconType)) - return nullptr; - return (ue_PyFSlateIcon *)obj; + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFSlateIconType)) + return nullptr; + return (ue_PyFSlateIcon *)obj; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h index bfd24f9dd..b32b07af5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h @@ -7,9 +7,10 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ - FSlateIcon *icon; + FSlateIcon icon; } ue_PyFSlateIcon; void ue_python_init_fslate_icon(PyObject *); +ue_PyFSlateIcon *py_ue_new_fslate_icon(const FSlateIcon slate_icon); ue_PyFSlateIcon *py_ue_is_fslate_icon(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp index 601a064e2..5a21fbf50 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp @@ -1,6 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "UEPyFSlateStyleSet.h" +#include "SlateTypes.h" +#include "Map.h" static PyObject *py_ue_fslate_style_set_set_content_root(ue_PyFSlateStyleSet *self, PyObject * args) { @@ -87,9 +89,119 @@ static PyObject *py_ue_fslate_style_set_set(ue_PyFSlateStyleSet *self, PyObject return PyErr_Format(PyExc_ValueError, "unsupported value type"); } +namespace { + template + PyObject* pyGetWidgetStyle(FSlateStyleSet& InStyle, FName PropertyName) + { + const WidgetStyleType styleWidgetStyle = InStyle.GetWidgetStyle(PropertyName); + return py_ue_new_uscriptstruct(WidgetStyleType::StaticStruct(), (const uint8*)&styleWidgetStyle); + } +} + +static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject * args) +{ + if (!(self && self->style_set)) + return PyErr_Format(PyExc_Exception, "fstyleset is in invalid state"); + + char *name = nullptr; + PyObject *py_type = nullptr; + + if (!PyArg_ParseTuple(args, "sO:get", &name, &py_type)) + return NULL; + + PyObject *ret = nullptr; + + if (ue_py_check_struct(py_type)) + { + const FSlateSound& styleSound = self->style_set->GetSound(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateSound::StaticStruct(), (const uint8*)&styleSound); + } + else if (ue_py_check_struct(py_type)) + { + if (const FSlateBrush* styleBrush = self->style_set->GetBrush(FName(name))) + { + ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (const uint8*)styleBrush); + } + } + else if (ue_py_check_struct(py_type)) + { + const FSlateColor styleSlateColor = self->style_set->GetSlateColor(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateColor::StaticStruct(), (const uint8*)&styleSlateColor); + } + else if (ue_py_check_struct(py_type)) + { + const FSlateFontInfo styleFontInfo = self->style_set->GetFontStyle(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateFontInfo::StaticStruct(), (const uint8*)&styleFontInfo); + } + else if (ue_py_check_childstruct(py_type)) + { + typedef TFunction WStyleGetter; + typedef TPair WStylePair; + static const WStylePair validWidgetStyleUStructList[] = { + WStylePair{ FTextBlockStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FButtonStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FComboButtonStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FComboBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FHyperlinkStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FEditableTextStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBarStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FEditableTextBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FInlineEditableTextBlockStyle::StaticStruct(), WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle(InStyle, InName); }) }, + WStylePair{ FProgressBarStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FExpandableAreaStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSearchBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSliderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FVolumeControlStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FInlineTextImageStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSpinBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSplitterStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FTableRowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FTableColumnHeaderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FHeaderRowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FDockTabStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBorderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FWindowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + }; + + for(WStylePair widgetStyleUStruct : validWidgetStyleUStructList) + { + if (do_ue_py_check_struct(py_type, widgetStyleUStruct.Key)) + { + ret = widgetStyleUStruct.Value(*self->style_set, FName(name)); + break; + } + } + + if (!ret) + { + return PyErr_Format(PyExc_ValueError, "Unsupported FSlateWidgetStyle type. Add it manually."); + } + } + else if (py_ue_is_flinearcolor(py_type)) + { + ret = py_ue_new_flinearcolor(self->style_set->GetColor(FName(name))); + } + else if (PyNumber_Check(py_type)) + { + ret = PyFloat_FromDouble(self->style_set->GetFloat(FName(name))); + } + else + { + return PyErr_Format(PyExc_ValueError, "unsupported value type"); + } + + if (!ret) + return PyErr_Format(PyExc_Exception, "Retrieved style object is in invalid state"); + + return ret; + +} + static PyMethodDef ue_PyFSlateStyleSet_methods[] = { { "set_content_root", (PyCFunction)py_ue_fslate_style_set_set_content_root, METH_VARARGS, "" }, { "set", (PyCFunction)py_ue_fslate_style_set_set, METH_VARARGS, "" }, + { "get", (PyCFunction)py_ue_fslate_style_set_get, METH_VARARGS, "" }, { "register", (PyCFunction)py_ue_fslate_style_set_register, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -139,11 +251,18 @@ static int ue_py_fslate_style_set_init(ue_PyFSlateStyleSet *self, PyObject *args { return -1; } - + //TODO: roberto: ikrimae- I think this leaks? How is FSlateIcon destroyed ever? self->style_set = new FSlateStyleSet(name); return 0; } +ue_PyFSlateStyleSet* py_ue_new_fslate_style_set(FSlateStyleSet* styleSet) +{ + ue_PyFSlateStyleSet *ret = (ue_PyFSlateStyleSet *)PyObject_New(ue_PyFSlateStyleSet, &ue_PyFSlateStyleSetType); + ret->style_set = styleSet; + return ret; +} + void ue_python_init_fslate_style_set(PyObject *ue_module) { ue_PyFSlateStyleSetType.tp_new = PyType_GenericNew; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h index 7192ce4f7..58ffe57fd 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h @@ -2,12 +2,12 @@ #include "UnrealEnginePython.h" -#include "Runtime/SlateCore/Public/Styling/SlateStyle.h" - typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FSlateStyleSet *style_set; } ue_PyFSlateStyleSet; +ue_PyFSlateStyleSet* py_ue_new_fslate_style_set(FSlateStyleSet* styleSet); + void ue_python_init_fslate_style_set(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index 9099097e5..6c3cf5fdc 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -54,7 +54,7 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); } - self->tool_bar_builder.AddToolBarButton(FUIAction(handler), FName(hook), FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), *py_slate_icon->icon); + self->tool_bar_builder.AddToolBarButton(FUIAction(handler), FName(hook), FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), py_slate_icon->icon); Py_INCREF(Py_None); return Py_None; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp new file mode 100644 index 000000000..3fb5f2c1c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp @@ -0,0 +1,96 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR + +#include "Editor/PropertyEditor/Public/IDetailsView.h" + +#include "UEPyIDetailsView.h" + + +#define sw_idetails_view StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + +static PyObject *py_ue_idetails_view_set_object(ue_PyIDetailsView *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_in_obj = nullptr; + PyObject *py_force_refresh = nullptr; + + char *kwlist[] = { + (char *)"uobject", + (char *)"force_refresh", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_object", kwlist, &py_in_obj, &py_force_refresh)) + { + return nullptr; + } + + UObject *u_object = ue_py_check_type(py_in_obj); + if (!u_object) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + + const bool bForceRefresh = py_force_refresh && PyObject_IsTrue(py_force_refresh); + + sw_idetails_view->SetObject(u_object, bForceRefresh); + + Py_RETURN_NONE; +} + +static PyMethodDef ue_PyIDetailsView_methods[] = { +#pragma warning(suppress: 4191) + { "set_object", (PyCFunction)py_ue_idetails_view_set_object, METH_VARARGS | METH_KEYWORDS, "" }, + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PyIDetailsViewType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.IDetailsView", /* tp_name */ + sizeof(ue_PyIDetailsView), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine IDetailsView", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyIDetailsView_methods, /* tp_methods */ +}; + +void ue_python_init_idetails_view(PyObject *ue_module) +{ + ue_PyIDetailsViewType.tp_base = &ue_PySCompoundWidgetType; + + if (PyType_Ready(&ue_PyIDetailsViewType) < 0) + return; + + Py_INCREF(&ue_PyIDetailsViewType); + PyModule_AddObject(ue_module, "IDetailsView", (PyObject *)&ue_PyIDetailsViewType); +} + +ue_PyIDetailsView *py_ue_is_idetails_view(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIDetailsViewType)) + return nullptr; + return (ue_PyIDetailsView *)obj; +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h new file mode 100644 index 000000000..b25ff89f8 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h @@ -0,0 +1,19 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +#include "UEPySCompoundWidget.h" + +extern PyTypeObject ue_PySCompoundWidgetType; + +typedef struct { + ue_PySCompoundWidget s_compound_widget; + /* Type-specific fields go here. */ +} ue_PyIDetailsView; + +void ue_python_init_idetails_view(PyObject *); + +ue_PyIDetailsView * py_ue_is_idetails_view(PyObject *obj); + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp index 760294214..a0791255b 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp @@ -39,14 +39,14 @@ static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *a if (!default_tooltip) default_tooltip = default_label; - SHeaderRow::FColumn column = SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) - .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) - .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) - .FixedWidth(fixed_width) - .HAlignCell((EHorizontalAlignment)cell_h_align) - .VAlignCell((EVerticalAlignment)cell_v_align); - - sw_header_row->AddColumn(column); + sw_header_row->AddColumn( + SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) + .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) + .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) + .FixedWidth(fixed_width) + .HAlignCell((EHorizontalAlignment)cell_h_align) + .VAlignCell((EVerticalAlignment)cell_v_align) + ); Py_INCREF(self); return (PyObject *)self; @@ -97,6 +97,7 @@ static int ue_py_sheader_row_init(ue_PySHeaderRow *self, PyObject *args, PyObjec void ue_python_init_sheader_row(PyObject *ue_module) { ue_PySHeaderRowType.tp_init = (initproc)ue_py_sheader_row_init; + ue_PySHeaderRowType.tp_call = (ternaryfunc)py_ue_sheader_row_add_column; ue_PySHeaderRowType.tp_base = &ue_PySBorderType; @@ -107,3 +108,10 @@ void ue_python_init_sheader_row(PyObject *ue_module) { PyModule_AddObject(ue_module, "SHeaderRow", (PyObject *)&ue_PySHeaderRowType); } +ue_PySHeaderRow * py_ue_is_sheader_row(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySHeaderRowType)) + return nullptr; + return (ue_PySHeaderRow *)obj; +} + diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h index 0c755ab0d..0a3129456 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h @@ -14,3 +14,5 @@ typedef struct { } ue_PySHeaderRow; void ue_python_init_sheader_row(PyObject *); + +ue_PySHeaderRow *py_ue_is_sheader_row(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp index ea9e138f2..e0a7332ad 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp @@ -65,6 +65,10 @@ static PyObject *py_ue_shorizontal_box_add_slot(ue_PySHorizontalBox *self, PyObj } fslot.Padding(margin); } + else if (FMargin *parsed_margin = ue_py_check_struct(padding)) + { + fslot.Padding(*parsed_margin); + } else if (PyNumber_Check(padding)) { PyObject *py_float = PyNumber_Float(padding); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp index 463b8a088..898ecd580 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp @@ -3,6 +3,8 @@ #include "UEPySListView.h" +#define sw_slist_view StaticCastSharedRef>>(self->s_table_view_base.s_compound_widget.s_widget.s_widget) + static PyMethodDef ue_PySListView_methods[] = { { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp index 5181efc45..8d0589caa 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp @@ -1,11 +1,15 @@ #include "UnrealEnginePythonPrivatePCH.h" -#include "Runtime/Slate/Public/Widgets/Views/SListView.h" #include "UEPySPythonListView.h" +void SPythonListView::SetHeaderRow(TSharedPtr InHeaderRowWidget) +{ + HeaderRow = InHeaderRowWidget; +} + #define sw_python_list_view StaticCastSharedRef(self->s_list_view.s_table_view_base.s_compound_widget.s_widget.s_widget) static PyObject *py_ue_spython_list_view_get_selected_items(ue_PySPythonListView *self, PyObject * args) { @@ -21,34 +25,101 @@ static PyObject *py_ue_spython_list_view_get_selected_items(ue_PySPythonListView return py_list; } -static PyObject *py_ue_spython_list_view_clear_selection(ue_PySPythonListView *self, PyObject * args) { - - +static PyObject *py_ue_spython_list_view_clear_selection(ue_PySPythonListView *self, PyObject * args) +{ sw_python_list_view->ClearSelection(); Py_INCREF(Py_None); return Py_None; - } static PyObject *py_ue_spython_list_view_get_num_items_selected(ue_PySPythonListView *self, PyObject * args) { return PyLong_FromLong(sw_python_list_view->GetNumItemsSelected()); } +static PyObject *py_ue_spython_list_view_set_header_row(ue_PySPythonListView *self, PyObject * args) +{ + PyObject *py_content; + if (!PyArg_ParseTuple(args, "O:set_header_row", &py_content)) + { + return NULL; + } + + ue_PySHeaderRow *py_sheader_row = py_ue_is_sheader_row(py_content); + if (!py_sheader_row) + { + return PyErr_Format(PyExc_Exception, "argument is not a SHeaderRow"); + } + + Py_INCREF(py_sheader_row); + sw_python_list_view->SetHeaderRow(StaticCastSharedRef(py_sheader_row->s_border.s_compound_widget.s_widget.s_widget->AsShared())); + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject *py_spython_list_view_update_item_source_list(ue_PySPythonListView *self, PyObject * args) +{ + PyObject *values; + if (!PyArg_ParseTuple(args, "O:update_item_source_list", &values)) + { + return NULL; + } + + values = PyObject_GetIter(values); + if (!values) { + return PyErr_Format(PyExc_Exception, "argument is not an iterable"); + } + + //NOTE: ikrimae: Increment first so we don't decrement and destroy python objects that + //we're passing in e.g. if you pass the same item source array into update_items(). + //Might not be necessary but I'm not too familiar with python's GC + TArray> tempNewArray; + while (PyObject *item = PyIter_Next(values)) { + Py_INCREF(item); + tempNewArray.Add(TSharedPtr(new FPythonItem(item))); + } + + for (TSharedPtr& item : self->item_source_list) + { + Py_XDECREF(item->py_object); + } + self->item_source_list.Empty(); + + Move>>(self->item_source_list, tempNewArray); + Py_RETURN_NONE; +} static PyMethodDef ue_PySPythonListView_methods[] = { { "get_selected_items", (PyCFunction)py_ue_spython_list_view_get_selected_items, METH_VARARGS, "" }, { "get_num_items_selected", (PyCFunction)py_ue_spython_list_view_get_num_items_selected, METH_VARARGS, "" }, { "clear_selection", (PyCFunction)py_ue_spython_list_view_clear_selection, METH_VARARGS, "" }, + { "set_header_row", (PyCFunction)py_ue_spython_list_view_set_header_row, METH_VARARGS, "" }, + { "update_item_source_list", (PyCFunction)py_spython_list_view_update_item_source_list, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; +static void ue_PySPythonListView_dealloc(ue_PySPythonListView *self) +{ +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PySPythonListView %p"), self); +#endif + + for (TSharedPtr& item : self->item_source_list) + { + Py_XDECREF(item->py_object); + } + self->item_source_list.Empty(); + + Py_TYPE(self)->tp_free((PyObject *)self); +} + PyTypeObject ue_PySPythonListViewType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.SPythonListView", /* tp_name */ sizeof(ue_PySPythonListView), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + (destructor)ue_PySPythonListView_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -78,29 +149,25 @@ static int ue_py_spython_list_view_init(ue_PySPythonListView *self, PyObject *ar ue_py_slate_setup_farguments(SPythonListView); - // first of all check for values - PyObject *values = ue_py_dict_get_item(kwargs, "list_items_source"); - if (!values) { - PyErr_SetString(PyExc_Exception, "you must specify list items"); - return -1; - } - - values = PyObject_GetIter(values); - if (!values) { - PyErr_SetString(PyExc_Exception, "values field is not an iterable"); - return -1; - } - - TArray> *items = new TArray>(); - while (PyObject *item = PyIter_Next(values)) { - Py_INCREF(item); - // keep track of items - self->s_list_view.s_table_view_base.s_compound_widget.s_widget.py_refs.Add(item); - items->Add(TSharedPtr(new FPythonItem(item))); - } - Py_DECREF(values); - - arguments.ListItemsSource(items); + // first of all check for values + PyObject *values = ue_py_dict_get_item(kwargs, "list_items_source"); + if (!values) { + PyErr_SetString(PyExc_Exception, "you must specify list items"); + return -1; + } + + values = PyObject_GetIter(values); + if (!values) { + Py_DECREF(values); + return -1; + } + + new(&self->item_source_list) TArray>(); + while (PyObject *item = PyIter_Next(values)) { + Py_INCREF(item); + self->item_source_list.Add(TSharedPtr(new FPythonItem(item))); + } + arguments.ListItemsSource(&self->item_source_list); ue_py_slate_farguments_optional_enum("allow_overscroll", AllowOverscroll, EAllowOverscroll); ue_py_slate_farguments_optional_bool("clear_selection_on_click", ClearSelectionOnClick); @@ -131,4 +198,4 @@ void ue_python_init_spython_list_view(PyObject *ue_module) { Py_INCREF(&ue_PySPythonListViewType); PyModule_AddObject(ue_module, "SPythonListView", (PyObject *)&ue_PySPythonListViewType); -} +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h index 31c1176b9..8e981336a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h @@ -11,11 +11,14 @@ class SPythonListView : public SListView> { ~SPythonListView() { delete(ItemsSource); } + + void SetHeaderRow(TSharedPtr InHeaderRowWidget); }; typedef struct { ue_PySListView s_list_view; /* Type-specific fields go here. */ + TArray> item_source_list; } ue_PySPythonListView; void ue_python_init_spython_list_view(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp new file mode 100644 index 000000000..707e08694 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp @@ -0,0 +1,83 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPySPythonMultiColumnTableRow.h" + + +#define sw_python_multicolumn_table_row StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + +static PyMethodDef ue_PySPythonListView_methods[] = { + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PySPythonMultiColumnTableRowType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SPythonMultiColumnTableRow", /* tp_name */ + sizeof(ue_PySPythonMultiColumnTableRow), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SPythonMultiColumnTableRow", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySPythonListView_methods, /* tp_methods */ +}; + +static int ue_py_spython_multicolumn_table_row_init(ue_PySPythonMultiColumnTableRow *self, PyObject *args, PyObject *kwargs) { + + PyObject *py_object = nullptr; + if (!PyArg_ParseTuple(args, "O", &py_object)) + { + return -1; + } + + ue_PySTableViewBase* py_owner_table_view_base = py_ue_is_stable_view_base(py_object); + if (!py_owner_table_view_base) { + PyErr_SetString(PyExc_Exception, "Argument is not a STableViewBase"); + return -1; + } + + Py_INCREF(py_owner_table_view_base); + ue_py_snew_simple_with_req_args( + SPythonMultiColumnTableRow, s_compound_widget.s_widget, + StaticCastSharedRef(py_owner_table_view_base->s_compound_widget.s_widget.s_widget), + (PyObject *)self); + return 0; +} + +void ue_python_init_spython_multicolumn_table_row(PyObject *ue_module) +{ + ue_PySPythonMultiColumnTableRowType.tp_base = &ue_PySCompoundWidgetType; + ue_PySPythonMultiColumnTableRowType.tp_init = (initproc)ue_py_spython_multicolumn_table_row_init; + + if (PyType_Ready(&ue_PySPythonMultiColumnTableRowType) < 0) + return; + + Py_INCREF(&ue_PySPythonMultiColumnTableRowType); + PyModule_AddObject(ue_module, "SPythonMultiColumnTableRow", (PyObject *)&ue_PySPythonMultiColumnTableRowType); +} + +ue_PySPythonMultiColumnTableRow *py_ue_is_spython_multicolumn_table_row(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySPythonMultiColumnTableRowType)) + return nullptr; + return (ue_PySPythonMultiColumnTableRow *)obj; +} diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h new file mode 100644 index 000000000..268ee97b2 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h @@ -0,0 +1,110 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#include "UEPySCompoundWidget.h" +#include "UEPyFGeometry.h" +#include "UEPyFPaintContext.h" +#include "UEPyFCharacterEvent.h" +#include "UEPyFKeyEvent.h" +#include "UEPyFPointerEvent.h" + +extern PyTypeObject ue_PySCompoundWidgetType; +extern PyTypeObject ue_PySTableViewBaseType; +class SPythonMultiColumnTableRow : public SMultiColumnTableRow> { +public: + SLATE_BEGIN_ARGS(SPythonMultiColumnTableRow) { } + SLATE_END_ARGS(); + + void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView, PyObject *in_py_self) + { + SetPyObject(in_py_self); + SMultiColumnTableRow >::Construct(FSuperRowType::FArguments(), InOwnerTableView); + } + + virtual TSharedRef GenerateWidgetForColumn( const FName& ColumnName ) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"generate_widget_for_column")) + return SNullWidget::NullWidget; + + PyObject *py_callable_generate_widget_for_column = PyObject_GetAttrString(self, (char *)"generate_widget_for_column"); + if (!PyCallable_Check(py_callable_generate_widget_for_column)) + { + UE_LOG(LogPython, Error, TEXT("generate_widget_for_column is not a callable")); + return SNullWidget::NullWidget; + } + + PyObject *ret = PyObject_CallFunction(py_callable_generate_widget_for_column, (char *)"s", TCHAR_TO_UTF8(*ColumnName.ToString())); + if (!ret) + { + unreal_engine_py_log_error(); + return SNullWidget::NullWidget; + } + + ue_PySWidget *s_widget = py_ue_is_swidget(ret); + if (!s_widget) + { + Py_DECREF(ret); + UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget")); + return SNullWidget::NullWidget; + } + + TSharedRef value = s_widget->s_widget; + Py_DECREF(ret); + return value; + } + + virtual FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) override + { + FScopePythonGIL gil; + + if (PyObject_HasAttrString(self, (char *)"on_mouse_button_double_click")) + { + PyObject *py_callable_on_mouse_button_double_click = PyObject_GetAttrString(self, (char *)"on_mouse_button_double_click"); + if (!PyCallable_Check(py_callable_on_mouse_button_double_click)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_double_click is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_double_click, (char *)"OO", py_ue_new_fgeometry(InMyGeometry), py_ue_new_fpointer_event(InMouseEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + else + { + return SPythonMultiColumnTableRow::OnMouseButtonDoubleClick(InMyGeometry, InMouseEvent); + } + } + + void SetPyObject(PyObject *py_obj) + { + self = py_obj; + } +private: + PyObject *self = nullptr; + + TSharedPtr RowPythonObject; +}; + +typedef struct { + ue_PySCompoundWidget s_compound_widget; + /* Type-specific fields go here. */ +} ue_PySPythonMultiColumnTableRow; + +void ue_python_init_spython_multicolumn_table_row(PyObject *); + +ue_PySPythonMultiColumnTableRow *py_ue_is_spython_multicolumn_table_row(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp index b55fe38c6..dbab4540c 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp @@ -31,8 +31,43 @@ static PyObject *py_ue_spython_widget_set_active(ue_PySPythonWidget *self, PyObj return (PyObject *)self; } +static PyObject *py_ue_spython_widget_set_content(ue_PySPythonWidget *self, PyObject *args) +{ + PyObject *py_content; + if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) + { + return NULL; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); + Py_INCREF(py_swidget); + self->s_compound_widget.s_widget.py_swidget_content = py_swidget; + + sw_python_widget->SetContent(py_swidget->s_widget->AsShared()); + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject *py_ue_spython_widget_clear_content(ue_PySPythonWidget *self, PyObject *args) +{ + sw_python_widget->ClearContent(); + Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); + + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef ue_PySPythonWidget_methods[] = { - { "set_active", (PyCFunction)py_ue_spython_widget_set_active, METH_VARARGS | METH_KEYWORDS, "" }, + { "set_active", (PyCFunction)py_ue_spython_widget_set_active, METH_VARARGS | METH_KEYWORDS, "" }, + { "set_content", (PyCFunction)py_ue_spython_widget_set_content, METH_VARARGS, "" }, + { "clear_content", (PyCFunction)py_ue_spython_widget_clear_content, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h index 7d2ceaf57..0b8223b81 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h @@ -7,6 +7,7 @@ #include "UEPyFPaintContext.h" #include "UEPyFCharacterEvent.h" #include "UEPyFKeyEvent.h" +#include "UEPyFPointerEvent.h" extern PyTypeObject ue_PySPythonWidgetType; @@ -88,6 +89,127 @@ class SPythonWidget : public SCompoundWidget return FReply::Handled(); } + virtual FReply OnMouseMove(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_move")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_move = PyObject_GetAttrString(self, (char *)"on_mouse_move"); + if (!PyCallable_Check(py_callable_on_mouse_move)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_move is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_move, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + + virtual FReply OnMouseWheel(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_wheel")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_wheel = PyObject_GetAttrString(self, (char *)"on_mouse_wheel"); + if (!PyCallable_Check(py_callable_on_mouse_wheel)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_wheel is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_wheel, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + + virtual FReply OnMouseButtonDown(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_button_down")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_button_down = PyObject_GetAttrString(self, (char *)"on_mouse_button_down"); + if (!PyCallable_Check(py_callable_on_mouse_button_down)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_down is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_down, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + + virtual FReply OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_button_up")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_button_up = PyObject_GetAttrString(self, (char *)"on_mouse_button_up"); + if (!PyCallable_Check(py_callable_on_mouse_button_up)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_up is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_up, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + + virtual int32 OnPaint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyClippingRect, @@ -96,17 +218,18 @@ class SPythonWidget : public SCompoundWidget const FWidgetStyle & InWidgetStyle, bool bParentEnabled) const override { + int32 MaxLayer = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); FScopePythonGIL gil; if (!PyObject_HasAttrString(self, (char *)"paint")) - return LayerId + 1; + return MaxLayer; PyObject *py_callable_paint = PyObject_GetAttrString(self, (char *)"paint"); if (!PyCallable_Check(py_callable_paint)) { UE_LOG(LogPython, Error, TEXT("paint is not a callable")); - return LayerId + 1; + return MaxLayer; } FPaintContext context(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); @@ -115,12 +238,12 @@ class SPythonWidget : public SCompoundWidget if (!ret) { unreal_engine_py_log_error(); - return LayerId + 1; + return MaxLayer; } Py_DECREF(ret); - return LayerId + 1; + return MaxLayer + 1; } @@ -167,6 +290,19 @@ class SPythonWidget : public SCompoundWidget } } + void SetContent(TSharedRef InContent) + { + ChildSlot + [ + InContent + ]; + } + + void ClearContent() + { + ChildSlot.DetachWidget(); + } + protected: PyObject *self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp index e99605d68..8165fea28 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp @@ -35,9 +35,17 @@ static PyObject *py_ue_stable_view_base_set_item_width(ue_PySTableViewBase *self return (PyObject *)self; } +static PyObject *py_ue_stable_view_base_request_list_refresh(ue_PySTableViewBase *self, PyObject * args) +{ + sw_table_view_base->RequestListRefresh(); + + Py_RETURN_NONE; +} + static PyMethodDef ue_PySTableViewBase_methods[] = { { "set_item_height", (PyCFunction)py_ue_stable_view_base_set_item_height, METH_VARARGS, "" }, { "set_item_width", (PyCFunction)py_ue_stable_view_base_set_item_width, METH_VARARGS, "" }, + { "request_list_refresh", (PyCFunction)py_ue_stable_view_base_request_list_refresh, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -83,3 +91,10 @@ void ue_python_init_stable_view_base(PyObject *ue_module) Py_INCREF(&ue_PySTableViewBaseType); PyModule_AddObject(ue_module, "STableViewBase", (PyObject *)&ue_PySTableViewBaseType); } + +ue_PySTableViewBase * py_ue_is_stable_view_base(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySTableViewBaseType)) + return nullptr; + return (ue_PySTableViewBase *)obj; +} diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h index a32ec85a8..5fd06b2cc 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h @@ -13,4 +13,6 @@ typedef struct { /* Type-specific fields go here. */ } ue_PySTableViewBase; -void ue_python_init_stable_view_base(PyObject *); \ No newline at end of file +void ue_python_init_stable_view_base(PyObject *); + +ue_PySTableViewBase *py_ue_is_stable_view_base(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 99cb31276..9563b1fb1 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -19,10 +19,10 @@ #include "Runtime/Slate/Public/Framework/Commands/UICommandInfo.h" #include "Runtime/Slate/Public/Framework/Docking/TabManager.h" #include "Runtime/Slate/Public/Widgets/Views/STableRow.h" +#include "Runtime/SlateCore/Public/Styling/SlateIconFinder.h" #include "Runtime/AppFramework/Public/Widgets/Colors/SColorPicker.h" - #include "UEPySlate.h" FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) @@ -320,12 +320,41 @@ TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr value = s_widget->s_widget; - Py_DECREF(ret); + + Py_INCREF(ret); return value; } +TSharedRef UPythonSlateDelegate::OnGetMenuContent() +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)""); + if (!ret) + { + unreal_engine_py_log_error(); + return SNullWidget::NullWidget; + } + + ue_PySWidget *s_widget = py_ue_is_swidget(ret); + if (!s_widget) + { + Py_DECREF(ret); + UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget")); + return SNullWidget::NullWidget; + } + TSharedRef value = s_widget->s_widget; + Py_DECREF(ret); + return value; +} + void UPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type) { + if (!py_item.IsValid()) + { + return; + } + FScopePythonGIL gil; PyObject *ret = PyObject_CallFunction(py_callable, (char *)"Oi", py_item.Get()->py_object, (int)select_type); @@ -615,20 +644,29 @@ TSharedRef UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &a TSharedRef UPythonSlateDelegate::GenerateRow(TSharedPtr InItem, const TSharedRef& OwnerTable) { + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char*)"O", InItem.Get()->py_object); if (!ret) { unreal_engine_py_log_error(); return SNew(STableRow>, OwnerTable); } - ue_PySWidget *s_widget = py_ue_is_swidget(ret); - if (!s_widget) + + if (ue_PySPythonMultiColumnTableRow *spython_multicolumn_table_row = py_ue_is_spython_multicolumn_table_row(ret)) + { + Py_INCREF(spython_multicolumn_table_row); + TSharedRef value = StaticCastSharedRef(spython_multicolumn_table_row->s_compound_widget.s_widget.s_widget->AsShared()); + return value; + } + else if (ue_PySWidget *s_widget = py_ue_is_swidget(ret)) { - UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); - return SNew(STableRow>, OwnerTable); + return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; } - return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; + UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); + return SNew(STableRow>, OwnerTable); + } void UPythonSlateDelegate::GetChildren(TSharedPtr InItem, TArray>& OutChildren) @@ -735,6 +773,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_stable_view_base(module); ue_python_init_slist_view(module); ue_python_init_spython_list_view(module); + ue_python_init_spython_multicolumn_table_row(module); ue_python_init_stree_view(module); ue_python_init_spython_tree_view(module); ue_python_init_ssplitter(module); @@ -756,6 +795,7 @@ void ue_python_init_slate(PyObject *module) #if WITH_EDITOR + ue_python_init_idetails_view(module); ue_python_init_seditor_viewport(module); ue_python_init_slevel_viewport(module); ue_python_init_spython_editor_viewport(module); @@ -785,6 +825,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_fpointer_event(module); ue_python_init_fkey_event(module); ue_python_init_fcharacter_event(module); + ue_python_init_eslate_enums(module); } PyObject *ue_py_dict_get_item(PyObject *dict, const char *key) @@ -805,6 +846,40 @@ PyObject *py_unreal_engine_get_editor_window(PyObject *self, PyObject *args) return (PyObject *)ue_py_get_swidget(FGlobalTabmanager::Get()->GetRootWindow().ToSharedRef()); } +PyObject *py_unreal_engine_find_slate_style(PyObject *self, PyObject *args) +{ + char *name = nullptr; + if (!PyArg_ParseTuple(args, "s:find_slate_style", &name)) + return nullptr; + + ISlateStyle const* const foundStyleSet = FSlateStyleRegistry::FindSlateStyle(FName(name)); + if (!foundStyleSet) + { + UE_LOG(LogPython, Warning, TEXT("Could not find SlateStyle")); + Py_RETURN_NONE; + } + + ue_PyFSlateStyleSet *ret = py_ue_new_fslate_style_set(static_cast(const_cast(foundStyleSet))); + return (PyObject *)ret; +} + +PyObject *py_unreal_engine_find_icon_for_class(PyObject *self, PyObject *args) +{ + PyObject* py_class = nullptr; + char *name = nullptr; + if (!PyArg_ParseTuple(args, "O|s:find_icon_for_class", &py_class, &name)) + return nullptr; + + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + return PyErr_Format(PyExc_Exception, "argument is not a UClass object"); + + FSlateIcon foundIcon = FSlateIconFinder::FindIconForClass(u_class, FName(UTF8_TO_TCHAR(name))); + ue_PyFSlateIcon *ret = py_ue_new_fslate_icon(foundIcon); + return (PyObject *)ret; +} + + // slate commands tool class class FPythonSlateCommands : public TCommands { @@ -899,32 +974,54 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P PyObject *py_object; PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + PyObject *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + char *kwlist[] = { (char *)"uobject", (char *)"allow_search", + + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", nullptr }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:create_detail_view", kwlist, - &py_object, &py_allow_search)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_detail_view", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) { return nullptr; } - UObject *u_object = ue_py_check_type(py_object); - if (!u_object) - { - return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - } FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); FDetailsViewArgs view_args; - view_args.bAllowSearch = (py_allow_search && PyObject_IsTrue(py_allow_search)); - view_args.NameAreaSettings = FDetailsViewArgs::ENameAreaSettings::HideNameArea; + view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + view_args.NameAreaSettings = [py_name_area_settings]() { + if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); TSharedPtr view = PropertyEditorModule.CreateDetailView(view_args); - view->SetObject(u_object); - - return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PySWidgetType); + + if (UObject *u_object = ue_py_check_type(py_object)) + { + view->SetObject(u_object); + } + + extern PyTypeObject ue_PyIDetailsViewType; + return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PyIDetailsViewType); } PyObject *py_unreal_engine_create_property_view(PyObject *self, PyObject * args, PyObject *kwargs) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 34c7de74d..85b468371 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -40,6 +40,7 @@ #include "UEPySTableViewBase.h" #include "UEPySListView.h" #include "UEPySPythonListView.h" +#include "UEPySPythonMultiColumnTableRow.h" #include "UEPySTreeView.h" #include "UEPySPythonTreeView.h" #include "UEPySSplitter.h" @@ -62,9 +63,9 @@ #include "UEPyFTabManager.h" #include "UEPyFTabSpawnerEntry.h" #include "UEPyFMenuBuilder.h" -#include "UEPyFSlateStyleSet.h" #include "UEPyFToolBarBuilder.h" #include "UEPyFSlateIcon.h" +#include "UEPyFSlateStyleSet.h" #include "UEPyFGeometry.h" #include "UEPyFPaintContext.h" @@ -84,6 +85,7 @@ #include "UEPySDropTarget.h" #include "UEPySAssetDropTarget.h" #include "UEPySObjectPropertyEntryBox.h" +#include "UEPyIDetailsView.h" #endif #include "Runtime/Core/Public/Misc/Attribute.h" @@ -93,10 +95,11 @@ #include "UEPySlate.generated.h" - - PyObject *py_unreal_engine_get_editor_window(PyObject *, PyObject *); +PyObject *py_unreal_engine_find_slate_style(PyObject *, PyObject *); +PyObject *py_unreal_engine_find_icon_for_class(PyObject *, PyObject *); + #if WITH_EDITOR PyObject *py_unreal_engine_add_menu_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_add_menu_bar_extension(PyObject *, PyObject *); @@ -140,6 +143,8 @@ template ue_PySWidget *py_ue_new_swidget(TSharedRef s_widge #define ue_py_snew_simple(T, field) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(), T::FArguments()) +#define ue_py_snew_simple_with_req_args(T, field, ... ) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(__VA_ARGS__), T::FArguments()) + #define ue_py_snew(T, field) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(), arguments) #define ue_py_snew_with_args(T, field, args) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(args), arguments) @@ -422,7 +427,7 @@ void ue_python_init_slate(PyObject *); struct FPythonItem { - PyObject *py_object; + PyObject *py_object = nullptr; FPythonItem(PyObject *item) { @@ -468,6 +473,7 @@ class UPythonSlateDelegate : public UPythonDelegate TSharedPtr OnContextMenuOpening(); TSharedRef OnGenerateWidget(TSharedPtr py_item); + TSharedRef OnGetMenuContent(); void OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type); void SimpleExecuteAction(); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 2853ac81e..802c0df17 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -201,6 +201,9 @@ static PyMethodDef unreal_engine_methods[] = { // slate + { "find_slate_style", py_unreal_engine_find_slate_style, METH_VARARGS, "" }, + { "find_icon_for_class", py_unreal_engine_find_icon_for_class, METH_VARARGS, "" }, + { "register_nomad_tab_spawner", py_unreal_engine_register_nomad_tab_spawner, METH_VARARGS, "" }, { "unregister_nomad_tab_spawner", py_unreal_engine_unregister_nomad_tab_spawner, METH_VARARGS, "" }, { "invoke_tab", py_unreal_engine_invoke_tab, METH_VARARGS, "" }, @@ -855,6 +858,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "sequencer_changed", (PyCFunction)py_ue_sequencer_changed, METH_VARARGS, "" }, { "sequencer_add_camera_cut_track", (PyCFunction)py_ue_sequencer_add_camera_cut_track, METH_VARARGS, "" }, { "sequencer_add_actor", (PyCFunction)py_ue_sequencer_add_actor, METH_VARARGS, "" }, + { "sequencer_add_actor_component", (PyCFunction)py_ue_sequencer_add_actor_component, METH_VARARGS, "" }, { "sequencer_make_new_spawnable", (PyCFunction)py_ue_sequencer_make_new_spawnable, METH_VARARGS, "" }, { "sequencer_add_possessable", (PyCFunction)py_ue_sequencer_add_possessable, METH_VARARGS, "" }, @@ -3464,3 +3468,27 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ return function; } + +uint8 * do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct) +{ + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) { + return nullptr; + } + + if (ue_py_struct->u_struct == chk_u_struct) { + return ue_py_struct->data; + } + + return nullptr; +} + +bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct) +{ + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) { + false; + } + + return ue_py_struct->u_struct->IsChildOf(parent_u_struct); +} diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 63db52acc..d2248f29e 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -57,15 +57,14 @@ template T *ue_py_check_type(ue_PyUObject *py_obj) { return Cast(py_obj->ue_object); } +uint8 *do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct); + template T *ue_py_check_struct(PyObject *py_obj) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); - if (!ue_py_struct) { - return nullptr; - } + return (T*)do_ue_py_check_struct(py_obj, T::StaticStruct()); +} - if (ue_py_struct->u_struct == T::StaticStruct()) { - return (T*)ue_py_struct->data; - } +bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct); - return nullptr; +template bool ue_py_check_childstruct(PyObject *py_obj) { + return do_ue_py_check_childstruct(py_obj, T::StaticStruct()); } diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index cd3b880de..2875c4b3e 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -259,7 +259,7 @@ void ue_python_init_uscriptstruct(PyObject *ue_module) { PyModule_AddObject(ue_module, "UScriptStruct", (PyObject *)&ue_PyUScriptStructType); } -PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, uint8 *data) { +PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, const uint8 *data) { ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); ret->u_struct = u_struct; uint8 *struct_data = (uint8*)FMemory::Malloc(u_struct->GetStructureSize()); diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h index c39a8346e..d93eb990a 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h @@ -10,7 +10,7 @@ typedef struct uint8 *data; } ue_PyUScriptStruct; -PyObject *py_ue_new_uscriptstruct(UScriptStruct *, uint8 *); +PyObject *py_ue_new_uscriptstruct(UScriptStruct *, const uint8 *); ue_PyUScriptStruct *py_ue_is_uscriptstruct(PyObject *); UProperty *ue_struct_get_field_from_name(UScriptStruct *, char *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index d3bf7c2d8..d187527ee 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -561,7 +561,12 @@ PyObject *py_ue_get_display_name(ue_PyUObject *self, PyObject * args) ue_py_check(self); #if WITH_EDITOR - if (AActor *actor = ue_py_check_type(self)) + if (UClass *uclass = ue_py_check_type(self)) + { + return PyUnicode_FromString(TCHAR_TO_UTF8(*uclass->GetDisplayNameText().ToString())); + } + + if (AActor *actor = ue_py_check_type(self)) { return PyUnicode_FromString(TCHAR_TO_UTF8(*actor->GetActorLabel())); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 6218631ce..25d99d834 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -255,6 +255,50 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } +PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * args) { + + ue_py_check(self); + + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:sequencer_add_actor_component", &py_obj)) { + return NULL; + } + + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + + ue_PyUObject *py_ue_obj = ue_is_pyuobject(py_obj); + if (!py_ue_obj) + return PyErr_Format(PyExc_Exception, "argument is not a uobject"); + + if (!py_ue_obj->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "argument is not an actorcomponent"); + + ULevelSequence *seq = (ULevelSequence *)self->ue_object; + + UActorComponent* actorComponent = (UActorComponent *)py_ue_obj->ue_object; + + // try to open the editor for the asset + FAssetEditorManager::Get().OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + FGuid new_guid; + if (editor) { + FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; + ISequencer *sequencer = toolkit->GetSequencer().Get(); + new_guid = sequencer->GetHandleToObject(actorComponent); + } + else { + return PyErr_Format(PyExc_Exception, "unable to access sequencer"); + } + + if (!new_guid.IsValid()) { + return PyErr_Format(PyExc_Exception, "unable to find guid"); + } + + return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); +} + PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args) { ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h index ded9524ef..0ab72f427 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h @@ -21,6 +21,7 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_add_actor(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 0b9c6420c..1854d6020 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -29,6 +29,8 @@ #include "UEPyModule.h" +#include "Wrappers/UEPyESlateEnums.h" + #include "Wrappers/UEPyFVector.h" #include "Wrappers/UEPyFHitResult.h" #include "Wrappers/UEPyFRotator.h" diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp new file mode 100644 index 000000000..815c2aaea --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp @@ -0,0 +1,96 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +static PyObject *py_ue_eslate_enums_get(ue_PyESlateEnums *self, void *closure) +{ + return PyLong_FromLong(self->val); +} + +static PyGetSetDef ue_PyESlateEnums_getseters[] = { + { (char*)"val", (getter)py_ue_eslate_enums_get, 0, (char *)"", NULL }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyESlateEnums_str(ue_PyESlateEnums *self) +{ + return PyUnicode_FromFormat("", PyLong_FromLong(self->val)); +} + +static PyTypeObject ue_PyESlateEnumsType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.ESlateEnums", /* tp_name */ + sizeof(ue_PyESlateEnums), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyESlateEnums_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine ESlateEnums", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, + ue_PyESlateEnums_getseters, +}; + +static int ue_py_eslate_enums_init(ue_PyESlateEnums *self, PyObject *args, PyObject *kwargs) { + int val = 0; + if (!PyArg_ParseTuple(args, "i", &val)) + return -1; + + self->val = val; + return 0; +} + +void ue_python_init_eslate_enums(PyObject *ue_module) +{ + ue_PyESlateEnumsType.tp_new = PyType_GenericNew; + + ue_PyESlateEnumsType.tp_init = (initproc)ue_py_eslate_enums_init; + + if (PyType_Ready(&ue_PyESlateEnumsType) < 0) + return; + + Py_INCREF(&ue_PyESlateEnumsType); + PyModule_AddObject(ue_module, "ESlateEnums", (PyObject *)&ue_PyESlateEnumsType); + + auto add_native_enum = [](const char *enum_name, uint8 val) + { + ue_PyESlateEnums* native_enum = (ue_PyESlateEnums *)PyObject_New(ue_PyESlateEnums, &ue_PyESlateEnumsType); + native_enum->val = val; + PyDict_SetItemString(ue_PyESlateEnumsType.tp_dict, enum_name, (PyObject *)native_enum); + }; + +#define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)##EnumType::Type::##EnumVal) + ADD_NATIVE_ENUM(EUserInterfaceActionType, None ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, Button ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, ToggleButton ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, RadioButton ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, Check ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, CollapsedButton); +#undef ADD_NATIVE_ENUM + +} + +ue_PyESlateEnums *py_ue_is_eslate_enums(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyESlateEnumsType)) + return nullptr; + return (ue_PyESlateEnums *)obj; + +} diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h new file mode 100644 index 000000000..dae16da2c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h @@ -0,0 +1,15 @@ +#pragma once + + + +#include "UnrealEnginePython.h" + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + uint8 val; +} ue_PyESlateEnums; + +void ue_python_init_eslate_enums(PyObject *); + +ue_PyESlateEnums *py_ue_is_eslate_enums(PyObject *); \ No newline at end of file From 0f2979d2c0575375df1a2b98ef78b80136535dcc Mon Sep 17 00:00:00 2001 From: ikrima Date: Fri, 8 Dec 2017 16:18:20 -0800 Subject: [PATCH 16/94] Add add_child window functionality --- .../Slate/UEPySPythonMultiColumnTableRow.cpp | 4 +-- .../Private/Slate/UEPySWindow.cpp | 36 ++++++++++++++++++- .../Private/Slate/UEPySWindow.h | 2 ++ UnrealEnginePython.uplugin | 2 +- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp index 707e08694..a4d8b3d2a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp @@ -6,7 +6,7 @@ #define sw_python_multicolumn_table_row StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) -static PyMethodDef ue_PySPythonListView_methods[] = { +static PyMethodDef ue_PySPythonMultiColumnTableRow_methods[] = { { NULL } /* Sentinel */ }; @@ -38,7 +38,7 @@ PyTypeObject ue_PySPythonMultiColumnTableRowType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - ue_PySPythonListView_methods, /* tp_methods */ + ue_PySPythonMultiColumnTableRow_methods, /* tp_methods */ }; static int ue_py_spython_multicolumn_table_row_init(ue_PySPythonMultiColumnTableRow *self, PyObject *args, PyObject *kwargs) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp index 1314cc3e2..247cb74c7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp @@ -108,6 +108,27 @@ static PyObject *py_ue_swindow_add_modal(ue_PySWindow *self, PyObject * args) } #endif +static PyObject *py_ue_swindow_add_child(ue_PySWindow *self, PyObject * args) +{ + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:add_child", &py_obj)) + { + return NULL; + } + + ue_PySWindow *py_swindow_child = py_ue_is_swindow(py_obj); + if (!py_swindow_child) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWindow"); + } + + FSlateApplication::Get().AddWindowAsNativeChild( + StaticCastSharedRef(py_swindow_child->s_compound_widget.s_widget.s_widget), + sw_window); + + Py_RETURN_NONE; +} + static PyMethodDef ue_PySWindow_methods[] = { { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, @@ -119,6 +140,7 @@ static PyMethodDef ue_PySWindow_methods[] = { #if WITH_EDITOR { "add_modal", (PyCFunction)py_ue_swindow_add_modal, METH_VARARGS, "" }, #endif + { "add_child", (PyCFunction)py_ue_swindow_add_child, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -222,7 +244,12 @@ static int ue_py_swindow_init(ue_PySWindow *self, PyObject *args, PyObject *kwar sw_window->SetOnWindowClosed(handler); } - FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); + // is it a child ? + PyObject *is_child = ue_py_dict_get_item(kwargs, "child"); + if (!(is_child && PyObject_IsTrue(is_child))) + { + FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); + } return 0; } @@ -241,3 +268,10 @@ void ue_python_init_swindow(PyObject *ue_module) Py_INCREF(&ue_PySWindowType); PyModule_AddObject(ue_module, "SWindow", (PyObject *)&ue_PySWindowType); } + +ue_PySWindow *py_ue_is_swindow(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySWindowType)) + return nullptr; + return (ue_PySWindow *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h index e5aac3668..396c860de 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h @@ -14,3 +14,5 @@ typedef struct { } ue_PySWindow; void ue_python_init_swindow(PyObject *); + +ue_PySWindow *py_ue_is_swindow(PyObject *obj); \ No newline at end of file diff --git a/UnrealEnginePython.uplugin b/UnrealEnginePython.uplugin index 5c3b2e07e..0731a2b1b 100644 --- a/UnrealEnginePython.uplugin +++ b/UnrealEnginePython.uplugin @@ -17,7 +17,7 @@ "Modules": [ { "Name": "UnrealEnginePython", - "Type": "Runtime", + "Type": "Developer", "LoadingPhase": "Default" }, { From 51dbf7ef7ee55e4552fe1a7f8a547340f97fa70c Mon Sep 17 00:00:00 2001 From: ikrima Date: Sun, 10 Dec 2017 10:04:40 -0800 Subject: [PATCH 17/94] ListView & Header Row extensions & fixes -Add filtering to py listviews, fixing column adjustments, adding sorting -FIX: Py Widget Visibility set properly now -FIX: Python header row in listview properly initialized as part of widget declaration so SListView construct() call properly initializes header related artifacts -Extending header row with more attributes like label,tooltip,align,width,fill,sort mode, etc -Adding string from args optional macro define -Adding get bp hierarchy from actor class & get all graphs out of a blueprint and ability to show settings --- .../Private/Slate/UEPySHeaderRow.cpp | 91 ++++++++------- .../Private/Slate/UEPySPythonListView.cpp | 16 +++ .../Private/Slate/UEPySWidget.cpp | 35 +++++- .../Private/Slate/UEPySlate.cpp | 13 +++ .../Private/Slate/UEPySlate.h | 7 ++ .../UnrealEnginePython/Private/UEPyEditor.cpp | 107 +++++++++++++----- .../UnrealEnginePython/Private/UEPyEditor.h | 3 + .../UnrealEnginePython/Private/UEPyModule.cpp | 7 +- 8 files changed, 202 insertions(+), 77 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp index a0791255b..59d574cc0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp @@ -4,49 +4,54 @@ #define sw_header_row StaticCastSharedRef(self->s_border.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *args, PyObject *kwargs) { - - char *column_id; - float fixed_width = 0; - int cell_h_align = 0; - int cell_v_align = 0; - char *default_label = nullptr; - char *default_tooltip = nullptr; - - char *kwlist[] = { - (char *)"column_id", - (char *)"fixed_width", - (char *)"cell_h_align", - (char *)"cell_v_align", - (char *)"default_label", - (char *)"default_tooltip", - nullptr - }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sf|iiss:add_column", kwlist, - &column_id, - &fixed_width, - &cell_h_align, - &cell_v_align, - &default_label, - &default_tooltip)) { - return NULL; - } - - if (!default_label) - default_label = column_id; - - if (!default_tooltip) - default_tooltip = default_label; - - sw_header_row->AddColumn( - SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) - .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) - .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) - .FixedWidth(fixed_width) - .HAlignCell((EHorizontalAlignment)cell_h_align) - .VAlignCell((EVerticalAlignment)cell_v_align) - ); +static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *args, PyObject *kwargs) +{ + int32 retCode = [&]() { + ue_py_slate_setup_farguments(SHeaderRow::FColumn); + + // first of all check for values + PyObject *py_columnid = ue_py_dict_get_item(kwargs, "column_id"); + if (!py_columnid) { + PyErr_SetString(PyExc_TypeError, "you must specify the column_id"); + return -1; + } + + ue_py_slate_farguments_optional_string("column_id", ColumnId); + + ue_py_slate_farguments_optional_text("default_label", DefaultLabel); + ue_py_slate_farguments_optional_text("default_tooltip", DefaultTooltip); + + ue_py_slate_farguments_optional_enum("h_align_header", HAlignHeader, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align_header", VAlignHeader, EVerticalAlignment); + + ue_py_slate_farguments_optional_float("fill_width", FillWidth); + ue_py_slate_farguments_optional_float("fixed_width", FixedWidth); + + ue_py_slate_farguments_optional_enum("h_align_cell", HAlignCell, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align_cell", VAlignCell, EVerticalAlignment); + + ue_py_slate_farguments_optional_enum("sort_mode", SortMode, EColumnSortMode::Type); + ue_py_slate_farguments_optional_enum("sort_priority", SortPriority, EColumnSortPriority::Type); + + ue_py_slate_farguments_event("on_sort", OnSort, FOnSortModeChanged, OnSort); + + //sw_header_row->AddColumn( + // SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) + // .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) + // .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) + // .FixedWidth(fixed_width) + // .HAlignCell((EHorizontalAlignment)cell_h_align) + // .VAlignCell((EVerticalAlignment)cell_v_align) + //); + + sw_header_row->AddColumn(arguments); + return 0; + }(); + + if (retCode != 0) + { + return PyErr_Format(PyExc_Exception, "could not add column slot"); + } Py_INCREF(self); return (PyObject *)self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp index 8d0589caa..f8c3b08ea 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp @@ -169,6 +169,22 @@ static int ue_py_spython_list_view_init(ue_PySPythonListView *self, PyObject *ar } arguments.ListItemsSource(&self->item_source_list); + { + PyObject *value = ue_py_dict_get_item(kwargs, "header_row"); + if (value) { + if (ue_PySHeaderRow *_py_swidget = py_ue_is_sheader_row(value)) { + + Py_INCREF(_py_swidget); + ((ue_PySWidget *)self)->py_refs.Add(value); + arguments.HeaderRow(StaticCastSharedRef(((ue_PySWidget *)_py_swidget)->s_widget)); + } + else { + PyErr_SetString(PyExc_TypeError, "unsupported type for attribute " "header_row"); + return -1; + } + } + } + ue_py_slate_farguments_optional_enum("allow_overscroll", AllowOverscroll, EAllowOverscroll); ue_py_slate_farguments_optional_bool("clear_selection_on_click", ClearSelectionOnClick); ue_py_slate_farguments_optional_enum("consume_mouse_wheel", ConsumeMouseWheel, EConsumeMouseWheel); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 2539e85a1..6a21e5b93 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -30,13 +30,42 @@ static PyObject *py_ue_swidget_get_children(ue_PySWidget *self, PyObject * args) static PyObject *py_ue_swidget_set_visibility(ue_PySWidget *self, PyObject * args) { - int visibility; - if (!PyArg_ParseTuple(args, "i:set_visibility", &visibility)) + PyObject* py_object; + if (!PyArg_ParseTuple(args, "O:set_visibility", &py_object)) { return nullptr; } - self->s_widget->SetVisibility(EVisibility::Visible); + if (!PyNumber_Check(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a ESlateVisibility"); + } + + PyObject *py_value = PyNumber_Long(py_object); + ESlateVisibility slateVisibility = (ESlateVisibility)PyLong_AsLong(py_value); + Py_DECREF(py_value); + + EVisibility visibility; + switch (slateVisibility) + { + case ESlateVisibility::Visible: + visibility = EVisibility::Visible; + break; + case ESlateVisibility::Collapsed: + visibility = EVisibility::Collapsed; + break; + case ESlateVisibility::Hidden: + visibility = EVisibility::Hidden; + break; + case ESlateVisibility::HitTestInvisible: + visibility = EVisibility::HitTestInvisible; + break; + case ESlateVisibility::SelfHitTestInvisible: + visibility = EVisibility::SelfHitTestInvisible; + break; + } + + self->s_widget->SetVisibility(visibility); Py_INCREF(self); return (PyObject *)self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 9563b1fb1..6c36e7605 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -176,6 +176,19 @@ void UPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commi Py_DECREF(ret); } +void UPythonSlateDelegate::OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"isi", (int)SortPriority, TCHAR_TO_UTF8(*ColumnName.ToString()), (int)NewSortMode); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + void UPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) { FScopePythonGIL gil; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 85b468371..1ff82a4c7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -375,6 +375,12 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ } +#define ue_py_slate_farguments_optional_string(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + if (PyUnicode_Check(value)) {\ + arguments.attribute(UTF8_TO_TCHAR(PyUnicode_AsUTF8(value)));\ + }\ +} + #define ue_py_slate_farguments_optional_text(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ if (value) {\ if (PyUnicode_Check(value)) {\ @@ -449,6 +455,7 @@ class UPythonSlateDelegate : public UPythonDelegate void OnTextCommitted(const FText &text, ETextCommit::Type commit_type); void OnFloatChanged(float value); void OnFloatCommitted(float value, ETextCommit::Type commit_type); + void OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode); void OnLinearColorChanged(FLinearColor color); diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index e739f2b4e..aff43a0c9 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -26,6 +26,7 @@ #include "Editor/LandscapeEditor/Public/LandscapeFileFormatInterface.h" #include "Developer/Settings/Public/ISettingsModule.h" +#include "Engine/Blueprint.h" PyObject *py_unreal_engine_editor_play_in_viewport(PyObject * self, PyObject * args) @@ -1120,6 +1121,33 @@ PyObject *py_unreal_engine_create_blueprint(PyObject * self, PyObject * args) } +PyObject *py_unreal_engine_get_blueprint_hierarchy_from_class(PyObject * self, PyObject * args) +{ + PyObject *py_class; + if (!PyArg_ParseTuple(args, "O:get_blueprint_hierarchy_from_class", &py_class)) + { + return NULL; + } + + UClass* u_class = ue_py_check_type(py_class); + if (!u_class) + { + return PyErr_Format(PyExc_Exception, "argument is not a UClass"); + } + + + TArray outBPs; + UBlueprint::GetBlueprintHierarchyFromClass(u_class, outBPs); + + PyObject *py_bpClasses = PyList_New(0); + + for (UBlueprint* bpClass : outBPs) { + ue_PyUObject *item = ue_get_python_wrapper(bpClass); + if (item) + PyList_Append(py_bpClasses, (PyObject *)item); + } + return py_bpClasses; +} PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) { @@ -1602,6 +1630,35 @@ PyObject *py_unreal_engine_blueprint_add_ubergraph_page(PyObject * self, PyObjec return ret; } +PyObject *py_unreal_engine_blueprint_get_all_graphs(PyObject * self, PyObject * args) +{ + PyObject *py_blueprint; + + if (!PyArg_ParseTuple(args, "O:blueprint_get_all_graphs", &py_blueprint)) + { + return nullptr; + } + + UBlueprint *bp = ue_py_check_type(py_blueprint); + if (!bp) + return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); + + PyObject *py_graphs = PyList_New(0); + + TArray graphs; + + bp->GetAllGraphs(graphs); + + for (UEdGraph *graph : graphs) + { + ue_PyUObject *item = ue_get_python_wrapper(graph); + if (item) + PyList_Append(py_graphs, (PyObject *)item); + } + + return py_graphs; +} + PyObject *py_unreal_engine_create_new_graph(PyObject * self, PyObject * args) { @@ -1665,35 +1722,6 @@ PyObject *py_unreal_engine_create_new_graph(PyObject * self, PyObject * args) Py_RETURN_UOBJECT(graph); } -PyObject *py_unreal_engine_editor_blueprint_graphs(PyObject * self, PyObject * args) -{ - PyObject *py_blueprint; - - if (!PyArg_ParseTuple(args, "O:blueprint_graphs", &py_blueprint)) - { - return nullptr; - } - - UBlueprint *bp = ue_py_check_type(py_blueprint); - if (!bp) - return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); - - PyObject *py_graphs = PyList_New(0); - - TArray graphs; - - bp->GetAllGraphs(graphs); - - for (UEdGraph *graph : graphs) - { - ue_PyUObject *item = ue_get_python_wrapper(graph); - if (item) - PyList_Append(py_graphs, (PyObject *)item); - } - - return py_graphs; -} - PyObject *py_unreal_engine_editor_on_asset_post_import(PyObject * self, PyObject * args) { PyObject *py_callable; @@ -2237,6 +2265,27 @@ PyObject *py_unreal_engine_register_settings(PyObject * self, PyObject * args) Py_RETURN_NONE; } +PyObject * py_unreal_engine_show_viewer(PyObject * self, PyObject * args) +{ + char *container_name; + char *category_name; + char *section_name; + + if (!PyArg_ParseTuple(args, "sss:register_settings", &container_name, &category_name, §ion_name)) + return nullptr; + + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->ShowViewer(container_name, category_name, section_name); + } + else + { + return PyErr_Format(PyExc_Exception, "unable to find the Settings Module"); + } + + Py_RETURN_NONE; +} + PyObject *py_unreal_engine_unregister_settings(PyObject * self, PyObject * args) { char *container_name; diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index 0498fa498..566ea43c7 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -40,6 +40,7 @@ PyObject *py_unreal_engine_get_long_package_path(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_compile_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_message_dialog_open(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_blueprint_hierarchy_from_class(PyObject *, PyObject *); PyObject *py_unreal_engine_reload_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_replace_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject *, PyObject *); @@ -75,6 +76,7 @@ PyObject *py_unreal_engine_editor_take_high_res_screen_shots(PyObject *, PyObjec PyObject *py_unreal_engine_blueprint_add_function(PyObject *, PyObject *); PyObject *py_unreal_engine_blueprint_add_ubergraph_page(PyObject *, PyObject *); +PyObject *py_unreal_engine_blueprint_get_all_graphs(PyObject *, PyObject *); PyObject *py_unreal_engine_create_new_graph(PyObject *, PyObject *); @@ -112,5 +114,6 @@ PyObject *py_unreal_engine_heightmap_import(PyObject *, PyObject *); PyObject *py_unreal_engine_play_preview_sound(PyObject *, PyObject *); PyObject *py_unreal_engine_register_settings(PyObject *, PyObject *); +PyObject *py_unreal_engine_show_viewer(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_settings(PyObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 628edcbbb..4c51fd5a9 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -272,7 +272,8 @@ static PyMethodDef unreal_engine_methods[] = { { "create_blueprint", py_unreal_engine_create_blueprint, METH_VARARGS, "" }, { "create_blueprint_from_actor", py_unreal_engine_create_blueprint_from_actor, METH_VARARGS, "" }, { "replace_blueprint", py_unreal_engine_replace_blueprint, METH_VARARGS, "" }, - { "reload_blueprint", py_unreal_engine_reload_blueprint, METH_VARARGS, "" }, + { "get_blueprint_hierarchy_from_class", py_unreal_engine_get_blueprint_hierarchy_from_class, METH_VARARGS, "" }, + { "reload_blueprint", py_unreal_engine_reload_blueprint, METH_VARARGS, "" }, { "compile_blueprint", py_unreal_engine_compile_blueprint, METH_VARARGS, "" }, { "blueprint_add_member_variable", py_unreal_engine_blueprint_add_member_variable, METH_VARARGS, "" }, { "blueprint_add_event_dispatcher", py_unreal_engine_blueprint_add_event_dispatcher, METH_VARARGS, "" }, @@ -280,6 +281,7 @@ static PyMethodDef unreal_engine_methods[] = { { "blueprint_set_variable_visibility", py_unreal_engine_blueprint_set_variable_visibility, METH_VARARGS, "" }, { "blueprint_add_function", py_unreal_engine_blueprint_add_function, METH_VARARGS, "" }, { "blueprint_add_ubergraph_page", py_unreal_engine_blueprint_add_ubergraph_page, METH_VARARGS, "" }, + { "blueprint_get_all_graphs", py_unreal_engine_blueprint_get_all_graphs, METH_VARARGS, "" }, { "blueprint_mark_as_structurally_modified", py_unreal_engine_blueprint_mark_as_structurally_modified, METH_VARARGS, "" }, { "add_component_to_blueprint", py_unreal_engine_add_component_to_blueprint, METH_VARARGS, "" }, { "get_blueprint_components", py_unreal_engine_get_blueprint_components, METH_VARARGS, "" }, @@ -377,6 +379,7 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_take_high_res_screen_shots", py_unreal_engine_editor_take_high_res_screen_shots, METH_VARARGS, "" }, { "register_settings", py_unreal_engine_register_settings, METH_VARARGS, "" }, + { "show_viewer", py_unreal_engine_show_viewer, METH_VARARGS, "" }, { "unregister_settings", py_unreal_engine_unregister_settings, METH_VARARGS, "" }, #endif @@ -3490,7 +3493,7 @@ bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct { ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); if (!ue_py_struct) { - false; + return false; } return ue_py_struct->u_struct->IsChildOf(parent_u_struct); From 9aff177500d6d2b1ea880ff921d04c5f4a0bc638 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 18 Dec 2017 22:40:18 -0800 Subject: [PATCH 18/94] FIX: SPythonWidget was not holding a refcount to its python object host --- Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp | 1 - Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp index dbab4540c..27beedcee 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp @@ -105,7 +105,6 @@ PyTypeObject ue_PySPythonWidgetType = { static int ue_py_spython_widget_init(ue_PySPythonWidget *self, PyObject *args, PyObject *kwargs) { ue_py_snew_simple(SPythonWidget, s_compound_widget.s_widget); - UE_LOG(LogPython, Warning, TEXT("Initializing World Widget!!!")); sw_python_widget->SetPyObject((PyObject *)self); return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h index 0b8223b81..4289d94bb 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h @@ -275,6 +275,8 @@ class SPythonWidget : public SCompoundWidget void SetPyObject(PyObject *py_obj) { + Py_XDECREF(self); + Py_INCREF(py_obj); self = py_obj; } From e01febc58119d56681e83142106339ddcf6cd205 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 22 Jan 2018 00:19:21 -0800 Subject: [PATCH 19/94] Slate extensions: -Extend python plugin to support boxing & unboxing of slate <> pyslate widgets (get_swidget_from_wrapper & create_wrapper_from_pyswidget) -Allows Python & BP exposed functions to pass around slate widgets to enable slate widget construction in python to be passed to C++ where more advanced/unexposed slate functionality can be used -Adding support for buttonstyle in sbutton slate initialization -Exposing ue.unload_package() to Python -Adding SNodePanel & SGraphPanel Misc: -FIX: Default alpha value for flinearcolor init to 1 instead of 255 -FIX: load_package was not properly using the full long package name -Bypassed error in UEPython console manager, it will now return always return the string value of a registered console variable without using the broken type checking code. --- .../ConsoleManager/UEPyIConsoleManager.cpp | 4 +- .../Private/Slate/UEPySButton.cpp | 1 + .../Private/Slate/UEPySGraphPanel.cpp | 134 ++++++++++++++++++ .../Private/Slate/UEPySGraphPanel.h | 17 +++ .../Private/Slate/UEPySNodePanel.cpp | 54 +++++++ .../Private/Slate/UEPySNodePanel.h | 17 +++ .../Private/Slate/UEPySlate.cpp | 42 ++++++ .../Private/Slate/UEPySlate.h | 5 + .../UnrealEnginePython/Private/UEPyEngine.cpp | 29 +++- .../UnrealEnginePython/Private/UEPyEngine.h | 3 + .../UnrealEnginePython/Private/UEPyModule.cpp | 6 +- .../Private/UObject/UEPyObject.cpp | 22 +-- .../Private/Wrappers/UEPyFLinearColor.cpp | 2 +- .../Public/PyNativeWidgetHost.h | 17 +++ 14 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp index bf911ff12..008b3a1a7 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp @@ -132,11 +132,11 @@ static PyObject *py_ue_iconsole_manager_get_string(PyObject *cls, PyObject * arg return PyErr_Format(PyExc_Exception, "console object \"%s\" is not a variable", key); } - TConsoleVariableData *c_string = c_object->AsVariableString(); + /*TConsoleVariableData *c_string = c_object->AsVariableString(); if (!c_string) { return PyErr_Format(PyExc_Exception, "console object \"%s\" is not a string", key); - } + }*/ return PyUnicode_FromString(TCHAR_TO_UTF8(*var->GetString())); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp index 7a3553935..90e35fced 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp @@ -106,6 +106,7 @@ static int ue_py_sbutton_init(ue_PySButton *self, PyObject *args, PyObject *kwar ue_py_slate_farguments_text("text", Text); ue_py_slate_farguments_optional_enum("text_flow_direction", TextFlowDirection, ETextFlowDirection); ue_py_slate_farguments_optional_enum("text_shaping_method", TextShapingMethod, ETextShapingMethod); + ue_py_slate_farguments_optional_struct_ptr("button_style", ButtonStyle, FButtonStyle); ue_py_slate_farguments_optional_struct_ptr("text_style", TextStyle, FTextBlockStyle); ue_py_slate_farguments_optional_enum("touch_method", TouchMethod, EButtonTouchMethod::Type); ue_py_slate_farguments_optional_enum("v_align", VAlign, EVerticalAlignment); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp new file mode 100644 index 000000000..6f810ac62 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp @@ -0,0 +1,134 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPySGraphPanel.h" + +#define sw_graph_panel StaticCastSharedRef(self->s_nodePanel.s_panel.s_widget) + +/* +static PyObject *py_ue_sgraph_panel_add_slot(ue_PySGraphPanel* self, PyObject *args, PyObject *kwargs) +{ + PyObject *py_content; + int z_order = -1; + int h_align = 0; + PyObject *padding = nullptr; + int v_align = 0; + + char *kwlist[] = { (char *)"widget", + (char *)"z_order", + (char *)"h_align", + (char *)"padding", + (char *)"v_align", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iiOi:add_slot", kwlist, + &py_content, + &z_order, + &h_align, + &padding, + &v_align)) + { + return nullptr; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + self->s_nodePanel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); + + sw_graph_panel; + + SOverlay::FOverlaySlot &fslot = sw_graph_panel->//sw_overlay->AddSlot(z_order); + fslot.AttachWidget(py_swidget->s_widget->AsShared()); + fslot.HAlign((EHorizontalAlignment)h_align); + if (padding) + { + if (PyTuple_Check(padding)) + { + FMargin margin; + if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) + { + return PyErr_Format(PyExc_Exception, "invalid padding value"); + } + fslot.Padding(margin); + } + else if (PyNumber_Check(padding)) + { + PyObject *py_float = PyNumber_Float(padding); + fslot.Padding(PyFloat_AsDouble(py_float)); + Py_DECREF(py_float); + } + else + { + return PyErr_Format(PyExc_Exception, "invalid padding value"); + } + } + fslot.VAlign((EVerticalAlignment)v_align); + + Py_INCREF(self); + return (PyObject *)self; +} +*/ + +static PyMethodDef ue_PySGraphPanel_methods[] = { + //{"add_slot", (PyCFunction)py_ue_sgraph_panel_add_slot, METH_VARARGS | METH_KEYWORDS, "" }, + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PySGraphPanelType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SGraphPanel", /* tp_name */ + sizeof(ue_PySGraphPanel), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SGraphPanel", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySGraphPanel_methods, /* tp_methods */ +}; + +//why do you need to return an integer? +static int ue_py_sgraph_panel_init(ue_PySGraphPanel *self, PyObject *args, PyObject *kwargs) +{ + //so for right now, let's just have this commented out to see if we get any errors + //if says we don't have s_nodePanel + ue_py_snew_simple(SGraphPanel, s_nodePanel.s_panel.s_widget);//s_nodePanel.s_panel); + //ue_py_snew(SGraphPanel, s_nodePanel.s_panel.s_widget); + + return 0; +} + +void ue_python_init_sgraph_panel(PyObject *ue_module) +{ + //ue_PySGraphPanelType.tp_init = (initproc)ue_py_sgraph_panel_init; + //ue_PySGraphPanelType.tp_call = (ternaryfunc)py_ue_sgraph_panel_add_slot; + ue_PySGraphPanelType.tp_base = &ue_PySNodePanelType; + + if (PyType_Ready(&ue_PySGraphPanelType) < 0) + return; + + Py_INCREF(&ue_PySGraphPanelType); + PyModule_AddObject(ue_module, "SGraphPanel", (PyObject *)&ue_PySGraphPanelType); +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h new file mode 100644 index 000000000..b2ff64f01 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#include "UEPySNodePanel.h" + +#include "Editor/GraphEditor/Public/SGraphPanel.h" + +extern PyTypeObject ue_PySGraphPanelType; + +typedef struct { + ue_PySNodePanel s_nodePanel; + /* Type-specific fields go here. */ +} ue_PySGraphPanel; + + +void ue_python_init_sgraph_panel(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp new file mode 100644 index 000000000..b166daa8a --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp @@ -0,0 +1,54 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPySNodePanel.h" + +#define sw_node_panel StaticCastSharedRef(self->s_panel.s_widget) + +static PyMethodDef ue_PySNodePanel_methods[] = { + { NULL } /* Sentinel */ +}; + + +PyTypeObject ue_PySNodePanelType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SNodePanel", /* tp_name */ + sizeof(ue_PySNodePanel), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SNode Panel", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySNodePanel_methods, /* tp_methods */ +}; + +void ue_python_init_snode_panel(PyObject *ue_module) +{ + + ue_PySNodePanelType.tp_base = &ue_PySPanelType; + + if (PyType_Ready(&ue_PySNodePanelType) < 0) + return; + + Py_INCREF(&ue_PySNodePanelType); + PyModule_AddObject(ue_module, "SNodePanel", (PyObject *)&ue_PySNodePanelType); +} diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h new file mode 100644 index 000000000..7b604f99c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#include "UEPySPanel.h" + +#include "Editor/GraphEditor/Public/SNodePanel.h" + +extern PyTypeObject ue_PySNodePanelType; + +typedef struct { + ue_PySPanel s_panel; + /* Type-specific fields go here. */ +} ue_PySNodePanel; + + +void ue_python_init_snode_panel(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 6c36e7605..f06302b9c 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -24,6 +24,7 @@ #include "Runtime/AppFramework/Public/Widgets/Colors/SColorPicker.h" #include "UEPySlate.h" +#include "PyNativeWidgetHost.h" FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) { @@ -805,6 +806,8 @@ void ue_python_init_slate(PyObject *module) ue_python_init_sspacer(module); ue_python_init_spython_widget(module); ue_python_init_soverlay(module); + ue_python_init_snode_panel(module); + ue_python_init_sgraph_panel(module); #if WITH_EDITOR @@ -1296,6 +1299,45 @@ PyObject *py_unreal_engine_invoke_tab(PyObject * self, PyObject * args) } +PyObject * py_unreal_engine_get_swidget_from_wrapper(PyObject *self, PyObject *args) +{ + PyObject *py_object; + + if (!PyArg_ParseTuple(args, "O:get_swidget_from_wrapper", &py_object)) + { + return NULL; + } + + FPythonSWidgetWrapper *py_swidget_wrapper = ue_py_check_struct(py_object); + if (!py_swidget_wrapper) + return PyErr_Format(PyExc_Exception, "argument is not a FPythonSWidgetWrapper"); + + if (!py_swidget_wrapper->Widget.IsValid()) + return PyErr_Format(PyExc_Exception, "wrapper contained invalid SWidget!"); + + return (PyObject *)py_ue_new_swidget(py_swidget_wrapper->Widget->AsShared(), &ue_PySWidgetType); +} + +PyObject * py_unreal_engine_create_wrapper_from_pyswidget(PyObject *self, PyObject *args) +{ + PyObject *py_object; + + if (!PyArg_ParseTuple(args, "O:create_wrapper_from_pyswidget", &py_object)) + { + return NULL; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_object); + if (!py_swidget) { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + FPythonSWidgetWrapper py_swidget_wrapper; + py_swidget_wrapper.Widget = py_swidget->s_widget; + return py_ue_new_uscriptstruct(FPythonSWidgetWrapper::StaticStruct(), (uint8 *)&py_swidget_wrapper); +} + PyObject *py_unreal_engine_open_color_picker(PyObject *self, PyObject *args, PyObject *kwargs) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 1ff82a4c7..71d6bd085 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -59,6 +59,8 @@ #include "UEPySSpacer.h" #include "UEPySPythonWidget.h" #include "UEPySOverlay.h" +#include "UEPySNodePanel.h" +#include "UEPySGraphPanel.h" #include "UEPyFTabManager.h" #include "UEPyFTabSpawnerEntry.h" @@ -111,6 +113,8 @@ PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self #endif PyObject *py_unreal_engine_invoke_tab(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_swidget_from_wrapper(PyObject *, PyObject *); +PyObject *py_unreal_engine_create_wrapper_from_pyswidget(PyObject *, PyObject *); PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_nomad_tab_spawner(PyObject *, PyObject *); @@ -125,6 +129,7 @@ void ue_py_unregister_swidget(SWidget *); void ue_py_setup_swidget(ue_PySWidget *); + PyObject *ue_py_dict_get_item(PyObject *, const char *); template ue_PySWidget *py_ue_new_swidget(TSharedRef s_widget, PyTypeObject *py_type) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 3cfeb51af..986ed22ce 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -5,7 +5,9 @@ #include "Developer/DesktopPlatform/Public/IDesktopPlatform.h" #include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" - +#if WITH_EDITOR +#include "PackageTools.h" +#endif PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) @@ -292,6 +294,31 @@ PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) return (PyObject *)ret; } +#if WITH_EDITOR +PyObject *py_unreal_engine_unload_package(PyObject * self, PyObject * args) +{ + PyObject *obj; + if (!PyArg_ParseTuple(args, "O:unload_package", &obj)) + { + return NULL; + } + + UPackage* packageToUnload = ue_py_check_type(obj); + if (!packageToUnload) + { + return PyErr_Format(PyExc_Exception, "argument is not a UPackage"); + } + + FText outErrorMsg; + if (!PackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) + { + return PyErr_Format(PyExc_Exception, TCHAR_TO_UTF8(*outErrorMsg.ToString())); + } + + Py_RETURN_NONE; +} +#endif + PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) { char *name; diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index d6f31b518..c5cc5adbb 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -32,6 +32,9 @@ PyObject *py_unreal_engine_load_class(PyObject *, PyObject *); PyObject *py_unreal_engine_load_struct(PyObject *, PyObject *); PyObject *py_unreal_engine_load_enum(PyObject *, PyObject *); PyObject *py_unreal_engine_load_package(PyObject *, PyObject *); +#if WITH_EDITOR +PyObject *py_unreal_engine_unload_package(PyObject *, PyObject *); +#endif PyObject *py_unreal_engine_string_to_guid(PyObject *, PyObject *); PyObject *py_unreal_engine_new_guid(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 1f9d28e11..3327445fa 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -166,7 +166,9 @@ static PyMethodDef unreal_engine_methods[] = { { "load_object", py_unreal_engine_load_object, METH_VARARGS, "" }, { "load_package", py_unreal_engine_load_package, METH_VARARGS, "" }, - +#if WITH_EDITOR + { "unload_package", py_unreal_engine_unload_package, METH_VARARGS, "" }, +#endif { "get_forward_vector", py_unreal_engine_get_forward_vector, METH_VARARGS, "" }, { "get_up_vector", py_unreal_engine_get_up_vector, METH_VARARGS, "" }, { "get_right_vector", py_unreal_engine_get_right_vector, METH_VARARGS, "" }, @@ -209,6 +211,8 @@ static PyMethodDef unreal_engine_methods[] = { { "register_nomad_tab_spawner", py_unreal_engine_register_nomad_tab_spawner, METH_VARARGS, "" }, { "unregister_nomad_tab_spawner", py_unreal_engine_unregister_nomad_tab_spawner, METH_VARARGS, "" }, { "invoke_tab", py_unreal_engine_invoke_tab, METH_VARARGS, "" }, + { "get_swidget_from_wrapper", py_unreal_engine_get_swidget_from_wrapper, METH_VARARGS, "" }, + { "create_wrapper_from_pyswidget", py_unreal_engine_create_wrapper_from_pyswidget, METH_VARARGS, "" }, #if WITH_EDITOR { "get_editor_window", py_unreal_engine_get_editor_window, METH_VARARGS, "" }, { "add_menu_extension", py_unreal_engine_add_menu_extension, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index d187527ee..392c2427a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1624,7 +1624,7 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) package = (UPackage *)outer; has_package = true; } - else if (u_object->IsA() && u_object != GetTransientPackage()) + else if (u_object && u_object->IsA() && u_object != GetTransientPackage()) { package = (UPackage *)u_object; has_package = true; @@ -1637,13 +1637,13 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "the object has no associated package, please specify a name"); } if (!has_package) - { - // unmark transient object - if (u_object->HasAnyFlags(RF_Transient)) - { - u_object->ClearFlags(RF_Transient); - u_object->SetFlags(RF_Public | RF_Standalone); - } + { + // unmark transient object + if (u_object->HasAnyFlags(RF_Transient)) + { + u_object->ClearFlags(RF_Transient); + u_object->SetFlags(RF_Public | RF_Standalone); + } } package = (UPackage *)StaticFindObject(nullptr, ANY_PACKAGE, UTF8_TO_TCHAR(name), true); // create a new package if it does not exist @@ -1686,6 +1686,12 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) UE_LOG(LogPython, Warning, TEXT("no file mapped to UPackage %s, setting its FileName to %s"), *package->GetPathName(), *package->FileName.ToString()); } + // NOTE: FileName may not be a fully qualified filepath + if (FPackageName::IsValidLongPackageName(package->FileName.ToString())) + { + package->FileName = *FPackageName::LongPackageNameToFilename(package->GetPathName(), FPackageName::GetAssetPackageExtension()); + } + if (UPackage::SavePackage(package, u_object, RF_Public | RF_Standalone, *package->FileName.ToString())) { FAssetRegistryModule::AssetCreated(u_object); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp index 8f17146d7..d9537a0bd 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp @@ -191,7 +191,7 @@ static int ue_py_flinearcolor_init(ue_PyFLinearColor *self, PyObject *args, PyOb float r = 0; float g = 0; float b = 0; - float a = 255; + float a = 1.0; if (!PyArg_ParseTuple(args, "|ffff", &r, &g, &b, &a)) return -1; diff --git a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h index b80dcf93e..dca7f6a1f 100644 --- a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h +++ b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h @@ -6,6 +6,23 @@ #include "Components/NativeWidgetHost.h" #include "PyNativeWidgetHost.generated.h" +USTRUCT(BlueprintType) +struct UNREALENGINEPYTHON_API FPythonSWidgetWrapper +{ + GENERATED_USTRUCT_BODY() + + TSharedPtr Widget; +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithCopy = true, + }; +}; + /** * */ From a9a72a4229a03bfc503fe5d4c689d4f3ce76a7a2 Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 22 Jan 2018 00:50:46 -0800 Subject: [PATCH 20/94] Add assert check to make sure we stack allocate enough memory in ufunction_call --- Source/UnrealEnginePython/Private/UEPyModule.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 7bd38d0fc..7071c6814 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -3023,7 +3023,10 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } - //TODO: rdeioris: Should assert warn if u_funciton->ParmsSize != u_function->PropertiesSize bc it overflowed + if (u_function->PropertiesSize > u_function->ParmsSize) + { + return PyErr_Format(PyExc_Exception, "UFunction PropertiesSize (%i) > ParmsSize (%i).", u_function->PropertiesSize, u_function->ParmsSize); + } uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); @@ -3034,7 +3037,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * UProperty *prop = *IArgs; if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { - prop->InitializeValue_InContainer(buffer); + prop->InitializeValue_InContainer(buffer); } #if WITH_EDITOR @@ -3047,10 +3050,9 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * #else prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); #endif - } + } #endif - -} + } Py_ssize_t tuple_len = PyTuple_Size(args); From 516cf31ce2f10a5237ed599d169528f3ec3109df Mon Sep 17 00:00:00 2001 From: ikrima Date: Mon, 22 Jan 2018 03:50:09 -0800 Subject: [PATCH 21/94] Extend Slate: Add macro to support named slots ue_py_slate_farguments_optional_named_slot() will attempt to look up a widget in the kwargs and set it to the named slot passed in. Works with default slots as well as they wrap named slots NOTE: This is confusing bad design imho > Macros with optional e.g. ue_py_slate_farguments_optional_enum do not actually mean they are optional vs not. _optional_ variants mean they are arguments vs attributes i.e. unable to take lambdas vs able to take lambdas as well as value types. --- .../Private/Slate/UEPySHeaderRow.cpp | 18 +++++++++++++----- .../Private/Slate/UEPySlate.h | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp index 59d574cc0..d3b01bfd7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp @@ -18,23 +18,31 @@ static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *a ue_py_slate_farguments_optional_string("column_id", ColumnId); - ue_py_slate_farguments_optional_text("default_label", DefaultLabel); - ue_py_slate_farguments_optional_text("default_tooltip", DefaultTooltip); + ue_py_slate_farguments_text("default_label", DefaultLabel); + ue_py_slate_farguments_text("default_tooltip", DefaultTooltip); ue_py_slate_farguments_optional_enum("h_align_header", HAlignHeader, EHorizontalAlignment); ue_py_slate_farguments_optional_enum("v_align_header", VAlignHeader, EVerticalAlignment); - ue_py_slate_farguments_optional_float("fill_width", FillWidth); + ue_py_slate_farguments_float("fill_width", FillWidth); ue_py_slate_farguments_optional_float("fixed_width", FixedWidth); + ue_py_slate_farguments_float("manual_width", ManualWidth); + + ue_py_slate_farguments_optional_named_slot("header_content", HeaderContent); + ue_py_slate_farguments_optional_struct("header_content_padding", HeaderContentPadding, FMargin); + + ue_py_slate_farguments_optional_named_slot("menu_content", MenuContent); ue_py_slate_farguments_optional_enum("h_align_cell", HAlignCell, EHorizontalAlignment); ue_py_slate_farguments_optional_enum("v_align_cell", VAlignCell, EVerticalAlignment); - ue_py_slate_farguments_optional_enum("sort_mode", SortMode, EColumnSortMode::Type); - ue_py_slate_farguments_optional_enum("sort_priority", SortPriority, EColumnSortPriority::Type); + ue_py_slate_farguments_enum("sort_mode", SortMode, EColumnSortMode::Type); + ue_py_slate_farguments_enum("sort_priority", SortPriority, EColumnSortPriority::Type); ue_py_slate_farguments_event("on_sort", OnSort, FOnSortModeChanged, OnSort); + ue_py_slate_farguments_bool("should_generate_widget", ShouldGenerateWidget); + //sw_header_row->AddColumn( // SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) // .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 71d6bd085..6c64ec8e5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -398,6 +398,22 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ } +#define ue_py_slate_farguments_optional_named_slot(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + if (value) {\ + if (ue_PySWidget *py_swidget = py_ue_is_swidget(value)) {\ + Py_INCREF(py_swidget);\ + arguments.attribute()\ + [\ + py_swidget->s_widget\ + ];\ + }\ + else {\ + PyErr_SetString(PyExc_TypeError, "unsupported type for attribute " param); \ + return -1;\ + }\ + }\ +} + #define ue_py_slate_farguments_bool(param, attribute) ue_py_slate_up(bool, GetterBool, param, attribute)\ else if (PyObject_IsTrue(value)) {\ From ab09d025f1cf42e2535ceb755b25b95c2eae05ce Mon Sep 17 00:00:00 2001 From: ikrima Date: Tue, 23 Jan 2018 06:35:30 -0800 Subject: [PATCH 22/94] Slate Extension: Adding NumericBox and generic support for slot arguments -Pass numeric_type=int() -Add support for ue_py_slate_farguments_tint -FIX: Mimic UObject::CallFunctionByNameWithArguments() in py_ue_ufunction_call Python code was importing return properties into stack allocated buffer which could stack corrupt. -Normal non-named slots are supported in Slate as well by using ue_py_slate_setup_hack_slot_args() and then proceeding to use the normal slate argument macros. This allows adding python lambdas to slate attributes on slots -SHorizontalBox is the only widget that supports that's been converted to the new way now -Benefit is now we can use widget= syntax to place the slot's widget content after the attribute specification --- .../Private/Slate/UEPySHorizontalBox.cpp | 116 +++++++----------- .../Private/Slate/UEPySNumericEntryBox.cpp | 82 +++++++++---- .../Private/Slate/UEPySlate.cpp | 26 ++++ .../Private/Slate/UEPySlate.h | 25 ++++ .../UnrealEnginePython/Private/UEPyModule.cpp | 38 +++--- 5 files changed, 169 insertions(+), 118 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp index e0a7332ad..56a42c4a0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp @@ -9,80 +9,48 @@ static PyObject *py_ue_shorizontal_box_add_slot(ue_PySHorizontalBox *self, PyObject * args, PyObject *kwargs) { - PyObject *py_content; - int h_align = 0; - float max_width = 0; - PyObject *padding = nullptr; - int v_align = 0; - float fill_width = 0; - PyObject *py_auto_width = nullptr; - - char *kwlist[] = { (char *)"widget", - (char *)"h_align", - (char *)"max_width", - (char *)"padding", - (char *)"v_align", - (char *)"fill_width", - (char *)"auto_width", - nullptr }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ifOifO:add_slot", kwlist, - &py_content, - &h_align, - &max_width, - &padding, - &v_align, - &fill_width, - &py_auto_width)) - { - return NULL; - } - - ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) - { - return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - } - - Py_INCREF(py_swidget); - self->s_box_panel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); - - SHorizontalBox::FSlot &fslot = sw_horizontal_box->AddSlot(); - fslot.AttachWidget(py_swidget->s_widget->AsShared()); - fslot.HAlign((EHorizontalAlignment)h_align); - if (max_width != 0) - fslot.MaxWidth(max_width); - if (fill_width) - fslot.FillWidth(fill_width); - if (padding) - { - if (PyTuple_Check(padding)) - { - FMargin margin; - if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) - { - return PyErr_Format(PyExc_Exception, "invalid padding value"); - } - fslot.Padding(margin); - } - else if (FMargin *parsed_margin = ue_py_check_struct(padding)) - { - fslot.Padding(*parsed_margin); - } - else if (PyNumber_Check(padding)) - { - PyObject *py_float = PyNumber_Float(padding); - fslot.Padding(PyFloat_AsDouble(py_float)); - Py_DECREF(py_float); - } - else - { - return PyErr_Format(PyExc_Exception, "invalid padding value"); - } - } - fslot.VAlign((EVerticalAlignment)v_align); - if (py_auto_width && PyObject_IsTrue(py_auto_width)) - fslot.AutoWidth(); + int32 retCode = [&]() { + ue_py_slate_setup_hack_slot_args(SHorizontalBox, sw_horizontal_box); + ue_py_slate_farguments_float("fill_width", FillWidth); + ue_py_slate_farguments_float("max_width", MaxWidth); + ue_py_slate_farguments_optional_enum("h_align", HAlign, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align", VAlign, EVerticalAlignment); + + //NOTE: Padding slot in slate is weird and manually supports different parameter constructions + if (PyObject *padding = ue_py_dict_get_item(kwargs, "padding")) + { + if (PyTuple_Check(padding)) + { + FMargin margin; + if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) + { + PyErr_SetString(PyExc_TypeError, "invalid padding value"); + return -1; + } + arguments.Padding(margin); + } + else if (PyNumber_Check(padding)) + { + PyObject *py_float = PyNumber_Float(padding); + arguments.Padding(PyFloat_AsDouble(py_float)); + Py_DECREF(py_float); + } + else + { + ue_py_slate_farguments_struct("padding", Padding, FMargin); + } + } + PyObject *py_auto_width = ue_py_dict_get_item(kwargs, "auto_width"); + if (py_auto_width && PyObject_IsTrue(py_auto_width)) + { arguments.AutoWidth(); } + + return 0; + }(); + + if (retCode != 0) + { + return PyErr_Format(PyExc_Exception, "could not add horizontal slot"); + } Py_INCREF(self); return (PyObject *)self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp index 716faec1c..a073b7b98 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp @@ -4,7 +4,7 @@ #include "UEPySNumericEntryBox.h" -#define sw_numeric_entry_box StaticCastSharedRef>(self->s_compound_widget.s_widget.s_widget) +#define sw_float_numeric_entry_box StaticCastSharedRef>(self->s_compound_widget.s_widget.s_widget) static PyMethodDef ue_PySNumericEntryBox_methods[] = { @@ -44,31 +44,63 @@ PyTypeObject ue_PySNumericEntryBoxType = { static int ue_py_snumeric_entry_box_init(ue_PySNumericEntryBox *self, PyObject *args, PyObject *kwargs) { - ue_py_slate_setup_farguments(SNumericEntryBox); - - ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); - ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); - ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); - ue_py_slate_farguments_float("delta", Delta); - ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); - ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); - ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); - ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); - ue_py_slate_farguments_tfloat("max_slider_value", MaxSliderValue); - ue_py_slate_farguments_tfloat("max_value", MaxValue); - ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); - ue_py_slate_farguments_tfloat("min_slider_value", MinSliderValue); - ue_py_slate_farguments_tfloat("min_value", MinValue); - ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); - ue_py_slate_farguments_float("slider_exponent", SliderExponent); - ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); - ue_py_slate_farguments_tfloat("value", Value); - ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); - ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnFloatValueChanged, OnFloatChanged); - ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnFloatValueChanged, OnFloatChanged); - ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnFloatValueCommitted, OnFloatCommitted); + PyObject *py_numeric_type = ue_py_dict_get_item(kwargs, "numeric_type"); + if (py_numeric_type && PyLong_Check(py_numeric_type)) + { + ue_py_slate_setup_farguments(SNumericEntryBox); + + ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); + ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); + ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); + ue_py_slate_farguments_int("delta", Delta); + ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); + ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); + ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); + ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); + ue_py_slate_farguments_tint("max_slider_value", MaxSliderValue); + ue_py_slate_farguments_tint("max_value", MaxValue); + ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); + ue_py_slate_farguments_tint("min_slider_value", MinSliderValue); + ue_py_slate_farguments_tint("min_value", MinValue); + ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); + ue_py_slate_farguments_float("slider_exponent", SliderExponent); + ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); + ue_py_slate_farguments_tint("value", Value); + ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); + ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnInt32ValueChanged, OnInt32Changed); + ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnInt32ValueChanged, OnInt32Changed); + ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnInt32ValueCommitted, OnInt32Committed); + + ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + } + else + { + ue_py_slate_setup_farguments(SNumericEntryBox); + + ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); + ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); + ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); + ue_py_slate_farguments_float("delta", Delta); + ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); + ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); + ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); + ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); + ue_py_slate_farguments_tfloat("max_slider_value", MaxSliderValue); + ue_py_slate_farguments_tfloat("max_value", MaxValue); + ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); + ue_py_slate_farguments_tfloat("min_slider_value", MinSliderValue); + ue_py_slate_farguments_tfloat("min_value", MinValue); + ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); + ue_py_slate_farguments_float("slider_exponent", SliderExponent); + ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); + ue_py_slate_farguments_tfloat("value", Value); + ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); + ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnFloatValueChanged, OnFloatChanged); + ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnFloatValueChanged, OnFloatChanged); + ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnFloatValueCommitted, OnFloatCommitted); - ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + } return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index f06302b9c..9d81973bd 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -125,6 +125,32 @@ void UPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type Py_DECREF(ret); } +void UPythonSlateDelegate::OnInt32Changed(int32 value) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"i", value); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +void UPythonSlateDelegate::OnInt32Committed(int32 value, ETextCommit::Type commit_type) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"ii", value, (int)commit_type); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + void UPythonSlateDelegate::OnFloatChanged(float value) { FScopePythonGIL gil; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 6c64ec8e5..d7af63369 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -241,6 +241,13 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ ue_py_slate_down(param) +#define ue_py_slate_farguments_tint(param, attribute) ue_py_slate_up(TOptional, GetterIntT>, param, attribute)\ + else if (PyNumber_Check(value)) {\ + PyObject *py_int = PyNumber_Long(value);\ + arguments.attribute((TOptional)PyLong_AsLong(py_int)); \ + Py_DECREF(py_int);\ + }\ + ue_py_slate_down(param) #define ue_py_slate_farguments_enum(param, attribute, _type) ue_py_slate_up(_type, GetterIntT<_type>, param, attribute)\ @@ -449,6 +456,22 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); ue_py_slate_farguments_text("tool_tip_text", ToolTipText);\ ue_py_slate_farguments_fvector2d("render_transform_pivot", RenderTransformPivot) +#define ue_py_slate_farguments_required_slot(param) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + value = value ? value : PyTuple_GetItem(args, 0);\ + if (ue_PySWidget *py_swidget = value ? py_ue_is_swidget(value) : nullptr) {\ + Py_INCREF(py_swidget);\ + ue_PySWidget *self_py_swidget = py_ue_is_swidget((PyObject*)self);\ + self_py_swidget->py_swidget_slots.Add(py_swidget);\ + arguments.AttachWidget(py_swidget->s_widget->AsShared());\ + }\ + else {\ + PyErr_SetString(PyExc_TypeError, "unsupported type for required slot " param); \ + return -1;\ + }\ +} + +#define ue_py_slate_setup_hack_slot_args(_type, _swidget_ref) _type::FSlot &arguments = _swidget_ref->AddSlot();\ + ue_py_slate_farguments_required_slot("widget"); void ue_python_init_slate(PyObject *); @@ -474,6 +497,8 @@ class UPythonSlateDelegate : public UPythonDelegate FReply OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event); void OnTextChanged(const FText &text); void OnTextCommitted(const FText &text, ETextCommit::Type commit_type); + void OnInt32Changed(int32 value); + void OnInt32Committed(int32 value, ETextCommit::Type commit_type); void OnFloatChanged(float value); void OnFloatCommitted(float value, ETextCommit::Type commit_type); void OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 7071c6814..53a67fa97 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -3023,16 +3023,11 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } - if (u_function->PropertiesSize > u_function->ParmsSize) - { - return PyErr_Format(PyExc_Exception, "UFunction PropertiesSize (%i) > ParmsSize (%i).", u_function->PropertiesSize, u_function->ParmsSize); - } + //NOTE: Still weird that we're not using u_function->PropertiesSize but mirroring behavior in UObject::CallFunctionByNameWithArguments() uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); - // initialize args - TFieldIterator IArgs(u_function); - for (; IArgs && (IArgs->PropertyFlags & CPF_Parm); ++IArgs) + for (TFieldIterator IArgs(u_function); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) { UProperty *prop = *IArgs; if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) @@ -3040,20 +3035,25 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * prop->InitializeValue_InContainer(buffer); } -#if WITH_EDITOR - FString default_key = FString("CPP_Default_") + prop->GetName(); - FString default_key_value = u_function->GetMetaData(FName(*default_key)); - if (!default_key_value.IsEmpty()) - { -#if ENGINE_MINOR_VERSION >= 17 - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); -#else - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); -#endif - } -#endif + //UObject::CallFunctionByNameWithArguments() only does this part on non return value params + if((IArgs->PropertyFlags & (CPF_Parm|CPF_ReturnParm)) == CPF_Parm) + { + #if WITH_EDITOR + FString default_key = FString("CPP_Default_") + prop->GetName(); + FString default_key_value = u_function->GetMetaData(FName(*default_key)); + if (!default_key_value.IsEmpty()) + { + #if ENGINE_MINOR_VERSION >= 17 + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); + #else + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); + #endif + } + #endif + } } + Py_ssize_t tuple_len = PyTuple_Size(args); int has_out_params = 0; From 95259c15f45bf8d18c0439b22b48f808b46e5408 Mon Sep 17 00:00:00 2001 From: ikrima Date: Wed, 24 Jan 2018 05:10:15 -0800 Subject: [PATCH 23/94] FIX: Editor widgets SNodePanel/SGraphPanel/etc were referenced in non-editor builds -Removing them by wrapping in WITH_EDITOR --- Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h | 3 ++- Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp | 5 ++++- Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h | 2 ++ Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp | 3 +++ Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h | 2 ++ Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp | 4 ++-- Source/UnrealEnginePython/Private/Slate/UEPySlate.h | 4 ++-- 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h index 00af7a55c..34e189067 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h @@ -2,7 +2,7 @@ #include "UnrealEnginePython.h" - +#if WITH_EDITOR #include "UEPySCompoundWidget.h" #include "Editor/UnrealEd/Public/GraphEditor.h" @@ -15,3 +15,4 @@ typedef struct { } ue_PySGraphEditor; void ue_python_init_sgraph_editor(PyObject *); +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp index 6f810ac62..e0774a8da 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp @@ -1,5 +1,6 @@ #include "UnrealEnginePythonPrivatePCH.h" +#if WITH_EDITOR #include "UEPySGraphPanel.h" #define sw_graph_panel StaticCastSharedRef(self->s_nodePanel.s_panel.s_widget) @@ -131,4 +132,6 @@ void ue_python_init_sgraph_panel(PyObject *ue_module) Py_INCREF(&ue_PySGraphPanelType); PyModule_AddObject(ue_module, "SGraphPanel", (PyObject *)&ue_PySGraphPanelType); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h index b2ff64f01..a1d15c923 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h @@ -2,6 +2,7 @@ #include "UnrealEnginePython.h" +#if WITH_EDITOR #include "UEPySNodePanel.h" #include "Editor/GraphEditor/Public/SGraphPanel.h" @@ -15,3 +16,4 @@ typedef struct { void ue_python_init_sgraph_panel(PyObject *); +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp index b166daa8a..d239341f4 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp @@ -1,6 +1,7 @@ #include "UnrealEnginePythonPrivatePCH.h" +#if WITH_EDITOR #include "UEPySNodePanel.h" #define sw_node_panel StaticCastSharedRef(self->s_panel.s_widget) @@ -52,3 +53,5 @@ void ue_python_init_snode_panel(PyObject *ue_module) Py_INCREF(&ue_PySNodePanelType); PyModule_AddObject(ue_module, "SNodePanel", (PyObject *)&ue_PySNodePanelType); } + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h index 7b604f99c..cb33072b4 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h @@ -2,6 +2,7 @@ #include "UnrealEnginePython.h" +#if WITH_EDITOR #include "UEPySPanel.h" #include "Editor/GraphEditor/Public/SNodePanel.h" @@ -15,3 +16,4 @@ typedef struct { void ue_python_init_snode_panel(PyObject *); +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 9d81973bd..1ca3539c7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -832,11 +832,11 @@ void ue_python_init_slate(PyObject *module) ue_python_init_sspacer(module); ue_python_init_spython_widget(module); ue_python_init_soverlay(module); - ue_python_init_snode_panel(module); - ue_python_init_sgraph_panel(module); #if WITH_EDITOR + ue_python_init_snode_panel(module); + ue_python_init_sgraph_panel(module); ue_python_init_idetails_view(module); ue_python_init_seditor_viewport(module); ue_python_init_slevel_viewport(module); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index d7af63369..b9af69772 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -59,8 +59,6 @@ #include "UEPySSpacer.h" #include "UEPySPythonWidget.h" #include "UEPySOverlay.h" -#include "UEPySNodePanel.h" -#include "UEPySGraphPanel.h" #include "UEPyFTabManager.h" #include "UEPyFTabSpawnerEntry.h" @@ -88,6 +86,8 @@ #include "UEPySAssetDropTarget.h" #include "UEPySObjectPropertyEntryBox.h" #include "UEPyIDetailsView.h" +#include "UEPySNodePanel.h" +#include "UEPySGraphPanel.h" #endif #include "Runtime/Core/Public/Misc/Attribute.h" From 743a4f12952c350d43639858940621629a9b6724 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Feb 2018 23:30:04 -0800 Subject: [PATCH 24/94] Adds doc for managing properties transaction --- docs/Transactions_API.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/Transactions_API.md b/docs/Transactions_API.md index 51064b6cf..c72c4c5bb 100644 --- a/docs/Transactions_API.md +++ b/docs/Transactions_API.md @@ -39,6 +39,38 @@ ue.editor_undo() ue.editor_redo() ``` + +Managing transactions with property modification +- + +```python + +import unreal_engine as ue +from unreal_engine import FVector +from unreal_engine.classes import StaticMesh, StaticMeshActor + +#Create procedurally a Cone in the Editor +world = ue.get_editor_world() +cone2 = world.actor_spawn(StaticMeshActor) +cone2.StaticMeshComponent.StaticMesh = ue.load_object(StaticMesh, '/Engine/BasicShapes/Cone') + +#Start Transaction +ue.begin_transaction("Move Up +15") +position = cone2.get_actor_location() +position.y += 15 + +#Call modify to track property changes before setting the position +cone2.modify() +cone2.set_actor_location(position) +ue.end_transaction() + +#Undo "Move Up +15" +##ue.editor_undo() +``` + + + + Functions - From c2da8f0e3ba6e88fb816e7b5e3a826e91e3f8140 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Feb 2018 23:33:21 -0800 Subject: [PATCH 25/94] Added description --- docs/Transactions_API.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Transactions_API.md b/docs/Transactions_API.md index c72c4c5bb..d788ea8df 100644 --- a/docs/Transactions_API.md +++ b/docs/Transactions_API.md @@ -42,6 +42,7 @@ ue.editor_redo() Managing transactions with property modification - +In this little example we are creating a cone using a static mesh object from the engine content. We start a transaction and move the cone by 15 units and use the `modify` call to keep our property modifications pushed to the undo stack. ```python From 31977397938ca3536418ef8af1895a2134fd9a1b Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 6 Feb 2018 13:01:45 +0100 Subject: [PATCH 26/94] preliminary infrastructure for FSlateApplication automations --- .../Private/Slate/UEPyFCharacterEvent.cpp | 28 ++++ .../Private/Slate/UEPyFCharacterEvent.h | 1 + .../Private/Slate/UEPyFKeyEvent.cpp | 31 ++++ .../Private/Slate/UEPyFKeyEvent.h | 2 + .../UEPyFSlateApplication.cpp | 158 ++++++++++++++++++ .../SlateApplication/UEPyFSlateApplication.h | 11 ++ .../UnrealEnginePython/Private/UEPyModule.cpp | 6 + .../Private/UObject/UEPyObject.cpp | 26 +++ .../Private/UObject/UEPyObject.h | 2 + .../Private/UnrealEnginePythonPrivatePCH.h | 1 + 10 files changed, 266 insertions(+) create mode 100644 Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp create mode 100644 Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp index 49c83b5c0..68ae13a95 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp @@ -53,10 +53,31 @@ static PyTypeObject ue_PyFCharacterEventType = { ue_PyFCharacterEvent_methods, /* tp_methods */ }; +static int ue_py_fcharacter_event_init(ue_PyFCharacterEvent *self, PyObject *args, PyObject *kwargs) +{ + char *key; + if (!PyArg_ParseTuple(args, "s", &key)) + { + return -1; + } + + // TODO make it configurable + FModifierKeysState modifier; + + // TODO make repeat configurable + FCharacterEvent Event(*UTF8_TO_TCHAR(key), modifier, 0, false); + + new(&self->character_event) FCharacterEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + void ue_python_init_fcharacter_event(PyObject *ue_module) { ue_PyFCharacterEventType.tp_base = &ue_PyFInputEventType; + ue_PyFCharacterEventType.tp_init = (initproc)ue_py_fcharacter_event_init; if (PyType_Ready(&ue_PyFCharacterEventType) < 0) return; @@ -72,3 +93,10 @@ PyObject *py_ue_new_fcharacter_event(FCharacterEvent key_event) new(&ret->f_input.input) FInputEvent(key_event); return (PyObject *)ret; } + +ue_PyFCharacterEvent *py_ue_is_fcharacter_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFCharacterEventType)) + return nullptr; + return (ue_PyFCharacterEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h index 79b40f4bd..7070e0ee5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h @@ -15,3 +15,4 @@ typedef struct { void ue_python_init_fcharacter_event(PyObject *); PyObject *py_ue_new_fcharacter_event(FCharacterEvent); +ue_PyFCharacterEvent *py_ue_is_fcharacter_event(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp index 2fe3a7d9f..0d90a7d98 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp @@ -55,9 +55,33 @@ static PyTypeObject ue_PyFKeyEventType = { ue_PyFKeyEvent_methods, /* tp_methods */ }; +static int ue_py_fkey_event_init(ue_PyFKeyEvent *self, PyObject *args, PyObject *kwargs) +{ + char *key; + if (!PyArg_ParseTuple(args, "s", &key)) + { + return -1; + } + + FKey InKey(key); + + // TODO make it configurable + FModifierKeysState modifier; + + // TODO configure repeat + FKeyEvent Event(InKey, modifier, 0, false, 0, 0); + + new(&self->key_event) FKeyEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + + void ue_python_init_fkey_event(PyObject *ue_module) { ue_PyFKeyEventType.tp_base = &ue_PyFInputEventType; + ue_PyFKeyEventType.tp_init = (initproc)ue_py_fkey_event_init; if (PyType_Ready(&ue_PyFKeyEventType) < 0) return; @@ -72,3 +96,10 @@ PyObject *py_ue_new_fkey_event(FKeyEvent key_event) { new(&ret->f_input.input) FInputEvent(key_event); return (PyObject *)ret; } + +ue_PyFKeyEvent *py_ue_is_fkey_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFKeyEventType)) + return nullptr; + return (ue_PyFKeyEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h index 98cbfeaef..417153acc 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h @@ -15,3 +15,5 @@ typedef struct { void ue_python_init_fkey_event(PyObject *); PyObject *py_ue_new_fkey_event(FKeyEvent); + +ue_PyFKeyEvent *py_ue_is_fkey_event(PyObject *); diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp new file mode 100644 index 000000000..be833499f --- /dev/null +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp @@ -0,0 +1,158 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPyFSlateApplication.h" + + +static PyObject *py_ue_get_average_delta_time(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetAverageDeltaTime()); +} + +static PyObject *py_ue_get_delta_time(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetDeltaTime()); +} + +static PyObject *py_ue_goto_line_in_source(PyObject *cls, PyObject * args) +{ + char *filename; + int line_number; + if (!PyArg_ParseTuple(args, "si:goto_line_in_source", &filename, &line_number)) + { + return nullptr; + } + + FSlateApplication::Get().GotoLineInSource(FString(UTF8_TO_TCHAR(filename)), line_number); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_is_gamepad_attached(PyObject *cls, PyObject * args) +{ + bool bAttached = FSlateApplication::Get().IsGamepadAttached(); + if (bAttached) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject *py_ue_is_mouse_attached(PyObject *cls, PyObject * args) +{ + bool bAttached = FSlateApplication::Get().IsMouseAttached(); + if (bAttached) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject *py_ue_process_key_down_event(PyObject *cls, PyObject * args) +{ + PyObject *py_event; + if (!PyArg_ParseTuple(args, "O:process_key_down_event", &py_event)) + { + return nullptr; + } + + FKeyEvent *InKeyEvent = ue_py_check_struct(py_event); + if (!InKeyEvent) + { + ue_PyFKeyEvent *py_fkey_event = py_ue_is_fkey_event(py_event); + if (!py_fkey_event) + { + return PyErr_Format(PyExc_Exception, "argument is not a FKeyEvent"); + } + InKeyEvent = &py_fkey_event->key_event; + } + + if (FSlateApplication::Get().ProcessKeyDownEvent(*InKeyEvent)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyObject *py_ue_process_key_char_event(PyObject *cls, PyObject * args) +{ + PyObject *py_event; + if (!PyArg_ParseTuple(args, "O:process_key_char_event", &py_event)) + { + return nullptr; + } + + FCharacterEvent *InCharEvent = ue_py_check_struct(py_event); + if (!InCharEvent) + { + ue_PyFCharacterEvent *py_fchar_event = py_ue_is_fcharacter_event(py_event); + if (!py_fchar_event) + { + return PyErr_Format(PyExc_Exception, "argument is not a FCharacterEvent"); + } + InCharEvent = &py_fchar_event->character_event; + } + + if (FSlateApplication::Get().ProcessKeyCharEvent(*InCharEvent)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyMethodDef ue_PyFSlateApplication_methods[] = { + { "get_average_delta_time", (PyCFunction)py_ue_get_average_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "get_delta_time", (PyCFunction)py_ue_get_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "goto_line_in_source", (PyCFunction)py_ue_goto_line_in_source, METH_VARARGS | METH_CLASS, "" }, + { "is_gamepad_attached", (PyCFunction)py_ue_is_gamepad_attached, METH_VARARGS | METH_CLASS, "" }, + { "is_mouse_attached", (PyCFunction)py_ue_is_mouse_attached, METH_VARARGS | METH_CLASS, "" }, + { "process_key_down_event", (PyCFunction)py_ue_process_key_down_event, METH_VARARGS | METH_CLASS, "" }, + { "process_key_char_event", (PyCFunction)py_ue_process_key_char_event, METH_VARARGS | METH_CLASS, "" }, + { NULL } /* Sentinel */ +}; + + +static PyTypeObject ue_PyFSlateApplicationType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FSlateApplication", /* tp_name */ + sizeof(ue_PyFSlateApplication), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine SlateApplication", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFSlateApplication_methods, /* tp_methods */ + 0, + 0, +}; + +static int py_ue_fslate_application_init(ue_PyFSlateApplication *self, PyObject * args) +{ + PyErr_SetString(PyExc_Exception, "FSlateApplication is a singleton"); + return -1; +} + +void ue_python_init_fslate_application(PyObject *ue_module) +{ + ue_PyFSlateApplicationType.tp_new = PyType_GenericNew; + ue_PyFSlateApplicationType.tp_init = (initproc)py_ue_fslate_application_init; + + if (PyType_Ready(&ue_PyFSlateApplicationType) < 0) + return; + + Py_INCREF(&ue_PyFSlateApplicationType); + PyModule_AddObject(ue_module, "FSlateApplication", (PyObject *)&ue_PyFSlateApplicationType); +} diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h new file mode 100644 index 000000000..8c9caa4f3 --- /dev/null +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h @@ -0,0 +1,11 @@ +#pragma once + +#include "UnrealEnginePython.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ +} ue_PyFSlateApplication; + +void ue_python_init_fslate_application(PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 3803ab625..93cd6067d 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -475,6 +475,10 @@ static PyMethodDef ue_PyUObject_methods[] = { { "get_path_name", (PyCFunction)py_ue_get_path_name, METH_VARARGS, "" }, { "get_full_name", (PyCFunction)py_ue_get_full_name, METH_VARARGS, "" }, +#if WITH_EDITOR + { "import_custom_properties", (PyCFunction)py_ue_import_custom_properties, METH_VARARGS, "" }, +#endif + #if ENGINE_MINOR_VERSION >= 15 { "can_modify", (PyCFunction)py_ue_can_modify, METH_VARARGS, "" }, #endif @@ -1941,6 +1945,8 @@ void unreal_engine_init_py_module() ue_python_init_iconsole_manager(new_unreal_engine_module); + ue_python_init_fslate_application(new_unreal_engine_module); + #if WITH_EDITOR ue_python_init_icollection_manager(new_unreal_engine_module); #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index e987c84e2..2d7a4f024 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -7,6 +7,7 @@ #include "Runtime/AssetRegistry/Public/AssetRegistryModule.h" #include "ObjectTools.h" #include "UnrealEd.h" +#include "Runtime/Core/Public/HAL/FeedbackContextAnsi.h" #endif PyObject *py_ue_get_class(ue_PyUObject * self, PyObject * args) @@ -1737,6 +1738,31 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "unable to save package"); } +PyObject *py_ue_import_custom_properties(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + char *t3d; + + if (!PyArg_ParseTuple(args, "s:import_custom_properties", &t3d)) + { + return nullptr; + } + + FFeedbackContextAnsi context; + self->ue_object->ImportCustomProperties(UTF8_TO_TCHAR(t3d), &context); + + TArray errors; + context.GetErrors(errors); + + if (errors.Num() > 0) + { + return PyErr_Format(PyExc_Exception, "%s", TCHAR_TO_UTF8(*errors[0])); + } + + Py_RETURN_NONE; +} + PyObject *py_ue_asset_can_reimport(ue_PyUObject * self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index c32e9776c..6d7727cdf 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -82,6 +82,8 @@ PyObject *py_ue_asset_reimport(ue_PyUObject *, PyObject *); PyObject *py_ue_get_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_set_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_has_metadata(ue_PyUObject *, PyObject *); + +PyObject *py_ue_import_custom_properties(ue_PyUObject *, PyObject *); #endif PyObject *py_ue_get_thumbnail(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 0b9c6420c..93be4274b 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -73,6 +73,7 @@ #include "Slate/UEPySlate.h" #include "Http/UEPyIHttp.h" #include "ConsoleManager/UEPyIConsoleManager.h" +#include "SlateApplication/UEPyFSlateApplication.h" #include "Voice/UEPyIVoiceCapture.h" From ca99abc96b2dd2d6d50bb372ceadca4e1412b927 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 6 Feb 2018 13:17:48 +0100 Subject: [PATCH 27/94] added clipboard api --- .../UnrealEnginePython/Private/UEPyEngine.cpp | 19 +++++++++++++++++++ .../UnrealEnginePython/Private/UEPyEngine.h | 3 +++ .../UnrealEnginePython/Private/UEPyModule.cpp | 3 +++ 3 files changed, 25 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 1219b5e1e..cdc14c748 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -1256,4 +1256,23 @@ PyObject *py_unreal_engine_set_random_seed(PyObject * self, PyObject * args) FGenericPlatformMath::RandInit(seed); Py_RETURN_NONE; +} + +PyObject *py_unreal_engine_clipboard_copy(PyObject * self, PyObject * args) +{ + char *text; + if (!PyArg_ParseTuple(args, "s:clipboard_copy", &text)) + { + return nullptr; + } + + FGenericPlatformMisc::ClipboardCopy(UTF8_TO_TCHAR(text)); + Py_RETURN_NONE; +} + +PyObject *py_unreal_engine_clipboard_paste(PyObject * self, PyObject * args) +{ + FString clipboard; + FGenericPlatformMisc::ClipboardPaste(clipboard); + return PyUnicode_FromString(TCHAR_TO_UTF8(*clipboard)); } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index 93dd6c5b9..c09bef5df 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -13,6 +13,9 @@ PyObject *py_unreal_engine_get_forward_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_right_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_up_vector(PyObject *, PyObject *); +PyObject *py_unreal_engine_clipboard_copy(PyObject *, PyObject *); +PyObject *py_unreal_engine_clipboard_paste(PyObject *, PyObject *); + PyObject *py_unreal_engine_set_random_seed(PyObject *, PyObject *); PyObject *py_unreal_engine_get_game_viewport_size(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 93cd6067d..04e6e0d4b 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -387,6 +387,9 @@ static PyMethodDef unreal_engine_methods[] = { { "unregister_settings", py_unreal_engine_unregister_settings, METH_VARARGS, "" }, #endif + { "clipboard_copy", py_unreal_engine_clipboard_copy, METH_VARARGS, "" }, + { "clipboard_paste", py_unreal_engine_clipboard_paste, METH_VARARGS, "" }, + #pragma warning(suppress: 4191) { "copy_properties_for_unrelated_objects", (PyCFunction)py_unreal_engine_copy_properties_for_unrelated_objects, METH_VARARGS | METH_KEYWORDS, "" }, From ae5fd16174cc144462a69c8317457d15e1dedcad Mon Sep 17 00:00:00 2001 From: taotang1984 <34328387+taotang1984@users.noreply.github.com> Date: Thu, 8 Feb 2018 17:22:14 +0800 Subject: [PATCH 28/94] Update PythonConsole.md using updated PyFbxFactory to import animation assets --- docs/PythonConsole.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/PythonConsole.md b/docs/PythonConsole.md index f8886c1ab..598edbc2b 100644 --- a/docs/PythonConsole.md +++ b/docs/PythonConsole.md @@ -121,6 +121,8 @@ This is an ad-hoc factory subclassing the standard FbxFactory (many thanks to Ch import unreal_engine as ue import os import os.path +from unreal_engine.classes import PyFbxFactory, Skeleton +from unreal_engine.enums import EFBXImportType """ Scan a directory for FBX's and import them with a single skeleton @@ -135,23 +137,19 @@ skeleton_name = 'UE4_Mannequin_Skeleton' skeleton = ue.find_object(skeleton_name) # get the PyFbxFactory -factory = ue.find_class('PyFbxFactory') +factory = PyFbxFactory() # setup fbx importer options -fbx_import_ui_class = ue.find_class('FbxImportUI') -fbx_import_ui = ue.new_object(fbx_import_ui_class) -fbx_import_ui.set_property('Skeleton', skeleton) - +factory.ImportUI.MeshTypeToImport = EFBXImportType.FBXIT_Animation +factory.ImportUI.Skeleton = skeleton # the fbx import procedure def fbx_import(filename, asset_name): ue.log('importing ' + filename + ' to ' + asset_name) - # set import options - ue.set_fbx_import_option(fbx_import_ui) - # import he file using PyFbxFactory - asset = ue.import_asset(filename, asset_name, factory) + # import the file using PyFbxFactory + asset = factory.factory_import_object(filename, asset_name) # print the skeleton name - ue.log('FBX imported with skeleton: ' + asset.get_property('Skeleton').get_name()) + ue.log('FBX imported with skeleton: ' + asset.Skeleton.get_name()) # scan the directory and uatomatically assign a name using an incremental id From 497c86e329ddd7162bda04525cca8b5353771847 Mon Sep 17 00:00:00 2001 From: ikrima Date: Thu, 8 Feb 2018 20:07:58 -0800 Subject: [PATCH 29/94] FIX: Python Plugin u_function call was not copying output ref params back to python layer -Adding utility to generate dockable tab spawners as easily as SWindow Extending IStructureDetailsView to python layer -Can now create structure details views. -NOTE: Structs are value types so you may need to copy them back out to the uobjects you bound the detail view to Python plugin layer fix: Copy function params that are const ref arrays back out to python -Other const params don't need to be copied Python Plugin Extensions -Exposing CreateStructureDetailView to Python as separate function -Exposing set_structure_data -Exposing Object Flags & ability to set them to Python to create non RF_Public objects in packages -Exposing ability to pass object flags to new_object Suppress compiler warning for python functions Add py.cmd for executing python commands from console Python Extension: Add ability to grab FBX String property from FBX object Exposed minimize function of SWindow. Exposed SetRealtime function of EditorViewportClient Exposed UActorComponent::GetRootComponent() to python. --- .../Private/Fbx/UEPyFbxProperty.cpp | 7 +- .../Slate/UEPyIStructureDetailsView.cpp | 220 ++++++++++++++++++ .../Private/Slate/UEPyIStructureDetailsView.h | 17 ++ .../Private/Slate/UEPySWindow.cpp | 13 +- .../Private/Slate/UEPySlate.cpp | 91 +++++++- .../Private/Slate/UEPySlate.h | 2 + .../UnrealEnginePython/Private/UEPyEngine.cpp | 5 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 48 +++- .../Private/UObject/UEPyActor.cpp | 21 ++ .../Private/UObject/UEPyActor.h | 1 + .../Private/UObject/UEPyObject.cpp | 24 ++ .../Private/UObject/UEPyObject.h | 2 + .../Private/UObject/UEPyPhysics.cpp | 1 - .../Private/UObject/UEPySequencer.cpp | 6 +- .../Private/UnrealEnginePython.cpp | 24 ++ .../Wrappers/UEPyFEditorViewportClient.cpp | 13 ++ 16 files changed, 474 insertions(+), 21 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp index eb4fd6dbc..62a076746 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp @@ -1,6 +1,6 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#if ENGINE_MINOR_VERSION > 12 #if WITH_EDITOR #include "UEPyFbx.h" @@ -14,6 +14,10 @@ static PyObject *py_ue_fbx_property_get_double3(ue_PyFbxProperty *self, PyObject return Py_BuildValue((char *)"(fff)", value[0], value[1], value[2]); } +static PyObject *py_ue_fbx_property_get_string(ue_PyFbxProperty *self, PyObject *args) { + return PyUnicode_FromString(self->fbx_property.Get()); +} + static PyObject *py_ue_fbx_property_is_valid(ue_PyFbxProperty *self, PyObject *args) { if (self->fbx_property.IsValid()) { Py_RETURN_TRUE; @@ -44,6 +48,7 @@ static PyObject *py_ue_fbx_property_get_curve_node(ue_PyFbxProperty *self, PyObj static PyMethodDef ue_PyFbxProperty_methods[] = { { "get_name", (PyCFunction)py_ue_fbx_property_get_name, METH_VARARGS, "" }, { "get_double3", (PyCFunction)py_ue_fbx_property_get_double3, METH_VARARGS, "" }, + { "get_string", (PyCFunction)py_ue_fbx_property_get_string, METH_VARARGS, "" }, { "is_valid", (PyCFunction)py_ue_fbx_property_is_valid, METH_VARARGS, "" }, { "get_curve_node", (PyCFunction)py_ue_fbx_property_get_curve_node, METH_VARARGS, "" }, { NULL } /* Sentinel */ diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp new file mode 100644 index 000000000..bed844b8c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp @@ -0,0 +1,220 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR +#include "UEPyIStructureDetailsView.h" +#include "IStructureDetailsView.h" + + +//#define sw_idetails_view StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + + +static PyObject *ue_PyIStructureDetailsView_str(ue_PyIStructureDetailsView *self) +{ +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount(), + self->ob_base.ob_refcnt); +#else + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount()); +#endif +} + + +static PyObject *py_ue_istructure_details_view_set_structure_data(ue_PyIStructureDetailsView *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_object = nullptr; + PyObject *py_force_refresh = nullptr; + + char *kwlist[] = { + (char *)"struct_data", + (char *)"force_refresh", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_structure_data", kwlist, &py_object, &py_force_refresh)) + { + return nullptr; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + + Py_XDECREF(self->ue_py_struct); + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view->SetStructureData(struct_scope); + + if (py_force_refresh && PyObject_IsTrue(py_force_refresh)) + { + self->istructure_details_view->GetDetailsView().ForceRefresh(); + } + + Py_RETURN_NONE; +} + +static PyObject *py_ue_istructure_details_view_get_widget(ue_PyIStructureDetailsView *self, PyObject * args) +{ + if (!self->istructure_details_view.IsValid()) + { + return PyErr_Format(PyExc_Exception, "IStructureDetailsView is not valid"); + } + + TSharedPtr ret_widget = self->istructure_details_view->GetWidget(); + if (!ret_widget.IsValid()) + { + return PyErr_Format(PyExc_Exception, "unable to create SingleProperty widget"); + } + + return (PyObject *)py_ue_new_swidget(ret_widget->AsShared(), &ue_PySWidgetType); + + Py_RETURN_NONE; +} + +static PyMethodDef ue_PyIStructureDetailsView_methods[] = { +#pragma warning(suppress: 4191) + { "set_structure_data", (PyCFunction)py_ue_istructure_details_view_set_structure_data, METH_VARARGS | METH_KEYWORDS, "" }, + { "get_widget", (PyCFunction)py_ue_istructure_details_view_get_widget, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static void ue_PyIStructureDetailsView_dealloc(ue_PyIStructureDetailsView *self) { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, &self->istructure_details_view.Get()); +#endif + Py_DECREF(self->ue_py_struct); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyTypeObject ue_PyIStructureDetailsViewType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.IStructureDetailsView", /* tp_name */ + sizeof(ue_PyIStructureDetailsView), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ue_PyIStructureDetailsView_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyIStructureDetailsView_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine IStructureDetailsView", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyIStructureDetailsView_methods, /* tp_methods */ +}; + +static int ue_py_istructure_details_view_init(ue_PyIStructureDetailsView *self, PyObject *args, PyObject *kwargs) { + + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:__init__", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return -1; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + PyErr_SetString(PyExc_Exception, "argument is not a UScriptStruct"); + return -1; + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + new(&self->istructure_details_view) TSharedPtr(nullptr); + + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return 0; +} + +void ue_python_init_istructure_details_view(PyObject *ue_module) +{ + ue_PyIStructureDetailsViewType.tp_new = PyType_GenericNew; + + ue_PyIStructureDetailsViewType.tp_init = (initproc)ue_py_istructure_details_view_init; + + ue_PyIStructureDetailsViewType.tp_getattro = PyObject_GenericGetAttr; + ue_PyIStructureDetailsViewType.tp_setattro = PyObject_GenericSetAttr; + + if (PyType_Ready(&ue_PyIStructureDetailsViewType) < 0) + return; + + Py_INCREF(&ue_PyIStructureDetailsViewType); + PyModule_AddObject(ue_module, "IStructureDetailsView", (PyObject *)&ue_PyIStructureDetailsViewType); +} + +ue_PyIStructureDetailsView *py_ue_is_istructure_details_view(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIStructureDetailsViewType)) + return nullptr; + return (ue_PyIStructureDetailsView *)obj; +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h new file mode 100644 index 000000000..54db231bf --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + TSharedPtr istructure_details_view; + ue_PyUScriptStruct *ue_py_struct; +} ue_PyIStructureDetailsView; + +void ue_python_init_istructure_details_view(PyObject *); + +ue_PyIStructureDetailsView * py_ue_is_istructure_details_view(PyObject *obj); + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp index 247cb74c7..5d645ef7f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp @@ -39,6 +39,14 @@ static PyObject *py_ue_swindow_resize(ue_PySWindow *self, PyObject * args) return (PyObject *)self; } +static PyObject *py_ue_swindow_minimize(ue_PySWindow *self, PyObject * args) +{ + sw_window->Minimize(); + + Py_INCREF(self); + return (PyObject *)self; +} + static PyObject *py_ue_swindow_set_content(ue_PySWindow *self, PyObject * args) { PyObject *py_content; @@ -130,8 +138,9 @@ static PyObject *py_ue_swindow_add_child(ue_PySWindow *self, PyObject * args) } static PyMethodDef ue_PySWindow_methods[] = { - { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, - { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, + { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, + { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, + { "minimize", (PyCFunction)py_ue_swindow_minimize, METH_VARARGS, "" }, { "resize", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_client_size", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_content", (PyCFunction)py_ue_swindow_set_content, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 1ca3539c7..432a8a77a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -838,6 +838,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_snode_panel(module); ue_python_init_sgraph_panel(module); ue_python_init_idetails_view(module); + ue_python_init_istructure_details_view(module); ue_python_init_seditor_viewport(module); ue_python_init_slevel_viewport(module); ue_python_init_spython_editor_viewport(module); @@ -1009,7 +1010,6 @@ class FPythonSlateCommands : public TCommands }; #if WITH_EDITOR - PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, PyObject *kwargs) { @@ -1018,7 +1018,7 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P PyObject *py_update_from_selection = nullptr; PyObject *py_lockable = nullptr; - PyObject *py_name_area_settings = nullptr; + char *py_name_area_settings = nullptr; PyObject *py_hide_selection_tip = nullptr; PyObject *py_search_initial_key_focus = nullptr; @@ -1047,12 +1047,13 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; - view_args.NameAreaSettings = [py_name_area_settings]() { - if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } - else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } - else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - else if (FCString::Stricmp(UTF8_TO_TCHAR(py_name_area_settings), TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } - else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } }(); TSharedPtr view = PropertyEditorModule.CreateDetailView(view_args); @@ -1066,6 +1067,80 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PyIDetailsViewType); } +PyObject *py_unreal_engine_create_structure_detail_view(PyObject *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_structure_detail_view", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return nullptr; + } + + if (py_object && !py_ue_is_uscriptstruct(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + + extern PyTypeObject ue_PyIStructureDetailsViewType; + ue_PyIStructureDetailsView *ret = (ue_PyIStructureDetailsView *)PyObject_New(ue_PyIStructureDetailsView, &ue_PyIStructureDetailsViewType); + new(&ret->istructure_details_view) TSharedPtr(nullptr); + ret->ue_py_struct = nullptr; + TSharedPtr struct_scope; + + if (ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object)) + { + Py_INCREF(ue_py_struct); + ret->ue_py_struct = ue_py_struct; + struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + } + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + ret->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return (PyObject *)ret; +} + PyObject *py_unreal_engine_create_property_view(PyObject *self, PyObject * args, PyObject *kwargs) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index b9af69772..0d95e74b8 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -86,6 +86,7 @@ #include "UEPySAssetDropTarget.h" #include "UEPySObjectPropertyEntryBox.h" #include "UEPyIDetailsView.h" +#include "UEPyIStructureDetailsView.h" #include "UEPySNodePanel.h" #include "UEPySGraphPanel.h" #endif @@ -107,6 +108,7 @@ PyObject *py_unreal_engine_add_menu_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_add_menu_bar_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_add_tool_bar_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_create_detail_view(PyObject *, PyObject *, PyObject *); +PyObject *py_unreal_engine_create_structure_detail_view(PyObject *, PyObject *, PyObject *); PyObject *py_unreal_engine_create_property_view(PyObject *, PyObject *, PyObject *); PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index da4f24de3..44ffcb397 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -552,7 +552,8 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) PyObject *obj; PyObject *py_outer = NULL; char *name = nullptr; - if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj, &py_outer, &name)) + uint64 flags = (uint64)(RF_Public | RF_Standalone); + if (!PyArg_ParseTuple(args, "O|OsK:new_object", &obj, &py_outer, &name, &flags)) { return NULL; } @@ -590,7 +591,7 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) outer = py_outer_obj->ue_object; } - UObject *new_object = NewObject(outer, obj_class, f_name, RF_Public | RF_Standalone); + UObject *new_object = NewObject(outer, obj_class, f_name, (EObjectFlags)flags); if (!new_object) return PyErr_Format(PyExc_Exception, "unable to create object"); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 53a67fa97..824833299 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -230,7 +230,9 @@ static PyMethodDef unreal_engine_methods[] = { { "add_asset_view_context_menu_extension", py_unreal_engine_add_asset_view_context_menu_extension, METH_VARARGS, "" }, #pragma warning(suppress: 4191) - { "create_detail_view", (PyCFunction)py_unreal_engine_create_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, + { "create_detail_view", (PyCFunction)py_unreal_engine_create_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, +#pragma warning(suppress: 4191) + { "create_structure_detail_view", (PyCFunction)py_unreal_engine_create_structure_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, #pragma warning(suppress: 4191) { "create_property_view", (PyCFunction)py_unreal_engine_create_property_view, METH_VARARGS | METH_KEYWORDS, "" }, @@ -662,6 +664,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "class_generated_by", (PyCFunction)py_ue_class_generated_by, METH_VARARGS, "" }, { "class_get_flags", (PyCFunction)py_ue_class_get_flags, METH_VARARGS, "" }, { "class_set_flags", (PyCFunction)py_ue_class_set_flags, METH_VARARGS, "" }, + { "get_obj_flags", (PyCFunction)py_ue_get_obj_flags, METH_VARARGS, "" }, + { "set_obj_flags", (PyCFunction)py_ue_set_obj_flags, METH_VARARGS, "" }, #if WITH_EDITOR { "class_get_config_name", (PyCFunction)py_ue_class_get_config_name, METH_VARARGS, "" }, @@ -732,6 +736,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "add_actor_component", (PyCFunction)py_ue_add_actor_component, METH_VARARGS, "" }, { "add_instance_component", (PyCFunction)py_ue_add_instance_component, METH_VARARGS, "" }, + { "get_actor_root_component", (PyCFunction)py_ue_get_actor_root_component, METH_VARARGS, "" }, { "add_actor_root_component", (PyCFunction)py_ue_add_actor_root_component, METH_VARARGS, "" }, { "get_actor_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, { "get_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, @@ -2007,6 +2012,37 @@ void unreal_engine_init_py_module() PyDict_SetItemString(unreal_engine_dict, "CLASS_ABSTRACT", PyLong_FromUnsignedLongLong((uint64)CLASS_Abstract)); PyDict_SetItemString(unreal_engine_dict, "CLASS_INTERFACE", PyLong_FromUnsignedLongLong((uint64)CLASS_Interface)); + // Objects + PyDict_SetItemString(unreal_engine_dict, "RF_NOFLAGS" , PyLong_FromUnsignedLongLong((uint64)RF_NoFlags )); + PyDict_SetItemString(unreal_engine_dict, "RF_PUBLIC" , PyLong_FromUnsignedLongLong((uint64)RF_Public )); + PyDict_SetItemString(unreal_engine_dict, "RF_STANDALONE" , PyLong_FromUnsignedLongLong((uint64)RF_Standalone )); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASNATIVE" , PyLong_FromUnsignedLongLong((uint64)RF_MarkAsNative )); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSACTIONAL" , PyLong_FromUnsignedLongLong((uint64)RF_Transactional )); + PyDict_SetItemString(unreal_engine_dict, "RF_CLASSDEFAULTOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_ClassDefaultObject )); + PyDict_SetItemString(unreal_engine_dict, "RF_ARCHETYPEOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_ArchetypeObject )); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_Transient )); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASROOTSET" , PyLong_FromUnsignedLongLong((uint64)RF_MarkAsRootSet )); + PyDict_SetItemString(unreal_engine_dict, "RF_TAGGARBAGETEMP" , PyLong_FromUnsignedLongLong((uint64)RF_TagGarbageTemp )); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDINITIALIZATION" , PyLong_FromUnsignedLongLong((uint64)RF_NeedInitialization )); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDLOAD" , PyLong_FromUnsignedLongLong((uint64)RF_NeedLoad )); + PyDict_SetItemString(unreal_engine_dict, "RF_KEEPFORCOOKER" , PyLong_FromUnsignedLongLong((uint64)RF_KeepForCooker )); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOAD" , PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoad )); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOADSUBOBJECTS" , PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoadSubobjects )); + PyDict_SetItemString(unreal_engine_dict, "RF_NEWERVERSIONEXISTS" , PyLong_FromUnsignedLongLong((uint64)RF_NewerVersionExists )); + PyDict_SetItemString(unreal_engine_dict, "RF_BEGINDESTROYED" , PyLong_FromUnsignedLongLong((uint64)RF_BeginDestroyed )); + PyDict_SetItemString(unreal_engine_dict, "RF_FINISHDESTROYED" , PyLong_FromUnsignedLongLong((uint64)RF_FinishDestroyed )); + PyDict_SetItemString(unreal_engine_dict, "RF_BEINGREGENERATED" , PyLong_FromUnsignedLongLong((uint64)RF_BeingRegenerated )); + PyDict_SetItemString(unreal_engine_dict, "RF_DEFAULTSUBOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_DefaultSubObject )); + PyDict_SetItemString(unreal_engine_dict, "RF_WASLOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WasLoaded )); + PyDict_SetItemString(unreal_engine_dict, "RF_TEXTEXPORTTRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_TextExportTransient )); + PyDict_SetItemString(unreal_engine_dict, "RF_LOADCOMPLETED" , PyLong_FromUnsignedLongLong((uint64)RF_LoadCompleted )); + PyDict_SetItemString(unreal_engine_dict, "RF_INHERITABLECOMPONENTTEMPLATE", PyLong_FromUnsignedLongLong((uint64)RF_InheritableComponentTemplate)); + PyDict_SetItemString(unreal_engine_dict, "RF_DUPLICATETRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_DuplicateTransient )); + PyDict_SetItemString(unreal_engine_dict, "RF_STRONGREFONFRAME" , PyLong_FromUnsignedLongLong((uint64)RF_StrongRefOnFrame )); + PyDict_SetItemString(unreal_engine_dict, "RF_NONPIEDUPLICATETRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_NonPIEDuplicateTransient )); + PyDict_SetItemString(unreal_engine_dict, "RF_DYNAMIC" , PyLong_FromUnsignedLongLong((uint64)RF_Dynamic )); + PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded )); + // Properties PyDict_SetItemString(unreal_engine_dict, "CPF_CONFIG", PyLong_FromUnsignedLongLong((uint64)CPF_Config)); PyDict_SetItemString(unreal_engine_dict, "CPF_GLOBAL_CONFIG", PyLong_FromUnsignedLongLong((uint64)CPF_GlobalConfig)); @@ -3023,7 +3059,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } - //NOTE: Still weird that we're not using u_function->PropertiesSize but mirroring behavior in UObject::CallFunctionByNameWithArguments() + //NOTE: u_function->PropertiesSize maps to local variable uproperties + ufunction paramaters uproperties uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); // initialize args @@ -3038,6 +3074,10 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * //UObject::CallFunctionByNameWithArguments() only does this part on non return value params if((IArgs->PropertyFlags & (CPF_Parm|CPF_ReturnParm)) == CPF_Parm) { + if (!prop->IsInContainer(u_function->ParmsSize)) + { + return PyErr_Format(PyExc_Exception, "Attempting to import func param property that's out of bounds. %s", *u_function->GetName()); + } #if WITH_EDITOR FString default_key = FString("CPP_Default_") + prop->GetName(); FString default_key_value = u_function->GetMetaData(FName(*default_key)); @@ -3089,7 +3129,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } } - if ((PArgs->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { has_out_params++; } @@ -3133,7 +3173,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * for (; OProps; ++OProps) { UProperty *prop = *OProps; - if ((prop->GetPropertyFlags() & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { // skip return param as it must be always the first if (prop->GetPropertyFlags() & CPF_ReturnParm) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e5025b4f6..e0eeced08 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -508,6 +508,27 @@ PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * a } +PyObject *py_ue_get_actor_root_component(ue_PyUObject * self, PyObject * args) { + + ue_py_check(self); + + AActor *actor = ue_get_actor(self); + if (!actor) + return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); + + UActorComponent *component = actor->GetRootComponent(); + if (component) { + ue_PyUObject *py_obj = ue_get_python_wrapper(component); + if (!py_obj) + return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); + Py_INCREF(py_obj); + return (PyObject *)py_obj; + } + + Py_INCREF(Py_None); + return Py_None; +} + PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) { ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.h b/Source/UnrealEnginePython/Private/UObject/UEPyActor.h index 39486eda1..5e46abbb1 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.h @@ -21,6 +21,7 @@ PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject *, PyObject *); PyObject *py_ue_get_owner(ue_PyUObject *, PyObject *); PyObject *py_ue_add_actor_component(ue_PyUObject *, PyObject *); PyObject *py_ue_add_python_component(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_actor_root_component(ue_PyUObject *, PyObject *); PyObject *py_ue_add_actor_root_component(ue_PyUObject *, PyObject *); PyObject *py_ue_actor_has_component_of_type(ue_PyUObject *, PyObject *); PyObject *py_ue_get_actor_component_by_type(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 2960103dd..5c2162a6f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -73,6 +73,30 @@ PyObject *py_ue_class_set_flags(ue_PyUObject * self, PyObject * args) Py_RETURN_NONE; } +PyObject *py_ue_get_obj_flags(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + return PyLong_FromUnsignedLongLong((uint64)self->ue_object->GetFlags()); +} + +PyObject *py_ue_set_obj_flags(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + uint64 flags; + if (!PyArg_ParseTuple(args, "K:set_obj_flags", &flags)) + { + return nullptr; + } + + self->ue_object->SetFlags((EObjectFlags)flags); + + Py_RETURN_NONE; +} + + #if WITH_EDITOR PyObject *py_ue_class_set_config_name(ue_PyUObject * self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index c32e9776c..d7eb08c08 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -69,6 +69,8 @@ PyObject *py_ue_class_generated_by(ue_PyUObject *, PyObject *); PyObject *py_ue_class_get_flags(ue_PyUObject *, PyObject *); PyObject *py_ue_class_set_flags(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_obj_flags(ue_PyUObject *, PyObject *); +PyObject *py_ue_set_obj_flags(ue_PyUObject *, PyObject *); #if WITH_EDITOR diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp index 3c1e829a2..01dca4b04 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp @@ -375,7 +375,6 @@ PyObject *py_ue_set_physics_angular_velocity(ue_PyUObject * self, PyObject * arg return Py_None; } - PyObject *py_ue_get_physics_angular_velocity(ue_PyUObject * self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 25d99d834..01564d7a3 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -304,7 +304,7 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_spawnable", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_make_new_spawnable", &py_obj)) { return NULL; } @@ -796,7 +796,7 @@ PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * arg ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "track:sequencer_remove_master_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_master_track", &py_track)) { return nullptr; } @@ -821,7 +821,7 @@ PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) { ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "track:sequencer_remove_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_track", &py_track)) { return nullptr; } diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 29c52441a..f3b991d38 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -147,12 +147,36 @@ namespace { } } + static void consoleExecString(const TArray& Args) + { + if (Args.Num() == 0) + { + UE_LOG(LogPython, Warning, TEXT("Usage: 'py.cmd '.")); + UE_LOG(LogPython, Warning, TEXT(" scriptname: Name of script, must reside in Scripts folder. Ex: myscript.py")); + } + else + { + FString cmdString; + for (const FString& argStr : Args) + { + cmdString += argStr.TrimQuotes() + '\n'; + } + + UPythonBlueprintFunctionLibrary::ExecutePythonString(cmdString); + } + } + } FAutoConsoleCommand ExecPythonScriptCommand( TEXT("py.exec"), *NSLOCTEXT("UnrealEnginePython", "CommandText_Exec", "Execute python script").ToString(), FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecScript)); +FAutoConsoleCommand ExecPythonStringCommand( + TEXT("py.cmd"), + *NSLOCTEXT("UnrealEnginePython", "CommandText_Cmd", "Execute python string").ToString(), + FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecString)); + void FUnrealEnginePythonModule::StartupModule() { diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp index d348a7291..6a30692df 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp @@ -83,6 +83,18 @@ static PyObject *py_ue_feditor_viewport_client_set_view_location(ue_PyFEditorVie Py_RETURN_NONE; } +static PyObject *py_ue_feditor_viewport_client_set_realtime(ue_PyFEditorViewportClient *self, PyObject * args) +{ + PyObject* bInRealtime; + PyObject* bStoreCurrentValue; + if (!PyArg_ParseTuple(args, "OO", &bInRealtime, &bStoreCurrentValue)) + return nullptr; + + self->editor_viewport_client->SetRealtime(PyObject_IsTrue(bInRealtime) ? true : false, + PyObject_IsTrue(bStoreCurrentValue) ? true : false); + Py_RETURN_NONE; +} + static PyMethodDef ue_PyFEditorViewportClient_methods[] = { { "take_high_res_screen_shot", (PyCFunction)py_ue_feditor_viewport_client_take_high_res_screen_shot, METH_VARARGS, "" }, { "tick", (PyCFunction)py_ue_feditor_viewport_client_tick, METH_VARARGS, "" }, @@ -94,6 +106,7 @@ static PyMethodDef ue_PyFEditorViewportClient_methods[] = { { "get_scene_depth_at_location", (PyCFunction)py_ue_feditor_viewport_client_get_scene_depth_at_location, METH_VARARGS, "" }, { "set_look_at_location", (PyCFunction)py_ue_feditor_viewport_client_set_look_at_location, METH_VARARGS, "" }, { "set_view_location", (PyCFunction)py_ue_feditor_viewport_client_set_view_location, METH_VARARGS, "" }, + { "set_realtime", (PyCFunction)py_ue_feditor_viewport_client_set_realtime, METH_VARARGS, "" }, { nullptr } /* Sentinel */ }; From 688c14914ddde523db250da10503351eeeca402a Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 08:16:00 +0100 Subject: [PATCH 30/94] cleaned up amazing KNL stuff --- Source/PythonConsole/Private/PythonScriptFactory.cpp | 2 -- .../Private/Slate/UEPyIStructureDetailsView.cpp | 2 +- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 2 -- UnrealEnginePython.uplugin | 5 ++--- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Source/PythonConsole/Private/PythonScriptFactory.cpp b/Source/PythonConsole/Private/PythonScriptFactory.cpp index b5dba512f..1b1828a7c 100644 --- a/Source/PythonConsole/Private/PythonScriptFactory.cpp +++ b/Source/PythonConsole/Private/PythonScriptFactory.cpp @@ -5,9 +5,7 @@ UPythonScriptFactory::UPythonScriptFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if !WITH_KNL_PYEXT Formats.Add(FString("py;Python Script")); -#endif bCreateNew = false; bEditAfterNew = true; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp index bed844b8c..0af6c4129 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp @@ -58,7 +58,7 @@ static PyObject *py_ue_istructure_details_view_set_structure_data(ue_PyIStructur if (py_force_refresh && PyObject_IsTrue(py_force_refresh)) { - self->istructure_details_view->GetDetailsView().ForceRefresh(); + self->istructure_details_view->GetDetailsView()->ForceRefresh(); } Py_RETURN_NONE; diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 7c03a068c..6498068c9 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -19,7 +19,6 @@ public class UnrealEnginePython : ModuleRules private string[] windowsKnownPaths = { - "../../../../../ThirdParty/Python3", "C:/Program Files/Python36", "C:/Program Files/Python35", "C:/Python27", @@ -80,7 +79,6 @@ public UnrealEnginePython(ReadOnlyTargetRules Target) : base(Target) public UnrealEnginePython(TargetInfo Target) #endif { - Definitions.Add("WITH_KNL_PYEXT=1"); PublicIncludePaths.AddRange( new string[] { diff --git a/UnrealEnginePython.uplugin b/UnrealEnginePython.uplugin index 0731a2b1b..a7ed3dabb 100644 --- a/UnrealEnginePython.uplugin +++ b/UnrealEnginePython.uplugin @@ -17,7 +17,7 @@ "Modules": [ { "Name": "UnrealEnginePython", - "Type": "Developer", + "Type": "Runtime", "LoadingPhase": "Default" }, { @@ -36,6 +36,5 @@ "Name": "LevelSequenceEditor", "Enabled": true } - ], - "BlacklistTargets": ["Server"] + ] } From 581a2791e86ebf7d197807fa8d88c7ebcdb1f3ec Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 08:34:42 +0100 Subject: [PATCH 31/94] first round of backports --- Source/UnrealEnginePython/Private/PyActor.cpp | 2 ++ Source/UnrealEnginePython/Private/PyCharacter.cpp | 2 ++ Source/UnrealEnginePython/Private/PyCommandlet.cpp | 2 ++ Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h | 2 ++ Source/UnrealEnginePython/Private/UEPyEngine.cpp | 2 ++ Source/UnrealEnginePython/Private/UEPyTicker.h | 2 ++ .../Private/Wrappers/UEPyFMorphTargetDelta.h | 3 +++ Source/UnrealEnginePython/Public/PyNativeWidgetHost.h | 4 +++- Source/UnrealEnginePython/Public/PythonScript.h | 2 +- 9 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 767b6670d..872f78af5 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyActor.h" diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index d0c43b97c..a2251707b 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyCharacter.h" diff --git a/Source/UnrealEnginePython/Private/PyCommandlet.cpp b/Source/UnrealEnginePython/Private/PyCommandlet.cpp index ab4873297..33c243450 100644 --- a/Source/UnrealEnginePython/Private/PyCommandlet.cpp +++ b/Source/UnrealEnginePython/Private/PyCommandlet.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyCommandlet.h" diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h index a1d15c923..d29887525 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h @@ -3,6 +3,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 12 #include "UEPySNodePanel.h" #include "Editor/GraphEditor/Public/SGraphPanel.h" @@ -16,4 +17,5 @@ typedef struct { void ue_python_init_sgraph_panel(PyObject *); +#endif #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index a3ca8249e..72c68696f 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "Kismet/KismetSystemLibrary.h" diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.h b/Source/UnrealEnginePython/Private/UEPyTicker.h index 22b9396ff..11ed989fe 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.h +++ b/Source/UnrealEnginePython/Private/UEPyTicker.h @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #pragma once #include "UnrealEnginePython.h" diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h index 1738d9ed4..4b0c81788 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h @@ -1,6 +1,8 @@ #pragma once #include "UnrealEnginePython.h" +#if ENGINE_MINOR_VERSION > 12 + #include "Runtime/Engine/Classes/Animation/MorphTarget.h" struct ue_PyFMorphTargetDelta { @@ -14,3 +16,4 @@ void ue_python_init_fmorph_target_delta(PyObject *); PyObject *py_ue_new_fmorph_target_delta(FMorphTargetDelta); ue_PyFMorphTargetDelta *py_ue_is_fmorph_target_delta(PyObject *); +#endif diff --git a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h index dca7f6a1f..220f706b4 100644 --- a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h +++ b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h @@ -2,10 +2,11 @@ #pragma once -#include "CoreMinimal.h" +#include "UnrealEnginePython.h" #include "Components/NativeWidgetHost.h" #include "PyNativeWidgetHost.generated.h" + USTRUCT(BlueprintType) struct UNREALENGINEPYTHON_API FPythonSWidgetWrapper { @@ -39,3 +40,4 @@ class UNREALENGINEPYTHON_API UPyNativeWidgetHost : public UNativeWidgetHost #endif }; + diff --git a/Source/UnrealEnginePython/Public/PythonScript.h b/Source/UnrealEnginePython/Public/PythonScript.h index 0c95a8253..15ecaa284 100644 --- a/Source/UnrealEnginePython/Public/PythonScript.h +++ b/Source/UnrealEnginePython/Public/PythonScript.h @@ -1,6 +1,6 @@ #pragma once -#include "CoreMinimal.h" +#include "UnrealEnginePython.h" #include "UObject/Object.h" #include "PythonScript.generated.h" From af2b9b1b9459804e49f64dee775342922541a3d3 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 09:18:33 +0100 Subject: [PATCH 32/94] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 685c97ecf..948902f8a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# Teaser: https://twitter.com/KNLstudio/status/932657812466843648 # UnrealEnginePython Embed Python in Unreal Engine 4 +'''Teaser:''' https://twitter.com/KNLstudio/status/932657812466843648 + # How and Why ? This is a plugin embedding a whole Python VM (versions 3.x [the default and suggested one] and 2.7) In Unreal Engine 4 (both the editor and runtime). From 141aa8886e25479027547c1bcac776b61bd7a21d Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 09:25:09 +0100 Subject: [PATCH 33/94] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 948902f8a..83dd95636 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # UnrealEnginePython Embed Python in Unreal Engine 4 -'''Teaser:''' https://twitter.com/KNLstudio/status/932657812466843648 +Teaser: https://twitter.com/KNLstudio/status/932657812466843648 # How and Why ? From d77edff3b9204389521f00ec8859387cf0d2b561 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 10:00:30 +0100 Subject: [PATCH 34/94] backported to 4.15 --- .../Private/Slate/UEPyFSlateStyleSet.cpp | 4 + .../Slate/UEPyIStructureDetailsView.cpp | 280 +++++++++--------- .../Private/Slate/UEPySGraphPanel.cpp | 2 + .../Private/Slate/UEPySGraphPanel.h | 2 +- .../Private/Slate/UEPySVectorInputBox.cpp | 5 +- .../Private/Slate/UEPySlate.cpp | 2 + .../UnrealEnginePython/Private/UEPyModule.cpp | 5 +- .../Private/UEPyUScriptStruct.h | 4 +- .../Private/UnrealEnginePythonPrivatePCH.h | 12 +- .../Private/Wrappers/UEPyESlateEnums.cpp | 2 + .../Public/PyNativeWidgetHost.h | 4 +- 11 files changed, 174 insertions(+), 148 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp index 5a21fbf50..78a29fa17 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp @@ -135,8 +135,10 @@ static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject } else if (ue_py_check_childstruct(py_type)) { + typedef TFunction WStyleGetter; typedef TPair WStylePair; +#if ENGINE_MINOR_VERSION > 15 static const WStylePair validWidgetStyleUStructList[] = { WStylePair{ FTextBlockStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, WStylePair{ FButtonStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, @@ -163,6 +165,7 @@ static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject WStylePair{ FScrollBorderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, WStylePair{ FWindowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, }; + for(WStylePair widgetStyleUStruct : validWidgetStyleUStructList) { @@ -172,6 +175,7 @@ static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject break; } } +#endif if (!ret) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp index 0af6c4129..aec81f315 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp @@ -12,89 +12,92 @@ static PyObject *ue_PyIStructureDetailsView_str(ue_PyIStructureDetailsView *self) { #if PY_MAJOR_VERSION >= 3 - return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), - self->istructure_details_view->GetWidget().Get(), - self->istructure_details_view->GetWidget().GetSharedReferenceCount(), - self->ob_base.ob_refcnt); + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount(), + self->ob_base.ob_refcnt); #else - return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), - self->istructure_details_view->GetWidget().Get(), - self->istructure_details_view->GetWidget().GetSharedReferenceCount()); + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount()); #endif } static PyObject *py_ue_istructure_details_view_set_structure_data(ue_PyIStructureDetailsView *self, PyObject * args, PyObject *kwargs) { - PyObject *py_object = nullptr; - PyObject *py_force_refresh = nullptr; - - char *kwlist[] = { - (char *)"struct_data", - (char *)"force_refresh", - nullptr - }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_structure_data", kwlist, &py_object, &py_force_refresh)) - { - return nullptr; - } - - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); - if (!ue_py_struct) - { - return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); - } - - - Py_XDECREF(self->ue_py_struct); - self->ue_py_struct = ue_py_struct; - Py_INCREF(self->ue_py_struct); - TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); - FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); - self->istructure_details_view->SetStructureData(struct_scope); - - if (py_force_refresh && PyObject_IsTrue(py_force_refresh)) - { - self->istructure_details_view->GetDetailsView()->ForceRefresh(); - } - - Py_RETURN_NONE; + PyObject *py_object = nullptr; + PyObject *py_force_refresh = nullptr; + + char *kwlist[] = { + (char *)"struct_data", + (char *)"force_refresh", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_structure_data", kwlist, &py_object, &py_force_refresh)) + { + return nullptr; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + + Py_XDECREF(self->ue_py_struct); + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view->SetStructureData(struct_scope); + +#if ENGINE_MINOR_VERSION > 17 + if (py_force_refresh && PyObject_IsTrue(py_force_refresh)) + { + self->istructure_details_view->GetDetailsView()->ForceRefresh(); + } +#endif + + Py_RETURN_NONE; } static PyObject *py_ue_istructure_details_view_get_widget(ue_PyIStructureDetailsView *self, PyObject * args) { - if (!self->istructure_details_view.IsValid()) - { - return PyErr_Format(PyExc_Exception, "IStructureDetailsView is not valid"); - } + if (!self->istructure_details_view.IsValid()) + { + return PyErr_Format(PyExc_Exception, "IStructureDetailsView is not valid"); + } - TSharedPtr ret_widget = self->istructure_details_view->GetWidget(); - if (!ret_widget.IsValid()) - { - return PyErr_Format(PyExc_Exception, "unable to create SingleProperty widget"); - } + TSharedPtr ret_widget = self->istructure_details_view->GetWidget(); + if (!ret_widget.IsValid()) + { + return PyErr_Format(PyExc_Exception, "unable to create SingleProperty widget"); + } - return (PyObject *)py_ue_new_swidget(ret_widget->AsShared(), &ue_PySWidgetType); + return (PyObject *)py_ue_new_swidget(ret_widget->AsShared(), &ue_PySWidgetType); - Py_RETURN_NONE; + Py_RETURN_NONE; } static PyMethodDef ue_PyIStructureDetailsView_methods[] = { #pragma warning(suppress: 4191) - { "set_structure_data", (PyCFunction)py_ue_istructure_details_view_set_structure_data, METH_VARARGS | METH_KEYWORDS, "" }, - { "get_widget", (PyCFunction)py_ue_istructure_details_view_get_widget, METH_VARARGS, "" }, + { "set_structure_data", (PyCFunction)py_ue_istructure_details_view_set_structure_data, METH_VARARGS | METH_KEYWORDS, "" }, + { "get_widget", (PyCFunction)py_ue_istructure_details_view_get_widget, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; -static void ue_PyIStructureDetailsView_dealloc(ue_PyIStructureDetailsView *self) { +static void ue_PyIStructureDetailsView_dealloc(ue_PyIStructureDetailsView *self) +{ #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, &self->istructure_details_view.Get()); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, &self->istructure_details_view.Get()); #endif - Py_DECREF(self->ue_py_struct); - Py_TYPE(self)->tp_free((PyObject *)self); + Py_DECREF(self->ue_py_struct); + Py_TYPE(self)->tp_free((PyObject *)self); } PyTypeObject ue_PyIStructureDetailsViewType = { @@ -102,7 +105,7 @@ PyTypeObject ue_PyIStructureDetailsViewType = { "unreal_engine.IStructureDetailsView", /* tp_name */ sizeof(ue_PyIStructureDetailsView), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)ue_PyIStructureDetailsView_dealloc, /* tp_dealloc */ + (destructor)ue_PyIStructureDetailsView_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -113,7 +116,7 @@ PyTypeObject ue_PyIStructureDetailsViewType = { 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ - (reprfunc)ue_PyIStructureDetailsView_str, /* tp_str */ + (reprfunc)ue_PyIStructureDetailsView_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ @@ -125,96 +128,97 @@ PyTypeObject ue_PyIStructureDetailsViewType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - ue_PyIStructureDetailsView_methods, /* tp_methods */ + ue_PyIStructureDetailsView_methods, /* tp_methods */ }; -static int ue_py_istructure_details_view_init(ue_PyIStructureDetailsView *self, PyObject *args, PyObject *kwargs) { - - PyObject *py_object = nullptr; - - PyObject *py_allow_search = nullptr; - PyObject *py_update_from_selection = nullptr; - PyObject *py_lockable = nullptr; - char *py_name_area_settings = nullptr; - PyObject *py_hide_selection_tip = nullptr; - PyObject *py_search_initial_key_focus = nullptr; - - char *kwlist[] = { - (char*)"struct_data", - (char *)"allow_search", - (char *)"update_from_selection", - (char *)"lockable", - (char *)"name_area_settings", - (char *)"hide_selection_tip", - (char *)"search_initial_key_focus", - nullptr }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:__init__", kwlist, - &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) - { - return -1; - } - - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); - if (!ue_py_struct) - { - PyErr_SetString(PyExc_Exception, "argument is not a UScriptStruct"); - return -1; - } - - FDetailsViewArgs view_args; - view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; - view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; - view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; - view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; - view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; - - FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); - view_args.NameAreaSettings = [&name_area_string]() { - if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } - else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - }(); - FStructureDetailsViewArgs struct_view_args; - { - struct_view_args.bShowObjects = true; - struct_view_args.bShowAssets = true; - struct_view_args.bShowClasses = true; - struct_view_args.bShowInterfaces = true; - } - new(&self->istructure_details_view) TSharedPtr(nullptr); - - self->ue_py_struct = ue_py_struct; - Py_INCREF(self->ue_py_struct); - TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); - FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); - self->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); - - return 0; +static int ue_py_istructure_details_view_init(ue_PyIStructureDetailsView *self, PyObject *args, PyObject *kwargs) +{ + + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:__init__", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return -1; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + PyErr_SetString(PyExc_Exception, "argument is not a UScriptStruct"); + return -1; + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + new(&self->istructure_details_view) TSharedPtr(nullptr); + + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return 0; } void ue_python_init_istructure_details_view(PyObject *ue_module) { - ue_PyIStructureDetailsViewType.tp_new = PyType_GenericNew; + ue_PyIStructureDetailsViewType.tp_new = PyType_GenericNew; - ue_PyIStructureDetailsViewType.tp_init = (initproc)ue_py_istructure_details_view_init; + ue_PyIStructureDetailsViewType.tp_init = (initproc)ue_py_istructure_details_view_init; ue_PyIStructureDetailsViewType.tp_getattro = PyObject_GenericGetAttr; - ue_PyIStructureDetailsViewType.tp_setattro = PyObject_GenericSetAttr; + ue_PyIStructureDetailsViewType.tp_setattro = PyObject_GenericSetAttr; - if (PyType_Ready(&ue_PyIStructureDetailsViewType) < 0) - return; + if (PyType_Ready(&ue_PyIStructureDetailsViewType) < 0) + return; - Py_INCREF(&ue_PyIStructureDetailsViewType); - PyModule_AddObject(ue_module, "IStructureDetailsView", (PyObject *)&ue_PyIStructureDetailsViewType); + Py_INCREF(&ue_PyIStructureDetailsViewType); + PyModule_AddObject(ue_module, "IStructureDetailsView", (PyObject *)&ue_PyIStructureDetailsViewType); } ue_PyIStructureDetailsView *py_ue_is_istructure_details_view(PyObject *obj) { - if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIStructureDetailsViewType)) - return nullptr; - return (ue_PyIStructureDetailsView *)obj; + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIStructureDetailsViewType)) + return nullptr; + return (ue_PyIStructureDetailsView *)obj; } #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp index e0774a8da..03a611652 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp @@ -1,6 +1,7 @@ #include "UnrealEnginePythonPrivatePCH.h" #if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 15 #include "UEPySGraphPanel.h" #define sw_graph_panel StaticCastSharedRef(self->s_nodePanel.s_panel.s_widget) @@ -134,4 +135,5 @@ void ue_python_init_sgraph_panel(PyObject *ue_module) PyModule_AddObject(ue_module, "SGraphPanel", (PyObject *)&ue_PySGraphPanelType); } +#endif #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h index d29887525..abf11ec9b 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h @@ -3,7 +3,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR -#if ENGINE_MINOR_VERSION > 12 +#if ENGINE_MINOR_VERSION > 15 #include "UEPySNodePanel.h" #include "Editor/GraphEditor/Public/SGraphPanel.h" diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp index 7e6242fe3..f5c950e0a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp @@ -43,10 +43,7 @@ PyTypeObject ue_PySVectorInputBoxType = { static int ue_py_svector_input_box_init(ue_PySVectorInputBox *self, PyObject *args, PyObject *kwargs) { ue_py_slate_setup_farguments(SVectorInputBox); -#if WITH_KNL_PYEXT - ue_py_slate_farguments_optional_float("delta", Delta); -#endif -#if ENGINE_MINOR_VERSION > 15 || WITH_KNL_PYEXT +#if ENGINE_MINOR_VERSION > 15 ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); #endif ue_py_slate_farguments_optional_bool("allow_responsive_layout", AllowResponsiveLayout); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 432a8a77a..63788d7a0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -836,7 +836,9 @@ void ue_python_init_slate(PyObject *module) #if WITH_EDITOR ue_python_init_snode_panel(module); +#if ENGINE_MINOR_VERSION > 15 ue_python_init_sgraph_panel(module); +#endif ue_python_init_idetails_view(module); ue_python_init_istructure_details_view(module); ue_python_init_seditor_viewport(module); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 857404192..65a3875e0 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -2050,7 +2050,10 @@ void unreal_engine_init_py_module() PyDict_SetItemString(unreal_engine_dict, "RF_STRONGREFONFRAME" , PyLong_FromUnsignedLongLong((uint64)RF_StrongRefOnFrame )); PyDict_SetItemString(unreal_engine_dict, "RF_NONPIEDUPLICATETRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_NonPIEDuplicateTransient )); PyDict_SetItemString(unreal_engine_dict, "RF_DYNAMIC" , PyLong_FromUnsignedLongLong((uint64)RF_Dynamic )); - PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded )); + +#if ENGINE_MINOR_VERSION > 15 + PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded )); +#endif // Properties PyDict_SetItemString(unreal_engine_dict, "CPF_CONFIG", PyLong_FromUnsignedLongLong((uint64)CPF_Config)); diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h index d93eb990a..a10c0125e 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h @@ -5,8 +5,8 @@ typedef struct { PyObject_HEAD - /* Type-specific fields go here. */ - UScriptStruct *u_struct; + /* Type-specific fields go here. */ + UScriptStruct *u_struct; uint8 *data; } ue_PyUScriptStruct; diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 27d8ce47f..e69e83b9c 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -1,4 +1,6 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 20Tab S.r.l. + +#pragma once //#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 @@ -93,3 +95,11 @@ bool PyUnicodeOrString_Check(PyObject *py_obj); return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ Py_INCREF(ret);\ return (PyObject *)ret; + +#if ENGINE_MINOR_VERSION < 16 +template +struct TStructOpsTypeTraitsBase2 : TStructOpsTypeTraitsBase +{ + +}; +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp index 815c2aaea..d8f7e09a8 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp @@ -76,6 +76,7 @@ void ue_python_init_eslate_enums(PyObject *ue_module) PyDict_SetItemString(ue_PyESlateEnumsType.tp_dict, enum_name, (PyObject *)native_enum); }; +#if ENGINE_MINOR_VERSION > 15 #define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)##EnumType::Type::##EnumVal) ADD_NATIVE_ENUM(EUserInterfaceActionType, None ); ADD_NATIVE_ENUM(EUserInterfaceActionType, Button ); @@ -84,6 +85,7 @@ void ue_python_init_eslate_enums(PyObject *ue_module) ADD_NATIVE_ENUM(EUserInterfaceActionType, Check ); ADD_NATIVE_ENUM(EUserInterfaceActionType, CollapsedButton); #undef ADD_NATIVE_ENUM +#endif } diff --git a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h index 220f706b4..fa20c574f 100644 --- a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h +++ b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h @@ -2,11 +2,13 @@ #pragma once -#include "UnrealEnginePython.h" +#include "UObject/Class.h" + #include "Components/NativeWidgetHost.h" #include "PyNativeWidgetHost.generated.h" + USTRUCT(BlueprintType) struct UNREALENGINEPYTHON_API FPythonSWidgetWrapper { From c9fe50e45f1168678d5b18d08c0c8e7354e3062a Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 10:26:09 +0100 Subject: [PATCH 35/94] added unit test for clipboard --- tests/test_clipboard.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/test_clipboard.py diff --git a/tests/test_clipboard.py b/tests/test_clipboard.py new file mode 100644 index 000000000..bdabedf73 --- /dev/null +++ b/tests/test_clipboard.py @@ -0,0 +1,12 @@ +import unittest +import unreal_engine as ue + +class TestClipboard(unittest.TestCase): + + def test_copy_and_paste(self): + ue.clipboard_copy('Hello from python unit test') + self.assertEqual(ue.clipboard_paste(), 'Hello from python unit test') + + def test_copy_and_paste_red_light(self): + ue.clipboard_copy('Hello from red light test') + self.assertNotEqual(ue.clipboard_paste(), 'Hello from python unit test') \ No newline at end of file From d7def058f0c56e93b8ae14320897c0bdc72d67b8 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 12:53:24 +0100 Subject: [PATCH 36/94] added support for work-by-ref structs --- .../Private/Slate/UEPyFSlateIcon.cpp | 2 +- .../Private/Slate/UEPyFSlateStyleSet.cpp | 10 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 203 +++++++++--------- .../Private/UEPyUScriptStruct.cpp | 65 ++++-- .../Private/UEPyUScriptStruct.h | 7 +- tests/test_structs.py | 16 ++ 6 files changed, 181 insertions(+), 122 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp index ba079ac02..c0618e126 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp @@ -4,7 +4,7 @@ static PyObject *py_ue_fslate_icon_get_icon(ue_PyFSlateIcon *self, PyObject * args) { - PyObject *ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (const uint8*)self->icon.GetIcon()); + PyObject *ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (uint8*)self->icon.GetIcon()); return ret; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp index 78a29fa17..7f5e7e45e 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp @@ -94,7 +94,7 @@ namespace { PyObject* pyGetWidgetStyle(FSlateStyleSet& InStyle, FName PropertyName) { const WidgetStyleType styleWidgetStyle = InStyle.GetWidgetStyle(PropertyName); - return py_ue_new_uscriptstruct(WidgetStyleType::StaticStruct(), (const uint8*)&styleWidgetStyle); + return py_ue_new_uscriptstruct(WidgetStyleType::StaticStruct(), (uint8*)&styleWidgetStyle); } } @@ -114,24 +114,24 @@ static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject if (ue_py_check_struct(py_type)) { const FSlateSound& styleSound = self->style_set->GetSound(FName(name)); - ret = py_ue_new_uscriptstruct(FSlateSound::StaticStruct(), (const uint8*)&styleSound); + ret = py_ue_new_uscriptstruct(FSlateSound::StaticStruct(), (uint8*)&styleSound); } else if (ue_py_check_struct(py_type)) { if (const FSlateBrush* styleBrush = self->style_set->GetBrush(FName(name))) { - ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (const uint8*)styleBrush); + ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (uint8*)styleBrush); } } else if (ue_py_check_struct(py_type)) { const FSlateColor styleSlateColor = self->style_set->GetSlateColor(FName(name)); - ret = py_ue_new_uscriptstruct(FSlateColor::StaticStruct(), (const uint8*)&styleSlateColor); + ret = py_ue_new_uscriptstruct(FSlateColor::StaticStruct(), (uint8*)&styleSlateColor); } else if (ue_py_check_struct(py_type)) { const FSlateFontInfo styleFontInfo = self->style_set->GetFontStyle(FName(name)); - ret = py_ue_new_uscriptstruct(FSlateFontInfo::StaticStruct(), (const uint8*)&styleFontInfo); + ret = py_ue_new_uscriptstruct(FSlateFontInfo::StaticStruct(), (uint8*)&styleFontInfo); } else if (ue_py_check_childstruct(py_type)) { diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 65a3875e0..481917330 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -175,7 +175,7 @@ static PyMethodDef unreal_engine_methods[] = { { "load_package", py_unreal_engine_load_package, METH_VARARGS, "" }, #if WITH_EDITOR - { "unload_package", py_unreal_engine_unload_package, METH_VARARGS, "" }, + { "unload_package", py_unreal_engine_unload_package, METH_VARARGS, "" }, #endif { "get_forward_vector", py_unreal_engine_get_forward_vector, METH_VARARGS, "" }, { "get_up_vector", py_unreal_engine_get_up_vector, METH_VARARGS, "" }, @@ -213,14 +213,14 @@ static PyMethodDef unreal_engine_methods[] = { // slate - { "find_slate_style", py_unreal_engine_find_slate_style, METH_VARARGS, "" }, - { "find_icon_for_class", py_unreal_engine_find_icon_for_class, METH_VARARGS, "" }, + { "find_slate_style", py_unreal_engine_find_slate_style, METH_VARARGS, "" }, + { "find_icon_for_class", py_unreal_engine_find_icon_for_class, METH_VARARGS, "" }, { "register_nomad_tab_spawner", py_unreal_engine_register_nomad_tab_spawner, METH_VARARGS, "" }, { "unregister_nomad_tab_spawner", py_unreal_engine_unregister_nomad_tab_spawner, METH_VARARGS, "" }, { "invoke_tab", py_unreal_engine_invoke_tab, METH_VARARGS, "" }, - { "get_swidget_from_wrapper", py_unreal_engine_get_swidget_from_wrapper, METH_VARARGS, "" }, - { "create_wrapper_from_pyswidget", py_unreal_engine_create_wrapper_from_pyswidget, METH_VARARGS, "" }, + { "get_swidget_from_wrapper", py_unreal_engine_get_swidget_from_wrapper, METH_VARARGS, "" }, + { "create_wrapper_from_pyswidget", py_unreal_engine_create_wrapper_from_pyswidget, METH_VARARGS, "" }, #if WITH_EDITOR { "get_editor_window", py_unreal_engine_get_editor_window, METH_VARARGS, "" }, { "add_menu_extension", py_unreal_engine_add_menu_extension, METH_VARARGS, "" }, @@ -232,7 +232,7 @@ static PyMethodDef unreal_engine_methods[] = { #pragma warning(suppress: 4191) { "create_detail_view", (PyCFunction)py_unreal_engine_create_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, #pragma warning(suppress: 4191) - { "create_structure_detail_view", (PyCFunction)py_unreal_engine_create_structure_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, + { "create_structure_detail_view", (PyCFunction)py_unreal_engine_create_structure_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, #pragma warning(suppress: 4191) { "create_property_view", (PyCFunction)py_unreal_engine_create_property_view, METH_VARARGS | METH_KEYWORDS, "" }, @@ -298,7 +298,7 @@ static PyMethodDef unreal_engine_methods[] = { { "blueprint_set_variable_visibility", py_unreal_engine_blueprint_set_variable_visibility, METH_VARARGS, "" }, { "blueprint_add_function", py_unreal_engine_blueprint_add_function, METH_VARARGS, "" }, { "blueprint_add_ubergraph_page", py_unreal_engine_blueprint_add_ubergraph_page, METH_VARARGS, "" }, - { "blueprint_get_all_graphs", py_unreal_engine_blueprint_get_all_graphs, METH_VARARGS, "" }, + { "blueprint_get_all_graphs", py_unreal_engine_blueprint_get_all_graphs, METH_VARARGS, "" }, { "blueprint_mark_as_structurally_modified", py_unreal_engine_blueprint_mark_as_structurally_modified, METH_VARARGS, "" }, { "add_component_to_blueprint", py_unreal_engine_add_component_to_blueprint, METH_VARARGS, "" }, { "get_blueprint_components", py_unreal_engine_get_blueprint_components, METH_VARARGS, "" }, @@ -396,7 +396,7 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_take_high_res_screen_shots", py_unreal_engine_editor_take_high_res_screen_shots, METH_VARARGS, "" }, { "register_settings", py_unreal_engine_register_settings, METH_VARARGS, "" }, - { "show_viewer", py_unreal_engine_show_viewer, METH_VARARGS, "" }, + { "show_viewer", py_unreal_engine_show_viewer, METH_VARARGS, "" }, { "unregister_settings", py_unreal_engine_unregister_settings, METH_VARARGS, "" }, #endif @@ -671,8 +671,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "class_generated_by", (PyCFunction)py_ue_class_generated_by, METH_VARARGS, "" }, { "class_get_flags", (PyCFunction)py_ue_class_get_flags, METH_VARARGS, "" }, { "class_set_flags", (PyCFunction)py_ue_class_set_flags, METH_VARARGS, "" }, - { "get_obj_flags", (PyCFunction)py_ue_get_obj_flags, METH_VARARGS, "" }, - { "set_obj_flags", (PyCFunction)py_ue_set_obj_flags, METH_VARARGS, "" }, + { "get_obj_flags", (PyCFunction)py_ue_get_obj_flags, METH_VARARGS, "" }, + { "set_obj_flags", (PyCFunction)py_ue_set_obj_flags, METH_VARARGS, "" }, #if WITH_EDITOR { "class_get_config_name", (PyCFunction)py_ue_class_get_config_name, METH_VARARGS, "" }, @@ -743,7 +743,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "add_actor_component", (PyCFunction)py_ue_add_actor_component, METH_VARARGS, "" }, { "add_instance_component", (PyCFunction)py_ue_add_instance_component, METH_VARARGS, "" }, - { "get_actor_root_component", (PyCFunction)py_ue_get_actor_root_component, METH_VARARGS, "" }, + { "get_actor_root_component", (PyCFunction)py_ue_get_actor_root_component, METH_VARARGS, "" }, { "add_actor_root_component", (PyCFunction)py_ue_add_actor_root_component, METH_VARARGS, "" }, { "get_actor_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, { "get_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, @@ -913,7 +913,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "sequencer_changed", (PyCFunction)py_ue_sequencer_changed, METH_VARARGS, "" }, { "sequencer_add_camera_cut_track", (PyCFunction)py_ue_sequencer_add_camera_cut_track, METH_VARARGS, "" }, { "sequencer_add_actor", (PyCFunction)py_ue_sequencer_add_actor, METH_VARARGS, "" }, - { "sequencer_add_actor_component", (PyCFunction)py_ue_sequencer_add_actor_component, METH_VARARGS, "" }, + { "sequencer_add_actor_component", (PyCFunction)py_ue_sequencer_add_actor_component, METH_VARARGS, "" }, { "sequencer_make_new_spawnable", (PyCFunction)py_ue_sequencer_make_new_spawnable, METH_VARARGS, "" }, { "sequencer_add_possessable", (PyCFunction)py_ue_sequencer_add_possessable, METH_VARARGS, "" }, @@ -990,7 +990,7 @@ void ue_pydelegates_cleanup(ue_PyUObject *self) #endif py_delegate->RemoveFromRoot(); } -} + } self->python_delegates_gc->clear(); delete self->python_delegates_gc; self->python_delegates_gc = nullptr; @@ -1086,9 +1086,9 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) #else return PyLong_FromLong(u_enum->FindEnumIndex(item.Key)); #endif + } } } - } #endif if (self->ue_object->IsA()) { @@ -1100,7 +1100,7 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) return PyLong_FromLong(u_enum->FindEnumIndex(FName(UTF8_TO_TCHAR(attr)))); #endif } - } + } if (function) { @@ -1108,8 +1108,8 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) PyErr_Clear(); return py_ue_new_callable(function, self->ue_object); } + } } -} return ret; } @@ -1200,7 +1200,7 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (self->ue_object->IsA()) { UScriptStruct *u_script_struct = (UScriptStruct *)self->ue_object; - uint8 *data = (uint8*)FMemory_Alloca(u_script_struct->GetStructureSize()); + uint8 *data = (uint8*)FMemory::Malloc(u_script_struct->GetStructureSize()); u_script_struct->InitializeStruct(data); #if WITH_EDITOR u_script_struct->InitializeDefaultValue(data); @@ -1214,7 +1214,10 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (!key) { if (PyErr_Occurred()) + { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "unable to build struct from dictionary"); + } break; } if (!PyUnicodeOrString_Check(key)) @@ -1225,7 +1228,10 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (!value) { if (PyErr_Occurred()) + { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "unable to build struct from dictionary"); + } break; } @@ -1234,16 +1240,18 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject { if (!ue_py_convert_pyobject(value, u_property, data)) { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "invalid value for UProperty"); } } else { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "UProperty %s not found", struct_key); } } } - return py_ue_new_uscriptstruct(u_script_struct, data); + return py_ue_wrap_uscriptstruct(u_script_struct, data); } return PyErr_Format(PyExc_Exception, "the specified uobject has no __call__ support"); } @@ -2021,38 +2029,38 @@ void unreal_engine_init_py_module() PyDict_SetItemString(unreal_engine_dict, "CLASS_ABSTRACT", PyLong_FromUnsignedLongLong((uint64)CLASS_Abstract)); PyDict_SetItemString(unreal_engine_dict, "CLASS_INTERFACE", PyLong_FromUnsignedLongLong((uint64)CLASS_Interface)); - // Objects - PyDict_SetItemString(unreal_engine_dict, "RF_NOFLAGS" , PyLong_FromUnsignedLongLong((uint64)RF_NoFlags )); - PyDict_SetItemString(unreal_engine_dict, "RF_PUBLIC" , PyLong_FromUnsignedLongLong((uint64)RF_Public )); - PyDict_SetItemString(unreal_engine_dict, "RF_STANDALONE" , PyLong_FromUnsignedLongLong((uint64)RF_Standalone )); - PyDict_SetItemString(unreal_engine_dict, "RF_MARKASNATIVE" , PyLong_FromUnsignedLongLong((uint64)RF_MarkAsNative )); - PyDict_SetItemString(unreal_engine_dict, "RF_TRANSACTIONAL" , PyLong_FromUnsignedLongLong((uint64)RF_Transactional )); - PyDict_SetItemString(unreal_engine_dict, "RF_CLASSDEFAULTOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_ClassDefaultObject )); - PyDict_SetItemString(unreal_engine_dict, "RF_ARCHETYPEOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_ArchetypeObject )); - PyDict_SetItemString(unreal_engine_dict, "RF_TRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_Transient )); - PyDict_SetItemString(unreal_engine_dict, "RF_MARKASROOTSET" , PyLong_FromUnsignedLongLong((uint64)RF_MarkAsRootSet )); - PyDict_SetItemString(unreal_engine_dict, "RF_TAGGARBAGETEMP" , PyLong_FromUnsignedLongLong((uint64)RF_TagGarbageTemp )); - PyDict_SetItemString(unreal_engine_dict, "RF_NEEDINITIALIZATION" , PyLong_FromUnsignedLongLong((uint64)RF_NeedInitialization )); - PyDict_SetItemString(unreal_engine_dict, "RF_NEEDLOAD" , PyLong_FromUnsignedLongLong((uint64)RF_NeedLoad )); - PyDict_SetItemString(unreal_engine_dict, "RF_KEEPFORCOOKER" , PyLong_FromUnsignedLongLong((uint64)RF_KeepForCooker )); - PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOAD" , PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoad )); - PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOADSUBOBJECTS" , PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoadSubobjects )); - PyDict_SetItemString(unreal_engine_dict, "RF_NEWERVERSIONEXISTS" , PyLong_FromUnsignedLongLong((uint64)RF_NewerVersionExists )); - PyDict_SetItemString(unreal_engine_dict, "RF_BEGINDESTROYED" , PyLong_FromUnsignedLongLong((uint64)RF_BeginDestroyed )); - PyDict_SetItemString(unreal_engine_dict, "RF_FINISHDESTROYED" , PyLong_FromUnsignedLongLong((uint64)RF_FinishDestroyed )); - PyDict_SetItemString(unreal_engine_dict, "RF_BEINGREGENERATED" , PyLong_FromUnsignedLongLong((uint64)RF_BeingRegenerated )); - PyDict_SetItemString(unreal_engine_dict, "RF_DEFAULTSUBOBJECT" , PyLong_FromUnsignedLongLong((uint64)RF_DefaultSubObject )); - PyDict_SetItemString(unreal_engine_dict, "RF_WASLOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WasLoaded )); - PyDict_SetItemString(unreal_engine_dict, "RF_TEXTEXPORTTRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_TextExportTransient )); - PyDict_SetItemString(unreal_engine_dict, "RF_LOADCOMPLETED" , PyLong_FromUnsignedLongLong((uint64)RF_LoadCompleted )); - PyDict_SetItemString(unreal_engine_dict, "RF_INHERITABLECOMPONENTTEMPLATE", PyLong_FromUnsignedLongLong((uint64)RF_InheritableComponentTemplate)); - PyDict_SetItemString(unreal_engine_dict, "RF_DUPLICATETRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_DuplicateTransient )); - PyDict_SetItemString(unreal_engine_dict, "RF_STRONGREFONFRAME" , PyLong_FromUnsignedLongLong((uint64)RF_StrongRefOnFrame )); - PyDict_SetItemString(unreal_engine_dict, "RF_NONPIEDUPLICATETRANSIENT" , PyLong_FromUnsignedLongLong((uint64)RF_NonPIEDuplicateTransient )); - PyDict_SetItemString(unreal_engine_dict, "RF_DYNAMIC" , PyLong_FromUnsignedLongLong((uint64)RF_Dynamic )); - + // Objects + PyDict_SetItemString(unreal_engine_dict, "RF_NOFLAGS", PyLong_FromUnsignedLongLong((uint64)RF_NoFlags)); + PyDict_SetItemString(unreal_engine_dict, "RF_PUBLIC", PyLong_FromUnsignedLongLong((uint64)RF_Public)); + PyDict_SetItemString(unreal_engine_dict, "RF_STANDALONE", PyLong_FromUnsignedLongLong((uint64)RF_Standalone)); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASNATIVE", PyLong_FromUnsignedLongLong((uint64)RF_MarkAsNative)); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSACTIONAL", PyLong_FromUnsignedLongLong((uint64)RF_Transactional)); + PyDict_SetItemString(unreal_engine_dict, "RF_CLASSDEFAULTOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_ClassDefaultObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_ARCHETYPEOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_ArchetypeObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_Transient)); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASROOTSET", PyLong_FromUnsignedLongLong((uint64)RF_MarkAsRootSet)); + PyDict_SetItemString(unreal_engine_dict, "RF_TAGGARBAGETEMP", PyLong_FromUnsignedLongLong((uint64)RF_TagGarbageTemp)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDINITIALIZATION", PyLong_FromUnsignedLongLong((uint64)RF_NeedInitialization)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDLOAD", PyLong_FromUnsignedLongLong((uint64)RF_NeedLoad)); + PyDict_SetItemString(unreal_engine_dict, "RF_KEEPFORCOOKER", PyLong_FromUnsignedLongLong((uint64)RF_KeepForCooker)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOAD", PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoad)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOADSUBOBJECTS", PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoadSubobjects)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEWERVERSIONEXISTS", PyLong_FromUnsignedLongLong((uint64)RF_NewerVersionExists)); + PyDict_SetItemString(unreal_engine_dict, "RF_BEGINDESTROYED", PyLong_FromUnsignedLongLong((uint64)RF_BeginDestroyed)); + PyDict_SetItemString(unreal_engine_dict, "RF_FINISHDESTROYED", PyLong_FromUnsignedLongLong((uint64)RF_FinishDestroyed)); + PyDict_SetItemString(unreal_engine_dict, "RF_BEINGREGENERATED", PyLong_FromUnsignedLongLong((uint64)RF_BeingRegenerated)); + PyDict_SetItemString(unreal_engine_dict, "RF_DEFAULTSUBOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_DefaultSubObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_WASLOADED", PyLong_FromUnsignedLongLong((uint64)RF_WasLoaded)); + PyDict_SetItemString(unreal_engine_dict, "RF_TEXTEXPORTTRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_TextExportTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_LOADCOMPLETED", PyLong_FromUnsignedLongLong((uint64)RF_LoadCompleted)); + PyDict_SetItemString(unreal_engine_dict, "RF_INHERITABLECOMPONENTTEMPLATE", PyLong_FromUnsignedLongLong((uint64)RF_InheritableComponentTemplate)); + PyDict_SetItemString(unreal_engine_dict, "RF_DUPLICATETRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_DuplicateTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_STRONGREFONFRAME", PyLong_FromUnsignedLongLong((uint64)RF_StrongRefOnFrame)); + PyDict_SetItemString(unreal_engine_dict, "RF_NONPIEDUPLICATETRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_NonPIEDuplicateTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_DYNAMIC", PyLong_FromUnsignedLongLong((uint64)RF_Dynamic)); + #if ENGINE_MINOR_VERSION > 15 - PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED" , PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded )); + PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED", PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded)); #endif // Properties @@ -2858,7 +2866,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer) return true; } - return false; + return false; } @@ -2866,12 +2874,12 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer) { if (auto casted_prop = Cast(prop)) { - // ensure the object type is correct, otherwise crash could happen (soon or later) - if (!ue_obj->ue_object->IsA(casted_prop->PropertyClass)) - return false; - casted_prop->SetObjectPropertyValue_InContainer(buffer, ue_obj->ue_object); - return true; - } + // ensure the object type is correct, otherwise crash could happen (soon or later) + if (!ue_obj->ue_object->IsA(casted_prop->PropertyClass)) + return false; + casted_prop->SetObjectPropertyValue_InContainer(buffer, ue_obj->ue_object); + return true; + } else if (auto casted_prop_soft_object = Cast(prop)) { @@ -3071,7 +3079,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } - //NOTE: u_function->PropertiesSize maps to local variable uproperties + ufunction paramaters uproperties + //NOTE: u_function->PropertiesSize maps to local variable uproperties + ufunction paramaters uproperties uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); // initialize args @@ -3080,30 +3088,30 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * UProperty *prop = *IArgs; if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { - prop->InitializeValue_InContainer(buffer); - } - - //UObject::CallFunctionByNameWithArguments() only does this part on non return value params - if((IArgs->PropertyFlags & (CPF_Parm|CPF_ReturnParm)) == CPF_Parm) - { - if (!prop->IsInContainer(u_function->ParmsSize)) - { - return PyErr_Format(PyExc_Exception, "Attempting to import func param property that's out of bounds. %s", *u_function->GetName()); - } - #if WITH_EDITOR - FString default_key = FString("CPP_Default_") + prop->GetName(); - FString default_key_value = u_function->GetMetaData(FName(*default_key)); - if (!default_key_value.IsEmpty()) - { - #if ENGINE_MINOR_VERSION >= 17 - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); - #else - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); - #endif - } - #endif - } - } + prop->InitializeValue_InContainer(buffer); + } + + //UObject::CallFunctionByNameWithArguments() only does this part on non return value params + if ((IArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm) + { + if (!prop->IsInContainer(u_function->ParmsSize)) + { + return PyErr_Format(PyExc_Exception, "Attempting to import func param property that's out of bounds. %s", *u_function->GetName()); + } +#if WITH_EDITOR + FString default_key = FString("CPP_Default_") + prop->GetName(); + FString default_key_value = u_function->GetMetaData(FName(*default_key)); + if (!default_key_value.IsEmpty()) + { +#if ENGINE_MINOR_VERSION >= 17 + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); +#else + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); +#endif + } +#endif + } +} Py_ssize_t tuple_len = PyTuple_Size(args); @@ -3141,7 +3149,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } } - if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { has_out_params++; } @@ -3185,7 +3193,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * for (; OProps; ++OProps) { UProperty *prop = *OProps; - if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { // skip return param as it must be always the first if (prop->GetPropertyFlags() & CPF_ReturnParm) @@ -3605,24 +3613,27 @@ FGuid *ue_py_check_fguid(PyObject *py_obj) } uint8 * do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); - if (!ue_py_struct) { - return nullptr; - } + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) + { + return nullptr; + } - if (ue_py_struct->u_struct == chk_u_struct) { - return ue_py_struct->data; - } + if (ue_py_struct->u_struct == chk_u_struct) + { + return ue_py_struct->data; + } - return nullptr; + return nullptr; } bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); - if (!ue_py_struct) { - return false; - } + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) + { + return false; + } - return ue_py_struct->u_struct->IsChildOf(parent_u_struct); + return ue_py_struct->u_struct->IsChildOf(parent_u_struct); } diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 1be6a3fe4..508051bd8 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -1,4 +1,4 @@ - +// Copyright 20Tab S.r.l. #include "UnrealEnginePythonPrivatePCH.h" @@ -8,7 +8,7 @@ static PyObject *py_ue_uscriptstruct_get_field(ue_PyUScriptStruct *self, PyObjec char *name; if (!PyArg_ParseTuple(args, "s:get_field", &name)) { - return NULL; + return nullptr; } UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); @@ -24,7 +24,7 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec PyObject *value; if (!PyArg_ParseTuple(args, "sO:set_field", &name, &value)) { - return NULL; + return nullptr; } UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); @@ -37,8 +37,7 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec return PyErr_Format(PyExc_Exception, "unable to set property %s", name); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -124,8 +123,8 @@ static PyMethodDef ue_PyUScriptStruct_methods[] = { static PyObject *ue_PyUScriptStruct_str(ue_PyUScriptStruct *self) { - return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize()); + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize(), self->data); } static UProperty *get_field_from_name(UScriptStruct *u_struct, char *name) @@ -147,10 +146,10 @@ static UProperty *get_field_from_name(UScriptStruct *u_struct, char *name) FString display_name = property->GetMetaData(DisplayNameKey); if (display_name.Len() > 0 && attr.Equals(display_name)) { - return property; + return property; + } } } - } #endif return nullptr; @@ -209,7 +208,10 @@ static void ue_PyUScriptStruct_dealloc(ue_PyUScriptStruct *self) #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUScriptStruct %p with size %d"), self, self->u_struct->GetStructureSize()); #endif - FMemory::Free(self->data); + if (!self->is_ptr) + { + FMemory::Free(self->data); + } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -271,6 +273,8 @@ static int ue_py_uscriptstruct_init(ue_PyUScriptStruct *self, PyObject *args, Py #if WITH_EDITOR self->u_struct->InitializeDefaultValue(self->data); #endif + self->original_data = self->data; + self->is_ptr = 0; return 0; } @@ -288,20 +292,27 @@ static PyObject *ue_py_uscriptstruct_richcompare(ue_PyUScriptStruct *u_struct1, { if (equals) { - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } if (equals) { - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; +} + +// get the original pointer of a struct +static PyObject *ue_py_uscriptstruct_get_ptr(ue_PyUScriptStruct *self, PyObject * args) +{ + ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); + ret->u_struct = self->u_struct; + ret->data = self->original_data; + ret->original_data = ret->data; + ret->is_ptr = 1; + return (PyObject *)ret; } @@ -313,6 +324,8 @@ void ue_python_init_uscriptstruct(PyObject *ue_module) ue_PyUScriptStructType.tp_init = (initproc)ue_py_uscriptstruct_init; + ue_PyUScriptStructType.tp_call = (ternaryfunc)ue_py_uscriptstruct_get_ptr; + if (PyType_Ready(&ue_PyUScriptStructType) < 0) return; @@ -320,13 +333,27 @@ void ue_python_init_uscriptstruct(PyObject *ue_module) PyModule_AddObject(ue_module, "UScriptStruct", (PyObject *)&ue_PyUScriptStructType); } -PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, const uint8 *data) { +PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, uint8 *data) +{ ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); ret->u_struct = u_struct; uint8 *struct_data = (uint8*)FMemory::Malloc(u_struct->GetStructureSize()); ret->u_struct->InitializeStruct(struct_data); ret->u_struct->CopyScriptStruct(struct_data, data); ret->data = struct_data; + ret->original_data = data; + ret->is_ptr = 0; + return (PyObject *)ret; +} + +// generate a new python UScriptStruct from an already allocated data block +PyObject *py_ue_wrap_uscriptstruct(UScriptStruct *u_struct, uint8 *data) +{ + ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); + ret->u_struct = u_struct; + ret->data = data; + ret->original_data = data; + ret->is_ptr = 0; return (PyObject *)ret; } diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h index a10c0125e..d7fcba2e2 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h @@ -8,9 +8,14 @@ typedef struct /* Type-specific fields go here. */ UScriptStruct *u_struct; uint8 *data; + // if 1, data points to un-owned memory + int is_ptr; + // points to the original struct memory (do not try this at home !) + uint8 *original_data; } ue_PyUScriptStruct; -PyObject *py_ue_new_uscriptstruct(UScriptStruct *, const uint8 *); +PyObject *py_ue_new_uscriptstruct(UScriptStruct *, uint8 *); +PyObject *py_ue_wrap_uscriptstruct(UScriptStruct *, uint8 *); ue_PyUScriptStruct *py_ue_is_uscriptstruct(PyObject *); UProperty *ue_struct_get_field_from_name(UScriptStruct *, char *); diff --git a/tests/test_structs.py b/tests/test_structs.py index 52eee7bf8..451a10924 100644 --- a/tests/test_structs.py +++ b/tests/test_structs.py @@ -1,6 +1,7 @@ import unittest import unreal_engine as ue from unreal_engine.structs import ColorMaterialInput, Key +from unreal_engine.structs import StaticMeshSourceModel, MeshBuildSettings class TestStructs(unittest.TestCase): @@ -37,6 +38,21 @@ def test_cmp(self): key2 = Key(KeyName='SpaceBar') self.assertEqual(key1, key2) + def test_ptr(self): + source_model = StaticMeshSourceModel() + source_model().BuildSettings().bRecomputeNormals=False + source_model().BuildSettings().bRecomputeTangents=True + source_model().BuildSettings().bUseMikkTSpace=True + source_model().BuildSettings().bBuildAdjacencyBuffer=True + source_model().BuildSettings().bRemoveDegenerates=True + + source_model2 = source_model.clone() + self.assertEqual(source_model2.BuildSettings.bRecomputeNormals, False) + self.assertEqual(source_model2.BuildSettings.bRecomputeTangents, True) + self.assertEqual(source_model2.BuildSettings.bUseMikkTSpace, True) + self.assertEqual(source_model2.BuildSettings.bBuildAdjacencyBuffer, True) + self.assertEqual(source_model2.BuildSettings.bRemoveDegenerates, True) + From 1b7f9ea4a9a0c7049b54a476dd95017e2edb0de9 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 9 Feb 2018 12:57:36 +0100 Subject: [PATCH 37/94] fixed typo --- Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 508051bd8..844288c61 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -123,7 +123,7 @@ static PyMethodDef ue_PyUScriptStruct_methods[] = { static PyObject *ue_PyUScriptStruct_str(ue_PyUScriptStruct *self) { - return PyUnicode_FromFormat("", + return PyUnicode_FromFormat("", TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize(), self->data); } From d643d5ed0a34cff436e8a013bbc983ee502272bd Mon Sep 17 00:00:00 2001 From: ikrima Date: Fri, 9 Feb 2018 22:14:36 -0800 Subject: [PATCH 38/94] Add PyCalllable_Check_Extended macro to guard against "callable" uscriptstructs which are mutable/reference structs -Without it, slate argument binding has errors bc it tries to bind delegates to the uscriptstructs --- .../Private/Slate/UEPyFMenuBuilder.cpp | 2 +- .../Private/Slate/UEPyFToolBarBuilder.cpp | 2 +- .../Private/Slate/UEPySButton.cpp | 2 +- .../Slate/UEPySPythonMultiColumnTableRow.h | 9 +++++---- .../Private/Slate/UEPySPythonShelf.cpp | 6 +++--- .../Private/Slate/UEPySPythonWidget.h | 16 ++++++++-------- .../Private/Slate/UEPySWidget.cpp | 8 ++++---- .../Private/Slate/UEPySWindow.cpp | 2 +- .../Private/Slate/UEPySlate.cpp | 12 ++++++------ .../UnrealEnginePython/Private/Slate/UEPySlate.h | 2 +- .../Private/UObject/UEPyInput.cpp | 6 +++--- .../Public/UnrealEnginePython.h | 3 +++ 12 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index f495a2db3..ea71c0d3a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -46,7 +46,7 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO if (!PyArg_ParseTuple(args, "ssO|OO:add_menu_entry", &label, &tooltip, &py_callable, &py_obj, &py_uiaction_obj)) return nullptr; - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index 6c3cf5fdc..4465e87e1 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -37,7 +37,7 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde return PyErr_Format(PyExc_Exception, "argument is not a FSlateIcon"); } - if (!PyCallable_Check(py_callable)) { + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp index 90e35fced..4ebddb07e 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp @@ -27,7 +27,7 @@ static PyObject *py_ue_sbutton_bind_on_clicked(ue_PySButton *self, PyObject * ar return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h index 268ee97b2..8a8933578 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h @@ -22,7 +22,7 @@ class SPythonMultiColumnTableRow : public SMultiColumnTableRow >::Construct(FSuperRowType::FArguments(), InOwnerTableView); } - virtual TSharedRef GenerateWidgetForColumn( const FName& ColumnName ) override + TSharedRef SPythonMultiColumnTableRow::GenerateWidgetForColumn(const FName& ColumnName) { FScopePythonGIL gil; @@ -30,7 +30,7 @@ class SPythonMultiColumnTableRow : public SMultiColumnTableRow(); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 63788d7a0..664f4d305 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -1228,7 +1228,7 @@ PyObject *py_unreal_engine_add_menu_extension(PyObject * self, PyObject * args) if (!menu_extension_interface) return PyErr_Format(PyExc_Exception, "module %s is not supported", module); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1261,7 +1261,7 @@ PyObject *py_unreal_engine_add_menu_bar_extension(PyObject * self, PyObject * ar FLevelEditorModule &ExtensibleModule = FModuleManager::LoadModuleChecked("LevelEditor"); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1294,7 +1294,7 @@ PyObject *py_unreal_engine_add_tool_bar_extension(PyObject * self, PyObject * ar FLevelEditorModule &ExtensibleModule = FModuleManager::LoadModuleChecked("LevelEditor"); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1322,7 +1322,7 @@ PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); FContentBrowserModule &ContentBrowser = FModuleManager::LoadModuleChecked("ContentBrowser"); @@ -1350,7 +1350,7 @@ PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject * self, PyObject return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); FOnSpawnTab spawn_tab; @@ -1455,7 +1455,7 @@ PyObject *py_unreal_engine_open_color_picker(PyObject *self, PyObject *args, PyO return nullptr; } - if (!PyCallable_Check(py_callable_on_color_committed)) + if (!PyCalllable_Check_Extended(py_callable_on_color_committed)) { return PyErr_Format(PyExc_Exception, "on_color_committed must be a callable"); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 0d95e74b8..131a372ad 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -163,7 +163,7 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); {\ PyObject *value = ue_py_dict_get_item(kwargs, _param);\ if (value) {\ - if (PyCallable_Check(value)) {\ + if (PyCalllable_Check_Extended(value)) {\ _base handler;\ UPythonSlateDelegate *py_delegate = NewObject();\ py_delegate->SetPyCallable(value);\ diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp index 458a1d5fd..f5cc2d975 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp @@ -380,7 +380,7 @@ PyObject *py_ue_bind_action(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } @@ -433,7 +433,7 @@ PyObject *py_ue_bind_axis(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } @@ -487,7 +487,7 @@ PyObject *py_ue_bind_key(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index 32d353a22..28421cae4 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -12,6 +12,9 @@ #include "Runtime/SlateCore/Public/Styling/ISlateStyle.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" +// We need to make sure reference structs do not mistaken for callable +#define PyCalllable_Check_Extended(value) PyCallable_Check(value) && py_ue_is_uscriptstruct(value) == nullptr + DECLARE_LOG_CATEGORY_EXTERN(LogPython, Log, All); From 097f08e33a6885286d712c4db240f31c0d4b4572 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 10 Feb 2018 18:49:21 +0100 Subject: [PATCH 39/94] added support for 4.19 --- .../Private/Blueprint/UEPyEdGraphPin.cpp | 16 +++ .../ConsoleManager/UEPyIConsoleManager.cpp | 8 ++ .../Private/PythonFunction.cpp | 4 + .../Slate/UEPySPythonEditorViewport.cpp | 4 + .../UnrealEnginePython/Private/UEPyModule.cpp | 4 + .../Private/UObject/UEPySkeletal.cpp | 134 ++++++++++++++++++ .../Private/Wrappers/UEPyFAssetData.cpp | 4 + .../Private/Wrappers/UEPyFSoftSkinVertex.h | 6 + .../Wrappers/UEPyFStringAssetReference.cpp | 1 + .../Wrappers/UEPyFStringAssetReference.h | 2 + .../Public/UnrealEnginePython.h | 4 + .../UnrealEnginePython.Build.cs | 2 +- 12 files changed, 188 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp index d93476d6e..e271dded8 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp @@ -79,15 +79,27 @@ static PyMethodDef ue_PyEdGraphPin_methods[] = { }; static PyObject *py_ue_edgraphpin_get_name(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinName.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinName))); +#endif } static PyObject *py_ue_edgraphpin_get_category(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinCategory.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinCategory))); +#endif } static PyObject *py_ue_edgraphpin_get_sub_category(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinSubCategory.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinSubCategory))); +#endif } static PyObject *py_ue_edgraphpin_get_default_value(ue_PyEdGraphPin *self, void *closure) { @@ -139,7 +151,11 @@ static PyGetSetDef ue_PyEdGraphPin_getseters[] = { static PyObject *ue_PyEdGraphPin_str(ue_PyEdGraphPin *self) { return PyUnicode_FromFormat("", +#if ENGINE_MINOR_VERSION > 18 + TCHAR_TO_UTF8(*self->pin->PinName.ToString()), TCHAR_TO_UTF8(*self->pin->PinType.PinCategory.ToString())); +#else TCHAR_TO_UTF8(*self->pin->PinName), TCHAR_TO_UTF8(*self->pin->PinType.PinCategory)); +#endif } static PyTypeObject ue_PyEdGraphPinType = { diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp index 008b3a1a7..2b080449d 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp @@ -12,7 +12,11 @@ static PyObject *py_ue_iconsole_manager_add_history_entry(PyObject *cls, PyObjec return nullptr; } +#if ENGINE_MINOR_VERSION > 18 + IConsoleManager::Get().AddConsoleHistoryEntry(TEXT(""), UTF8_TO_TCHAR(entry)); +#else IConsoleManager::Get().AddConsoleHistoryEntry(UTF8_TO_TCHAR(entry)); +#endif Py_RETURN_NONE; } @@ -20,7 +24,11 @@ static PyObject *py_ue_iconsole_manager_add_history_entry(PyObject *cls, PyObjec static PyObject *py_ue_iconsole_manager_get_history(PyObject *cls, PyObject * args) { TArray history; +#if ENGINE_MINOR_VERSION > 18 + IConsoleManager::Get().GetConsoleHistory(TEXT(""), history); +#else IConsoleManager::Get().GetConsoleHistory(history); +#endif PyObject *py_history = PyList_New(0); for (FString item : history) { diff --git a/Source/UnrealEnginePython/Private/PythonFunction.cpp b/Source/UnrealEnginePython/Private/PythonFunction.cpp index 6208d84a9..febb7718d 100644 --- a/Source/UnrealEnginePython/Private/PythonFunction.cpp +++ b/Source/UnrealEnginePython/Private/PythonFunction.cpp @@ -10,7 +10,11 @@ void UPythonFunction::SetPyCallable(PyObject *callable) } +#if ENGINE_MINOR_VERSION > 18 +void UPythonFunction::CallPythonCallable(UObject *Context, FFrame& Stack, RESULT_DECL) +#else void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) +#endif { FScopePythonGIL gil; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp index 4123ca159..8a8fbc09f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp @@ -334,7 +334,11 @@ TSharedRef SPythonEditorViewport::MakeEditorViewportClien FExposureSettings settings; settings.bFixed = true; +#if ENGINE_MINOR_VERSION > 18 + settings.FixedEV100 = 0; +#else settings.LogOffset = 0; +#endif client->ExposureSettings = settings; diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 481917330..48108b8b3 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -3558,7 +3558,11 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ function->FunctionFlags = function_flags; #endif +#if ENGINE_MINOR_VERSION > 18 + function->SetNativeFunc((FNativeFuncPtr)&UPythonFunction::CallPythonCallable); +#else function->SetNativeFunc((Native)&UPythonFunction::CallPythonCallable); +#endif function->Next = u_class->Children; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp index 789a94fe8..fa9218a5c 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp @@ -3,6 +3,9 @@ #include "Runtime/Engine/Public/ComponentReregisterContext.h" #if WITH_EDITOR #include "Developer/MeshUtilities/Public/MeshUtilities.h" +#if ENGINE_MINOR_VERSION > 18 +#include "Runtime/Engine/Public/Rendering/SkeletalMeshModel.h" +#endif #endif @@ -213,12 +216,20 @@ PyObject *py_ue_skeletal_mesh_set_soft_vertices(ue_PyUObject *self, PyObject * a if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -284,12 +295,20 @@ PyObject *py_ue_skeletal_mesh_get_soft_vertices(ue_PyUObject *self, PyObject * a if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -319,25 +338,41 @@ PyObject *py_ue_skeletal_mesh_get_lod(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); TArray indices; +#if ENGINE_MINOR_VERSION > 18 + indices = model.IndexBuffer; +#else model.MultiSizeIndexContainer.GetIndexBuffer(indices); +#endif for (int32 index = 0; index < indices.Num(); index++) { int32 section_index; int32 vertex_index; +#if ENGINE_MINOR_VERSION > 18 + model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index); +#else bool has_extra_influences; model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index, has_extra_influences); +#endif FSoftSkinVertex ssv = model.Sections[section_index].SoftVertices[vertex_index]; // fix bone id for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; i++) @@ -374,12 +409,20 @@ PyObject *py_ue_skeletal_mesh_get_raw_indices(ue_PyUObject *self, PyObject * arg if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -448,12 +491,20 @@ PyObject *py_ue_skeletal_mesh_set_bone_map(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -516,12 +567,20 @@ PyObject *py_ue_skeletal_mesh_get_bone_map(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -549,12 +608,20 @@ PyObject *py_ue_skeletal_mesh_get_active_bone_indices(ue_PyUObject *self, PyObje if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -579,12 +646,20 @@ PyObject *py_ue_skeletal_mesh_set_active_bone_indices(ue_PyUObject *self, PyObje if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_iter = PyObject_GetIter(py_map); if (!py_iter) @@ -642,12 +717,20 @@ PyObject *py_ue_skeletal_mesh_get_required_bones(ue_PyUObject *self, PyObject * if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -672,12 +755,20 @@ PyObject *py_ue_skeletal_mesh_set_required_bones(ue_PyUObject *self, PyObject * if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_iter = PyObject_GetIter(py_map); if (!py_iter) @@ -730,7 +821,11 @@ PyObject *py_ue_skeletal_mesh_lods_num(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif return PyLong_FromLong(resource->LODModels.Num()); } @@ -747,7 +842,11 @@ PyObject *py_ue_skeletal_mesh_sections_num(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); @@ -778,7 +877,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a SkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); @@ -787,16 +890,28 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO if (lod_index == resource->LODModels.Num()) { +#if ENGINE_MINOR_VERSION < 19 resource->LODModels.Add(new FStaticLODModel()); +#else + resource->LODModels.Add(new FSkeletalMeshLODModel()); +#endif mesh->LODInfo.AddZeroed(); } else { // reinitialized already existent LOD +#if ENGINE_MINOR_VERSION < 19 new(&resource->LODModels[lod_index]) FStaticLODModel(); +#else + new(&resource->LODModels[lod_index]) FSkeletalMeshLODModel(); +#endif } +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel& LODModel = resource->LODModels[lod_index]; +#endif mesh->LODInfo[lod_index].LODHysteresis = 0.02; @@ -895,7 +1010,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO faces.Add(face); } +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel & lod_model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel & lod_model = resource->LODModels[lod_index]; +#endif IMeshUtilities::MeshBuildOptions build_settings; build_settings.bUseMikkTSpace = (py_use_mikk && PyObject_IsTrue(py_use_mikk)); @@ -910,10 +1029,12 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO return PyErr_Format(PyExc_Exception, "unable to create new Skeletal LOD"); } +#if ENGINE_MINOR_VERSION < 19 for (int32 i = 0; i < lod_model.Sections.Num(); i++) { mesh->LODInfo[lod_index].TriangleSortSettings.AddZeroed(); } +#endif mesh->CalculateRequiredBones(LODModel, mesh->RefSkeleton, nullptr); mesh->CalculateInvRefMatrices(); @@ -1007,7 +1128,12 @@ PyObject *py_ue_morph_target_populate_deltas(ue_PyUObject *self, PyObject * args Py_DECREF(py_iter); +#if ENGINE_MINOR_VERSION < 19 morph->PopulateDeltas(deltas, lod_index); +#else + FSkeletalMeshModel *model = morph->BaseSkelMesh->GetImportedModel(); + morph->PopulateDeltas(deltas, lod_index, model->LODModels[lod_index].Sections); +#endif #if ENGINE_MINOR_VERSION > 16 if (morph->HasValidData()) @@ -1064,12 +1190,20 @@ PyObject *py_ue_skeletal_mesh_to_import_vertex_map(ue_PyUObject *self, PyObject if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &LODModel = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp index 4220d0af0..0ddca80ba 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp @@ -48,7 +48,11 @@ static PyObject *py_ue_fassetdata_get_thumbnail(ue_PyFAssetData *self, PyObject static PyObject *py_ue_fassetdata_has_custom_thumbnail(ue_PyFAssetData *self, PyObject * args) { +#if ENGINE_MINOR_VERSION > 18 + if (!ThumbnailTools::AssetHasCustomThumbnail(self->asset_data.GetFullName())) +#else if (!ThumbnailTools::AssetHasCustomThumbnail(self->asset_data)) +#endif { Py_RETURN_FALSE; } diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h index a8ed3f6e5..edc386582 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h @@ -3,6 +3,12 @@ #include "Runtime/Engine/Public/SkeletalMeshTypes.h" +#if ENGINE_MINOR_VERSION > 18 + +#include "Runtime/Engine/Public/Rendering/SkeletalMeshLODModel.h" + +#endif + struct ue_PyFSoftSkinVertex { PyObject_HEAD /* Type-specific fields go here. */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp index 1ddb2b6f5..ec600856d 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp @@ -104,6 +104,7 @@ void ue_python_init_fstring_asset_reference(PyObject *ue_module) Py_INCREF(&ue_PyFStringAssetReferenceType); PyModule_AddObject(ue_module, "FStringAssetReference", (PyObject *)&ue_PyFStringAssetReferenceType); + PyModule_AddObject(ue_module, "FSoftObjectPath", (PyObject *)&ue_PyFStringAssetReferenceType); } PyObject *py_ue_new_fstring_asset_reference(FStringAssetReference ref) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h index 058cf005a..f70e84329 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h @@ -4,7 +4,9 @@ #include "UnrealEnginePython.h" +#if ENGINE_MINOR_VERSION < 18 #include "Runtime/CoreUObject/Public/Misc/StringAssetReference.h" +#endif typedef struct { PyObject_HEAD diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index 28421cae4..e150ac67a 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -15,6 +15,10 @@ // We need to make sure reference structs do not mistaken for callable #define PyCalllable_Check_Extended(value) PyCallable_Check(value) && py_ue_is_uscriptstruct(value) == nullptr +#if ENGINE_MINOR_VERSION >= 18 +#define FStringAssetReference FSoftObjectPath +#endif + DECLARE_LOG_CATEGORY_EXTERN(LogPython, Log, All); diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 6498068c9..20366ea62 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -139,7 +139,7 @@ public UnrealEnginePython(TargetInfo Target) ); - if (UEBuildConfiguration.bBuildEditor) + if (Target.bBuildEditor) { PrivateDependencyModuleNames.AddRange(new string[]{ "UnrealEd", From 449cab44972a6f1b84e2fd6421dde1ca27e90b23 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 10 Feb 2018 18:54:22 +0100 Subject: [PATCH 40/94] fixed compilation on 4.15 --- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 20366ea62..23c240272 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -138,8 +138,11 @@ public UnrealEnginePython(TargetInfo Target) } ); - +#if WITH_FORWARDED_MODULE_RULES_CTOR if (Target.bBuildEditor) +#else + if (UEBuildConfiguration.bBuildEditor) +#endif { PrivateDependencyModuleNames.AddRange(new string[]{ "UnrealEd", From a716da71b024546761e8497629f66bbeb4dd8a6e Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 10 Feb 2018 19:05:58 +0100 Subject: [PATCH 41/94] expose ENGINE VERSION macros to UnrealEnginePython.h --- Source/UnrealEnginePython/Public/UnrealEnginePython.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index e150ac67a..a839dc474 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -9,6 +9,7 @@ #include "ModuleManager.h" #include "Engine.h" +#include "Runtime/Launch/Resources/Version.h" #include "Runtime/SlateCore/Public/Styling/ISlateStyle.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" From f6ae6cddc124fa9289bac9aa90839308683af882 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 10 Feb 2018 19:46:16 +0100 Subject: [PATCH 42/94] added support for setting asset_import_data --- .../Private/UEPyAssetUserData.cpp | 72 +++++++++++++++++-- .../Private/UEPyAssetUserData.h | 1 + .../UnrealEnginePython/Private/UEPyModule.cpp | 1 + 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp index 808b66929..1ca8e7d13 100644 --- a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp +++ b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp @@ -2,20 +2,23 @@ #if WITH_EDITOR -PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); - if (!u_property) { + if (!u_property) + { return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); - + } UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); FAssetImportInfo *import_info = &import_data->SourceData; PyObject *ret = PyList_New(import_info->SourceFiles.Num()); - for (int i=0; i < import_info->SourceFiles.Num(); i++) { + for (int i = 0; i < import_info->SourceFiles.Num(); i++) + { PyObject *py_source_file = PyDict_New(); PyDict_SetItemString(py_source_file, "absolute_filepath", PyUnicode_FromString(TCHAR_TO_UTF8(*import_data->ResolveImportFilename(import_info->SourceFiles[i].RelativeFilename, NULL)))); PyDict_SetItemString(py_source_file, "relative_filepath", PyUnicode_FromString(TCHAR_TO_UTF8(*import_info->SourceFiles[i].RelativeFilename))); @@ -25,4 +28,65 @@ PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) { } return ret; } + +PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_files; + if (!PyArg_ParseTuple(args, "O:asset_import_data_set_sources", &py_files)) + { + return nullptr; + } + + TArray filenames; + + UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); + UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); + if (!u_property) + { + return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); + } + + if (PyUnicode_Check(py_files)) + { + filenames.Add(FString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_files)))); + } + else + { + PyObject *py_iter = PyObject_GetIter(py_files); + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not a string or an interable of strings"); + } + + while (PyObject *py_item = PyIter_Next(py_iter)) + { + if (!PyUnicode_Check(py_item)) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not a string or an interable of strings"); + } + filenames.Add(FString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_item)))); + } + + Py_DECREF(py_iter); + } + + + UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); + FAssetImportInfo *import_info = &import_data->SourceData; + + TArray sources; + + for (FString filename : filenames) + { + sources.Add(FAssetImportInfo::FSourceFile(filename)); + } + + import_info->SourceFiles = sources; + + Py_RETURN_NONE; +} #endif diff --git a/Source/UnrealEnginePython/Private/UEPyAssetUserData.h b/Source/UnrealEnginePython/Private/UEPyAssetUserData.h index 09dcce21a..9506d13c3 100644 --- a/Source/UnrealEnginePython/Private/UEPyAssetUserData.h +++ b/Source/UnrealEnginePython/Private/UEPyAssetUserData.h @@ -6,4 +6,5 @@ #if WITH_EDITOR PyObject *py_ue_asset_import_data(ue_PyUObject *, PyObject *); +PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 48108b8b3..1a32039d9 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -589,6 +589,7 @@ static PyMethodDef ue_PyUObject_methods[] = { #if WITH_EDITOR // AssetUserData { "asset_import_data", (PyCFunction)py_ue_asset_import_data, METH_VARARGS, "" }, + { "asset_import_data_set_sources", (PyCFunction)py_ue_asset_import_data_set_sources, METH_VARARGS, "" }, #endif // AnimSequence From 17a212b953231b2fbb45234c3e91cc35e57dc344 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sun, 11 Feb 2018 12:12:38 +0100 Subject: [PATCH 43/94] rename wrapper to uobject --- Source/UnrealEnginePython/Private/PyActor.cpp | 4 +- .../Private/PyCharacter.cpp | 2 +- Source/UnrealEnginePython/Private/PyHUD.cpp | 2 +- Source/UnrealEnginePython/Private/PyPawn.cpp | 2 +- .../Private/PyUserWidget.cpp | 2 +- .../Private/PythonComponent.cpp | 8 +- .../Slate/UEPyIStructureDetailsView.cpp | 2 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 50 +-- .../UnrealEnginePython/Private/UEPyModule.h | 3 +- .../Private/UEPyUClassesImporter.cpp | 6 +- .../Private/UObject/UEPyActor.cpp | 420 ++++++++++-------- .../Private/UnrealEnginePythonPrivatePCH.h | 10 +- .../UnrealEnginePython.Build.cs | 8 +- 13 files changed, 281 insertions(+), 238 deletions(-) diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 872f78af5..05f1ddac1 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -23,7 +23,7 @@ void APyActor::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); @@ -295,7 +295,7 @@ APyActor::~APyActor() Py_XDECREF(py_actor_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index a2251707b..12035c9c6 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -453,7 +453,7 @@ APyCharacter::~APyCharacter() Py_XDECREF(py_character_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python ACharacter (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python ACharacter (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/PyHUD.cpp b/Source/UnrealEnginePython/Private/PyHUD.cpp index 114e77581..41b729460 100644 --- a/Source/UnrealEnginePython/Private/PyHUD.cpp +++ b/Source/UnrealEnginePython/Private/PyHUD.cpp @@ -253,7 +253,7 @@ APyHUD::~APyHUD() Py_XDECREF(py_hud_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python AHUD %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python AHUD %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/PyPawn.cpp b/Source/UnrealEnginePython/Private/PyPawn.cpp index 5ca19eeda..cfcb9e4c9 100644 --- a/Source/UnrealEnginePython/Private/PyPawn.cpp +++ b/Source/UnrealEnginePython/Private/PyPawn.cpp @@ -250,7 +250,7 @@ APyPawn::~APyPawn() #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python APawn (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python APawn (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index 063f6631f..1e1e17ce8 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -365,7 +365,7 @@ UPyUserWidget::~UPyUserWidget() Py_XDECREF(py_user_widget_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python UUserWidget %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python UUserWidget %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/PythonComponent.cpp b/Source/UnrealEnginePython/Private/PythonComponent.cpp index ac2151b5d..875babc2b 100644 --- a/Source/UnrealEnginePython/Private/PythonComponent.cpp +++ b/Source/UnrealEnginePython/Private/PythonComponent.cpp @@ -24,7 +24,7 @@ void UPythonComponent::InitializePythonComponent() { FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -191,7 +191,7 @@ void UPythonComponent::SetPythonAttrObject(FString attr, UObject *object) FScopePythonGIL gil; - ue_PyUObject *py_obj = ue_get_python_wrapper(object); + ue_PyUObject *py_obj = ue_get_python_uobject(object); if (!py_obj) { PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); unreal_engine_py_log_error(); @@ -425,7 +425,7 @@ UObject *UPythonComponent::CallPythonComponentMethodObject(FString method_name, ret = PyObject_CallMethod(py_component_instance, TCHAR_TO_UTF8(*method_name), NULL); } else { - PyObject *py_arg_uobject = (PyObject *)ue_get_python_wrapper(arg); + PyObject *py_arg_uobject = (PyObject *)ue_get_python_uobject(arg); if (!py_arg_uobject) { unreal_engine_py_log_error(); return nullptr; @@ -566,7 +566,7 @@ UPythonComponent::~UPythonComponent() Py_XDECREF(py_component_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python UActorComponent %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python UActorComponent %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp index aec81f315..dea026a94 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp @@ -94,7 +94,7 @@ static PyMethodDef ue_PyIStructureDetailsView_methods[] = { static void ue_PyIStructureDetailsView_dealloc(ue_PyIStructureDetailsView *self) { #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, &self->istructure_details_view.Get()); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, self->istructure_details_view.Get()); #endif Py_DECREF(self->ue_py_struct); Py_TYPE(self)->tp_free((PyObject *)self); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 1a32039d9..3a98b9750 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -148,8 +148,7 @@ static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) return (PyObject *)self->py_proxy; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef unreal_engine_methods[] = { @@ -982,6 +981,7 @@ void ue_pydelegates_cleanup(ue_PyUObject *self) // start deallocating delegates mapped to the object if (!self || !self->python_delegates_gc) return; + UE_LOG(LogPython, Warning, TEXT("Delegates = %d"), self->python_delegates_gc->size()); for (UPythonDelegate *py_delegate : *(self->python_delegates_gc)) { if (py_delegate && py_delegate->IsValidLowLevel()) @@ -2096,7 +2096,7 @@ void unreal_engine_init_py_module() // utility functions -ue_PyUObject *ue_get_python_wrapper(UObject *ue_obj) +ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) { if (!ue_obj || !ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) return nullptr; @@ -2122,10 +2122,20 @@ ue_PyUObject *ue_get_python_wrapper(UObject *ue_obj) //Py_INCREF(ue_py_object); return ue_py_object; } - + Py_INCREF(it->second); return it->second; } +ue_PyUObject *ue_get_python_uobject_noinc(UObject *ue_obj) +{ + ue_PyUObject *ret = ue_get_python_uobject(ue_obj); + if (ret) + { + Py_DECREF(ret); + } + return ret; +} + void unreal_engine_py_log_error() { PyObject *type = NULL; @@ -2327,11 +2337,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) auto value = casted_prop->GetObjectPropertyValue_InContainer(buffer); if (value) { - ue_PyUObject *ret = ue_get_python_wrapper(value); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(value); } Py_RETURN_NONE; } @@ -2341,11 +2347,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) auto value = casted_prop->GetPropertyValue_InContainer(buffer); if (value) { - ue_PyUObject *ret = ue_get_python_wrapper(value); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(value); } return PyErr_Format(PyExc_Exception, "invalid UClass type for %s", TCHAR_TO_UTF8(*casted_prop->GetName())); } @@ -2397,11 +2399,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) UObject *strong_obj = value.Get(); if (strong_obj) { - ue_PyUObject *ret = ue_get_python_wrapper(strong_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(strong_obj); } // nullptr Py_INCREF(Py_None); @@ -2410,11 +2408,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) if (auto casted_prop = Cast(prop)) { - ue_PyUObject *ret = ue_get_python_wrapper(casted_prop); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(casted_prop); } if (auto casted_prop = Cast(prop)) @@ -2967,7 +2961,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class { if (n == 1) { - if (!ue_bind_pyevent(ue_get_python_wrapper(actor), parts[0], item, true)) + if (!ue_bind_pyevent(ue_get_python_uobject(actor), parts[0], item, true)) { unreal_engine_py_log_error(); } @@ -3234,8 +3228,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ { if (fail_on_wrong_property) return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } if (auto casted_prop = Cast(u_property)) @@ -3265,8 +3258,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ return PyErr_Format(PyExc_Exception, "property %s is not an event", TCHAR_TO_UTF8(*event_name)); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_callable, uint32 function_flags) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 072d42b97..4995bafac 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -24,7 +24,8 @@ typedef struct { void unreal_engine_py_log_error(); -ue_PyUObject *ue_get_python_wrapper(UObject *); +ue_PyUObject *ue_get_python_uobject(UObject *); +ue_PyUObject *ue_get_python_uobject_inc(UObject *); UWorld *ue_get_uworld(ue_PyUObject *); AActor *ue_get_actor(ue_PyUObject *); PyObject *ue_py_convert_property(UProperty *, uint8 *); diff --git a/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp b/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp index 293ea2e97..d1457ec03 100644 --- a/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp @@ -15,11 +15,7 @@ static PyObject *ue_PyUClassesImporter_getattro(ue_PyUClassesImporter *self, PyO { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_class); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_class); } } } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e0eeced08..260f2915f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -5,22 +5,26 @@ #include "PythonComponent.h" #include "UEPyObject.h" -PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *tag; - if (!PyArg_ParseTuple(args, "s:actor_has_tag", &tag)) { + if (!PyArg_ParseTuple(args, "s:actor_has_tag", &tag)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (actor->ActorHasTag(FName(UTF8_TO_TCHAR(tag)))) { + if (actor->ActorHasTag(FName(UTF8_TO_TCHAR(tag)))) + { Py_INCREF(Py_True); return Py_True; } @@ -29,7 +33,8 @@ PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) { return Py_False; } -PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -45,12 +50,14 @@ PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } @@ -65,7 +72,8 @@ PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -74,25 +82,24 @@ PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); char *name; - if (!PyArg_ParseTuple(args, "s:get_actor_component", &name)) { + if (!PyArg_ParseTuple(args, "s:get_actor_component", &name)) + { return NULL; } - for (UActorComponent *component : actor->GetComponents()) { - if (component->GetName().Equals(UTF8_TO_TCHAR(name))) { - ue_PyUObject *py_obj = ue_get_python_wrapper(component); - if (!py_obj) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(py_obj); - return (PyObject *)py_obj; + for (UActorComponent *component : actor->GetComponents()) + { + if (component->GetName().Equals(UTF8_TO_TCHAR(name))) + { + Py_RETURN_UOBJECT(component); } } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -101,7 +108,8 @@ PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); PyObject *py_component; - if (!PyArg_ParseTuple(args, "O:actor_destroy_component", &py_component)) { + if (!PyArg_ParseTuple(args, "O:actor_destroy_component", &py_component)) + { return NULL; } @@ -119,11 +127,13 @@ PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { return Py_None; } -PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } @@ -131,14 +141,13 @@ PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) { actor->Destroy(); - Py_INCREF(Py_None); - return Py_None; - + Py_RETURN_NONE; } -PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -148,8 +157,9 @@ PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { PyObject *ret = PyList_New(0); - for (UActorComponent *component : actor->GetComponents()) { - ue_PyUObject *py_obj = ue_get_python_wrapper(component); + for (UActorComponent *component : actor->GetComponents()) + { + ue_PyUObject *py_obj = ue_get_python_uobject_noinc(component); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -159,7 +169,8 @@ PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -173,16 +184,19 @@ PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) { #if WITH_EDITOR -PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { AActor *actor = (AActor *)self->ue_object; return PyUnicode_FromString(TCHAR_TO_UTF8(*(actor->GetActorLabel()))); } - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UActorComponent *component = (UActorComponent *)self->ue_object; return PyUnicode_FromString(TCHAR_TO_UTF8(*(component->GetOwner()->GetActorLabel()))); } @@ -190,7 +204,8 @@ PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not an actor or a component"); } -PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -199,7 +214,8 @@ PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); char *label; - if (!PyArg_ParseTuple(args, "s:set_actor_label", &label)) { + if (!PyArg_ParseTuple(args, "s:set_actor_label", &label)) + { return NULL; } @@ -209,12 +225,14 @@ PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { return Py_None; } -PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name; - if (!PyArg_ParseTuple(args, "s:find_actor_by_label", &name)) { + if (!PyArg_ParseTuple(args, "s:find_actor_by_label", &name)) + { return NULL; } @@ -224,51 +242,46 @@ PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) { UObject *u_object = nullptr; - for (TActorIterator Itr(world); Itr; ++Itr) { + for (TActorIterator Itr(world); Itr; ++Itr) + { AActor *u_obj = *Itr; - if (u_obj->GetActorLabel().Equals(UTF8_TO_TCHAR(name))) { + if (u_obj->GetActorLabel().Equals(UTF8_TO_TCHAR(name))) + { u_object = u_obj; break; } } - if (u_object) { - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + if (u_object) + { + Py_RETURN_UOBJECT(u_object); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #endif -PyObject *py_ue_get_owner(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_owner(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + UActorComponent *component = ue_py_check_type(self); + if (!component) return PyErr_Format(PyExc_Exception, "uobject is not a component"); - } - - UActorComponent *component = (UActorComponent *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(component->GetOwner()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(component->GetOwner()); } -PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a component"); } @@ -281,17 +294,20 @@ PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) { return Py_None; } -PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a component"); } UActorComponent *component = (UActorComponent *)self->ue_object; - if (component->IsRegistered()) { + if (component->IsRegistered()) + { Py_INCREF(Py_True); return Py_True; } @@ -300,7 +316,8 @@ PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) { return Py_False; } -PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -309,7 +326,8 @@ PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { return nullptr; USceneComponent *child = ue_py_check_type(self); - if (!child) { + if (!child) + { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } @@ -318,7 +336,8 @@ PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UActorComponent *component = ue_py_check_type(self); @@ -331,7 +350,8 @@ PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_destroy_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_destroy_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UActorComponent *component = ue_py_check_type(self); @@ -373,44 +393,52 @@ PyObject *py_ue_add_instance_component(ue_PyUObject * self, PyObject * args) } -PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; PyObject *py_parent = nullptr; - if (!PyArg_ParseTuple(args, "Os|O:add_actor_component", &obj, &name, &py_parent)) { + if (!PyArg_ParseTuple(args, "Os|O:add_actor_component", &obj, &name, &py_parent)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass"); } UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from UActorComponent"); } USceneComponent *parent_component = nullptr; - if (py_parent) { + if (py_parent) + { parent_component = ue_py_check_type(py_parent); - if (!parent_component) { + if (!parent_component) + { return PyErr_Format(PyExc_Exception, "argument is not a USceneComponent"); } } @@ -419,38 +447,39 @@ PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) { if (!component) return PyErr_Format(PyExc_Exception, "unable to create component"); - if (py_parent && component->IsA()) { + if (py_parent && component->IsA()) + { USceneComponent *scene_component = (USceneComponent *)component; scene_component->SetupAttachment(parent_component); } - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name; char *module_name; char *class_name; - if (!PyArg_ParseTuple(args, "sss:add_python_component", &name, &module_name, &class_name)) { + if (!PyArg_ParseTuple(args, "sss:add_python_component", &name, &module_name, &class_name)) + { return NULL; } AActor *actor = ue_py_check_type(self); - if (!actor) { + if (!actor) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } @@ -461,27 +490,26 @@ PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) { component->PythonModule = FString(UTF8_TO_TCHAR(module_name)); component->PythonClass = FString(UTF8_TO_TCHAR(class_name)); - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; - if (!PyArg_ParseTuple(args, "Os:actor_create_default_subobject", &obj, &name)) { + if (!PyArg_ParseTuple(args, "Os:actor_create_default_subobject", &obj, &name)) + { return NULL; } @@ -500,58 +528,55 @@ PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * a if (!ret_obj) return PyErr_Format(PyExc_Exception, "unable to create component"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(ret_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(ret_obj); } -PyObject *py_ue_get_actor_root_component(ue_PyUObject * self, PyObject * args) { - - ue_py_check(self); - - AActor *actor = ue_get_actor(self); - if (!actor) - return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); - - UActorComponent *component = actor->GetRootComponent(); - if (component) { - ue_PyUObject *py_obj = ue_get_python_wrapper(component); - if (!py_obj) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(py_obj); - return (PyObject *)py_obj; - } - - Py_INCREF(Py_None); - return Py_None; +PyObject *py_ue_get_actor_root_component(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + AActor *actor = ue_get_actor(self); + if (!actor) + return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); + + UActorComponent *component = actor->GetRootComponent(); + if (component) + { + Py_RETURN_UOBJECT(component); + } + + Py_RETURN_NONE; } -PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; - if (!PyArg_ParseTuple(args, "Os:add_actor_root_component", &obj, &name)) { + if (!PyArg_ParseTuple(args, "Os:add_actor_root_component", &obj, &name)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a class"); } @@ -561,43 +586,44 @@ PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) { actor->SetRootComponent(component); - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:actor_has_component_of_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:actor_has_component_of_type", &obj)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (actor->GetComponentByClass((UClass *)py_obj->ue_object)) { + if (actor->GetComponentByClass((UClass *)py_obj->ue_object)) + { Py_INCREF(Py_True); return Py_True; } @@ -607,27 +633,32 @@ PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args } -PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:get_actor_component_by_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:get_actor_component_by_type", &obj)) + { return NULL; } ue_PyUObject *py_obj = nullptr; - if (ue_is_pyuobject(obj)) { + if (ue_is_pyuobject(obj)) + { py_obj = (ue_PyUObject *)obj; } // shortcut for finding class by string - else if (PyUnicodeOrString_Check(obj)) { + else if (PyUnicodeOrString_Check(obj)) + { char *class_name = PyUnicode_AsUTF8(obj); UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); - if (u_class) { - py_obj = ue_get_python_wrapper(u_class); + if (u_class) + { + py_obj = ue_get_python_uobject_noinc(u_class); } } @@ -642,40 +673,41 @@ PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args return PyErr_Format(PyExc_Exception, "argument is not a UClass"); UActorComponent *component = actor->GetComponentByClass((UClass *)py_obj->ue_object); - if (component) { - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + if (component) + { + Py_RETURN_UOBJECT(component); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:get_actor_components_by_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:get_actor_components_by_type", &obj)) + { return NULL; } ue_PyUObject *py_obj = nullptr; - if (ue_is_pyuobject(obj)) { + if (ue_is_pyuobject(obj)) + { py_obj = (ue_PyUObject *)obj; } // shortcut for finding class by string - else if (PyUnicodeOrString_Check(obj)) { + else if (PyUnicodeOrString_Check(obj)) + { char *class_name = PyUnicode_AsUTF8(obj); UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); - if (u_class) { - py_obj = ue_get_python_wrapper(u_class); + if (u_class) + { + py_obj = ue_get_python_uobject_noinc(u_class); } } @@ -691,8 +723,10 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg PyObject *components = PyList_New(0); - for (UActorComponent *component : actor->GetComponentsByClass((UClass *)py_obj->ue_object)) { - ue_PyUObject *item = ue_get_python_wrapper(component); + for (UActorComponent *component : actor->GetComponentsByClass((UClass *)py_obj->ue_object)) + { + // noinc as list will increase refcnt + ue_PyUObject *item = ue_get_python_uobject_noinc(component); if (item) PyList_Append(components, (PyObject *)item); } @@ -702,7 +736,8 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg } -PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwargs) { +PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwargs) +{ ue_py_check(self); @@ -714,37 +749,43 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); PyObject *obj; - if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &obj, &py_obj_location, &py_obj_rotation)) { + if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &obj, &py_obj_location, &py_obj_rotation)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from AActor"); } UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from AActor"); } FVector location = FVector(0, 0, 0); FRotator rotation = FRotator(0, 0, 0); - if (py_obj_location) { + if (py_obj_location) + { ue_PyFVector *py_location = py_ue_is_fvector(py_obj_location); if (!py_location) return PyErr_Format(PyExc_Exception, "location must be an FVector"); location = py_location->vec; } - if (py_obj_rotation) { + if (py_obj_rotation) + { ue_PyFRotator *py_rotation = py_ue_is_frotator(py_obj_rotation); if (!py_rotation) return PyErr_Format(PyExc_Exception, "location must be an FRotator"); @@ -754,22 +795,25 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar AActor *actor = nullptr; PyObject *ret = nullptr; - if (kwargs && PyDict_Size(kwargs) > 0) { + if (kwargs && PyDict_Size(kwargs) > 0) + { FTransform transform; transform.SetTranslation(location); transform.SetRotation(rotation.Quaternion()); actor = world->SpawnActorDeferred((UClass *)py_obj->ue_object, transform); if (!actor) return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ue_PyUObject *py_u_obj = ue_get_python_wrapper(actor); + ue_PyUObject *py_u_obj = ue_get_python_uobject(actor); if (!py_u_obj) return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); PyObject *py_iter = PyObject_GetIter(kwargs); - while (PyObject *py_key = PyIter_Next(py_iter)) { + while (PyObject *py_key = PyIter_Next(py_iter)) + { PyObject *void_ret = py_ue_set_property(py_u_obj, Py_BuildValue("OO", py_key, PyDict_GetItem(kwargs, py_key))); - if (!void_ret) { + if (!void_ret) + { return PyErr_Format(PyExc_Exception, "unable to set property for new Actor"); } } @@ -777,20 +821,21 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar UGameplayStatics::FinishSpawningActor(actor, transform); ret = (PyObject *)py_u_obj; } - else { + else + { actor = world->SpawnActor((UClass *)py_obj->ue_object, &location, &rotation); if (!actor) return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ret = (PyObject *)ue_get_python_wrapper(actor); + ret = (PyObject *)ue_get_python_uobject(actor); } if (!ret) return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); return ret; } -PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -799,13 +844,15 @@ PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve actor from UObject"); PyObject *class_filter = nullptr; - if (!PyArg_ParseTuple(args, "|O:get_overlapping_actors", &class_filter)) { + if (!PyArg_ParseTuple(args, "|O:get_overlapping_actors", &class_filter)) + { return NULL; } UClass *filtering = AActor::StaticClass(); - if (class_filter) { + if (class_filter) + { if (!ue_is_pyuobject(class_filter)) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); @@ -823,31 +870,37 @@ PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { TArray overalpping_actors; actor->GetOverlappingActors(overalpping_actors, filtering); - for (AActor *overlapping_actor : overalpping_actors) { - ue_PyUObject *item = ue_get_python_wrapper(overlapping_actor); - if (item) { + for (AActor *overlapping_actor : overalpping_actors) + { + ue_PyUObject *item = ue_get_python_uobject_noinc(overlapping_actor); + if (item) + { PyList_Append(py_overlapping_actors, (PyObject *)item); } } return py_overlapping_actors; } -PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_sequence; - if (!PyArg_ParseTuple(args, "O:actor_set_level_sequence", &py_sequence)) { + if (!PyArg_ParseTuple(args, "O:actor_set_level_sequence", &py_sequence)) + { return NULL; } ALevelSequenceActor *actor = ue_py_check_type(self); - if (!actor) { + if (!actor) + { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequenceActor"); } ULevelSequence *sequence = ue_py_check_type(py_sequence); - if (!sequence) { + if (!sequence) + { return PyErr_Format(PyExc_Exception, "argument is not a LevelSequence"); } @@ -858,7 +911,8 @@ PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) { #if WITH_EDITOR -PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -870,10 +924,6 @@ PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject if (!editor_actor) return PyErr_Format(PyExc_Exception, "unable to retrieve editor counterpart actor"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(editor_actor); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(editor_actor); } #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index e69e83b9c..ff5a52252 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -//#define UEPY_MEMORY_DEBUG 1 +#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" @@ -90,10 +90,14 @@ int PyGILState_Check(); #endif bool PyUnicodeOrString_Check(PyObject *py_obj); -#define Py_RETURN_UOBJECT(py_uobj) ue_PyUObject *ret = ue_get_python_wrapper(py_uobj);\ +#define Py_RETURN_UOBJECT(py_uobj) ue_PyUObject *ret = ue_get_python_uobject_inc(py_uobj);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ + return (PyObject *)ret; + +#define Py_RETURN_UOBJECT_NOINC(py_uobj) ue_PyUObject *ret = ue_get_python_uobject(py_uobj);\ if (!ret)\ return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ - Py_INCREF(ret);\ return (PyObject *)ret; #if ENGINE_MINOR_VERSION < 16 diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 23c240272..d9bb0eb31 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -16,7 +16,6 @@ public class UnrealEnginePython : ModuleRules // on Linux an include;libs syntax is expected: //private string pythonHome = "/usr/local/include/python3.6;/usr/local/lib/libpython3.6.so" - private string[] windowsKnownPaths = { "C:/Program Files/Python36", @@ -80,6 +79,7 @@ public UnrealEnginePython(TargetInfo Target) #endif { + PublicIncludePaths.AddRange( new string[] { "UnrealEnginePython/Public", @@ -207,8 +207,8 @@ public UnrealEnginePython(TargetInfo Target) File.SetAttributes(Path.Combine(dllsDir, dllToCopy), FileAttributes.Normal); } } - catch(System.IO.IOException) { } - catch(System.UnauthorizedAccessException) + catch (System.IO.IOException) { } + catch (System.UnauthorizedAccessException) { System.Console.WriteLine("WARNING: Unable to copy python dlls, they are probably in use..."); } @@ -285,7 +285,7 @@ private string DiscoverPythonPath(string[] knownPaths) foreach (string path in paths) { string actualPath = path; - + if (IsPathRelative(actualPath)) { actualPath = Path.GetFullPath(Path.Combine(ModuleDirectory, actualPath)); From db9462788170ea78e9e839ddba81e98fa07ab889 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sun, 11 Feb 2018 15:40:18 +0100 Subject: [PATCH 44/94] second round of reference counting refactoring --- .../Private/Blueprint/UEPyEdGraph.cpp | 240 ++++++----- .../Private/Blueprint/UEPyEdGraphPin.cpp | 9 +- .../Private/PyCharacter.cpp | 6 +- Source/UnrealEnginePython/Private/PyHUD.cpp | 2 +- Source/UnrealEnginePython/Private/PyPawn.cpp | 2 +- .../Private/PyUserWidget.cpp | 2 +- .../Private/PythonDelegate.cpp | 2 +- .../Private/PythonFunction.cpp | 2 +- .../Private/Slate/UEPySLevelViewport.cpp | 45 +- .../Slate/UEPySPythonEditorViewport.cpp | 176 +++++--- .../UnrealEnginePython/Private/UEPyEditor.cpp | 227 ++++------ .../UnrealEnginePython/Private/UEPyEngine.cpp | 90 ++-- .../Private/UEPyIPlugin.cpp | 6 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 31 +- .../Private/UEPyUScriptStruct.cpp | 6 +- .../Private/UEPyUStructsImporter.cpp | 6 +- .../Private/UObject/UEPyActor.cpp | 11 +- .../Private/UObject/UEPyAnimSequence.cpp | 6 +- .../Private/UObject/UEPyAttaching.cpp | 61 ++- .../Private/UObject/UEPyController.cpp | 12 +- .../Private/UObject/UEPyLandscape.cpp | 12 +- .../Private/UObject/UEPyMaterial.cpp | 211 +++++---- .../Private/UObject/UEPyObject.cpp | 109 ++--- .../Private/UObject/UEPyPawn.cpp | 6 +- .../Private/UObject/UEPyPlayer.cpp | 24 +- .../Private/UObject/UEPySequencer.cpp | 403 ++++++++++-------- .../Private/UObject/UEPyTransform.cpp | 234 ++++++---- .../Private/UObject/UEPyViewport.cpp | 85 ++-- .../Private/UObject/UEPyWidget.cpp | 12 +- 29 files changed, 1066 insertions(+), 972 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index ac33dcb83..1e82e45d7 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp @@ -13,18 +13,21 @@ #include "Editor/AIGraph/Classes/AIGraph.h" #include "Editor/AIGraph/Classes/AIGraphNode.h" -PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_function = NULL; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "O|ii:graph_add_node_call_function", &py_function, &x, &y)) { + if (!PyArg_ParseTuple(args, "O|ii:graph_add_node_call_function", &py_function, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -32,16 +35,20 @@ PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * arg UFunction *function = nullptr; - if (ue_is_pyuobject(py_function)) { + if (ue_is_pyuobject(py_function)) + { ue_PyUObject *py_function_obj = (ue_PyUObject *)py_function; - if (py_function_obj->ue_object->IsA()) { + if (py_function_obj->ue_object->IsA()) + { function = (UFunction *)py_function_obj->ue_object; } - else { + else + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } } - else if (py_ue_is_callable(py_function)) { + else if (py_ue_is_callable(py_function)) + { ue_PyCallable *py_callable = (ue_PyCallable *)py_function; function = py_callable->u_function; } @@ -61,30 +68,29 @@ PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * arg UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|ii:graph_add_node_custom_event", &name, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|ii:graph_add_node_custom_event", &name, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -102,23 +108,21 @@ PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -131,7 +135,8 @@ PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject return ret; } -PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -140,22 +145,26 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "Os|ii:graph_add_node_event", &py_class, &name, &x, &y)) { + if (!PyArg_ParseTuple(args, "Os|ii:graph_add_node_event", &py_class, &name, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; - if (!ue_is_pyuobject(py_class)) { + if (!ue_is_pyuobject(py_class)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)py_class; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass"); } @@ -163,7 +172,8 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { UBlueprint *bp = (UBlueprint *)graph->GetOuter(); UK2Node_Event *node = FBlueprintEditorUtils::FindOverrideForFunction(bp, u_class, UTF8_TO_TCHAR(name)); - if (!node) { + if (!node) + { node = NewObject(graph); UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); node->EventReference.SetExternalMember(UTF8_TO_TCHAR(name), u_class); @@ -172,15 +182,11 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -188,23 +194,28 @@ PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args PyObject *py_struct = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_get", &name, &py_struct, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_get", &name, &py_struct, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; UStruct *u_struct = nullptr; - if (py_struct && py_struct != Py_None) { - if (!ue_is_pyuobject(py_struct)) { + if (py_struct && py_struct != Py_None) + { + if (!ue_is_pyuobject(py_struct)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *ue_py_struct = (ue_PyUObject *)py_struct; - if (!ue_py_struct->ue_object->IsA()) { + if (!ue_py_struct->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UStruct"); } u_struct = (UStruct *)ue_py_struct->ue_object; @@ -216,18 +227,14 @@ PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args UEdGraphSchema_K2::ConfigureVarNode(node, FName(UTF8_TO_TCHAR(name)), u_struct, bp); node = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate(graph, node, FVector2D(x, y)); - - FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -235,23 +242,28 @@ PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args PyObject *py_struct = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_set", &name, &py_struct, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_set", &name, &py_struct, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; UStruct *u_struct = nullptr; - if (py_struct && py_struct != Py_None) { - if (!ue_is_pyuobject(py_struct)) { + if (py_struct && py_struct != Py_None) + { + if (!ue_is_pyuobject(py_struct)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *ue_py_struct = (ue_PyUObject *)py_struct; - if (!ue_py_struct->ue_object->IsA()) { + if (!ue_py_struct->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UStruct"); } u_struct = (UStruct *)ue_py_struct->ue_object; @@ -266,15 +278,11 @@ PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -283,39 +291,47 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { int y = 0; PyObject *py_data = nullptr; char *metadata = nullptr; - if (!PyArg_ParseTuple(args, "O|iisO:graph_add_node", &py_node_class, &x, &y, &metadata, &py_data)) { + if (!PyArg_ParseTuple(args, "O|iisO:graph_add_node", &py_node_class, &x, &y, &metadata, &py_data)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; - if (!ue_is_pyuobject(py_node_class)) { + if (!ue_is_pyuobject(py_node_class)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } UEdGraphNode *node = nullptr; ue_PyUObject *py_obj = (ue_PyUObject *)py_node_class; - if (py_obj->ue_object->IsA()) { + if (py_obj->ue_object->IsA()) + { UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a child of UEdGraphNode"); } node = (UEdGraphNode *)NewObject(graph, u_class); node->PostLoad(); } - else if (py_obj->ue_object->IsA()) { + else if (py_obj->ue_object->IsA()) + { node = (UEdGraphNode *)py_obj->ue_object; - if (node->GetOuter() != graph) { + if (node->GetOuter() != graph) + { node->Rename(*node->GetName(), graph); } } - if (!node) { + if (!node) + { return PyErr_Format(PyExc_Exception, "argument is not a supported type"); } @@ -327,63 +343,71 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { node->NodePosY = y; // do something with data, based on the node type - if (node->IsA()) { + if (node->IsA()) + { UAIGraphNode *ai_node = (UAIGraphNode *)node; - if (py_data) { + if (py_data) + { FGraphNodeClassData *class_data = ue_py_check_struct(py_data); - if (class_data == nullptr) { + if (class_data == nullptr) + { UE_LOG(LogPython, Warning, TEXT("Unable to manage data argument for UAIGraphNode")); } - else { + else + { ai_node->ClassData = *class_data; } } } - if (metadata == nullptr || strlen(metadata) == 0) { + if (metadata == nullptr || strlen(metadata) == 0) + { UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); } - else { + else + { UEdGraphSchema_K2::SetNodeMetaData(node, FName(UTF8_TO_TCHAR(metadata))); } graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_node_pins(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_pins(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); } UEdGraphNode *node = (UEdGraphNode *)self->ue_object; PyObject *pins_list = PyList_New(0); - for (UEdGraphPin *pin : node->Pins) { + for (UEdGraphPin *pin : node->Pins) + { PyList_Append(pins_list, (PyObject *)py_ue_new_edgraphpin(pin)); } return pins_list; } -PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); int title_type = ENodeTitleType::FullTitle; - if (!PyArg_ParseTuple(args, "|i:node_get_title", &title_type)) { + if (!PyArg_ParseTuple(args, "|i:node_get_title", &title_type)) + { return NULL; } @@ -392,11 +416,12 @@ PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); FText title = node->GetNodeTitle((ENodeTitleType::Type)title_type); - + return PyUnicode_FromString(TCHAR_TO_UTF8(*(title.ToString()))); } -PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -410,7 +435,8 @@ PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) return Py_None; } -PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -424,23 +450,27 @@ PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) { return Py_None; } -PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name = nullptr; - if (!PyArg_ParseTuple(args, "s:node_find_pin", &name)) { + if (!PyArg_ParseTuple(args, "s:node_find_pin", &name)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); } UEdGraphNode *node = (UEdGraphNode *)self->ue_object; UEdGraphPin *pin = node->FindPin(UTF8_TO_TCHAR(name)); - if (!pin) { + if (!pin) + { return PyErr_Format(PyExc_Exception, "unable to find pin \"%s\"", name); } @@ -449,7 +479,8 @@ PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) { return ret; } -PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -457,7 +488,8 @@ PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { PyObject *pin_type; char *name = nullptr; int index = 0; - if (!PyArg_ParseTuple(args, "iOs|i:node_create_pin", &pin_direction, &pin_type, &name, &index)) { + if (!PyArg_ParseTuple(args, "iOs|i:node_create_pin", &pin_direction, &pin_type, &name, &index)) + { return nullptr; } @@ -471,18 +503,22 @@ PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { UEdGraphPin *pin = nullptr; - if (node->IsA()) { + if (node->IsA()) + { UK2Node_EditablePinBase *node_base = (UK2Node_EditablePinBase *)node; pin = node_base->CreateUserDefinedPin(UTF8_TO_TCHAR(name), *pin_struct, (EEdGraphPinDirection)pin_direction); } - else { + else + { pin = node->CreatePin((EEdGraphPinDirection)pin_direction, *pin_struct, UTF8_TO_TCHAR(name), index); } - if (!pin) { + if (!pin) + { return PyErr_Format(PyExc_Exception, "unable to create pin \"%s\"", name); } - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp index e271dded8..c9fc2441c 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp @@ -119,14 +119,9 @@ static int py_ue_edgraphpin_set_default_value(ue_PyEdGraphPin *self, PyObject *v static PyObject *py_ue_edgraphpin_get_default_object(ue_PyEdGraphPin *self, void *closure) { UObject *u_object = self->pin->DefaultObject; if (!u_object) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - PyObject *ret = (PyObject *)ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(u_object); } static int py_ue_edgraphpin_set_default_object(ue_PyEdGraphPin *self, PyObject *value, void *closure) { diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index 12035c9c6..75e82b39a 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -29,7 +29,7 @@ void APyCharacter::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -191,7 +191,7 @@ void APyCharacter::SetPythonAttrObject(FString attr, UObject *object) FScopePythonGIL gil; - ue_PyUObject *py_obj = ue_get_python_wrapper(object); + ue_PyUObject *py_obj = ue_get_python_uobject(object); if (!py_obj) { PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); unreal_engine_py_log_error(); @@ -408,7 +408,7 @@ void APyCharacter::SetupPlayerInputComponent(class UInputComponent* input) // no need to check for method availability, we did it in begin_play - PyObject *ret = PyObject_CallMethod(py_character_instance, (char *)"setup_player_input_component", (char *)"O", ue_get_python_wrapper(input)); + PyObject *ret = PyObject_CallMethod(py_character_instance, (char *)"setup_player_input_component", (char *)"O", ue_get_python_uobject(input)); if (!ret) { unreal_engine_py_log_error(); return; diff --git a/Source/UnrealEnginePython/Private/PyHUD.cpp b/Source/UnrealEnginePython/Private/PyHUD.cpp index 41b729460..cb2cf7f2f 100644 --- a/Source/UnrealEnginePython/Private/PyHUD.cpp +++ b/Source/UnrealEnginePython/Private/PyHUD.cpp @@ -25,7 +25,7 @@ void APyHUD::BeginPlay() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); diff --git a/Source/UnrealEnginePython/Private/PyPawn.cpp b/Source/UnrealEnginePython/Private/PyPawn.cpp index cfcb9e4c9..3923579f8 100644 --- a/Source/UnrealEnginePython/Private/PyPawn.cpp +++ b/Source/UnrealEnginePython/Private/PyPawn.cpp @@ -41,7 +41,7 @@ void APyPawn::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index 1e1e17ce8..6e655fc15 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -27,7 +27,7 @@ void UPyUserWidget::NativeConstruct() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index b009c0784..302f4b618 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -113,7 +113,7 @@ bool UPythonDelegate::Tick(float DeltaTime) void UPythonDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) { FScopePythonGIL gil; - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_wrapper((UObject *)factory), ue_get_python_wrapper(u_object)); + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_uobject((UObject *)factory), ue_get_python_uobject(u_object)); if (!ret) { unreal_engine_py_log_error(); return; diff --git a/Source/UnrealEnginePython/Private/PythonFunction.cpp b/Source/UnrealEnginePython/Private/PythonFunction.cpp index febb7718d..f30ada1dd 100644 --- a/Source/UnrealEnginePython/Private/PythonFunction.cpp +++ b/Source/UnrealEnginePython/Private/PythonFunction.cpp @@ -36,7 +36,7 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) PyObject *py_args = PyTuple_New(argn); if (Stack.Object && !is_static) { - PyObject *py_obj = (PyObject *)ue_get_python_wrapper(Stack.Object); + PyObject *py_obj = (PyObject *)ue_get_python_uobject(Stack.Object); if (!py_obj) { unreal_engine_py_log_error(); on_error = true; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp index 96ec555cb..981cacce6 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp @@ -8,18 +8,16 @@ #define sw_level_viewport StaticCastSharedRef(self->s_editor_viewport.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_slevel_viewport_get_world(ue_PySLevelViewport *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(sw_level_viewport->GetWorld()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; +static PyObject *py_ue_slevel_viewport_get_world(ue_PySLevelViewport *self, PyObject * args) +{ + Py_RETURN_UOBJECT(sw_level_viewport->GetWorld()); } -static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) + { return NULL; } @@ -29,9 +27,11 @@ static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) + { return NULL; } @@ -41,9 +41,11 @@ static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) + { return NULL; } @@ -53,14 +55,17 @@ static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_settings; - if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) { + if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) + { return NULL; } FExposureSettings *settings = ue_py_check_struct(py_settings); - if (!settings) { + if (!settings) + { return PyErr_Format(PyExc_Exception, "argument is not a FExposureSettings"); } @@ -120,7 +125,8 @@ PyTypeObject ue_PySLevelViewportType = { ue_PySLevelViewport_methods, /* tp_methods */ }; -static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, PyObject *kwargs) { +static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, PyObject *kwargs) +{ FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); @@ -130,7 +136,7 @@ static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, ue_py_slate_farguments_optional_bool("realtime", Realtime); ue_py_slate_farguments_optional_enum("viewport_type", ViewportType, ELevelViewportType); - + ue_py_snew(SLevelViewport, s_editor_viewport.s_compound_widget.s_widget); EditorModule.GetFirstLevelEditor()->AddStandaloneLevelViewport(sw_level_viewport); @@ -138,7 +144,8 @@ static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, return 0; } -void ue_python_init_slevel_viewport(PyObject *ue_module) { +void ue_python_init_slevel_viewport(PyObject *ue_module) +{ ue_PySLevelViewportType.tp_init = (initproc)ue_py_slevel_viewport_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp index 8a8fbc09f..7874cf9d5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp @@ -7,18 +7,16 @@ #define sw_python_editor_viewport StaticCastSharedRef(self->s_editor_viewport.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_spython_editor_viewport_get_world(ue_PySPythonEditorViewport *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(sw_python_editor_viewport->GetPythonWorld()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; +static PyObject *py_ue_spython_editor_viewport_get_world(ue_PySPythonEditorViewport *self, PyObject * args) +{ + Py_RETURN_UOBJECT(sw_python_editor_viewport->GetPythonWorld()); } -static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) + { return NULL; } @@ -29,9 +27,11 @@ static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEdito return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) + { return NULL; } @@ -42,9 +42,11 @@ static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditor return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorViewport *self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) + { return NULL; } @@ -55,14 +57,17 @@ static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorV return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_settings; - if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) { + if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) + { return NULL; } FExposureSettings *settings = ue_py_check_struct(py_settings); - if (!settings) { + if (!settings) + { return PyErr_Format(PyExc_Exception, "argument is not a FExposureSettings"); } @@ -73,20 +78,25 @@ static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPytho return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEditorViewport *self, PyObject * args) +{ float x = 0, y = 0, z = 0; FVector vec; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFVector *ue_py_vec = py_ue_is_fvector(PyTuple_GetItem(args, 0)); - if (!ue_py_vec) { + if (!ue_py_vec) + { return PyErr_Format(PyExc_Exception, "argument is not a FVector"); } vec = ue_py_vec->vec; } - else { - if (!PyArg_ParseTuple(args, "fff", &x, &y, &z)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &x, &y, &z)) + { return nullptr;; } vec.X = x; @@ -100,20 +110,25 @@ static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEdi return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEditorViewport *self, PyObject * args) +{ float roll = 0, pitch = 0, yaw = 0; FRotator rot; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(PyTuple_GetItem(args, 0)); - if (!ue_py_rot) { + if (!ue_py_rot) + { return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); } rot = ue_py_rot->rot; } - else { - if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + { return nullptr;; } rot.Roll = roll; @@ -127,9 +142,11 @@ static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEdi return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:simulate", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:simulate", &py_bool)) + { return NULL; } @@ -138,7 +155,8 @@ static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewpo Py_RETURN_NONE; } -static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_obj; if (!PyArg_ParseTuple(args, "O", &py_obj)) @@ -154,20 +172,25 @@ static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEdito return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonEditorViewport *self, PyObject * args) +{ float roll = 0, pitch = 0, yaw = 0; FRotator rot; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(PyTuple_GetItem(args, 0)); - if (!ue_py_rot) { + if (!ue_py_rot) + { return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); } rot = ue_py_rot->rot; } - else { - if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + { return nullptr;; } rot.Roll = roll; @@ -181,7 +204,8 @@ static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonE return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEditorViewport *self, PyObject * args) +{ float brightness; if (!PyArg_ParseTuple(args, "f", &brightness)) @@ -193,7 +217,8 @@ static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEd return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPythonEditorViewport *self, PyObject * args) +{ float brightness; if (!PyArg_ParseTuple(args, "f", &brightness)) @@ -205,16 +230,12 @@ static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPython return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_get_light(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_get_light(ue_PySPythonEditorViewport *self, PyObject * args) +{ UDirectionalLightComponent *light = sw_python_editor_viewport->GetPreviewScene()->DirectionalLight; - ue_PyUObject *ret = ue_get_python_wrapper(light); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(light); } static PyMethodDef ue_PySPythonEditorViewport_methods[] = { @@ -234,10 +255,12 @@ static PyMethodDef ue_PySPythonEditorViewport_methods[] = { { NULL } /* Sentinel */ }; -class FPythonEditorViewportClient : public FEditorViewportClient { +class FPythonEditorViewportClient : public FEditorViewportClient +{ public: FPythonEditorViewportClient(FEditorModeTools * InModeTools, FPreviewScene * InPreviewScene, const TWeakPtr & InEditorViewportWidget) : - FEditorViewportClient(InModeTools, InPreviewScene, InEditorViewportWidget) { + FEditorViewportClient(InModeTools, InPreviewScene, InEditorViewportWidget) + { EngineShowFlags.SetSelection(true); EngineShowFlags.SetSelectionOutline(true); @@ -247,14 +270,17 @@ class FPythonEditorViewportClient : public FEditorViewportClient { SelectedActor = nullptr; } - virtual void ProcessClick(FSceneView & InView, HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) { - if (!HitProxy) { + virtual void ProcessClick(FSceneView & InView, HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) + { + if (!HitProxy) + { GEditor->SelectNone(true, true, true); SelectedActor = nullptr; return; } - if (HitProxy->IsA(HActor::StaticGetType())) { + if (HitProxy->IsA(HActor::StaticGetType())) + { HActor *h_actor = (HActor *)HitProxy; GEditor->SelectNone(true, true, true); GEditor->SelectActor(h_actor->Actor, true, true); @@ -263,35 +289,42 @@ class FPythonEditorViewportClient : public FEditorViewportClient { SetWidgetMode(FWidget::WM_Translate); } - else if (HitProxy->IsA(HWidgetAxis::StaticGetType())) { + else if (HitProxy->IsA(HWidgetAxis::StaticGetType())) + { // TODO do something ? } - else { + else + { GEditor->SelectNone(true, true, true); SelectedActor = nullptr; } } - virtual FWidget::EWidgetMode GetWidgetMode() const { + virtual FWidget::EWidgetMode GetWidgetMode() const + { if (SelectedActor == nullptr) return FWidget::WM_None; return FEditorViewportClient::GetWidgetMode(); } - virtual FMatrix GetWidgetCoordSystem() const { + virtual FMatrix GetWidgetCoordSystem() const + { if (!SelectedActor) return FMatrix::Identity; return FRotationMatrix(SelectedActor->GetActorTransform().Rotator()); } - virtual FVector GetWidgetLocation() const { + virtual FVector GetWidgetLocation() const + { if (!SelectedActor) return FVector(0, 0, 0); return SelectedActor->GetActorLocation(); } - virtual bool InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector &Scale) { - if (SelectedActor && !bAltPressed) { + virtual bool InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector &Scale) + { + if (SelectedActor && !bAltPressed) + { SelectedActor->AddActorWorldOffset(Drag); SelectedActor->AddActorWorldRotation(Rot); SelectedActor->SetActorScale3D(SelectedActor->GetActorScale3D() + Scale); @@ -302,11 +335,13 @@ class FPythonEditorViewportClient : public FEditorViewportClient { return false; } - virtual void TrackingStarted(const FInputEventState & InInpuState, bool bIsDragginWidget, bool bNudge) { + virtual void TrackingStarted(const FInputEventState & InInpuState, bool bIsDragginWidget, bool bNudge) + { bAltPressed = InInpuState.IsAltButtonPressed(); } - virtual void ResetCamera() { + virtual void ResetCamera() + { if (!SelectedActor) return; FocusViewportOnBox(SelectedActor->GetComponentsBoundingBox()); @@ -318,11 +353,13 @@ class FPythonEditorViewportClient : public FEditorViewportClient { AActor *SelectedActor; }; -UWorld *SPythonEditorViewport::GetPythonWorld() { +UWorld *SPythonEditorViewport::GetPythonWorld() +{ return GetWorld(); } -TSharedRef SPythonEditorViewport::MakeEditorViewportClient() { +TSharedRef SPythonEditorViewport::MakeEditorViewportClient() +{ PreviewScene = new FPreviewScene(); @@ -345,23 +382,28 @@ TSharedRef SPythonEditorViewport::MakeEditorViewportClien return client.ToSharedRef(); } -TSharedPtr SPythonEditorViewport::GetExtenders() const { +TSharedPtr SPythonEditorViewport::GetExtenders() const +{ TSharedPtr Result(MakeShareable(new FExtender())); return Result; } -TSharedRef SPythonEditorViewport::GetViewportWidget() { +TSharedRef SPythonEditorViewport::GetViewportWidget() +{ return SharedThis(this); } -TSharedPtr SPythonEditorViewport::MakeViewportToolbar() { +TSharedPtr SPythonEditorViewport::MakeViewportToolbar() +{ return SNew(SCommonEditorViewportToolbarBase, SharedThis(this)); } -void SPythonEditorViewport::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { +void SPythonEditorViewport::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ SEditorViewport::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); - if (bSimulate) { + if (bSimulate) + { GetWorld()->Tick(ELevelTick::LEVELTICK_All, InDeltaTime); } } @@ -397,12 +439,14 @@ PyTypeObject ue_PySPythonEditorViewportType = { ue_PySPythonEditorViewport_methods, /* tp_methods */ }; -static int ue_py_spython_editor_viewport_init(ue_PySPythonEditorViewport *self, PyObject *args, PyObject *kwargs) { +static int ue_py_spython_editor_viewport_init(ue_PySPythonEditorViewport *self, PyObject *args, PyObject *kwargs) +{ ue_py_snew_simple(SPythonEditorViewport, s_editor_viewport.s_compound_widget.s_widget); return 0; } -void ue_python_init_spython_editor_viewport(PyObject *ue_module) { +void ue_python_init_spython_editor_viewport(PyObject *ue_module) +{ ue_PySPythonEditorViewportType.tp_init = (initproc)ue_py_spython_editor_viewport_init; diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 0006d588c..b437f35a0 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -78,11 +78,7 @@ PyObject *py_unreal_engine_get_editor_world(PyObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "no GEditor found"); UWorld *world = GEditor->GetEditorWorldContext().World(); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(world); } PyObject *py_unreal_engine_console_exec(PyObject * self, PyObject * args) @@ -100,8 +96,7 @@ PyObject *py_unreal_engine_console_exec(PyObject * self, PyObject * args) GEditor->Exec(GEditor->GetEditorWorldContext().World(), UTF8_TO_TCHAR(command), *GLog); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_unreal_engine_allow_actor_script_execution_in_editor(PyObject * self, PyObject * args) @@ -144,7 +139,7 @@ PyObject *py_unreal_engine_editor_get_selected_actors(PyObject * self, PyObject if (!obj->IsA()) continue; AActor *actor = (AActor *)obj; - ue_PyUObject *item = ue_get_python_wrapper(actor); + ue_PyUObject *item = ue_get_python_uobject(actor); if (item) PyList_Append(actors, (PyObject *)item); } @@ -345,9 +340,11 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); if (u_class) { - ue_PyUObject *py_obj = ue_get_python_wrapper(u_class); - if (py_obj) - Py_INCREF(py_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_class); + if (!py_obj) + { + return PyErr_Format(PyExc_Exception, "invalid uobject"); + } factory_class = (UClass *)py_obj->ue_object; } } @@ -417,11 +414,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) { UObject *object = objects[0]; - ue_PyUObject *ret = ue_get_python_wrapper(object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(object); } else if (objects.Num() > 1) { @@ -429,9 +422,13 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) for (UObject *object : objects) { - ue_PyUObject *ret = ue_get_python_wrapper(object); + ue_PyUObject *ret = ue_get_python_uobject(object); if (!ret) + { + Py_DECREF(assets_list); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); + } + PyList_Append(assets_list, (PyObject *)ret); } @@ -439,8 +436,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_unreal_engine_editor_tick(PyObject * self, PyObject * args) @@ -521,11 +517,7 @@ PyObject *py_unreal_engine_get_asset(PyObject * self, PyObject * args) FAssetData asset = AssetRegistryModule.Get().GetAssetByObjectPath(UTF8_TO_TCHAR(path)); if (!asset.IsValid()) return PyErr_Format(PyExc_Exception, "unable to find asset %s", path); - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(asset.GetAsset()); } PyObject *py_unreal_engine_find_asset(PyObject * self, PyObject * args) @@ -547,11 +539,7 @@ PyObject *py_unreal_engine_find_asset(PyObject * self, PyObject * args) Py_INCREF(Py_None); return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(asset.GetAsset()); } PyObject *py_unreal_engine_get_asset_referencers(PyObject * self, PyObject * args) @@ -686,11 +674,7 @@ PyObject *py_unreal_engine_duplicate_asset(PyObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "unable to duplicate asset %s", path); } - ue_PyUObject *ret = ue_get_python_wrapper(new_asset); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_asset); } PyObject *py_unreal_engine_delete_asset(PyObject * self, PyObject * args) @@ -801,7 +785,7 @@ PyObject *py_unreal_engine_get_assets(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -852,8 +836,9 @@ PyObject *py_unreal_engine_get_assets_by_filter(PyObject * self, PyObject * args } else { - ret = (PyObject *)ue_get_python_wrapper(asset.GetAsset()); + ret = (PyObject *)ue_get_python_uobject(asset.GetAsset()); } + if (ret) { PyList_Append(assets_list, ret); @@ -951,7 +936,7 @@ PyObject *py_unreal_engine_get_assets_by_class(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -979,7 +964,7 @@ PyObject *py_unreal_engine_get_selected_assets(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -1113,40 +1098,36 @@ PyObject *py_unreal_engine_create_blueprint(PyObject * self, PyObject * args) outer->MarkPackageDirty(); - ue_PyUObject *ret = ue_get_python_wrapper(bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(bp); } PyObject *py_unreal_engine_get_blueprint_hierarchy_from_class(PyObject * self, PyObject * args) { - PyObject *py_class; - if (!PyArg_ParseTuple(args, "O:get_blueprint_hierarchy_from_class", &py_class)) - { - return NULL; - } + PyObject *py_class; + if (!PyArg_ParseTuple(args, "O:get_blueprint_hierarchy_from_class", &py_class)) + { + return NULL; + } - UClass* u_class = ue_py_check_type(py_class); - if (!u_class) - { - return PyErr_Format(PyExc_Exception, "argument is not a UClass"); - } + UClass* u_class = ue_py_check_type(py_class); + if (!u_class) + { + return PyErr_Format(PyExc_Exception, "argument is not a UClass"); + } - TArray outBPs; - UBlueprint::GetBlueprintHierarchyFromClass(u_class, outBPs); + TArray outBPs; + UBlueprint::GetBlueprintHierarchyFromClass(u_class, outBPs); - PyObject *py_bpClasses = PyList_New(0); + PyObject *py_bpClasses = PyList_New(0); - for (UBlueprint* bpClass : outBPs) { - ue_PyUObject *item = ue_get_python_wrapper(bpClass); - if (item) - PyList_Append(py_bpClasses, (PyObject *)item); - } - return py_bpClasses; + for (UBlueprint* bpClass : outBPs) + { + ue_PyUObject *item = ue_get_python_uobject(bpClass); + if (item) + PyList_Append(py_bpClasses, (PyObject *)item); + } + return py_bpClasses; } PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) @@ -1170,12 +1151,7 @@ PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) UBlueprint *reloaded_bp = FKismetEditorUtilities::ReloadBlueprint(bp); - ue_PyUObject *ret = ue_get_python_wrapper(reloaded_bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(reloaded_bp); } PyObject *py_unreal_engine_compile_blueprint(PyObject * self, PyObject * args) @@ -1236,12 +1212,7 @@ PyObject *py_unreal_engine_replace_blueprint(PyObject * self, PyObject * args) UBlueprint *replaced_bp = FKismetEditorUtilities::ReplaceBlueprint(bp, bp_new); - ue_PyUObject *ret = ue_get_python_wrapper(replaced_bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(replaced_bp); } PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject * self, PyObject * args) @@ -1270,12 +1241,7 @@ PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject * self, PyObject UBlueprint *bp = FKismetEditorUtilities::CreateBlueprintFromActor(UTF8_TO_TCHAR(name), actor, true); - ue_PyUObject *ret = ue_get_python_wrapper(bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(bp); } PyObject *py_unreal_engine_get_blueprint_components(PyObject * self, PyObject * args) @@ -1297,7 +1263,7 @@ PyObject *py_unreal_engine_get_blueprint_components(PyObject * self, PyObject * for (USCS_Node *node : bp->SimpleConstructionScript->GetAllNodes()) { - ue_PyUObject *item = ue_get_python_wrapper(node->ComponentTemplate); + ue_PyUObject *item = ue_get_python_uobject(node->ComponentTemplate); if (item) PyList_Append(py_list, (PyObject *)item); @@ -1379,12 +1345,7 @@ PyObject *py_unreal_engine_add_component_to_blueprint(PyObject * self, PyObject bp->SimpleConstructionScript->AddNode(node); } - ue_PyUObject *ret = ue_get_python_wrapper(node->ComponentTemplate); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(node->ComponentTemplate); } PyObject *py_unreal_engine_blueprint_add_member_variable(PyObject * self, PyObject * args) @@ -1443,7 +1404,7 @@ PyObject *py_unreal_engine_blueprint_set_variable_visibility(PyObject * self, Py if (!PyArg_ParseTuple(args, "OsO:blueprint_set_variable_visibility", &py_blueprint, &name, &visibility)) { return NULL; - } +} if (!ue_is_pyuobject(py_blueprint)) { @@ -1493,11 +1454,7 @@ PyObject *py_unreal_engine_blueprint_add_new_timeline(PyObject * self, PyObject return PyErr_Format(PyExc_Exception, "unable to add new timeline %s", name); } - ue_PyUObject *ret = ue_get_python_wrapper(timeline); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(timeline); } PyObject *py_unreal_engine_blueprint_add_function(PyObject * self, PyObject * args) @@ -1523,11 +1480,7 @@ PyObject *py_unreal_engine_blueprint_add_function(PyObject * self, PyObject * ar UEdGraph *graph = FBlueprintEditorUtils::CreateNewGraph(bp, FName(UTF8_TO_TCHAR(name)), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(bp, graph, true, nullptr); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); } PyObject *py_unreal_engine_blueprint_add_event_dispatcher(PyObject * self, PyObject * args) @@ -1574,11 +1527,7 @@ PyObject *py_unreal_engine_blueprint_add_event_dispatcher(PyObject * self, PyObj bp->DelegateSignatureGraphs.Add(graph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); } PyObject *py_unreal_engine_blueprint_mark_as_structurally_modified(PyObject * self, PyObject * args) @@ -1623,40 +1572,36 @@ PyObject *py_unreal_engine_blueprint_add_ubergraph_page(PyObject * self, PyObjec UEdGraph *graph = FBlueprintEditorUtils::CreateNewGraph(bp, FName(UTF8_TO_TCHAR(name)), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddUbergraphPage(bp, graph); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); } PyObject *py_unreal_engine_blueprint_get_all_graphs(PyObject * self, PyObject * args) { - PyObject *py_blueprint; + PyObject *py_blueprint; - if (!PyArg_ParseTuple(args, "O:blueprint_get_all_graphs", &py_blueprint)) - { - return nullptr; - } + if (!PyArg_ParseTuple(args, "O:blueprint_get_all_graphs", &py_blueprint)) + { + return nullptr; + } - UBlueprint *bp = ue_py_check_type(py_blueprint); - if (!bp) - return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); + UBlueprint *bp = ue_py_check_type(py_blueprint); + if (!bp) + return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); - PyObject *py_graphs = PyList_New(0); + PyObject *py_graphs = PyList_New(0); - TArray graphs; + TArray graphs; - bp->GetAllGraphs(graphs); + bp->GetAllGraphs(graphs); - for (UEdGraph *graph : graphs) - { - ue_PyUObject *item = ue_get_python_wrapper(graph); - if (item) - PyList_Append(py_graphs, (PyObject *)item); - } + for (UEdGraph *graph : graphs) + { + ue_PyUObject *item = ue_get_python_uobject(graph); + if (item) + PyList_Append(py_graphs, (PyObject *)item); + } - return py_graphs; + return py_graphs; } PyObject *py_unreal_engine_create_new_graph(PyObject * self, PyObject * args) @@ -2267,23 +2212,23 @@ PyObject *py_unreal_engine_register_settings(PyObject * self, PyObject * args) PyObject * py_unreal_engine_show_viewer(PyObject * self, PyObject * args) { - char *container_name; - char *category_name; - char *section_name; + char *container_name; + char *category_name; + char *section_name; - if (!PyArg_ParseTuple(args, "sss:register_settings", &container_name, &category_name, §ion_name)) - return nullptr; + if (!PyArg_ParseTuple(args, "sss:register_settings", &container_name, &category_name, §ion_name)) + return nullptr; - if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) - { - SettingsModule->ShowViewer(container_name, category_name, section_name); - } - else - { - return PyErr_Format(PyExc_Exception, "unable to find the Settings Module"); - } + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->ShowViewer(container_name, category_name, section_name); + } + else + { + return PyErr_Format(PyExc_Exception, "unable to find the Settings Module"); + } - Py_RETURN_NONE; + Py_RETURN_NONE; } PyObject *py_unreal_engine_unregister_settings(PyObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 72c68696f..38bdc356e 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -228,11 +228,7 @@ PyObject *py_unreal_engine_create_world(PyObject * self, PyObject * args) UWorld *world = UWorld::CreateWorld((EWorldType::Type)world_type, false); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(world); } PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) @@ -248,11 +244,7 @@ PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) if (!u_class) return PyErr_Format(PyExc_Exception, "unable to find class %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_class); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class); } PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) @@ -268,12 +260,7 @@ PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) if (!u_enum) return PyErr_Format(PyExc_Exception, "unable to find enum %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_enum); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_enum); } PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) @@ -289,35 +276,31 @@ PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) if (!u_package) return PyErr_Format(PyExc_Exception, "unable to load package %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } #if WITH_EDITOR PyObject *py_unreal_engine_unload_package(PyObject * self, PyObject * args) { - PyObject *obj; - if (!PyArg_ParseTuple(args, "O:unload_package", &obj)) - { - return NULL; - } - - UPackage* packageToUnload = ue_py_check_type(obj); - if (!packageToUnload) - { - return PyErr_Format(PyExc_Exception, "argument is not a UPackage"); - } - - FText outErrorMsg; - if (!PackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) - { - return PyErr_Format(PyExc_Exception, TCHAR_TO_UTF8(*outErrorMsg.ToString())); - } - - Py_RETURN_NONE; + PyObject *obj; + if (!PyArg_ParseTuple(args, "O:unload_package", &obj)) + { + return NULL; + } + + UPackage* packageToUnload = ue_py_check_type(obj); + if (!packageToUnload) + { + return PyErr_Format(PyExc_Exception, "argument is not a UPackage"); + } + + FText outErrorMsg; + if (!PackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) + { + return PyErr_Format(PyExc_Exception, TCHAR_TO_UTF8(*outErrorMsg.ToString())); + } + + Py_RETURN_NONE; } #endif @@ -339,11 +322,7 @@ PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) if (!u_class) return PyErr_Format(PyExc_Exception, "unable to find class %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_class); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class); } PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) @@ -364,11 +343,7 @@ PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) if (!u_enum) return PyErr_Format(PyExc_Exception, "unable to find enum %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_enum); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_enum); } PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) @@ -384,12 +359,7 @@ PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) if (!u_struct) return PyErr_Format(PyExc_Exception, "unable to find struct %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_struct); } PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) @@ -554,7 +524,7 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) PyObject *obj; PyObject *py_outer = NULL; char *name = nullptr; - uint64 flags = (uint64)(RF_Public | RF_Standalone); + uint64 flags = (uint64)(RF_Public | RF_Standalone); if (!PyArg_ParseTuple(args, "O|OsK:new_object", &obj, &py_outer, &name, &flags)) { return NULL; @@ -681,7 +651,7 @@ PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) for (TObjectIterator Itr; Itr; ++Itr) { - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -723,7 +693,7 @@ PyObject *py_unreal_engine_tobject_iterator(PyObject * self, PyObject * args) if (!(*Itr)->IsA(u_class)) continue; - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -1149,7 +1119,7 @@ PyObject *py_unreal_engine_open_directory_dialog(PyObject *self, PyObject * args PyObject *py_unreal_engine_open_font_dialog(PyObject *self, PyObject * args) { - + IDesktopPlatform *DesktopPlatform = FDesktopPlatformModule::Get(); if (!DesktopPlatform) return PyErr_Format(PyExc_Exception, "unable to get reference to DesktopPlatform module"); diff --git a/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp b/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp index 415724572..bee1b113a 100644 --- a/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp +++ b/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp @@ -245,12 +245,10 @@ static PyObject *py_ue_iplugin_get_requires_build_platform(ue_PyIPlugin *self, v { if (self->plugin->GetDescriptor().bRequiresBuildPlatform) { - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } static PyGetSetDef ue_PyIPlugin_getseters[] = { diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 3a98b9750..ae9f906ee 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1472,7 +1472,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k } else { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()), class_key, value))) + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, value))) { unreal_engine_py_log_error(); return -1; @@ -1482,7 +1482,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k } else if (py_obj->ue_object->IsA()) { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()), class_key, value))) + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, value))) { unreal_engine_py_log_error(); return -1; @@ -1515,7 +1515,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k else { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()), class_key, first_item))) + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, first_item))) { unreal_engine_py_log_error(); return -1; @@ -1525,7 +1525,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k } else if (py_obj->ue_object->IsA()) { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()), class_key, first_item))) + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, first_item))) { unreal_engine_py_log_error(); return -1; @@ -1560,12 +1560,12 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k } else { - first_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()); + first_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); } } else if (py_obj->ue_object->IsA()) { - first_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()); + first_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); } ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; @@ -1578,12 +1578,12 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k } else { - second_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()); + second_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); } } else if (py_obj2->ue_object->IsA()) { - second_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()); + second_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); } if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) @@ -1697,7 +1697,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k if (UPythonClass *u_py_class_casted = Cast(u_class)) { - ue_PyUObject *new_self = ue_get_python_wrapper(ObjectInitializer.GetObj()); + ue_PyUObject *new_self = ue_get_python_uobject(ObjectInitializer.GetObj()); if (!new_self) { unreal_engine_py_log_error(); @@ -1797,7 +1797,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k if (self->py_dict) { - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); if (!new_default_self) { @@ -1837,7 +1837,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k // add default uproperties values if (py_additional_properties) { - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); if (!new_default_self) { unreal_engine_py_log_error(); @@ -1863,7 +1863,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k // fake initializer FObjectInitializer initializer(new_u_py_class->ClassDefaultObject, nullptr, false, true); new_u_py_class->SetPyConstructor(py_init); - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); if (!new_default_self) { @@ -2122,16 +2122,15 @@ ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) //Py_INCREF(ue_py_object); return ue_py_object; } - Py_INCREF(it->second); return it->second; } -ue_PyUObject *ue_get_python_uobject_noinc(UObject *ue_obj) +ue_PyUObject *ue_get_python_uobject_inc(UObject *ue_obj) { ue_PyUObject *ret = ue_get_python_uobject(ue_obj); if (ret) { - Py_DECREF(ret); + Py_INCREF(ret); } return ret; } @@ -2973,7 +2972,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class { if (component->GetFName() == FName(*parts[0])) { - if (!ue_bind_pyevent(ue_get_python_wrapper(component), parts[1], item, true)) + if (!ue_bind_pyevent(ue_get_python_uobject(component), parts[1], item, true)) { unreal_engine_py_log_error(); } diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 844288c61..f481dd784 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -58,11 +58,7 @@ static PyObject *py_ue_uscriptstruct_fields(ue_PyUScriptStruct *self, PyObject * static PyObject *py_ue_uscriptstruct_get_struct(ue_PyUScriptStruct *self, PyObject * args) { - ue_PyUObject *ret = ue_get_python_wrapper(self->u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(self->u_struct); } static PyObject *py_ue_uscriptstruct_clone(ue_PyUScriptStruct *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp b/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp index 7142eceee..3d396e572 100644 --- a/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp @@ -10,11 +10,7 @@ static PyObject *ue_PyUStructsImporter_getattro(ue_PyUStructsImporter *self, PyO if (u_struct) { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_struct); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_struct); } } } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index 260f2915f..e54c7ec00 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -159,7 +159,7 @@ PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) for (UActorComponent *component : actor->GetComponents()) { - ue_PyUObject *py_obj = ue_get_python_uobject_noinc(component); + ue_PyUObject *py_obj = ue_get_python_uobject(component); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -658,7 +658,7 @@ PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args if (u_class) { - py_obj = ue_get_python_uobject_noinc(u_class); + py_obj = ue_get_python_uobject(u_class); } } @@ -707,7 +707,7 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg if (u_class) { - py_obj = ue_get_python_uobject_noinc(u_class); + py_obj = ue_get_python_uobject(u_class); } } @@ -725,8 +725,7 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg for (UActorComponent *component : actor->GetComponentsByClass((UClass *)py_obj->ue_object)) { - // noinc as list will increase refcnt - ue_PyUObject *item = ue_get_python_uobject_noinc(component); + ue_PyUObject *item = ue_get_python_uobject(component); if (item) PyList_Append(components, (PyObject *)item); } @@ -872,7 +871,7 @@ PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) for (AActor *overlapping_actor : overalpping_actors) { - ue_PyUObject *item = ue_get_python_uobject_noinc(overlapping_actor); + ue_PyUObject *item = ue_get_python_uobject(overlapping_actor); if (item) { PyList_Append(py_overlapping_actors, (PyObject *)item); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp index 0115058a6..215b0fb63 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp @@ -15,11 +15,7 @@ PyObject *py_ue_anim_get_skeleton(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)skeleton); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)skeleton); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp index f73b22f47..4fd21b6b0 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp @@ -2,12 +2,14 @@ -PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_location", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_location", &socket_name)) + { return NULL; } @@ -20,12 +22,14 @@ PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) { return py_ue_new_fvector(vec); } -PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_rotation", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_rotation", &socket_name)) + { return NULL; } @@ -38,12 +42,14 @@ PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) { return py_ue_new_frotator(rot); } -PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_transform", &socket_name)) + { return NULL; } @@ -56,12 +62,14 @@ PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_world_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_world_transform", &socket_name)) + { return NULL; } @@ -74,12 +82,14 @@ PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_actor_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_actor_transform", &socket_name)) + { return NULL; } @@ -92,11 +102,13 @@ PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_include_descendants = NULL; - if (!PyArg_ParseTuple(args, "|O:get_all_child_actors", &py_include_descendants)) { + if (!PyArg_ParseTuple(args, "|O:get_all_child_actors", &py_include_descendants)) + { return NULL; } @@ -114,15 +126,17 @@ PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) { TArray children; actor->GetAllChildActors(children, include_descendants); - for (AActor *child : children) { - ue_PyUObject *item = ue_get_python_wrapper(child); + for (AActor *child : children) + { + ue_PyUObject *item = ue_get_python_uobject(child); if (item) PyList_Append(py_children, (PyObject *)item); } return py_children; } -PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -135,15 +149,17 @@ PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) { TArray children; actor->GetAttachedActors(children); - for (AActor *child : children) { - ue_PyUObject *item = ue_get_python_wrapper(child); + for (AActor *child : children) + { + ue_PyUObject *item = ue_get_python_uobject(child); if (item) PyList_Append(py_children, (PyObject *)item); } return py_children; } -PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -153,7 +169,8 @@ PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { int rotation_rule = (int)EAttachmentRule::KeepWorld; int scale_rule = (int)EAttachmentRule::SnapToTarget; PyObject *py_weld = nullptr; - if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_actor", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) { + if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_actor", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) + { return NULL; } @@ -182,7 +199,8 @@ PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -192,7 +210,8 @@ PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) { int rotation_rule = (int)EAttachmentRule::KeepWorld; int scale_rule = (int)EAttachmentRule::SnapToTarget; PyObject *py_weld = nullptr; - if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_component", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) { + if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_component", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) + { return NULL; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp index 3f0c845fd..3dcd6e2a2 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp @@ -35,11 +35,7 @@ PyObject *py_ue_controller_get_hud(ue_PyUObject * self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "uobject is not an AController"); - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)controller->GetHUD()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)controller->GetHUD()); } PyObject *py_ue_controller_unposses(ue_PyUObject * self, PyObject * args) @@ -75,11 +71,7 @@ PyObject *py_ue_get_controlled_pawn(ue_PyUObject * self, PyObject * args) Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(pawn); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(pawn); } PyObject *py_ue_controller_project_world_location_to_screen(ue_PyUObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp index ec76ddd59..900c1434a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp @@ -12,11 +12,7 @@ PyObject *py_ue_create_landscape_info(ue_PyUObject *self, PyObject * args) if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); - ue_PyUObject *ret = ue_get_python_wrapper(landscape->CreateLandscapeInfo()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(landscape->CreateLandscapeInfo()); } PyObject *py_ue_get_landscape_info(ue_PyUObject *self, PyObject * args) @@ -32,11 +28,7 @@ PyObject *py_ue_get_landscape_info(ue_PyUObject *self, PyObject * args) if (!info) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(info); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(info); } PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index a2f90161f..3b23d0189 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -7,13 +7,15 @@ #include "Editor/UnrealEd/Classes/MaterialGraph/MaterialGraphSchema.h" #endif -PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; float scalarValue = 0; - if (!PyArg_ParseTuple(args, "sf:set_material_scalar_parameter", &scalarName, &scalarValue)) { + if (!PyArg_ParseTuple(args, "sf:set_material_scalar_parameter", &scalarName, &scalarValue)) + { return NULL; } @@ -22,38 +24,43 @@ PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * arg bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetScalarParameterValueEditorOnly(parameterName, scalarValue); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetScalarParameterValue(parameterName, scalarValue); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_scalar_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_scalar_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -69,16 +76,19 @@ PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * arg } -PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *switchName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_static_switch_parameter", &switchName)) { + if (!PyArg_ParseTuple(args, "s:get_material_static_switch_parameter", &switchName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -91,40 +101,45 @@ PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObjec FGuid guid; material_instance->GetStaticSwitchParameterValue(parameterName, value, guid); - if (value) { - Py_INCREF(Py_True); - return Py_True; + if (value) + { + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } -PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *vectorName; PyObject *vectorValue = nullptr; - if (!PyArg_ParseTuple(args, "sO:set_material_vector_parameter", &vectorName, &vectorValue)) { + if (!PyArg_ParseTuple(args, "sO:set_material_vector_parameter", &vectorName, &vectorValue)) + { return NULL; } FLinearColor vectorParameter(0, 0, 0, 0); ue_PyFLinearColor *py_vector = py_ue_is_flinearcolor(vectorValue); - if (!py_vector) { + if (!py_vector) + { ue_PyFVector *py_true_vector = py_ue_is_fvector(vectorValue); - if (!py_true_vector) { + if (!py_true_vector) + { return PyErr_Format(PyExc_Exception, "argument must be an FLinearColor or FVector"); } - else { + else + { vectorParameter.R = py_true_vector->vec.X; vectorParameter.G = py_true_vector->vec.Y; vectorParameter.B = py_true_vector->vec.Z; vectorParameter.A = 1; } } - else { + else + { vectorParameter = py_vector->color; } @@ -133,37 +148,42 @@ PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * arg bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetVectorParameterValueEditorOnly(parameterName, vectorParameter); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetVectorParameterValue(parameterName, vectorParameter); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_vector_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_vector_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -179,16 +199,19 @@ PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * arg } -PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *textureName; PyObject *textureObject = nullptr; - if (!PyArg_ParseTuple(args, "sO:set_texture_parameter", &textureName, &textureObject)) { + if (!PyArg_ParseTuple(args, "sO:set_texture_parameter", &textureName, &textureObject)) + { return NULL; } - if (!ue_is_pyuobject(textureObject)) { + if (!ue_is_pyuobject(textureObject)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -203,37 +226,42 @@ PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * ar bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetTextureParameterValueEditorOnly(parameterName, ue_texture); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetTextureParameterValue(parameterName, ue_texture); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_texture_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_texture_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -243,25 +271,23 @@ PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * ar UTexture *texture = nullptr; - if (!material_instance->GetTextureParameterValue(parameterName, texture)) { - Py_INCREF(Py_None); - return Py_None; + if (!material_instance->GetTextureParameterValue(parameterName, texture)) + { + Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } -PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "O:create_material_instance_dynamic", &py_material)) { + if (!PyArg_ParseTuple(args, "O:create_material_instance_dynamic", &py_material)) + { return nullptr; } @@ -271,38 +297,38 @@ PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * UMaterialInstanceDynamic *material_dynamic = UMaterialInstanceDynamic::Create(material_interface, self->ue_object); - ue_PyUObject *ret = ue_get_python_wrapper(material_dynamic); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(material_dynamic); } -PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int index; PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "iO:set_material", &index, &py_material)) { + if (!PyArg_ParseTuple(args, "iO:set_material", &index, &py_material)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UPrimitiveComponent"); } UPrimitiveComponent *component = (UPrimitiveComponent *)self->ue_object; - if (!ue_is_pyuobject(py_material)) { + if (!ue_is_pyuobject(py_material)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)py_material; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInterface"); } @@ -310,29 +336,32 @@ PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) { component->SetMaterial(index, material_interface); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #if WITH_EDITOR -PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "O:set_parent", &py_material)) { + if (!PyArg_ParseTuple(args, "O:set_parent", &py_material)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstanceConstant"); } - if (!ue_is_pyuobject(py_material)) { + if (!ue_is_pyuobject(py_material)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -346,30 +375,33 @@ PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) { material_instance->SetParentEditorOnly(materialInterface); material_instance->PostEditChange(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int lod_index; int material_index; PyObject *py_bool; - if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_collision_for_lod", &lod_index, &material_index, &py_bool)) { + if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_collision_for_lod", &lod_index, &material_index, &py_bool)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a StaticMesh"); } UStaticMesh *mesh = (UStaticMesh *)self->ue_object; bool enabled = false; - if (PyObject_IsTrue(py_bool)) { + if (PyObject_IsTrue(py_bool)) + { enabled = true; } @@ -383,25 +415,29 @@ PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * return Py_None; } -PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int lod_index; int material_index; PyObject *py_bool; - if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_shadow_for_lod", &lod_index, &material_index, &py_bool)) { + if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_shadow_for_lod", &lod_index, &material_index, &py_bool)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a StaticMesh"); } UStaticMesh *mesh = (UStaticMesh *)self->ue_object; bool enabled = false; - if (PyObject_IsTrue(py_bool)) { + if (PyObject_IsTrue(py_bool)) + { enabled = true; } @@ -411,15 +447,16 @@ PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * ar mesh->MarkPackageDirty(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInterface"); } @@ -431,11 +468,7 @@ PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) { if (!graph) return PyErr_Format(PyExc_Exception, "Unable to retrieve/allocate MaterialGraph"); - ue_PyUObject *ret = ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(graph); } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 5e3c79539..249d2dd4c 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -15,11 +15,7 @@ PyObject *py_ue_get_class(ue_PyUObject * self, PyObject * args) ue_py_check(self); - ue_PyUObject *ret = ue_get_python_wrapper(self->ue_object->GetClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(self->ue_object->GetClass()); } PyObject *py_ue_class_generated_by(ue_PyUObject * self, PyObject * args) @@ -35,11 +31,7 @@ PyObject *py_ue_class_generated_by(ue_PyUObject * self, PyObject * args) if (!u_object) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } PyObject *py_ue_class_get_flags(ue_PyUObject * self, PyObject * args) @@ -181,11 +173,7 @@ PyObject *py_ue_get_super_class(ue_PyUObject * self, PyObject * args) u_class = self->ue_object->GetClass(); } - ue_PyUObject *ret = ue_get_python_wrapper(u_class->GetSuperClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class->GetSuperClass()); } PyObject *py_ue_get_outer(ue_PyUObject *self, PyObject * args) @@ -197,11 +185,7 @@ PyObject *py_ue_get_outer(ue_PyUObject *self, PyObject * args) if (!outer) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(outer); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(outer); } PyObject *py_ue_get_outermost(ue_PyUObject *self, PyObject * args) @@ -213,11 +197,7 @@ PyObject *py_ue_get_outermost(ue_PyUObject *self, PyObject * args) if (!outermost) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(outermost); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(outermost); } PyObject *py_ue_conditional_begin_destroy(ue_PyUObject *self, PyObject * args) @@ -538,12 +518,7 @@ PyObject *py_ue_find_function(ue_PyUObject * self, PyObject * args) return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)function); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT((UObject *)function); } #if ENGINE_MINOR_VERSION >= 15 @@ -629,12 +604,12 @@ PyObject *py_ue_get_display_name(ue_PyUObject *self, PyObject * args) ue_py_check(self); #if WITH_EDITOR - if (UClass *uclass = ue_py_check_type(self)) - { - return PyUnicode_FromString(TCHAR_TO_UTF8(*uclass->GetDisplayNameText().ToString())); - } + if (UClass *uclass = ue_py_check_type(self)) + { + return PyUnicode_FromString(TCHAR_TO_UTF8(*uclass->GetDisplayNameText().ToString())); + } - if (AActor *actor = ue_py_check_type(self)) + if (AActor *actor = ue_py_check_type(self)) { return PyUnicode_FromString(TCHAR_TO_UTF8(*actor->GetActorLabel())); } @@ -1091,11 +1066,7 @@ PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); - ue_PyUObject *ret = ue_get_python_wrapper(u_property); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property); } @@ -1153,11 +1124,8 @@ PyObject *py_ue_get_property_class(ue_PyUObject *self, PyObject * args) if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); - ue_PyUObject *ret = ue_get_python_wrapper(u_property->GetClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property->GetClass()); + } PyObject *py_ue_is_rooted(ue_PyUObject *self, PyObject * args) @@ -1596,11 +1564,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) // TODO add default value - ue_PyUObject *ret = ue_get_python_wrapper(u_property); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property); } PyObject *py_ue_as_dict(ue_PyUObject * self, PyObject * args) @@ -1652,11 +1616,7 @@ PyObject *py_ue_get_cdo(ue_PyUObject * self, PyObject * args) UClass *u_class = (UClass *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(u_class->GetDefaultObject()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class->GetDefaultObject()); } @@ -1705,13 +1665,13 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "the object has no associated package, please specify a name"); } if (!has_package) - { - // unmark transient object - if (u_object->HasAnyFlags(RF_Transient)) - { - u_object->ClearFlags(RF_Transient); - u_object->SetFlags(RF_Public | RF_Standalone); - } + { + // unmark transient object + if (u_object->HasAnyFlags(RF_Transient)) + { + u_object->ClearFlags(RF_Transient); + u_object->SetFlags(RF_Public | RF_Standalone); + } } package = (UPackage *)StaticFindObject(nullptr, ANY_PACKAGE, UTF8_TO_TCHAR(name), true); // create a new package if it does not exist @@ -1754,20 +1714,16 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) UE_LOG(LogPython, Warning, TEXT("no file mapped to UPackage %s, setting its FileName to %s"), *package->GetPathName(), *package->FileName.ToString()); } - // NOTE: FileName may not be a fully qualified filepath - if (FPackageName::IsValidLongPackageName(package->FileName.ToString())) - { - package->FileName = *FPackageName::LongPackageNameToFilename(package->GetPathName(), FPackageName::GetAssetPackageExtension()); - } + // NOTE: FileName may not be a fully qualified filepath + if (FPackageName::IsValidLongPackageName(package->FileName.ToString())) + { + package->FileName = *FPackageName::LongPackageNameToFilename(package->GetPathName(), FPackageName::GetAssetPackageExtension()); + } if (UPackage::SavePackage(package, u_object, RF_Public | RF_Standalone, *package->FileName.ToString())) { FAssetRegistryModule::AssetCreated(u_object); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } return PyErr_Format(PyExc_Exception, "unable to save package"); @@ -1874,12 +1830,7 @@ PyObject *py_ue_duplicate(ue_PyUObject * self, PyObject * args) if (!new_asset) return PyErr_Format(PyExc_Exception, "unable to duplicate object"); - ue_PyUObject *ret = ue_get_python_wrapper(new_asset); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(new_asset); } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp index 5b817a2fa..9a2f8124f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp @@ -13,11 +13,7 @@ PyObject *py_ue_pawn_get_controller(ue_PyUObject * self, PyObject * args) APawn *pawn = (APawn *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(pawn->GetController()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(pawn->GetController()); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp index 0852250fe..745c1f426 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp @@ -19,11 +19,7 @@ PyObject *py_ue_get_player_controller(ue_PyUObject *self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "unable to retrieve controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(controller); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller); } PyObject *py_ue_get_player_camera_manager(ue_PyUObject *self, PyObject * args) @@ -45,11 +41,7 @@ PyObject *py_ue_get_player_camera_manager(ue_PyUObject *self, PyObject * args) if (!camera) return PyErr_Format(PyExc_Exception, "unable to retrieve camera manager for controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(camera); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(camera); } PyObject *py_ue_get_player_hud(ue_PyUObject *self, PyObject * args) @@ -71,11 +63,7 @@ PyObject *py_ue_get_player_hud(ue_PyUObject *self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "unable to retrieve controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(controller->GetHUD()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller->GetHUD()); } PyObject *py_ue_set_player_hud(ue_PyUObject *self, PyObject * args) @@ -130,11 +118,7 @@ PyObject *py_ue_get_player_pawn(ue_PyUObject *self, PyObject * args) if (!controller->GetPawn()) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(controller->GetPawn()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller->GetPawn()); } PyObject *py_ue_create_player(ue_PyUObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 01564d7a3..d8260d8fb 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -23,12 +23,14 @@ #endif #if WITH_EDITOR -PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_bool = nullptr; - if (!PyArg_ParseTuple(args, "|O:sequencer_changed", &py_bool)) { + if (!PyArg_ParseTuple(args, "|O:sequencer_changed", &py_bool)) + { return NULL; } @@ -39,13 +41,15 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { ULevelSequence *seq = (ULevelSequence *)self->ue_object; - if (py_bool && PyObject_IsTrue(py_bool)) { + if (py_bool && PyObject_IsTrue(py_bool)) + { // try to open the editor for the asset FAssetEditorManager::Get().OpenEditorForAsset(seq); } IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (editor) { + if (editor) + { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; ISequencer *sequencer = toolkit->GetSequencer().Get(); #if ENGINE_MINOR_VERSION < 13 @@ -61,12 +65,14 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { } #endif -PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_possessable_tracks", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_possessable_tracks", &guid)) + { return NULL; } @@ -74,7 +80,8 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } @@ -88,12 +95,15 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return PyErr_Format(PyExc_Exception, "GUID not found"); TArray bindings = scene->GetBindings(); - for (FMovieSceneBinding binding : bindings) { + for (FMovieSceneBinding binding : bindings) + { if (binding.GetObjectGuid() != f_guid) continue; - for (UMovieSceneTrack *track : binding.GetTracks()) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)track); - if (!ret) { + for (UMovieSceneTrack *track : binding.GetTracks()) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)track); + if (!ret) + { Py_DECREF(py_tracks); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -105,12 +115,14 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return py_tracks; } -PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_find_possessable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_find_possessable", &guid)) + { return NULL; } @@ -118,7 +130,8 @@ PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } @@ -130,27 +143,25 @@ PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) UObject *u_obj = nullptr; TArray> u_objects; seq->LocateBoundObjects(f_guid, nullptr, u_objects); - if (u_objects.Num() > 0) { + if (u_objects.Num() > 0) + { u_obj = u_objects[0]; } #endif if (!u_obj) return PyErr_Format(PyExc_Exception, "unable to find uobject with GUID \"%s\"", guid); - PyObject *ret = (PyObject *)ue_get_python_wrapper(u_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(u_obj); } -PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_find_spawnable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_find_spawnable", &guid)) + { return NULL; } @@ -158,7 +169,8 @@ PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } @@ -171,12 +183,14 @@ PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_possessable", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_possessable", &py_obj)) + { return NULL; } @@ -195,7 +209,8 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { name = actor->GetActorLabel(); FGuid new_guid = seq->MovieScene->AddPossessable(name, py_ue_obj->ue_object->GetClass()); - if (!new_guid.IsValid()) { + if (!new_guid.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to possess object"); } @@ -204,12 +219,14 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } -PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_actor", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_actor", &py_obj)) + { return NULL; } @@ -232,12 +249,14 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { FAssetEditorManager::Get().OpenEditorForAsset(seq); IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (editor) { + if (editor) + { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; ISequencer *sequencer = toolkit->GetSequencer().Get(); sequencer->AddActors(actors); } - else { + else + { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); } @@ -248,63 +267,71 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { #else FGuid new_guid = seq->FindPossessableObjectId(u_obj, u_obj.GetWorld()); #endif - if (!new_guid.IsValid()) { + if (!new_guid.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to find guid"); } return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } -PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * args) +{ - ue_py_check(self); + ue_py_check(self); - PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_actor_component", &py_obj)) { - return NULL; - } + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:sequencer_add_actor_component", &py_obj)) + { + return NULL; + } - if (!self->ue_object->IsA()) - return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); - ue_PyUObject *py_ue_obj = ue_is_pyuobject(py_obj); - if (!py_ue_obj) - return PyErr_Format(PyExc_Exception, "argument is not a uobject"); + ue_PyUObject *py_ue_obj = ue_is_pyuobject(py_obj); + if (!py_ue_obj) + return PyErr_Format(PyExc_Exception, "argument is not a uobject"); - if (!py_ue_obj->ue_object->IsA()) - return PyErr_Format(PyExc_Exception, "argument is not an actorcomponent"); + if (!py_ue_obj->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "argument is not an actorcomponent"); - ULevelSequence *seq = (ULevelSequence *)self->ue_object; + ULevelSequence *seq = (ULevelSequence *)self->ue_object; - UActorComponent* actorComponent = (UActorComponent *)py_ue_obj->ue_object; + UActorComponent* actorComponent = (UActorComponent *)py_ue_obj->ue_object; - // try to open the editor for the asset - FAssetEditorManager::Get().OpenEditorForAsset(seq); + // try to open the editor for the asset + FAssetEditorManager::Get().OpenEditorForAsset(seq); - IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - FGuid new_guid; - if (editor) { - FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; - ISequencer *sequencer = toolkit->GetSequencer().Get(); - new_guid = sequencer->GetHandleToObject(actorComponent); - } - else { - return PyErr_Format(PyExc_Exception, "unable to access sequencer"); - } + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + FGuid new_guid; + if (editor) + { + FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; + ISequencer *sequencer = toolkit->GetSequencer().Get(); + new_guid = sequencer->GetHandleToObject(actorComponent); + } + else + { + return PyErr_Format(PyExc_Exception, "unable to access sequencer"); + } - if (!new_guid.IsValid()) { - return PyErr_Format(PyExc_Exception, "unable to find guid"); - } + if (!new_guid.IsValid()) + { + return PyErr_Format(PyExc_Exception, "unable to find guid"); + } - return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); + return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } -PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_make_new_spawnable", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_make_new_spawnable", &py_obj)) + { return NULL; } @@ -326,7 +353,8 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args FAssetEditorManager::Get().OpenEditorForAsset(seq); IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (!editor) { + if (!editor) + { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); } @@ -338,7 +366,8 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args } #endif -PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -352,9 +381,11 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { TArray tracks = scene->GetMasterTracks(); - for (UMovieSceneTrack *track : tracks) { + for (UMovieSceneTrack *track : tracks) + { ue_PyUObject *ret = ue_get_python_wrapper((UObject *)track); - if (!ret) { + if (!ret) + { Py_DECREF(py_tracks); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -364,7 +395,8 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { return py_tracks; } -PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -375,7 +407,8 @@ PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { UMovieScene *scene = seq->GetMovieScene(); PyObject *py_possessables = PyList_New(0); - for (int32 i = 0; i < scene->GetPossessableCount(); i++) { + for (int32 i = 0; i < scene->GetPossessableCount(); i++) + { FMovieScenePossessable possessable = scene->GetPossessable(i); PyObject *py_possessable = py_ue_new_uscriptstruct(possessable.StaticStruct(), (uint8 *)&possessable); PyList_Append(py_possessables, py_possessable); @@ -384,7 +417,8 @@ PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { return py_possessables; } -PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -395,7 +429,8 @@ PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) UMovieScene *scene = seq->GetMovieScene(); PyObject *py_possessables = PyList_New(0); - for (int32 i = 0; i < scene->GetPossessableCount(); i++) { + for (int32 i = 0; i < scene->GetPossessableCount(); i++) + { FMovieScenePossessable possessable = scene->GetPossessable(i); PyObject *py_possessable = PyUnicode_FromString(TCHAR_TO_UTF8(*possessable.GetGuid().ToString())); PyList_Append(py_possessables, py_possessable); @@ -405,7 +440,8 @@ PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) } #if WITH_EDITOR -PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -413,19 +449,23 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); PyObject *py_obj = nullptr; - if (!PyArg_ParseTuple(args, "|O:sequencer_folders", &py_obj)) { + if (!PyArg_ParseTuple(args, "|O:sequencer_folders", &py_obj)) + { return NULL; } UMovieSceneFolder *parent_folder = nullptr; - if (py_obj) { + if (py_obj) + { ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } parent_folder = Cast(ue_py_obj->ue_object); - if (!parent_folder) { + if (!parent_folder) + { return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneFolder"); } } @@ -438,13 +478,16 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { TArray folders; if (!parent_folder) folders = scene->GetRootFolders(); - else { + else + { folders = parent_folder->GetChildFolders(); } - for (UMovieSceneFolder *folder : folders) { + for (UMovieSceneFolder *folder : folders) + { ue_PyUObject *ret = ue_get_python_wrapper((UObject *)folder); - if (!ret) { + if (!ret) + { Py_DECREF(py_folders); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -454,7 +497,8 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { return py_folders; } -PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -463,19 +507,23 @@ PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { PyObject *py_obj = nullptr; char *name; - if (!PyArg_ParseTuple(args, "s|O:sequencer_create_folder", &name, &py_obj)) { + if (!PyArg_ParseTuple(args, "s|O:sequencer_create_folder", &name, &py_obj)) + { return NULL; } UMovieSceneFolder *parent_folder = nullptr; - if (py_obj) { + if (py_obj) + { ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } parent_folder = Cast(ue_py_obj->ue_object); - if (!parent_folder) { + if (!parent_folder) + { return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneFolder"); } } @@ -486,24 +534,23 @@ PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { UMovieSceneFolder *new_folder = NewObject(scene, NAME_None, RF_Transactional); new_folder->SetFolderName(FName(name)); - if (parent_folder) { + if (parent_folder) + { parent_folder->Modify(); parent_folder->AddChildFolder(new_folder); } - else { + else + { scene->Modify(); scene->GetRootFolders().Add(new_folder); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(new_folder); - if (!ret) { - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - } - return ret; + Py_RETURN_UOBJECT(new_folder); } #endif -PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -517,9 +564,11 @@ PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { TArray sections = scene->GetAllSections(); - for (UMovieSceneSection *section : sections) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)section); - if (!ret) { + for (UMovieSceneSection *section : sections) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)section); + if (!ret) + { Py_DECREF(py_sections); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -529,7 +578,8 @@ PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { return py_sections; } -PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -542,9 +592,11 @@ PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { TArray sections = track->GetAllSections(); - for (UMovieSceneSection *section : sections) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)section); - if (!ret) { + for (UMovieSceneSection *section : sections) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)section); + if (!ret) + { Py_DECREF(py_sections); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -555,7 +607,8 @@ PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -568,21 +621,18 @@ PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) track->AddSection(*new_section); - PyObject *ret = (PyObject *)ue_get_python_wrapper(new_section); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(new_section); } #endif -PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_master_track", &obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_master_track", &obj)) + { return NULL; } @@ -608,23 +658,20 @@ PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) if (!track) return PyErr_Format(PyExc_Exception, "unable to create new master track"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } #if WITH_EDITOR -PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); float time; PyObject *py_value; int interpolation = 0; - if (!PyArg_ParseTuple(args, "fO|i:sequencer_section_add_key", &time, &py_value, &interpolation)) { + if (!PyArg_ParseTuple(args, "fO|i:sequencer_section_add_key", &time, &py_value, &interpolation)) + { return NULL; } @@ -633,30 +680,34 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { UMovieSceneSection *section = (UMovieSceneSection *)self->ue_object; - if (auto section_float = Cast(section)) { - if (PyNumber_Check(py_value)) { + if (auto section_float = Cast(section)) + { + if (PyNumber_Check(py_value)) + { PyObject *f_value = PyNumber_Float(py_value); float value = PyFloat_AsDouble(f_value); Py_DECREF(f_value); section_float->AddKey(time, value, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_bool = Cast(section)) { - if (PyBool_Check(py_value)) { + else if (auto section_bool = Cast(section)) + { + if (PyBool_Check(py_value)) + { bool value = false; if (PyObject_IsTrue(py_value)) value = true; section_bool->AddKey(time, value, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_transform = Cast(section)) { - if (ue_PyFTransform *py_transform = py_ue_is_ftransform(py_value)) { + else if (auto section_transform = Cast(section)) + { + if (ue_PyFTransform *py_transform = py_ue_is_ftransform(py_value)) + { bool unwind = false; FTransform transform = py_transform->transform; @@ -682,13 +733,14 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { section_transform->AddKey(time, sy, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, sz, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_vector = Cast(section)) { - if (ue_PyFVector *py_vector = py_ue_is_fvector(py_value)) { + else if (auto section_vector = Cast(section)) + { + if (ue_PyFVector *py_vector = py_ue_is_fvector(py_value)) + { FVector vec = py_vector->vec; FVectorKey vx = FVectorKey(EKeyVectorChannel::X, vec.X); FVectorKey vy = FVectorKey(EKeyVectorChannel::Y, vec.Y); @@ -698,15 +750,15 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { section_vector->AddKey(time, vy, (EMovieSceneKeyInterpolation)interpolation); section_vector->AddKey(time, vz, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } return PyErr_Format(PyExc_Exception, "unsupported section type: %s", TCHAR_TO_UTF8(*section->GetClass()->GetName())); } -PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -718,46 +770,46 @@ PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * ar UMovieSceneTrack *track = scene->AddCameraCutTrack(UMovieSceneCameraCutTrack::StaticClass()); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } -PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_remove_possessable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_remove_possessable", &guid)) + { return nullptr; } - + ULevelSequence *seq = ue_py_check_type(self); if (!seq) return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); UMovieScene *scene = seq->GetMovieScene(); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } - + if (scene->RemovePossessable(f_guid)) Py_RETURN_TRUE; Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_remove_spawnable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_remove_spawnable", &guid)) + { return nullptr; } @@ -767,7 +819,8 @@ PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) UMovieScene *scene = seq->GetMovieScene(); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } @@ -777,7 +830,8 @@ PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -791,12 +845,14 @@ PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * Py_RETURN_NONE; } -PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "O:sequencer_remove_master_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_master_track", &py_track)) + { return nullptr; } @@ -816,12 +872,14 @@ PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * arg Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "O:sequencer_remove_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_track", &py_track)) + { return nullptr; } @@ -843,13 +901,15 @@ PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) { #endif -PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *guid; - if (!PyArg_ParseTuple(args, "Os:sequencer_add_track", &obj, &guid)) { + if (!PyArg_ParseTuple(args, "Os:sequencer_add_track", &obj, &guid)) + { return NULL; } @@ -872,49 +932,50 @@ PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneTrack class"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } UMovieSceneTrack *track = scene->AddTrack(u_class, f_guid); if (!track) return PyErr_Format(PyExc_Exception, "unable to create new track"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } #if WITH_EDITOR // smart functions allowing the set/get of display names to various sequencer objects -PyObject *py_ue_sequencer_set_display_name(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_set_display_name(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *name; - if (!PyArg_ParseTuple(args, "s:sequencer_set_display_name", &name)) { + if (!PyArg_ParseTuple(args, "s:sequencer_set_display_name", &name)) + { return NULL; } - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMovieSceneNameableTrack *track = (UMovieSceneNameableTrack *)self->ue_object; track->SetDisplayName(FText::FromString(UTF8_TO_TCHAR(name))); } - else { + else + { return PyErr_Format(PyExc_Exception, "the uobject does not expose the SetDisplayName() method"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_sequencer_get_display_name(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_get_display_name(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMovieSceneNameableTrack *track = (UMovieSceneNameableTrack *)self->ue_object; FText name = track->GetDefaultDisplayName(); return PyUnicode_FromString(TCHAR_TO_UTF8(*name.ToString())); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp index 71c0d88e1..d6f2041da 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp @@ -1,25 +1,30 @@ #include "UnrealEnginePythonPrivatePCH.h" -static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &teleport_physics) { +static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &teleport_physics) +{ PyObject *py_vec = nullptr; float x = 0, y = 0, z = 0; PyObject *py_sweep = nullptr; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|OO", &py_vec, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|OO", &py_vec, &py_sweep, &py_teleport_physics)) + { return false; } ue_PyFVector *ue_py_vec = py_ue_is_fvector(py_vec); - if (!ue_py_vec) { - if (!PyArg_ParseTuple(args, "fff|OO", &x, &y, &z, &py_sweep, &py_teleport_physics)) { + if (!ue_py_vec) + { + if (!PyArg_ParseTuple(args, "fff|OO", &x, &y, &z, &py_sweep, &py_teleport_physics)) + { return false; } vec.X = x; vec.Y = y; vec.Z = z; } - else { + else + { vec = ue_py_vec->vec; } @@ -29,34 +34,42 @@ static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &t return true; } -static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool &teleport_physics) { +static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool &teleport_physics) +{ PyObject *py_rotation = nullptr; float roll = 0, pitch = 0, yaw = 0; PyObject *py_sweep = nullptr; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|OO", &py_rotation, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|OO", &py_rotation, &py_sweep, &py_teleport_physics)) + { PyErr_Clear(); - if (!PyArg_ParseTuple(args, "fff|OO", &roll, &pitch, &yaw, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "fff|OO", &roll, &pitch, &yaw, &py_sweep, &py_teleport_physics)) + { return false; } } - if (py_rotation) { + if (py_rotation) + { ue_PyFQuat *ue_py_quat = py_ue_is_fquat(py_rotation); - if (ue_py_quat) { + if (ue_py_quat) + { quat = ue_py_quat->quat; } - else { + else + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(py_rotation); - if (!ue_py_rot) { + if (!ue_py_rot) + { PyErr_SetString(PyExc_Exception, "argument is not a FQuat or FRotator"); return false; } quat = ue_py_rot->rot.Quaternion(); } } - else { + else + { quat = FQuat(FRotator(pitch, yaw, roll)); } @@ -66,33 +79,41 @@ static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool & return true; } -static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &teleport_physics) { +static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &teleport_physics) +{ PyObject *py_rotation = nullptr; float roll = 0, pitch = 0, yaw = 0; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|O", &py_rotation, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|O", &py_rotation, &py_teleport_physics)) + { PyErr_Clear(); - if (!PyArg_ParseTuple(args, "fff|O", &roll, &pitch, &yaw, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "fff|O", &roll, &pitch, &yaw, &py_teleport_physics)) + { return false; } } - if (py_rotation) { + if (py_rotation) + { ue_PyFQuat *ue_py_quat = py_ue_is_fquat(py_rotation); - if (ue_py_quat) { + if (ue_py_quat) + { quat = ue_py_quat->quat; } - else { + else + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(py_rotation); - if (!ue_py_rot) { + if (!ue_py_rot) + { PyErr_SetString(PyExc_Exception, "argument is not a FQuat or FRotator"); return false; } quat = ue_py_rot->rot.Quaternion(); } } - else { + else + { quat = FQuat(FRotator(pitch, yaw, roll)); } @@ -101,7 +122,8 @@ static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &tele return true; } -PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -113,7 +135,8 @@ PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -125,7 +148,8 @@ PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -137,7 +161,8 @@ PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -149,7 +174,8 @@ PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -161,7 +187,8 @@ PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -173,7 +200,8 @@ PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -187,7 +215,8 @@ PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -204,8 +233,10 @@ PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { FHitResult hit; bool success = actor->SetActorLocation(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { - if (success) { + if (!sweep) + { + if (success) + { Py_RETURN_TRUE; } Py_RETURN_FALSE; @@ -214,7 +245,8 @@ PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { return Py_BuildValue("(OO)", success ? Py_True : Py_False, py_ue_new_fhitresult(hit)); } -PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -231,14 +263,16 @@ PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorWorldOffset(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); } -PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -255,7 +289,8 @@ PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorLocalOffset(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } @@ -263,7 +298,8 @@ PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -279,14 +315,16 @@ PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorWorldRotation(quat, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); } -PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -302,7 +340,8 @@ PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorLocalRotation(quat, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } @@ -310,7 +349,8 @@ PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -329,7 +369,8 @@ PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -341,14 +382,16 @@ PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) { AActor *actor = ue_get_actor(self); if (!actor) return PyErr_Format(PyExc_Exception, "uobject is not an actor or a component"); - if (actor->SetActorRotation(quat, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None)) { + if (actor->SetActorRotation(quat, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None)) + { Py_RETURN_TRUE; } Py_RETURN_FALSE; } -PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -366,61 +409,74 @@ PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_world_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetComponentLocation(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FRotator rot = ((USceneComponent *)self->ue_object)->GetComponentRotation(); return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_world_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetComponentScale(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeLocation; return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FRotator rot = ((USceneComponent *)self->ue_object)->RelativeRotation; return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeScale3D; return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); USceneComponent *component = ue_py_check_type(self); if (!component) @@ -430,7 +486,8 @@ PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(t); } -PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); USceneComponent *component = ue_py_check_type(self); if (!component) @@ -440,34 +497,41 @@ PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(t); } -PyObject *py_ue_get_forward_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_forward_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetForwardVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_up_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_up_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetUpVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_right_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_right_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetRightVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; bool sweep; @@ -477,9 +541,11 @@ PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { FHitResult hit; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldLocation(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); @@ -487,12 +553,14 @@ PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldRotation(rot); Py_INCREF(Py_None); return Py_None; @@ -500,13 +568,15 @@ PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldScale3D(vec); Py_INCREF(Py_None); return Py_None; @@ -514,7 +584,8 @@ PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FTransform t; @@ -530,7 +601,8 @@ PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FTransform t; @@ -546,13 +618,15 @@ PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeLocation(vec); Py_INCREF(Py_None); return Py_None; @@ -560,12 +634,14 @@ PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeRotation(rot); Py_INCREF(Py_None); return Py_None; @@ -573,13 +649,15 @@ PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_relative_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeScale3D(vec); Py_INCREF(Py_None); return Py_None; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 12e7bc0a2..364947cc3 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp @@ -6,38 +6,36 @@ #include "Editor/UnrealEd/Public/LevelEditorViewport.h" #endif -PyObject *py_unreal_engine_get_game_viewport_client(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_game_viewport_client(PyObject * self, PyObject * args) +{ UGameViewportClient *viewport_client = GEngine->GameViewport; - if (!viewport_client) { + if (!viewport_client) + { return PyErr_Format(PyExc_Exception, "no engine GameViewport found"); } - ue_PyUObject *ret = ue_get_python_wrapper(GEngine->GameViewport); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(GEngine->GameViewport); } #if WITH_EDITOR -PyObject *py_unreal_engine_get_editor_pie_game_viewport_client(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_editor_pie_game_viewport_client(PyObject * self, PyObject * args) +{ UGameViewportClient *viewport_client = GEditor->GameViewport; - if (!viewport_client) { + if (!viewport_client) + { return PyErr_Format(PyExc_Exception, "no editor GameViewport found"); } - ue_PyUObject *ret = ue_get_python_wrapper(viewport_client); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(viewport_client); } -PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:editor_set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:editor_set_view_mode", &mode)) + { return NULL; } @@ -49,15 +47,17 @@ PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); viewport_client.SetViewMode((EViewModeIndex)mode); - + Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * args) +{ int speed; - if (!PyArg_ParseTuple(args, "f:editor_set_camera_speed", &speed)) { + if (!PyArg_ParseTuple(args, "f:editor_set_camera_speed", &speed)) + { return NULL; } @@ -73,11 +73,13 @@ PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * a Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * args) +{ PyObject *py_vector; - if (!PyArg_ParseTuple(args, "O:editor_set_view_location", &py_vector)) { + if (!PyArg_ParseTuple(args, "O:editor_set_view_location", &py_vector)) + { return NULL; } @@ -97,11 +99,13 @@ PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * args) +{ PyObject *py_rotator; - if (!PyArg_ParseTuple(args, "O:editor_set_view_rotation", &py_rotator)) { + if (!PyArg_ParseTuple(args, "O:editor_set_view_rotation", &py_rotator)) + { return NULL; } @@ -123,19 +127,22 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * #endif -PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_widget; int z_order = 0; - if (!PyArg_ParseTuple(args, "O|i:add_viewport_widget_content", &py_widget, &z_order)) { + if (!PyArg_ParseTuple(args, "O|i:add_viewport_widget_content", &py_widget, &z_order)) + { return NULL; } UGameViewportClient *viewport = ue_py_check_type(self); - if (!viewport) { + if (!viewport) + { UWorld *world = ue_py_check_type(self); if (!world) return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient or a UWorld"); @@ -145,7 +152,8 @@ PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) } ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } // Do not increment reference count as it is assumed this function is used in a PyComponent/PyActor/ that can holds reference to @@ -154,17 +162,18 @@ PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) viewport->AddViewportWidgetContent(py_swidget->s_widget, z_order); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_widget; - if (!PyArg_ParseTuple(args, "O:remove_viewport_widget_content", &py_widget)) { + if (!PyArg_ParseTuple(args, "O:remove_viewport_widget_content", &py_widget)) + { return NULL; } @@ -173,18 +182,19 @@ PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * ar return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient"); ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_DECREF(py_swidget); viewport->RemoveViewportWidgetContent(py_swidget->s_widget); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -192,10 +202,9 @@ PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) if (!viewport) return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient"); - + viewport->RemoveAllViewportWidgets(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp index 0f833c15d..bac67a6d8 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp @@ -4,7 +4,8 @@ #include "Runtime/UMG/Public/Components/Widget.h" -PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -17,7 +18,8 @@ PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) { return (PyObject *)s_widget; } -PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -40,9 +42,5 @@ PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) { if (!widget) return PyErr_Format(PyExc_Exception, "unable to create new widget"); - ue_PyUObject *ret = ue_get_python_wrapper(widget); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(widget); } From 05cfcb45de6f107d9bf6e0ee95d1bd9a3f06be3d Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sun, 11 Feb 2018 16:19:32 +0100 Subject: [PATCH 45/94] third round of refactoring --- .../UnrealEnginePython/Private/UEPyEngine.cpp | 58 ++++--------------- .../Private/UEPyEnumsImporter.cpp | 27 +++++---- .../Private/UObject/UEPySequencer.cpp | 4 +- .../Private/UObject/UEPySkeletal.cpp | 6 +- .../Private/UObject/UEPyTexture.cpp | 24 ++------ .../Private/UObject/UEPyWorld.cpp | 27 +++------ .../Private/Wrappers/UEPyFAssetData.cpp | 8 +-- .../Private/Wrappers/UEPyFHitResult.cpp | 48 ++++++++------- .../UnrealEnginePython.Build.cs | 29 +--------- 9 files changed, 69 insertions(+), 162 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 38bdc356e..45c5ff937 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -380,12 +380,7 @@ PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) if (!u_struct) return PyErr_Format(PyExc_Exception, "unable to find struct %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_struct); } @@ -421,11 +416,7 @@ PyObject *py_unreal_engine_load_object(PyObject * self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to load object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } @@ -510,11 +501,7 @@ PyObject *py_unreal_engine_find_object(PyObject * self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to find object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } @@ -569,11 +556,7 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) new_object->PostLoad(); - ue_PyUObject *ret = ue_get_python_wrapper(new_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_object); } PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) @@ -601,11 +584,7 @@ PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) if (!mutable_object) return PyErr_Format(PyExc_Exception, "unable to create object"); - ue_PyUObject *ret = ue_get_python_wrapper(mutable_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(mutable_object); } @@ -637,11 +616,7 @@ PyObject *py_unreal_engine_new_class(PyObject * self, PyObject * args) if (!new_object) return PyErr_Format(PyExc_Exception, "unable to create UClass"); - ue_PyUObject *ret = ue_get_python_wrapper(new_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_object); } PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) @@ -664,7 +639,7 @@ PyObject *py_unreal_engine_all_worlds(PyObject * self, PyObject * args) PyObject *ret = PyList_New(0); for (TObjectIterator Itr; Itr; ++Itr) { - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -997,11 +972,7 @@ PyObject *py_unreal_engine_create_package(PyObject *self, PyObject * args) u_package->FullyLoad(); u_package->MarkPackageDirty(); - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args) @@ -1027,21 +998,12 @@ PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args u_package->MarkPackageDirty(); } - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } PyObject *py_unreal_engine_get_transient_package(PyObject *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(GetTransientPackage()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(GetTransientPackage()); } PyObject *py_unreal_engine_open_file_dialog(PyObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp b/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp index 798c9c7eb..3792694fd 100644 --- a/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp @@ -1,20 +1,21 @@ #include "UnrealEnginePythonPrivatePCH.h" -static PyObject *ue_PyEnumsImporter_getattro(ue_PyEnumsImporter *self, PyObject *attr_name) { +static PyObject *ue_PyEnumsImporter_getattro(ue_PyEnumsImporter *self, PyObject *attr_name) +{ PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); - if (!ret) { - if (PyUnicodeOrString_Check(attr_name)) { + if (!ret) + { + if (PyUnicodeOrString_Check(attr_name)) + { char *attr = PyUnicode_AsUTF8(attr_name); - if (attr[0] != '_') { + if (attr[0] != '_') + { UEnum *u_enum = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(attr)); - if (u_enum) { + if (u_enum) + { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_enum); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_enum); } } } @@ -55,7 +56,8 @@ static PyTypeObject ue_PyEnumsImporterType = { 0, }; -void ue_python_init_enumsimporter(PyObject *ue_module) { +void ue_python_init_enumsimporter(PyObject *ue_module) +{ ue_PyEnumsImporterType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyEnumsImporterType) < 0) @@ -65,7 +67,8 @@ void ue_python_init_enumsimporter(PyObject *ue_module) { PyModule_AddObject(ue_module, "EnumsImporter", (PyObject *)&ue_PyEnumsImporterType); } -PyObject *py_ue_new_enumsimporter() { +PyObject *py_ue_new_enumsimporter() +{ ue_PyEnumsImporter *ret = (ue_PyEnumsImporter *)PyObject_New(ue_PyEnumsImporter, &ue_PyEnumsImporterType); return (PyObject *)ret; } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index d8260d8fb..96ae7a414 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -383,7 +383,7 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) for (UMovieSceneTrack *track : tracks) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)track); + ue_PyUObject *ret = ue_get_python_uobject((UObject *)track); if (!ret) { Py_DECREF(py_tracks); @@ -485,7 +485,7 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) for (UMovieSceneFolder *folder : folders) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)folder); + ue_PyUObject *ret = ue_get_python_uobject((UObject *)folder); if (!ret) { Py_DECREF(py_folders); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp index fa9218a5c..3d11753e7 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp @@ -26,11 +26,7 @@ PyObject *py_ue_get_anim_instance(ue_PyUObject *self, PyObject * args) return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)anim); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)anim); } PyObject *py_ue_set_skeletal_mesh(ue_PyUObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp index 71699e980..52ec3c289 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp @@ -268,11 +268,7 @@ PyObject *py_unreal_engine_create_checkerboard_texture(PyObject * self, PyObject UTexture2D *texture = FImageUtils::CreateCheckerboardTexture(color_one->color, color_two->color, checker_size); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * args) @@ -292,11 +288,7 @@ PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * texture->UpdateResource(); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * self, PyObject * args) @@ -316,11 +308,7 @@ PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * s texture->InitCustomFormat(width, height, (EPixelFormat)format, py_linear && PyObject_IsTrue(py_linear)); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } #if WITH_EDITOR @@ -366,11 +354,7 @@ PyObject *py_unreal_engine_create_texture(PyObject * self, PyObject * args) if (!texture) return PyErr_Format(PyExc_Exception, "unable to create texture"); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp index e6bbd0945..ce33397d0 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp @@ -111,7 +111,7 @@ PyObject *py_ue_all_objects(ue_PyUObject * self, PyObject * args) UObject *u_obj = *Itr; if (u_obj->GetWorld() != world) continue; - ue_PyUObject *py_obj = ue_get_python_wrapper(u_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_obj); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -133,7 +133,7 @@ PyObject *py_ue_all_actors(ue_PyUObject * self, PyObject * args) for (TActorIterator Itr(world); Itr; ++Itr) { UObject *u_obj = *Itr; - ue_PyUObject *py_obj = ue_get_python_wrapper(u_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_obj); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -164,11 +164,7 @@ PyObject *py_ue_find_object(ue_PyUObject *self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to find object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } PyObject *py_ue_get_world(ue_PyUObject *self, PyObject * args) @@ -180,12 +176,7 @@ PyObject *py_ue_get_world(ue_PyUObject *self, PyObject * args) if (!world) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(world); } PyObject *py_ue_get_game_viewport(ue_PyUObject *self, PyObject * args) @@ -271,7 +262,7 @@ PyObject *py_ue_get_levels(ue_PyUObject * self, PyObject * args) for (ULevel *level : world->GetLevels()) { - ue_PyUObject *py_obj = ue_get_python_wrapper(level); + ue_PyUObject *py_obj = ue_get_python_uobject(level); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -293,11 +284,7 @@ PyObject *py_ue_get_current_level(ue_PyUObject *self, PyObject * args) if (!level) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(level); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(level); } PyObject *py_ue_set_current_level(ue_PyUObject *self, PyObject * args) @@ -371,7 +358,7 @@ PyObject *py_ue_add_foliage_asset(ue_PyUObject *self, PyObject * args) { foliage_type = (UFoliageType *)u_object; ifa->AddFoliageType(foliage_type); - + } if (!foliage_type) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp index 0ddca80ba..ea4251cc0 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp @@ -6,13 +6,7 @@ static PyObject *py_ue_fassetdata_get_asset(ue_PyFAssetData *self, PyObject * args) { - PyObject *ret = (PyObject *)ue_get_python_wrapper(self->asset_data.GetAsset()); - if (!ret) - { - return PyErr_Format(PyExc_Exception, "unable to get UObject from asset"); - } - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(self->asset_data.GetAsset()); } static PyObject *py_ue_fassetdata_is_asset_loaded(ue_PyFAssetData *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp index ea978bc11..6aeb0ce83 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp @@ -1,7 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" -static PyObject *py_ue_fhitresult_get_reversed_hit(ue_PyFHitResult *self, PyObject * args) { +static PyObject *py_ue_fhitresult_get_reversed_hit(ue_PyFHitResult *self, PyObject * args) +{ return py_ue_new_fhitresult(FHitResult::GetReversedHit(self->hit)); } @@ -11,45 +12,49 @@ static PyMethodDef ue_PyFHitResult_methods[] = { { NULL } /* Sentinel */ }; -static PyObject *py_ue_fhitresult_get_location(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_location(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.Location); } -static PyObject *py_ue_fhitresult_get_normal(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_normal(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.Normal); } -static PyObject *py_ue_fhitresult_get_impact_point(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_impact_point(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.ImpactPoint); } -static PyObject *py_ue_fhitresult_get_impact_normal(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_impact_normal(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.ImpactNormal); } -static PyObject *py_ue_fhitresult_get_distance(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_distance(ue_PyFHitResult *self, void *closure) +{ return PyFloat_FromDouble(self->hit.Distance); } -static PyObject *py_ue_fhitresult_get_time(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_time(ue_PyFHitResult *self, void *closure) +{ return PyFloat_FromDouble(self->hit.Time); } -static PyObject *py_ue_fhitresult_get_bone_name(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_bone_name(ue_PyFHitResult *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->hit.BoneName.ToString())); } -static PyObject *py_ue_fhitresult_get_actor(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_actor(ue_PyFHitResult *self, void *closure) +{ AActor *actor = self->hit.Actor.Get(); - if (!actor) { - Py_INCREF(Py_None); - return Py_None; + if (!actor) + { + Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(actor); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(actor); } @@ -106,7 +111,8 @@ static PyTypeObject ue_PyFHitResultType = { ue_PyFHitResult_getseters, }; -void ue_python_init_fhitresult(PyObject *ue_module) { +void ue_python_init_fhitresult(PyObject *ue_module) +{ ue_PyFHitResultType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyFHitResultType) < 0) @@ -116,13 +122,15 @@ void ue_python_init_fhitresult(PyObject *ue_module) { PyModule_AddObject(ue_module, "FHitResult", (PyObject *)&ue_PyFHitResultType); } -PyObject *py_ue_new_fhitresult(FHitResult hit) { +PyObject *py_ue_new_fhitresult(FHitResult hit) +{ ue_PyFHitResult *ret = (ue_PyFHitResult *)PyObject_New(ue_PyFHitResult, &ue_PyFHitResultType); ret->hit = hit; return (PyObject *)ret; } -ue_PyFHitResult *py_ue_is_fhitresult(PyObject *obj) { +ue_PyFHitResult *py_ue_is_fhitresult(PyObject *obj) +{ if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFHitResultType)) return nullptr; return (ue_PyFHitResult *)obj; diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index d9bb0eb31..ec41b0dec 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -185,33 +185,6 @@ public UnrealEnginePython(TargetInfo Target) string libPath = GetWindowsPythonLibFile(pythonHome); PublicLibraryPaths.Add(Path.GetDirectoryName(libPath)); PublicAdditionalLibraries.Add(libPath); - - // copy the dlls into the plugins dlls folder, so they don't have to be on the path - string dllsDir = Path.Combine(ModuleDirectory, "../../Binaries", Target.Platform == UnrealTargetPlatform.Win32 ? "Win32" : "Win64"); - try - { - string[] dllsToCopy = - { - "python3.dll", - "python36.dll" - }; - foreach (string dllToCopy in dllsToCopy) - { - // If the dll exist, make sure to set attributes so they are actually accessible - if (File.Exists(Path.Combine(dllsDir, dllToCopy))) - { - File.SetAttributes(Path.Combine(dllsDir, dllToCopy), FileAttributes.Normal); - } - - File.Copy(Path.Combine(pythonHome, dllToCopy), Path.Combine(dllsDir, dllToCopy), true); - File.SetAttributes(Path.Combine(dllsDir, dllToCopy), FileAttributes.Normal); - } - } - catch (System.IO.IOException) { } - catch (System.UnauthorizedAccessException) - { - System.Console.WriteLine("WARNING: Unable to copy python dlls, they are probably in use..."); - } } else if (Target.Platform == UnrealTargetPlatform.Mac) { @@ -397,7 +370,7 @@ private string GetWindowsPythonLibFile(string basePath) // first try with python3 for (int i = 9; i >= 0; i--) { - string fileName = string.Format("python3{0}.lib", i); + string fileName = string.Format("python3{0}_d.lib", i); string fullPath = Path.Combine(basePath, "libs", fileName); if (File.Exists(fullPath)) { From e57d0b6387b7a6bbc86478ebfb4ea1d7817f6eaf Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sun, 11 Feb 2018 18:46:13 +0100 Subject: [PATCH 46/94] last round of refactoring --- Source/UnrealEnginePython/Private/PyActor.cpp | 2 +- .../Private/Slate/UEPyFPointerEvent.cpp | 28 ++++-- .../Private/Slate/UEPyFPointerEvent.h | 1 + .../Private/Slate/UEPySWidget.cpp | 87 +++++++++++++------ .../UnrealEnginePython/Private/UEPyModule.h | 37 +++++--- .../Private/UnrealEnginePythonPrivatePCH.h | 2 +- .../UnrealEnginePython.Build.cs | 2 +- 7 files changed, 111 insertions(+), 48 deletions(-) diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 05f1ddac1..f442b2cfe 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -298,6 +298,6 @@ APyActor::~APyActor() UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif - // this could trigger the distruction of the python/uobject mapper + // this could trigger the destruction of the python/uobject mapper Py_XDECREF(py_uobject); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp index 033c0e3b3..dc987bb13 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp @@ -2,27 +2,32 @@ #include "UEPyFPointerEvent.h" -static PyObject *py_ue_fpointer_event_get_effecting_button(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_effecting_button(ue_PyFPointerEvent *self, PyObject * args) +{ FKey key = self->pointer.GetEffectingButton(); return py_ue_new_uscriptstruct(FKey::StaticStruct(), (uint8*)&key); } -static PyObject *py_ue_fpointer_event_get_effecting_button_name(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_effecting_button_name(ue_PyFPointerEvent *self, PyObject * args) +{ FKey key = self->pointer.GetEffectingButton(); return PyUnicode_FromString(TCHAR_TO_UTF8(*key.ToString())); } -static PyObject *py_ue_fpointer_event_get_wheel_delta(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_wheel_delta(ue_PyFPointerEvent *self, PyObject * args) +{ float delta = self->pointer.GetWheelDelta(); return PyFloat_FromDouble(delta); } -static PyObject *py_ue_fpointer_event_get_cursor_delta(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_cursor_delta(ue_PyFPointerEvent *self, PyObject * args) +{ FVector2D delta = self->pointer.GetCursorDelta(); return Py_BuildValue((char *)"(ff)", delta.X, delta.Y); } -static PyObject *py_ue_fpointer_event_get_screen_space_position(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_screen_space_position(ue_PyFPointerEvent *self, PyObject * args) +{ FVector2D pos = self->pointer.GetScreenSpacePosition(); return Py_BuildValue((char *)"(ff)", pos.X, pos.Y); } @@ -73,7 +78,8 @@ static PyTypeObject ue_PyFPointerEventType = { ue_PyFPointerEvent_methods, /* tp_methods */ }; -void ue_python_init_fpointer_event(PyObject *ue_module) { +void ue_python_init_fpointer_event(PyObject *ue_module) +{ ue_PyFPointerEventType.tp_base = &ue_PyFInputEventType; @@ -84,9 +90,17 @@ void ue_python_init_fpointer_event(PyObject *ue_module) { PyModule_AddObject(ue_module, "FPointerEvent", (PyObject *)&ue_PyFPointerEventType); } -PyObject *py_ue_new_fpointer_event(FPointerEvent pointer) { +PyObject *py_ue_new_fpointer_event(FPointerEvent pointer) +{ ue_PyFPointerEvent *ret = (ue_PyFPointerEvent *)PyObject_New(ue_PyFPointerEvent, &ue_PyFPointerEventType); new(&ret->pointer) FPointerEvent(pointer); new(&ret->f_input.input) FInputEvent(pointer); return (PyObject *)ret; } + +ue_PyFPointerEvent *py_ue_is_fpointer_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFPointerEventType)) + return nullptr; + return (ue_PyFPointerEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h index 0a6821a29..e66ad673f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h @@ -15,3 +15,4 @@ typedef struct { void ue_python_init_fpointer_event(PyObject *); PyObject *py_ue_new_fpointer_event(FPointerEvent); +ue_PyFPointerEvent *py_ue_is_fpointer_event(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 5c138d06a..36ebad3ef 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -36,34 +36,34 @@ static PyObject *py_ue_swidget_set_visibility(ue_PySWidget *self, PyObject * arg return nullptr; } - if (!PyNumber_Check(py_object)) - { - return PyErr_Format(PyExc_Exception, "argument is not a ESlateVisibility"); - } - - PyObject *py_value = PyNumber_Long(py_object); - ESlateVisibility slateVisibility = (ESlateVisibility)PyLong_AsLong(py_value); - Py_DECREF(py_value); - - EVisibility visibility; - switch (slateVisibility) - { - case ESlateVisibility::Visible: - visibility = EVisibility::Visible; - break; - case ESlateVisibility::Collapsed: - visibility = EVisibility::Collapsed; - break; - case ESlateVisibility::Hidden: - visibility = EVisibility::Hidden; - break; - case ESlateVisibility::HitTestInvisible: - visibility = EVisibility::HitTestInvisible; - break; - case ESlateVisibility::SelfHitTestInvisible: - visibility = EVisibility::SelfHitTestInvisible; - break; - } + if (!PyNumber_Check(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a ESlateVisibility"); + } + + PyObject *py_value = PyNumber_Long(py_object); + ESlateVisibility slateVisibility = (ESlateVisibility)PyLong_AsLong(py_value); + Py_DECREF(py_value); + + EVisibility visibility; + switch (slateVisibility) + { + case ESlateVisibility::Visible: + visibility = EVisibility::Visible; + break; + case ESlateVisibility::Collapsed: + visibility = EVisibility::Collapsed; + break; + case ESlateVisibility::Hidden: + visibility = EVisibility::Hidden; + break; + case ESlateVisibility::HitTestInvisible: + visibility = EVisibility::HitTestInvisible; + break; + case ESlateVisibility::SelfHitTestInvisible: + visibility = EVisibility::SelfHitTestInvisible; + break; + } self->s_widget->SetVisibility(visibility); @@ -258,6 +258,36 @@ static PyObject *py_ue_swidget_invalidate(ue_PySWidget *self, PyObject * args) Py_RETURN_NONE; } +static PyObject *py_ue_swidget_on_mouse_button_down(ue_PySWidget *self, PyObject * args) +{ + PyObject *py_geometry; + PyObject *py_pointer_event; + if (!PyArg_ParseTuple(args, "OO:on_mouse_button_down", &py_geometry, &py_pointer_event)) + { + return nullptr; + } + + ue_PyFGeometry *geometry = py_ue_is_fgeometry(py_geometry); + if (!geometry) + { + return PyErr_Format(PyExc_Exception, "argument is not a FGeomtry"); + } + + ue_PyFPointerEvent *pointer = py_ue_is_fpointer_event(py_pointer_event); + if (!pointer) + { + return PyErr_Format(PyExc_Exception, "argument is not a FPointerEvent"); + } + + FReply reply = self->s_widget->OnMouseButtonDown(geometry->geometry, pointer->pointer); + + if (reply.IsEventHandled()) + { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} static PyMethodDef ue_PySWidget_methods[] = { { "get_shared_reference_count", (PyCFunction)py_ue_swidget_get_shared_reference_count, METH_VARARGS, "" }, @@ -276,6 +306,7 @@ static PyMethodDef ue_PySWidget_methods[] = { { "bind_on_mouse_double_click", (PyCFunction)py_ue_swidget_bind_on_mouse_double_click, METH_VARARGS, "" }, { "bind_on_mouse_move", (PyCFunction)py_ue_swidget_bind_on_mouse_move, METH_VARARGS, "" }, #endif + { "on_mouse_button_down", (PyCFunction)py_ue_swidget_on_mouse_button_down, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 4995bafac..3b8a3558b 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -8,10 +8,11 @@ //#include "UEPyModule.generated.h" -typedef struct { +typedef struct +{ PyObject_HEAD - /* Type-specific fields go here. */ - UObject *ue_object; + /* Type-specific fields go here. */ + UObject *ue_object; // reference to proxy class (can be null) PyObject *py_proxy; // list of exposed delegates @@ -45,29 +46,45 @@ UClass *unreal_engine_new_uclass(char *, UClass *); UFunction *unreal_engine_add_function(UClass *, char *, PyObject *, uint32); -template T *ue_py_check_type(PyObject *py_obj) { +template T *ue_py_check_type(PyObject *py_obj) +{ ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { + return nullptr; + } + + if (!ue_py_obj->ue_object || !ue_py_obj->ue_object->IsValidLowLevel() || ue_py_obj->ue_object->IsPendingKillOrUnreachable()) + { + UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), ue_py_obj); return nullptr; } return Cast(ue_py_obj->ue_object); } -template T *ue_py_check_type(ue_PyUObject *py_obj) { +template T *ue_py_check_type(ue_PyUObject *py_obj) +{ + if (!py_obj->ue_object || !py_obj->ue_object->IsValidLowLevel() || py_obj->ue_object->IsPendingKillOrUnreachable()) + { + UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), py_obj); + return nullptr; + } return Cast(py_obj->ue_object); } uint8 *do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct); -template T *ue_py_check_struct(PyObject *py_obj) { - return (T*)do_ue_py_check_struct(py_obj, T::StaticStruct()); +template T *ue_py_check_struct(PyObject *py_obj) +{ + return (T*)do_ue_py_check_struct(py_obj, T::StaticStruct()); } bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct); -template bool ue_py_check_childstruct(PyObject *py_obj) { - return do_ue_py_check_childstruct(py_obj, T::StaticStruct()); +template bool ue_py_check_childstruct(PyObject *py_obj) +{ + return do_ue_py_check_childstruct(py_obj, T::StaticStruct()); } FGuid *ue_py_check_fguid(PyObject *); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index ff5a52252..342c4e96d 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -#define UEPY_MEMORY_DEBUG 1 +//#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index ec41b0dec..623768cd6 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -370,7 +370,7 @@ private string GetWindowsPythonLibFile(string basePath) // first try with python3 for (int i = 9; i >= 0; i--) { - string fileName = string.Format("python3{0}_d.lib", i); + string fileName = string.Format("python3{0}.lib", i); string fullPath = Path.Combine(basePath, "libs", fileName); if (File.Exists(fullPath)) { From feb0ae4315a7de828b1eb6708059f34d4987eaae Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sun, 11 Feb 2018 20:13:48 +0100 Subject: [PATCH 47/94] added FModifierKeysState wrapper --- .../Private/Slate/UEPyFCharacterEvent.cpp | 19 ++- .../Private/Slate/UEPyFModifierKeysState.cpp | 141 ++++++++++++++++++ .../Private/Slate/UEPyFModifierKeysState.h | 15 ++ .../Private/Slate/UEPyFPointerEvent.cpp | 51 +++++++ .../Private/Slate/UEPySWidget.cpp | 38 +++++ .../Private/Slate/UEPySlate.cpp | 1 + .../Private/Slate/UEPySlate.h | 1 + 7 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp create mode 100644 Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp index 68ae13a95..b56af1690 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp @@ -56,16 +56,27 @@ static PyTypeObject ue_PyFCharacterEventType = { static int ue_py_fcharacter_event_init(ue_PyFCharacterEvent *self, PyObject *args, PyObject *kwargs) { char *key; - if (!PyArg_ParseTuple(args, "s", &key)) + PyObject *py_repeat = nullptr; + PyObject *py_modifier = nullptr; + if (!PyArg_ParseTuple(args, "sO|O", &key, &py_repeat, &py_modifier)) { return -1; } - // TODO make it configurable FModifierKeysState modifier; + if (py_modifier) + { + ue_PyFModifierKeysState *f_modifier = py_ue_is_fmodifier_keys_state(py_modifier); + if (!f_modifier) + { + PyErr_SetString(PyExc_Exception, "argument is not a FModifierKeysState"); + return -1; + } + modifier = f_modifier->modifier; + } + - // TODO make repeat configurable - FCharacterEvent Event(*UTF8_TO_TCHAR(key), modifier, 0, false); + FCharacterEvent Event(*UTF8_TO_TCHAR(key), modifier, 0, (py_repeat && PyObject_IsTrue(py_repeat))); new(&self->character_event) FCharacterEvent(Event); new(&self->f_input.input) FInputEvent(Event); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp new file mode 100644 index 000000000..1a4ea4bdd --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp @@ -0,0 +1,141 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPyFModifierKeysState.h" + +static PyObject *py_ue_fmodifier_keys_state_are_caps_locked(ue_PyFModifierKeysState *self, PyObject * args) +{ + if (self->modifier.AreCapsLocked()) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyMethodDef ue_PyFModifierKeysState_methods[] = { + { "are_caps_locked", (PyCFunction)py_ue_fmodifier_keys_state_are_caps_locked, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyFModifierKeysState_str(ue_PyFModifierKeysState *self) +{ + return PyUnicode_FromFormat("", self); +} + +static PyTypeObject ue_PyFModifierKeysStateType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FModifierKeysState", /* tp_name */ + sizeof(ue_PyFModifierKeysState), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyFModifierKeysState_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine FModifierKeysState", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFModifierKeysState_methods, /* tp_methods */ +}; + +static int ue_py_fmodifier_keys_state_init(ue_PyFModifierKeysState *self, PyObject *args, PyObject *kwargs) +{ + static char *kw_names[] = { + (char *)"left_shift_down", + (char *)"right_shift_down", + (char *)"left_control_down", + (char *)"right_control_down", + (char *)"left_alt_down", + (char *)"right_alt_down", + (char *)"left_command_down", + (char *)"right_command_down", + (char *)"are_caps_locked", + NULL }; + + PyObject *py_left_shift_down = nullptr; + PyObject *py_right_shift_down = nullptr; + PyObject *py_left_control_down = nullptr; + PyObject *py_right_control_down = nullptr; + PyObject *py_left_alt_down = nullptr; + PyObject *py_right_alt_down = nullptr; + PyObject *py_left_command_down = nullptr; + PyObject *py_right_command_down = nullptr; + PyObject *py_are_capse_locked = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOOOO", kw_names, + &py_left_shift_down, + &py_right_shift_down, + &py_left_control_down, + &py_right_control_down, + &py_left_alt_down, + &py_right_alt_down, + &py_left_command_down, + &py_right_command_down, + &py_are_capse_locked)) + { + return -1; + } + + bool bInIsLeftShiftDown = (py_left_shift_down && PyObject_IsTrue(py_left_shift_down)); + bool bInIsRightShiftDown = (py_right_shift_down && PyObject_IsTrue(py_right_shift_down)); + bool bInIsLeftControlDown = (py_left_control_down && PyObject_IsTrue(py_left_control_down)); + bool bInIsRightControlDown = (py_right_control_down && PyObject_IsTrue(py_right_control_down)); + bool bInIsLeftAltDown = (py_left_alt_down && PyObject_IsTrue(py_left_alt_down)); + bool bInIsRightAltDown = (py_right_alt_down && PyObject_IsTrue(py_right_alt_down)); + bool bInIsLeftCommandDown = (py_left_command_down && PyObject_IsTrue(py_left_command_down)); + bool bInIsRightCommandDown = (py_right_command_down && PyObject_IsTrue(py_right_command_down)); + bool bInAreCapsLocked = (py_are_capse_locked && PyObject_IsTrue(py_are_capse_locked)); + + new(&self->modifier) FModifierKeysState( + bInIsLeftShiftDown, + bInIsRightShiftDown, + bInIsLeftControlDown, + bInIsRightControlDown, + bInIsLeftAltDown, + bInIsRightAltDown, + bInIsLeftCommandDown, + bInIsRightCommandDown, + bInAreCapsLocked); + + return 0; +} + +void ue_python_init_fmodifier_keys_state(PyObject *ue_module) +{ + + ue_PyFModifierKeysStateType.tp_init = (initproc)ue_py_fmodifier_keys_state_init; + + if (PyType_Ready(&ue_PyFModifierKeysStateType) < 0) + return; + + Py_INCREF(&ue_PyFModifierKeysStateType); + PyModule_AddObject(ue_module, "FModifierKeysState", (PyObject *)&ue_PyFModifierKeysStateType); +} + +PyObject *py_ue_new_fmodifier_keys_state(FModifierKeysState modifier) +{ + ue_PyFModifierKeysState *ret = (ue_PyFModifierKeysState *)PyObject_New(ue_PyFModifierKeysState, &ue_PyFModifierKeysStateType); + new(&ret->modifier) FModifierKeysState(modifier); + return (PyObject *)ret; +} + +ue_PyFModifierKeysState *py_ue_is_fmodifier_keys_state(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFModifierKeysStateType)) + return nullptr; + return (ue_PyFModifierKeysState *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h new file mode 100644 index 000000000..97b5190da --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h @@ -0,0 +1,15 @@ +#pragma once + +#include "UnrealEnginePython.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + FModifierKeysState modifier; +} ue_PyFModifierKeysState; + +void ue_python_init_fmodifier_keys_state(PyObject *); + +PyObject *py_ue_new_fmodifier_keys_state(FModifierKeysState); +ue_PyFModifierKeysState *py_ue_is_fmodifier_keys_state(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp index dc987bb13..54c1b1853 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp @@ -78,10 +78,61 @@ static PyTypeObject ue_PyFPointerEventType = { ue_PyFPointerEvent_methods, /* tp_methods */ }; +static int ue_py_fpointer_event_init(ue_PyFPointerEvent *self, PyObject *args, PyObject *kwargs) +{ + int pointer_index; + float ssp_x; + float ssp_y; + float lssp_x; + float lssp_y; + char *effecting; + float wheel_delta; + PyObject *py_modifier = nullptr; + + if (!PyArg_ParseTuple(args, "i(ff)(ff)s|fO", + &pointer_index, + &ssp_x, + &ssp_y, + &lssp_x, + &lssp_y, + &effecting, + &wheel_delta, + &py_modifier)) + { + return -1; + } + + TSet keys; + + FModifierKeysState modifier; + if (py_modifier) + { + ue_PyFModifierKeysState *f_modifier = py_ue_is_fmodifier_keys_state(py_modifier); + if (!f_modifier) + { + PyErr_SetString(PyExc_Exception, "argument is not a FModifierKeysState"); + return -1; + } + modifier = f_modifier->modifier; + } + + FPointerEvent Event(pointer_index, + FVector2D(ssp_x, ssp_y), + FVector2D(lssp_x, lssp_y), + keys, + FKey(effecting), wheel_delta, modifier); + + new(&self->pointer) FPointerEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + void ue_python_init_fpointer_event(PyObject *ue_module) { ue_PyFPointerEventType.tp_base = &ue_PyFInputEventType; + ue_PyFPointerEventType.tp_init = (initproc)ue_py_fpointer_event_init; if (PyType_Ready(&ue_PyFPointerEventType) < 0) return; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 36ebad3ef..091dc665f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -242,6 +242,11 @@ static PyObject *py_ue_swidget_get_type(ue_PySWidget *self, PyObject * args) return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->s_widget->GetTypeAsString()))); } +static PyObject *py_ue_swidget_get_cached_geometry(ue_PySWidget *self, PyObject * args) +{ + return py_ue_new_fgeometry(self->s_widget->GetCachedGeometry()); +} + static PyObject *py_ue_swidget_get_shared_reference_count(ue_PySWidget *self, PyObject * args) { return PyLong_FromLong(self->s_widget.GetSharedReferenceCount()); @@ -289,8 +294,40 @@ static PyObject *py_ue_swidget_on_mouse_button_down(ue_PySWidget *self, PyObject Py_RETURN_FALSE; } +static PyObject *py_ue_swidget_on_mouse_button_up(ue_PySWidget *self, PyObject * args) +{ + PyObject *py_geometry; + PyObject *py_pointer_event; + if (!PyArg_ParseTuple(args, "OO:on_mouse_button_up", &py_geometry, &py_pointer_event)) + { + return nullptr; + } + + ue_PyFGeometry *geometry = py_ue_is_fgeometry(py_geometry); + if (!geometry) + { + return PyErr_Format(PyExc_Exception, "argument is not a FGeomtry"); + } + + ue_PyFPointerEvent *pointer = py_ue_is_fpointer_event(py_pointer_event); + if (!pointer) + { + return PyErr_Format(PyExc_Exception, "argument is not a FPointerEvent"); + } + + FReply reply = self->s_widget->OnMouseButtonUp(geometry->geometry, pointer->pointer); + + if (reply.IsEventHandled()) + { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + static PyMethodDef ue_PySWidget_methods[] = { { "get_shared_reference_count", (PyCFunction)py_ue_swidget_get_shared_reference_count, METH_VARARGS, "" }, + { "get_cached_geometry", (PyCFunction)py_ue_swidget_get_cached_geometry, METH_VARARGS, "" }, { "get_children", (PyCFunction)py_ue_swidget_get_children, METH_VARARGS, "" }, { "get_type", (PyCFunction)py_ue_swidget_get_type, METH_VARARGS, "" }, { "set_tooltip_text", (PyCFunction)py_ue_swidget_set_tooltip_text, METH_VARARGS, "" }, @@ -307,6 +344,7 @@ static PyMethodDef ue_PySWidget_methods[] = { { "bind_on_mouse_move", (PyCFunction)py_ue_swidget_bind_on_mouse_move, METH_VARARGS, "" }, #endif { "on_mouse_button_down", (PyCFunction)py_ue_swidget_on_mouse_button_down, METH_VARARGS, "" }, + { "on_mouse_button_up", (PyCFunction)py_ue_swidget_on_mouse_button_up, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 664f4d305..100de470d 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -870,6 +870,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_fpointer_event(module); ue_python_init_fkey_event(module); ue_python_init_fcharacter_event(module); + ue_python_init_fmodifier_keys_state(module); ue_python_init_eslate_enums(module); } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 131a372ad..481b453d1 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -74,6 +74,7 @@ #include "UEPyFPointerEvent.h" #include "UEPyFKeyEvent.h" #include "UEPyFCharacterEvent.h" +#include "UEPyFModifierKeysState.h" #if WITH_EDITOR #include "UEPySEditorViewport.h" From 1940a769808fbd8f3508db3068fa54c77eaec1fd Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 12 Feb 2018 16:47:04 +0100 Subject: [PATCH 48/94] more FSlateApplication functions --- .../Private/Slate/UEPyFGeometry.cpp | 7 ++ .../UEPyFSlateApplication.cpp | 65 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp index f4177b3be..81bf1bf47 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp @@ -8,6 +8,12 @@ static PyObject *py_ue_fgeometry_get_local_size(ue_PyFGeometry *self, PyObject * return Py_BuildValue("(ff)", size.X, size.Y); } +static PyObject *py_ue_fgeometry_get_absolute_position(ue_PyFGeometry *self, PyObject * args) +{ + FVector2D size = self->geometry.GetAbsolutePosition(); + return Py_BuildValue("(ff)", size.X, size.Y); +} + static PyObject *py_ue_fgeometry_absolute_to_local(ue_PyFGeometry *self, PyObject * args) { float x, y; @@ -20,6 +26,7 @@ static PyObject *py_ue_fgeometry_absolute_to_local(ue_PyFGeometry *self, PyObjec static PyMethodDef ue_PyFGeometry_methods[] = { { "get_local_size", (PyCFunction)py_ue_fgeometry_get_local_size, METH_VARARGS, "" }, + { "get_absolute_position", (PyCFunction)py_ue_fgeometry_get_absolute_position, METH_VARARGS, "" }, { "absolute_to_local", (PyCFunction)py_ue_fgeometry_absolute_to_local, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp index be833499f..21935b116 100644 --- a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp @@ -14,6 +14,16 @@ static PyObject *py_ue_get_delta_time(PyObject *cls, PyObject * args) return PyFloat_FromDouble(FSlateApplication::Get().GetDeltaTime()); } +static PyObject *py_ue_get_modifier_keys(PyObject *cls, PyObject * args) +{ + return py_ue_new_fmodifier_keys_state(FSlateApplication::Get().GetModifierKeys()); +} + +static PyObject *py_ue_get_cursor_radius(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetCursorRadius()); +} + static PyObject *py_ue_goto_line_in_source(PyObject *cls, PyObject * args) { char *filename; @@ -44,6 +54,56 @@ static PyObject *py_ue_is_mouse_attached(PyObject *cls, PyObject * args) Py_RETURN_FALSE; } +static PyObject *py_ue_set_all_user_focus(PyObject *cls, PyObject * args) +{ + PyObject *py_widget; + int focus_cause = (int)EFocusCause::SetDirectly; + + if (!PyArg_ParseTuple(args, "O|i:set_all_user_focus", &py_widget, &focus_cause)) + { + return nullptr; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + TSharedPtr widget_ptr(py_swidget->s_widget); + + FSlateApplication::Get().SetAllUserFocus(widget_ptr, (EFocusCause)focus_cause); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_set_application_scale(PyObject *cls, PyObject * args) +{ + float scale; + if (!PyArg_ParseTuple(args, "f:set_application_scale", &scale)) + { + return nullptr; + } + + FSlateApplication::Get().SetApplicationScale(scale); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_set_cursor_pos(PyObject *cls, PyObject * args) +{ + float x; + float y; + if (!PyArg_ParseTuple(args, "(ff):set_cursor_pos", &x, &y)) + { + return nullptr; + } + + FSlateApplication::Get().SetCursorPos(FVector2D(x, y)); + + Py_RETURN_NONE; +} + static PyObject *py_ue_process_key_down_event(PyObject *cls, PyObject * args) { PyObject *py_event; @@ -96,12 +156,17 @@ static PyObject *py_ue_process_key_char_event(PyObject *cls, PyObject * args) static PyMethodDef ue_PyFSlateApplication_methods[] = { { "get_average_delta_time", (PyCFunction)py_ue_get_average_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "get_cursor_radius", (PyCFunction)py_ue_get_cursor_radius, METH_VARARGS | METH_CLASS, "" }, { "get_delta_time", (PyCFunction)py_ue_get_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "get_modifier_keys", (PyCFunction)py_ue_get_modifier_keys, METH_VARARGS | METH_CLASS, "" }, { "goto_line_in_source", (PyCFunction)py_ue_goto_line_in_source, METH_VARARGS | METH_CLASS, "" }, { "is_gamepad_attached", (PyCFunction)py_ue_is_gamepad_attached, METH_VARARGS | METH_CLASS, "" }, { "is_mouse_attached", (PyCFunction)py_ue_is_mouse_attached, METH_VARARGS | METH_CLASS, "" }, { "process_key_down_event", (PyCFunction)py_ue_process_key_down_event, METH_VARARGS | METH_CLASS, "" }, { "process_key_char_event", (PyCFunction)py_ue_process_key_char_event, METH_VARARGS | METH_CLASS, "" }, + { "set_application_scale", (PyCFunction)py_ue_set_application_scale, METH_VARARGS | METH_CLASS, "" }, + { "set_all_user_focus", (PyCFunction)py_ue_set_all_user_focus, METH_VARARGS | METH_CLASS, "" }, + { "set_cursor_pos", (PyCFunction)py_ue_set_cursor_pos, METH_VARARGS | METH_CLASS, "" }, { NULL } /* Sentinel */ }; From 08298530e9969f053dd0960dbe62f3b92b4869e7 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 12 Feb 2018 17:02:56 +0100 Subject: [PATCH 49/94] improved FPointerEvent --- Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp index 54c1b1853..0a13b1e51 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp @@ -103,6 +103,7 @@ static int ue_py_fpointer_event_init(ue_PyFPointerEvent *self, PyObject *args, P } TSet keys; + keys.Add(FKey(effecting)); FModifierKeysState modifier; if (py_modifier) From a1b8c99d191a1c95902a7102e41173ffaa3a8556 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Wed, 14 Feb 2018 09:32:29 +0100 Subject: [PATCH 50/94] first round of shared pointers refactoring --- .../ConsoleManager/UEPyIConsoleManager.cpp | 7 +-- .../ConsoleManager/UEPyIConsoleManager.h | 7 +-- .../Private/PythonDelegate.cpp | 52 +++++----------- .../Private/PythonSmartDelegate.cpp | 59 +++++++++++++++++++ .../UnrealEnginePython/Private/UEPyEditor.cpp | 10 ++-- .../UnrealEnginePython/Private/UEPyModule.cpp | 8 +-- .../UnrealEnginePython/Private/UEPyModule.h | 1 + .../UnrealEnginePython/Private/UEPyTicker.cpp | 45 +++++++------- .../UnrealEnginePython/Private/UEPyTicker.h | 1 - .../Private/UnrealEnginePythonPrivatePCH.h | 2 +- .../Public/PythonDelegate.h | 8 +-- .../Public/PythonSmartDelegate.h | 27 +++++++++ 12 files changed, 137 insertions(+), 90 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp create mode 100644 Source/UnrealEnginePython/Public/PythonSmartDelegate.h diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp index 2b080449d..31af0269e 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp @@ -515,7 +515,7 @@ static PyObject *py_ue_iconsole_manager_register_variable_float(PyObject *cls, P Py_RETURN_NONE; } -void UPythonConsoleDelegate::OnConsoleCommand(const TArray < FString > & InArgs) +void FPythonSmartConsoleDelegate::OnConsoleCommand(const TArray < FString > & InArgs) { FScopePythonGIL gil; @@ -563,11 +563,10 @@ static PyObject *py_ue_iconsole_manager_register_command(PyObject *cls, PyObject return PyErr_Format(PyExc_Exception, "console object \"%s\" already exists", key); } - UPythonConsoleDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartConsoleDelegate); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); FConsoleCommandWithArgsDelegate console_delegate; - console_delegate.BindUObject(py_delegate, &UPythonConsoleDelegate::OnConsoleCommand); + console_delegate.BindSP(py_delegate, &FPythonSmartConsoleDelegate::OnConsoleCommand); if (!IConsoleManager::Get().RegisterConsoleCommand(UTF8_TO_TCHAR(key), help ? UTF8_TO_TCHAR(help) : UTF8_TO_TCHAR(key), console_delegate, 0)) { diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h index b147fdf7e..01815ccfd 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h @@ -3,18 +3,15 @@ #include "UnrealEnginePython.h" #include "Runtime/Core/Public/HAL/IConsoleManager.h" -#include "UEPyIConsoleManager.generated.h" typedef struct { PyObject_HEAD - /* Type-specific fields go here. */ + /* Type-specific fields go here. */ } ue_PyIConsoleManager; -UCLASS() -class UPythonConsoleDelegate : public UPythonDelegate +class FPythonSmartConsoleDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: void OnConsoleCommand(const TArray < FString > &InArgs); diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index 302f4b618..b466a5ec2 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -1,7 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PythonDelegate.h" -UPythonDelegate::UPythonDelegate() { +UPythonDelegate::UPythonDelegate() +{ py_callable = nullptr; signature_set = false; } @@ -29,15 +30,18 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) PyObject *py_args = nullptr; - if (signature_set) { + if (signature_set) + { py_args = PyTuple_New(signature->NumParms); Py_ssize_t argn = 0; TFieldIterator PArgs(signature); - for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) { + for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) + { UProperty *prop = *PArgs; PyObject *arg = ue_py_convert_property(prop, (uint8 *)Parms); - if (!arg) { + if (!arg) + { unreal_engine_py_log_error(); Py_DECREF(py_args); return; @@ -49,7 +53,8 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) PyObject *ret = PyObject_CallObject(py_callable, py_args); Py_XDECREF(py_args); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } @@ -75,7 +80,8 @@ void UPythonDelegate::PyInputHandler() { FScopePythonGIL gil; PyObject *ret = PyObject_CallObject(py_callable, NULL); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } @@ -86,41 +92,13 @@ void UPythonDelegate::PyInputAxisHandler(float value) { FScopePythonGIL gil; PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", value); - if (!ret) { - unreal_engine_py_log_error(); - return; - } - Py_DECREF(ret); -} - -bool UPythonDelegate::Tick(float DeltaTime) -{ - FScopePythonGIL gil; - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", DeltaTime); - if (!ret) { - unreal_engine_py_log_error(); - return false; - } - if (PyObject_IsTrue(ret)) { - Py_DECREF(ret); - return true; - } - Py_DECREF(ret); - return false; -} - -#if WITH_EDITOR -void UPythonDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) -{ - FScopePythonGIL gil; - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_uobject((UObject *)factory), ue_get_python_uobject(u_object)); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -#endif UPythonDelegate::~UPythonDelegate() @@ -130,4 +108,4 @@ UPythonDelegate::~UPythonDelegate() #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("PythonDelegate callable XDECREF'ed")); #endif -} +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp new file mode 100644 index 000000000..1bcc6ef26 --- /dev/null +++ b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp @@ -0,0 +1,59 @@ +#include "UnrealEnginePythonPrivatePCH.h" +#include "PythonSmartDelegate.h" + +FPythonSmartDelegate::FPythonSmartDelegate() +{ + py_callable = nullptr; +} + +void FPythonSmartDelegate::SetPyCallable(PyObject *callable) +{ + // do not acquire the gil here as we set the callable in python call themselves + py_callable = callable; + Py_INCREF(py_callable); +} + + + +bool FPythonSmartDelegate::Tick(float DeltaTime) +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", DeltaTime); + if (!ret) + { + unreal_engine_py_log_error(); + return false; + } + if (PyObject_IsTrue(ret)) + { + Py_DECREF(ret); + return true; + } + Py_DECREF(ret); + return false; +} + +#if WITH_EDITOR +void FPythonSmartDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_uobject((UObject *)factory), ue_get_python_uobject(u_object)); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); + +} +#endif + + +FPythonSmartDelegate::~FPythonSmartDelegate() +{ + FScopePythonGIL gil; + Py_XDECREF(py_callable); +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("PythonSmartDelegate callable XDECREF'ed")); +#endif +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index b437f35a0..db643f084 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -1404,7 +1404,7 @@ PyObject *py_unreal_engine_blueprint_set_variable_visibility(PyObject * self, Py if (!PyArg_ParseTuple(args, "OsO:blueprint_set_variable_visibility", &py_blueprint, &name, &visibility)) { return NULL; -} + } if (!ue_is_pyuobject(py_blueprint)) { @@ -1678,12 +1678,10 @@ PyObject *py_unreal_engine_editor_on_asset_post_import(PyObject * self, PyObject if (!PyCallable_Check(py_callable)) return PyErr_Format(PyExc_Exception, "object is not a callable"); - UPythonDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - FEditorDelegates::OnAssetPostImport.AddUObject(py_delegate, &UPythonDelegate::PyFOnAssetPostImport); - Py_INCREF(Py_None); - return Py_None; + FEditorDelegates::OnAssetPostImport.AddSP(py_delegate, &FPythonSmartDelegate::PyFOnAssetPostImport); + Py_RETURN_NONE; } PyObject *py_unreal_engine_create_material_instance(PyObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index ae9f906ee..098ffc0de 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -2156,7 +2156,7 @@ void unreal_engine_py_log_error() if (zero) { msg = PyBytes_AsString(zero); -} + } #else msg = PyString_AsString(PyObject_Str(value)); #endif @@ -2211,7 +2211,7 @@ void unreal_engine_py_log_error() } PyErr_Clear(); - } +} // retrieve a UWorld from a generic UObject (if possible) UWorld *ue_get_uworld(ue_PyUObject *py_obj) @@ -3102,10 +3102,10 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * #else prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); #endif - } + } #endif + } } -} Py_ssize_t tuple_len = PyTuple_Size(args); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 3b8a3558b..58f87cb77 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -2,6 +2,7 @@ #include "UnrealEnginePython.h" #include "PythonDelegate.h" +#include "PythonSmartDelegate.h" #include "UEPyUScriptStruct.h" #include #include diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.cpp b/Source/UnrealEnginePython/Private/UEPyTicker.cpp index 04de782ea..55dcd660a 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTicker.cpp @@ -3,15 +3,14 @@ #include "UEPyTicker.h" // destructor -static void ue_pyfdelegatehandle_dealloc(ue_PyFDelegateHandle *self) { - if (!self->garbaged) { +static void ue_pyfdelegatehandle_dealloc(ue_PyFDelegateHandle *self) +{ + if (!self->garbaged) + { FTicker::GetCoreTicker().RemoveTicker(self->dhandle); // useless ;) self->garbaged = true; } - if (self->py_delegate && self->py_delegate->IsValidLowLevel() && self->py_delegate->IsRooted()) { - self->py_delegate->RemoveFromRoot(); - } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -46,7 +45,8 @@ static PyTypeObject ue_PyFDelegateHandleType = { 0, /* tp_methods */ }; -void ue_python_init_fdelegatehandle(PyObject *ue_module) { +void ue_python_init_fdelegatehandle(PyObject *ue_module) +{ ue_PyFDelegateHandleType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyFDelegateHandleType) < 0) return; @@ -55,11 +55,13 @@ void ue_python_init_fdelegatehandle(PyObject *ue_module) { PyModule_AddObject(ue_module, "FDelegateHandle", (PyObject *)&ue_PyFDelegateHandleType); } -PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) +{ PyObject *py_callable; float delay = 0; - if (!PyArg_ParseTuple(args, "O|f:add_ticker", &py_callable, &delay)) { + if (!PyArg_ParseTuple(args, "O|f:add_ticker", &py_callable, &delay)) + { return nullptr; } @@ -67,32 +69,29 @@ PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "argument is not a callable"); FTickerDelegate ticker_delegate; - UPythonDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); - ticker_delegate.BindUObject(py_delegate, &UPythonDelegate::Tick); - - // avoid the delegate to be GC'ed - py_delegate->AddToRoot(); + ticker_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Tick); FDelegateHandle dhandle = FTicker::GetCoreTicker().AddTicker(ticker_delegate, delay); ue_PyFDelegateHandle *ret = (ue_PyFDelegateHandle *)PyObject_New(ue_PyFDelegateHandle, &ue_PyFDelegateHandleType); - if (!ret) { + if (!ret) + { FTicker::GetCoreTicker().RemoveTicker(dhandle); - py_delegate->RemoveFromRoot(); return PyErr_Format(PyExc_Exception, "unable to allocate FDelegateHandle python object"); } ret->dhandle = dhandle; - ret->py_delegate = py_delegate; ret->garbaged = false; - return (PyObject *)ret; } -PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) +{ PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:remove_ticker", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:remove_ticker", &py_obj)) + { return NULL; } @@ -101,15 +100,11 @@ PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) { ue_PyFDelegateHandle *py_handle = (ue_PyFDelegateHandle *)py_obj; - if (!py_handle->garbaged) { + if (!py_handle->garbaged) + { FTicker::GetCoreTicker().RemoveTicker(py_handle->dhandle); py_handle->garbaged = true; } - if (py_handle->py_delegate) { - py_handle->py_delegate->RemoveFromRoot(); - py_handle->py_delegate = nullptr; - } - Py_RETURN_NONE; } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.h b/Source/UnrealEnginePython/Private/UEPyTicker.h index 11ed989fe..b83c33975 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.h +++ b/Source/UnrealEnginePython/Private/UEPyTicker.h @@ -8,7 +8,6 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FDelegateHandle dhandle; - UPythonDelegate *py_delegate; bool garbaged; } ue_PyFDelegateHandle; diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 342c4e96d..ff5a52252 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -//#define UEPY_MEMORY_DEBUG 1 +#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" diff --git a/Source/UnrealEnginePython/Public/PythonDelegate.h b/Source/UnrealEnginePython/Public/PythonDelegate.h index 2a8b0bc8a..2a80205c6 100644 --- a/Source/UnrealEnginePython/Public/PythonDelegate.h +++ b/Source/UnrealEnginePython/Public/PythonDelegate.h @@ -18,12 +18,6 @@ class UPythonDelegate : public UObject void PyInputHandler(); void PyInputAxisHandler(float value); - bool Tick(float DeltaTime); - -#if WITH_EDITOR - void PyFOnAssetPostImport(UFactory *factory, UObject *u_object); -#endif - protected: UFunction *signature; bool signature_set; @@ -33,6 +27,6 @@ class UPythonDelegate : public UObject PyObject *py_callable; - + }; diff --git a/Source/UnrealEnginePython/Public/PythonSmartDelegate.h b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h new file mode 100644 index 000000000..f08a43a87 --- /dev/null +++ b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h @@ -0,0 +1,27 @@ +#pragma once + +#include "UnrealEnginePythonPrivatePCH.h" + +class FPythonSmartDelegate : public TSharedFromThis +{ + +public: + FPythonSmartDelegate(); + ~FPythonSmartDelegate(); + + void SetPyCallable(PyObject *callable); + + + + bool Tick(float DeltaTime); + +#if WITH_EDITOR + void PyFOnAssetPostImport(UFactory *factory, UObject *u_object); +#endif + +protected: + + PyObject *py_callable; + +}; + From 5ad1931472d7a2380398edc1ebdc3f503c72822f Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Wed, 14 Feb 2018 10:57:34 +0100 Subject: [PATCH 51/94] another attempt of improving delegates memory management --- .../Private/Http/UEPyIHttpRequest.cpp | 143 +++++++++++------- .../Private/Http/UEPyIHttpRequest.h | 8 +- .../UnrealEnginePython/Private/UEPyTicker.cpp | 28 +++- .../UnrealEnginePython/Private/UEPyTicker.h | 1 + 4 files changed, 115 insertions(+), 65 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp index a69bd690e..5147c25c9 100644 --- a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp +++ b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp @@ -3,10 +3,12 @@ #include "Runtime/Online/HTTP/Public/HttpManager.h" -static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject * args) +{ char *verb; - if (!PyArg_ParseTuple(args, "s:set_verb", &verb)) { + if (!PyArg_ParseTuple(args, "s:set_verb", &verb)) + { return NULL; } @@ -16,10 +18,12 @@ static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject return Py_None; } -static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * args) +{ char *url; - if (!PyArg_ParseTuple(args, "s:set_url", &url)) { + if (!PyArg_ParseTuple(args, "s:set_url", &url)) + { return NULL; } @@ -29,11 +33,13 @@ static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * return Py_None; } -static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObject * args) +{ char *key; char *value; - if (!PyArg_ParseTuple(args, "ss:set_header", &key, &value)) { + if (!PyArg_ParseTuple(args, "ss:set_header", &key, &value)) + { return NULL; } @@ -43,11 +49,13 @@ static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObjec return Py_None; } -static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, PyObject * args) +{ char *key; char *value; - if (!PyArg_ParseTuple(args, "ss:append_to_header", &key, &value)) { + if (!PyArg_ParseTuple(args, "ss:append_to_header", &key, &value)) + { return NULL; } @@ -57,17 +65,21 @@ static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, P return Py_None; } -static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:set_content", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:set_content", &py_obj)) + { return NULL; } - if (PyUnicode_Check(py_obj)) { + if (PyUnicode_Check(py_obj)) + { self->http_request->SetContentAsString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_obj))); } - else if (PyBytes_Check(py_obj)) { + else if (PyBytes_Check(py_obj)) + { char *buf = nullptr; Py_ssize_t len = 0; PyBytes_AsStringAndSize(py_obj, &buf, &len); @@ -80,10 +92,12 @@ static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObje return Py_None; } -static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * args) +{ float delta_seconds; - if (!PyArg_ParseTuple(args, "f:tick", &delta_seconds)) { + if (!PyArg_ParseTuple(args, "f:tick", &delta_seconds)) + { return NULL; } @@ -95,112 +109,129 @@ static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * ar return Py_None; } -static PyObject *py_ue_ihttp_request_process_request(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_process_request(ue_PyIHttpRequest *self, PyObject * args) +{ self->http_request->ProcessRequest(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ihttp_request_cancel_request(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_cancel_request(ue_PyIHttpRequest *self, PyObject * args) +{ self->http_request->CancelRequest(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ihttp_request_get_status(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_status(ue_PyIHttpRequest *self, PyObject * args) +{ return PyLong_FromLong((int)self->http_request->GetStatus()); } -static PyObject *py_ue_ihttp_request_get_verb(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_verb(ue_PyIHttpRequest *self, PyObject * args) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->http_request->GetVerb())); } -static PyObject *py_ue_ihttp_request_get_elapsed_time(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_elapsed_time(ue_PyIHttpRequest *self, PyObject * args) +{ return PyFloat_FromDouble(self->http_request->GetElapsedTime()); } -static PyObject *py_ue_ihttp_request_get_response(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_response(ue_PyIHttpRequest *self, PyObject * args) +{ FHttpResponsePtr response = self->http_request->GetResponse(); - if (!response.IsValid()) { + if (!response.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to retrieve IHttpResponse"); } return py_ue_new_ihttp_response(response.Get()); } -void UPythonHttpDelegate::OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful) { +void FPythonSmartHttpDelegate::OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful) +{ FScopePythonGIL gil; - if (!request.IsValid() || !response.IsValid()) { + if (!request.IsValid() || !response.IsValid()) + { UE_LOG(LogPython, Error, TEXT("Unable to retrieve HTTP infos")); return; } PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OOO", py_http_request, py_ue_new_ihttp_response(response.Get()), successful ? Py_True : Py_False); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -void UPythonHttpDelegate::OnRequestProgress(FHttpRequestPtr request, int32 sent, int32 received) { +void FPythonSmartHttpDelegate::OnRequestProgress(FHttpRequestPtr request, int32 sent, int32 received) +{ FScopePythonGIL gil; - if (!request.IsValid()) { + if (!request.IsValid()) + { UE_LOG(LogPython, Error, TEXT("Unable to retrieve HTTP infos")); return; } PyObject *ret = PyObject_CallFunction(py_callable, (char *)"Oii", py_http_request, sent, received); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -static PyObject *py_ue_ihttp_request_bind_on_process_request_complete(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_bind_on_process_request_complete(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_process_request_complete", &py_callable)) { - return NULL; + if (!PyArg_ParseTuple(args, "O:bind_on_process_request_complete", &py_callable)) + { + return nullptr; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not a callable"); } - UPythonHttpDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartHttpDelegate); py_delegate->SetPyCallable(py_callable); // this trick avoids generating a new python object py_delegate->SetPyHttpRequest(self); - self->http_request->OnProcessRequestComplete().BindUObject(py_delegate, &UPythonHttpDelegate::OnRequestComplete); + self->http_request->OnProcessRequestComplete().BindSP(py_delegate, &FPythonSmartHttpDelegate::OnRequestComplete); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -static PyObject *py_ue_ihttp_request_bind_on_request_progress(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_bind_on_request_progress(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_request_progress", &py_callable)) { - return NULL; + if (!PyArg_ParseTuple(args, "O:bind_on_request_progress", &py_callable)) + { + return nullptr; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not a callable"); } - UPythonHttpDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartHttpDelegate); py_delegate->SetPyCallable(py_callable); // this trick avoids generating a new python object py_delegate->SetPyHttpRequest(self); - self->http_request->OnRequestProgress().BindUObject(py_delegate, &UPythonHttpDelegate::OnRequestProgress); + self->http_request->OnRequestProgress().BindSP(py_delegate, &FPythonSmartHttpDelegate::OnRequestProgress); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef ue_PyIHttpRequest_methods[] = { @@ -228,12 +259,13 @@ static PyObject *ue_PyIHttpRequest_str(ue_PyIHttpRequest *self) &self->http_request.Get()); } -static void ue_PyIHttpRequest_dealloc(ue_PyIHttpRequest *self) { +static void ue_PyIHttpRequest_dealloc(ue_PyIHttpRequest *self) +{ #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIHttpRequest %p mapped to IHttpRequest %p"), self, &self->http_request.Get()); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIHttpRequest %p mapped to IHttpRequest %p"), self, &self->http_request.Get()); #endif Py_DECREF(self->py_dict); - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyTypeObject ue_PyIHttpRequestType = { @@ -269,19 +301,23 @@ static PyTypeObject ue_PyIHttpRequestType = { 0, }; -static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyObject *kwargs) { +static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyObject *kwargs) +{ char *verb = nullptr; char* url = nullptr; - if (!PyArg_ParseTuple(args, "|ss:__init__", &verb, &url)) { + if (!PyArg_ParseTuple(args, "|ss:__init__", &verb, &url)) + { return -1; } new(&self->http_request) TSharedRef(FHttpModule::Get().CreateRequest()); self->py_dict = PyDict_New(); - if (verb) { + if (verb) + { self->http_request->SetVerb(UTF8_TO_TCHAR(verb)); } - if (url) { + if (url) + { self->http_request->SetURL(UTF8_TO_TCHAR(url)); } @@ -289,7 +325,8 @@ static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyO return 0; } -void ue_python_init_ihttp_request(PyObject *ue_module) { +void ue_python_init_ihttp_request(PyObject *ue_module) +{ ue_PyIHttpRequestType.tp_new = PyType_GenericNew; ue_PyIHttpRequestType.tp_init = (initproc)ue_py_ihttp_request_init; @@ -297,8 +334,8 @@ void ue_python_init_ihttp_request(PyObject *ue_module) { ue_PyIHttpRequestType.tp_base = &ue_PyIHttpBaseType; ue_PyIHttpRequestType.tp_getattro = PyObject_GenericGetAttr; - ue_PyIHttpRequestType.tp_setattro = PyObject_GenericSetAttr; - ue_PyIHttpRequestType.tp_dictoffset = offsetof(ue_PyIHttpRequest, py_dict); + ue_PyIHttpRequestType.tp_setattro = PyObject_GenericSetAttr; + ue_PyIHttpRequestType.tp_dictoffset = offsetof(ue_PyIHttpRequest, py_dict); if (PyType_Ready(&ue_PyIHttpRequestType) < 0) return; diff --git a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h index b527012d9..4873942f6 100644 --- a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h +++ b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h @@ -5,8 +5,6 @@ #include "Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h" #include "Runtime/Online/HTTP/Public/HttpModule.h" -#include "UEPyIHttpRequest.generated.h" - extern PyTypeObject ue_PyIHttpBaseType; @@ -20,10 +18,8 @@ typedef struct { void ue_python_init_ihttp_request(PyObject *); -UCLASS() -class UPythonHttpDelegate : public UPythonDelegate +class FPythonSmartHttpDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: void OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful); @@ -34,7 +30,7 @@ class UPythonHttpDelegate : public UPythonDelegate Py_INCREF(py_http_request); } - ~UPythonHttpDelegate() { + ~FPythonSmartHttpDelegate() { Py_XDECREF(py_http_request); } protected: diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.cpp b/Source/UnrealEnginePython/Private/UEPyTicker.cpp index 55dcd660a..73af0f7a5 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTicker.cpp @@ -11,6 +11,12 @@ static void ue_pyfdelegatehandle_dealloc(ue_PyFDelegateHandle *self) // useless ;) self->garbaged = true; } + + if (self->delegate_ptr.IsValid()) + { + self->delegate_ptr.Reset(); + } + Py_TYPE(self)->tp_free((PyObject *)self); } @@ -55,6 +61,7 @@ void ue_python_init_fdelegatehandle(PyObject *ue_module) PyModule_AddObject(ue_module, "FDelegateHandle", (PyObject *)&ue_PyFDelegateHandleType); } + PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) { @@ -68,20 +75,25 @@ PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) if (!PyCallable_Check(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not a callable"); + ue_PyFDelegateHandle *ret = (ue_PyFDelegateHandle *)PyObject_New(ue_PyFDelegateHandle, &ue_PyFDelegateHandleType); + if (!ret) + { + return PyErr_Format(PyExc_Exception, "unable to allocate FDelegateHandle python object"); + } + FTickerDelegate ticker_delegate; TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); ticker_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Tick); - FDelegateHandle dhandle = FTicker::GetCoreTicker().AddTicker(ticker_delegate, delay); - ue_PyFDelegateHandle *ret = (ue_PyFDelegateHandle *)PyObject_New(ue_PyFDelegateHandle, &ue_PyFDelegateHandleType); - if (!ret) + ret->dhandle = FTicker::GetCoreTicker().AddTicker(ticker_delegate, delay); + if (!ret->dhandle.IsValid()) { - FTicker::GetCoreTicker().RemoveTicker(dhandle); - return PyErr_Format(PyExc_Exception, "unable to allocate FDelegateHandle python object"); + Py_DECREF(ret); + return PyErr_Format(PyExc_Exception, "unable to add FTicker"); } - ret->dhandle = dhandle; + new(&ret->delegate_ptr) TSharedPtr(py_delegate); ret->garbaged = false; return (PyObject *)ret; } @@ -104,6 +116,10 @@ PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) { FTicker::GetCoreTicker().RemoveTicker(py_handle->dhandle); py_handle->garbaged = true; + if (py_handle->delegate_ptr.IsValid()) + { + py_handle->delegate_ptr.Reset(); + } } Py_RETURN_NONE; diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.h b/Source/UnrealEnginePython/Private/UEPyTicker.h index b83c33975..3e387277d 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.h +++ b/Source/UnrealEnginePython/Private/UEPyTicker.h @@ -9,6 +9,7 @@ typedef struct { /* Type-specific fields go here. */ FDelegateHandle dhandle; bool garbaged; + TSharedPtr delegate_ptr; } ue_PyFDelegateHandle; PyObject *py_unreal_engine_add_ticker(PyObject *, PyObject *); From 8a93833cb0f4f0db5ce70875d6106f093dc0e881 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Wed, 14 Feb 2018 19:37:48 +0100 Subject: [PATCH 52/94] added canary --- Source/UnrealEnginePython/Private/PyActor.cpp | 6 +- .../Private/PyCharacter.cpp | 2 - Source/UnrealEnginePython/Private/PyHUD.cpp | 2 - Source/UnrealEnginePython/Private/PyPawn.cpp | 2 - .../Private/PyUserWidget.cpp | 2 - .../Private/PythonComponent.cpp | 2 - .../Private/PythonSmartDelegate.cpp | 12 ++ .../UnrealEnginePython/Private/UEPyModule.cpp | 149 +++++++++++------- .../UnrealEnginePython/Private/UEPyModule.h | 17 +- .../UnrealEnginePython/Private/UEPyTimer.cpp | 8 +- Source/UnrealEnginePython/Private/UEPyTimer.h | 1 + .../Private/UObject/UEPyActor.cpp | 55 +++---- .../Private/UObject/UEPyInput.cpp | 30 +--- .../Private/UnrealEnginePython.cpp | 2 + .../UnrealEnginePython/Public/PythonCanary.h | 25 +++ .../Public/PythonSmartDelegate.h | 1 + .../Public/UnrealEnginePython.h | 11 +- 17 files changed, 186 insertions(+), 141 deletions(-) create mode 100644 Source/UnrealEnginePython/Public/PythonCanary.h diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index f442b2cfe..9aa9dcdb1 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -284,8 +284,6 @@ APyActor::~APyActor() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_actor_instance && py_actor_instance->ob_refcnt != 1) { @@ -299,5 +297,9 @@ APyActor::~APyActor() #endif // this could trigger the destruction of the python/uobject mapper + if (py_uobject) + { + UE_LOG(LogPython, Error, TEXT("UObject refcnt = %d"), py_uobject->ob_base.ob_refcnt); + } Py_XDECREF(py_uobject); } diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index 75e82b39a..cf3c45788 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -443,8 +443,6 @@ APyCharacter::~APyCharacter() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_character_instance && py_character_instance->ob_refcnt != 1) { UE_LOG(LogPython, Error, TEXT("Inconsistent Python ACharacter wrapper refcnt = %d"), py_character_instance->ob_refcnt); diff --git a/Source/UnrealEnginePython/Private/PyHUD.cpp b/Source/UnrealEnginePython/Private/PyHUD.cpp index cb2cf7f2f..bdf59af35 100644 --- a/Source/UnrealEnginePython/Private/PyHUD.cpp +++ b/Source/UnrealEnginePython/Private/PyHUD.cpp @@ -242,8 +242,6 @@ APyHUD::~APyHUD() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_hud_instance && py_hud_instance->ob_refcnt != 1) { diff --git a/Source/UnrealEnginePython/Private/PyPawn.cpp b/Source/UnrealEnginePython/Private/PyPawn.cpp index 3923579f8..02a79ff6c 100644 --- a/Source/UnrealEnginePython/Private/PyPawn.cpp +++ b/Source/UnrealEnginePython/Private/PyPawn.cpp @@ -239,8 +239,6 @@ APyPawn::~APyPawn() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_pawn_instance && py_pawn_instance->ob_refcnt != 1) { UE_LOG(LogPython, Error, TEXT("Inconsistent Python APawn wrapper refcnt = %d"), py_pawn_instance->ob_refcnt); diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index 6e655fc15..e07b3c7c6 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -355,8 +355,6 @@ UPyUserWidget::~UPyUserWidget() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_user_widget_instance && py_user_widget_instance->ob_refcnt != 1) { UE_LOG(LogPython, Error, TEXT("Inconsistent Python UUserWidget wrapper refcnt = %d"), py_user_widget_instance->ob_refcnt); diff --git a/Source/UnrealEnginePython/Private/PythonComponent.cpp b/Source/UnrealEnginePython/Private/PythonComponent.cpp index 875babc2b..2a8c630ef 100644 --- a/Source/UnrealEnginePython/Private/PythonComponent.cpp +++ b/Source/UnrealEnginePython/Private/PythonComponent.cpp @@ -556,8 +556,6 @@ UPythonComponent::~UPythonComponent() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - #if defined(UEPY_MEMORY_DEBUG) if (py_component_instance && py_component_instance->ob_refcnt != 1) { UE_LOG(LogPython, Error, TEXT("Inconsistent Python UActorComponent wrapper refcnt = %d"), py_component_instance->ob_refcnt); diff --git a/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp index 1bcc6ef26..62e72dcaf 100644 --- a/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp @@ -33,6 +33,18 @@ bool FPythonSmartDelegate::Tick(float DeltaTime) return false; } +void FPythonSmartDelegate::Void() +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, nullptr); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + #if WITH_EDITOR void FPythonSmartDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) { diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 098ffc0de..7ba935e52 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -81,13 +81,76 @@ static PyObject *init_unreal_engine(void) } #endif -std::map ue_python_gc; +namespace UnrealEnginePythonHouseKeeper +{ + std::map u_object_py_mapping; + + struct FPythonDelegateTracker + { + FWeakObjectPtr owner; + UPythonDelegate *delegate; + + FPythonDelegateTracker(UPythonDelegate *delegate_to_track, UObject *delegate_owner) : owner(delegate_owner), delegate(delegate_to_track) + { + } + + ~FPythonDelegateTracker() + { + } + }; + + std::list py_delegates_tracker; +}; + +static void ue_py_delegates_gc() +{ + auto &lst = UnrealEnginePythonHouseKeeper::py_delegates_tracker; + for (auto itr = lst.begin(); itr != lst.end(); /*noop*/) + //for (auto itr = lst.begin(); itr != lst.end(); ++itr) + { + /*if (itr->owner.IsValid()) + { + UE_LOG(LogPython, Error, TEXT("Delegate for %s"), *itr->owner.Get()->GetName()); + } + else + { + UE_LOG(LogPython, Error, TEXT("Found bad delegate")); + } + */ + if (!itr->owner.IsValid()) + { + itr->delegate->RemoveFromRoot(); + itr = lst.erase(itr); + } + else + ++itr; + } +} + +UPythonDelegate *ue_py_new_delegate(UObject *owner, PyObject *py_callable, UFunction *signature) +{ + // TODO: do a round of gc only if enough time passed + ue_py_delegates_gc(); + + UPythonDelegate *py_delegate = NewObject(); + if (!py_delegate) + return nullptr; + + py_delegate->AddToRoot(); + py_delegate->SetPyCallable(py_callable); + py_delegate->SetSignature(signature); + + UnrealEnginePythonHouseKeeper::FPythonDelegateTracker tracker(py_delegate, owner); + UnrealEnginePythonHouseKeeper::py_delegates_tracker.push_back(tracker); + + return py_delegate; +} static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) { std::list broken_list; - for (auto it : ue_python_gc) + for (auto it : UnrealEnginePythonHouseKeeper::u_object_py_mapping) { #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), it.first); @@ -100,12 +163,17 @@ static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) #endif broken_list.push_back(u_obj); } + else + { + UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), u_obj, *u_obj->GetName()); + } } + /* for (UObject *u_obj : broken_list) { - ue_PyUObject *py_obj = ue_python_gc.at(u_obj); + ue_PyUObject *py_obj = UnrealEnginePythonHouseKeeper::u_object_py_mapping.at(u_obj); Py_DECREF(py_obj); - } + }*/ return PyLong_FromLong(broken_list.size()); @@ -975,43 +1043,21 @@ static PyMethodDef ue_PyUObject_methods[] = { { NULL } /* Sentinel */ }; -void ue_pydelegates_cleanup(ue_PyUObject *self) + +// destructor +static void ue_pyobject_dealloc(ue_PyUObject *self) { - // this could happen during engine shutdown, so have mercy - // start deallocating delegates mapped to the object - if (!self || !self->python_delegates_gc) - return; - UE_LOG(LogPython, Warning, TEXT("Delegates = %d"), self->python_delegates_gc->size()); - for (UPythonDelegate *py_delegate : *(self->python_delegates_gc)) - { - if (py_delegate && py_delegate->IsValidLowLevel()) - { + PyObject_GC_UnTrack(self); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Removing UPythonDelegate %p from ue_PyUObject %p mapped to UObject %p"), py_delegate, self, self->ue_object); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); #endif - py_delegate->RemoveFromRoot(); - } - } - self->python_delegates_gc->clear(); - delete self->python_delegates_gc; - self->python_delegates_gc = nullptr; - - if (self->auto_rooted && self->ue_object->IsRooted()) + if (self->auto_rooted && (self->ue_object && self->ue_object->IsValidLowLevel() && self->ue_object->IsRooted())) { self->ue_object->RemoveFromRoot(); } Py_XDECREF(self->py_dict); -} - -// destructor -static void ue_pyobject_dealloc(ue_PyUObject *self) -{ -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); -#endif - ue_pydelegates_cleanup(self); - ue_python_gc.erase(self->ue_object); + UnrealEnginePythonHouseKeeper::u_object_py_mapping.erase(self->ue_object); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -1162,6 +1208,12 @@ static PyObject *ue_PyUObject_str(ue_PyUObject *self) #endif } +static int ue_PyUObject_traverse(ue_PyUObject *self, visitproc visit, void *arg) +{ + UE_LOG(LogPython, Error, TEXT("TRAVERSING %p"), self); + return 0; +} + static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject *kw) { // if it is a class, create a new object @@ -1277,9 +1329,9 @@ static PyTypeObject ue_PyUObjectType = { (getattrofunc)ue_PyUObject_getattro, /* tp_getattro */ (setattrofunc)ue_PyUObject_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Unreal Engine generic UObject", /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)ue_PyUObject_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -1727,12 +1779,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(mc_value); - py_delegate->SetSignature(casted_prop->SignatureFunction); - // avoid delegates to be destroyed by the GC - py_delegate->AddToRoot(); - + UPythonDelegate *py_delegate = ue_py_new_delegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -2100,21 +2147,21 @@ ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) { if (!ue_obj || !ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) return nullptr; - std::map::iterator it = ue_python_gc.find(ue_obj); + std::map::iterator it = UnrealEnginePythonHouseKeeper::u_object_py_mapping.find(ue_obj); // not found ?? - if (it == ue_python_gc.end()) + if (it == UnrealEnginePythonHouseKeeper::u_object_py_mapping.end()) { - ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); + //ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); + ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_GC_New(ue_PyUObject, &ue_PyUObjectType); if (!ue_py_object) { return nullptr; } ue_py_object->ue_object = ue_obj; - ue_py_object->python_delegates_gc = new std::list; ue_py_object->py_dict = PyDict_New(); - ue_python_gc[ue_obj] = ue_py_object; + UnrealEnginePythonHouseKeeper::u_object_py_mapping[ue_obj] = ue_py_object; #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("CREATED UPyObject at %p for %p %s"), ue_py_object, ue_obj, *ue_obj->GetName()); @@ -2211,7 +2258,7 @@ void unreal_engine_py_log_error() } PyErr_Clear(); -} + } // retrieve a UWorld from a generic UObject (if possible) UWorld *ue_get_uworld(ue_PyUObject *py_obj) @@ -3235,13 +3282,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->SetSignature(casted_prop->SignatureFunction); - // avoid delegates to be destroyed by the GC - py_delegate->AddToRoot(); - u_obj->python_delegates_gc->push_back(py_delegate); - + UPythonDelegate *py_delegate = ue_py_new_delegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 58f87cb77..5a8d1019b 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -4,20 +4,18 @@ #include "PythonDelegate.h" #include "PythonSmartDelegate.h" #include "UEPyUScriptStruct.h" -#include -#include + + //#include "UEPyModule.generated.h" typedef struct { PyObject_HEAD - /* Type-specific fields go here. */ - UObject *ue_object; + /* Type-specific fields go here. */ + UObject *ue_object; // reference to proxy class (can be null) PyObject *py_proxy; - // list of exposed delegates - std::list *python_delegates_gc; // the __dict__ PyObject *py_dict; // if true RemoveFromRoot will be called at object destruction time @@ -25,6 +23,9 @@ typedef struct } ue_PyUObject; + + + void unreal_engine_py_log_error(); ue_PyUObject *ue_get_python_uobject(UObject *); ue_PyUObject *ue_get_python_uobject_inc(UObject *); @@ -41,8 +42,6 @@ PyObject *ue_bind_pyevent(ue_PyUObject *, FString, PyObject *, bool); PyObject *py_ue_ufunction_call(UFunction *, UObject *, PyObject *, int, PyObject *); -void ue_pydelegates_cleanup(ue_PyUObject *); - UClass *unreal_engine_new_uclass(char *, UClass *); UFunction *unreal_engine_add_function(UClass *, char *, PyObject *, uint32); @@ -89,3 +88,5 @@ template bool ue_py_check_childstruct(PyObject *py_obj) } FGuid *ue_py_check_fguid(PyObject *); + +UPythonDelegate *ue_py_new_delegate(UObject *, PyObject *p_callable, UFunction *); diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.cpp b/Source/UnrealEnginePython/Private/UEPyTimer.cpp index cf1ef4841..277dbc053 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTimer.cpp @@ -108,14 +108,10 @@ PyObject *py_ue_set_timer(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); FTimerDelegate timer_delegate; - UPythonDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); - // fake UFUNCTION for bypassing checks - timer_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); - // allow the delegate to not be destroyed - py_delegate->AddToRoot(); - self->python_delegates_gc->push_back(py_delegate); + timer_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Void); FTimerHandle thandle; world->GetTimerManager().SetTimer(thandle, timer_delegate, rate, loop, first_delay); diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.h b/Source/UnrealEnginePython/Private/UEPyTimer.h index 65e3caac4..41d4fe9ad 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.h +++ b/Source/UnrealEnginePython/Private/UEPyTimer.h @@ -10,6 +10,7 @@ typedef struct { FTimerHandle thandle; PyObject *py_callable; UWorld *world; + TSharedPtr delegate_ptr; } ue_PyFTimerHandle; PyObject *py_ue_set_timer(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e54c7ec00..5887067cb 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -739,33 +739,26 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar { ue_py_check(self); + + PyObject *py_class; PyObject *py_obj_location = nullptr; PyObject *py_obj_rotation = nullptr; - UWorld *world = ue_get_uworld(self); - if (!world) - return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - - PyObject *obj; - if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &obj, &py_obj_location, &py_obj_rotation)) + if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &py_class, &py_obj_location, &py_obj_rotation)) { - return NULL; + return nullptr; } - if (!ue_is_pyuobject(obj)) - { - return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - } + UWorld *world = ue_get_uworld(self); + if (!world) + return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) - { - return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from AActor"); - } + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + return PyErr_Format(PyExc_Exception, "argument is not a UClass"); - UClass *u_class = (UClass *)py_obj->ue_object; if (!u_class->IsChildOf()) { @@ -791,26 +784,23 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar rotation = py_rotation->rot; } - AActor *actor = nullptr; - PyObject *ret = nullptr; - if (kwargs && PyDict_Size(kwargs) > 0) { FTransform transform; transform.SetTranslation(location); transform.SetRotation(rotation.Quaternion()); - actor = world->SpawnActorDeferred((UClass *)py_obj->ue_object, transform); + AActor *actor = world->SpawnActorDeferred(u_class, transform); if (!actor) return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ue_PyUObject *py_u_obj = ue_get_python_uobject(actor); - if (!py_u_obj) + ue_PyUObject *py_actor = ue_get_python_uobject(actor); + if (!py_actor) return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); PyObject *py_iter = PyObject_GetIter(kwargs); while (PyObject *py_key = PyIter_Next(py_iter)) { - PyObject *void_ret = py_ue_set_property(py_u_obj, Py_BuildValue("OO", py_key, PyDict_GetItem(kwargs, py_key))); + PyObject *void_ret = py_ue_set_property(py_actor, Py_BuildValue("OO", py_key, PyDict_GetItem(kwargs, py_key))); if (!void_ret) { return PyErr_Format(PyExc_Exception, "unable to set property for new Actor"); @@ -818,19 +808,14 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar } Py_DECREF(py_iter); UGameplayStatics::FinishSpawningActor(actor, transform); - ret = (PyObject *)py_u_obj; - } - else - { - actor = world->SpawnActor((UClass *)py_obj->ue_object, &location, &rotation); - if (!actor) - return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ret = (PyObject *)ue_get_python_uobject(actor); + return (PyObject *)py_actor; } - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - return ret; + AActor *actor = world->SpawnActor(u_class, &location, &rotation); + if (!actor) + return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); + Py_RETURN_UOBJECT(actor); + } PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp index f5cc2d975..677a528ce 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp @@ -405,19 +405,13 @@ PyObject *py_ue_bind_action(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); FInputActionBinding input_action_binding(FName(UTF8_TO_TCHAR(action_name)), (const EInputEvent)key); input_action_binding.ActionDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); input->AddActionBinding(input_action_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -458,19 +452,13 @@ PyObject *py_ue_bind_axis(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); FInputAxisBinding input_axis_binding(FName(UTF8_TO_TCHAR(axis_name))); input_axis_binding.AxisDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputAxisHandler); input->AxisBindings.Add(input_axis_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -515,19 +503,13 @@ PyObject *py_ue_bind_key(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); FInputKeyBinding input_key_binding(FKey(UTF8_TO_TCHAR(key_name)), (const EInputEvent)key); input_key_binding.KeyDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); input->KeyBindings.Add(input_key_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index f3b991d38..be68ba904 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -2,6 +2,7 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PythonBlueprintFunctionLibrary.h" +#include "PythonCanary.h" #include "HAL/IConsoleManager.h" #if ENGINE_MINOR_VERSION < 13 #include "ClassIconFinder.h" @@ -338,6 +339,7 @@ void FUnrealEnginePythonModule::StartupModule() // release the GIL PythonGILRelease(); + NewObject(); } void FUnrealEnginePythonModule::ShutdownModule() diff --git a/Source/UnrealEnginePython/Public/PythonCanary.h b/Source/UnrealEnginePython/Public/PythonCanary.h new file mode 100644 index 000000000..1f811fbfa --- /dev/null +++ b/Source/UnrealEnginePython/Public/PythonCanary.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UnrealEnginePython.h" +#include "UObject/Object.h" +#include "PythonCanary.generated.h" + +UCLASS(MinimalAPI) +class UPythonCanary : public UObject +{ + GENERATED_BODY() + +public: + UPythonCanary() + { + UE_LOG(LogPython, Error, TEXT("Canary Spawned")); + } + + ~UPythonCanary() + { + UE_LOG(LogPython, Error, TEXT("Canary IS DEAD !!!")); + + NewObject(); + } +}; + diff --git a/Source/UnrealEnginePython/Public/PythonSmartDelegate.h b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h index f08a43a87..22296a298 100644 --- a/Source/UnrealEnginePython/Public/PythonSmartDelegate.h +++ b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h @@ -14,6 +14,7 @@ class FPythonSmartDelegate : public TSharedFromThis bool Tick(float DeltaTime); + void Void(); #if WITH_EDITOR void PyFOnAssetPostImport(UFactory *factory, UObject *u_object); diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index a839dc474..4a7035e8d 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -13,6 +13,9 @@ #include "Runtime/SlateCore/Public/Styling/ISlateStyle.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" +#include +#include + // We need to make sure reference structs do not mistaken for callable #define PyCalllable_Check_Extended(value) PyCallable_Check(value) && py_ue_is_uscriptstruct(value) == nullptr @@ -59,7 +62,10 @@ class UNREALENGINEPYTHON_API FUnrealEnginePythonModule : public IModuleInterface TSharedPtr StyleSet; }; -struct FScopePythonGIL { + + +struct FScopePythonGIL +{ FScopePythonGIL() { #if defined(UEPY_THREADING) @@ -71,7 +77,8 @@ struct FScopePythonGIL { ~FScopePythonGIL() { #if defined(UEPY_THREADING) - if (safeForRelease) { + if (safeForRelease) + { UnrealEnginePythonModule.PythonGILRelease(); } #endif From 36fdba4b4ac0e778bb7c3c7d5b59c3d0b87400c1 Mon Sep 17 00:00:00 2001 From: Nick Moutafis Date: Fri, 16 Feb 2018 10:54:27 +0000 Subject: [PATCH 53/94] Added extra info on create_material_instance Documented the additional parameters for the create_material_instance function. --- docs/Material_API.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/Material_API.md b/docs/Material_API.md index 8d7bcda55..ec5909798 100644 --- a/docs/Material_API.md +++ b/docs/Material_API.md @@ -70,6 +70,13 @@ import unreal_engine as ue material_instance = ue.create_material_instance(new_material) ``` +```python +import unreal_engine as ue +# you can also specify a path and a name to the new material instance. +material_instance = ue.create_material_instance(new_material, '/Game/Materials/', 'New Funny Material Instance') +``` + + Or the factory way: ```python From ff24810874c4761559ca716aba4a6a2d5ec9c950 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Feb 2018 17:29:52 +0100 Subject: [PATCH 54/94] added PythonHouseKeeper class --- Source/UnrealEnginePython/Private/PyActor.cpp | 14 +- .../Private/PythonDelegate.cpp | 6 + .../UnrealEnginePython/Private/UEPyEngine.cpp | 2 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 195 +++++---------- .../UnrealEnginePython/Private/UEPyModule.h | 1 - .../Private/UObject/UEPyActor.cpp | 7 +- .../Private/UObject/UEPyInput.cpp | 6 +- .../Private/UnrealEnginePython.cpp | 227 +++++++++++------- .../Private/UnrealEnginePythonPrivatePCH.h | 12 +- .../UnrealEnginePython/Public/PythonCanary.h | 25 -- .../Public/PythonHouseKeeper.h | 187 +++++++++++++++ .../Public/UnrealEnginePython.h | 3 - 12 files changed, 422 insertions(+), 263 deletions(-) delete mode 100644 Source/UnrealEnginePython/Public/PythonCanary.h create mode 100644 Source/UnrealEnginePython/Public/PythonHouseKeeper.h diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 9aa9dcdb1..9e1304c90 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -70,6 +70,7 @@ void APyActor::PreInitializeComponents() PyObject_SetAttrString(py_actor_instance, (char*)"uobject", (PyObject *)py_uobject); + if (!PyObject_HasAttrString(py_actor_instance, (char *)"tick") || PythonTickForceDisabled) { SetActorTickEnabled(false); @@ -80,6 +81,7 @@ void APyActor::PreInitializeComponents() ue_bind_events_for_py_class_by_attribute(this, py_actor_instance); + if (!PyObject_HasAttrString(py_actor_instance, (char *)"pre_initialize_components")) return; @@ -134,6 +136,7 @@ void APyActor::BeginPlay() return; } Py_DECREF(bp_ret); + } @@ -179,6 +182,8 @@ void APyActor::EndPlay(const EEndPlayReason::Type EndPlayReason) Py_XDECREF(ep_ret); } + + Super::EndPlay(EndPlayReason); // ... @@ -296,10 +301,9 @@ APyActor::~APyActor() UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif - // this could trigger the destruction of the python/uobject mapper - if (py_uobject) - { - UE_LOG(LogPython, Error, TEXT("UObject refcnt = %d"), py_uobject->ob_base.ob_refcnt); - } + Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); + + } diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index b466a5ec2..e25881521 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -12,6 +12,7 @@ void UPythonDelegate::SetPyCallable(PyObject *callable) // do not acquire the gil here as we set the callable in python call themselves py_callable = callable; Py_INCREF(py_callable); + UE_LOG(LogPython, Error, TEXT("CALLABLE %d"), py_callable->ob_refcnt); } void UPythonDelegate::SetSignature(UFunction *original_signature) @@ -104,6 +105,11 @@ void UPythonDelegate::PyInputAxisHandler(float value) UPythonDelegate::~UPythonDelegate() { FScopePythonGIL gil; + + if (py_callable) + { + UE_LOG(LogPython, Error, TEXT("DELEGATE N %d\n"), py_callable->ob_refcnt); + } Py_XDECREF(py_callable); #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("PythonDelegate callable XDECREF'ed")); diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 45c5ff937..e4f5b1b50 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -511,7 +511,7 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) PyObject *obj; PyObject *py_outer = NULL; char *name = nullptr; - uint64 flags = (uint64)(RF_Public | RF_Standalone); + uint64 flags = (uint64)(RF_Public); if (!PyArg_ParseTuple(args, "O|OsK:new_object", &obj, &py_outer, &name, &flags)) { return NULL; diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 7ba935e52..109251c4e 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1,7 +1,5 @@ #include "UnrealEnginePythonPrivatePCH.h" - - #include "UEPyEngine.h" #include "UEPyTimer.h" #include "UEPyTicker.h" @@ -66,6 +64,7 @@ DEFINE_LOG_CATEGORY(LogPython); PyDoc_STRVAR(unreal_engine_py_doc, "Unreal Engine Python module."); + #if PY_MAJOR_VERSION >= 3 static PyModuleDef unreal_engine_module = { PyModuleDef_HEAD_INIT, @@ -74,108 +73,21 @@ static PyModuleDef unreal_engine_module = { -1, NULL, }; +static PyObject *init_unreal_engine(void); -static PyObject *init_unreal_engine(void) -{ - return PyModule_Create(&unreal_engine_module); -} -#endif - -namespace UnrealEnginePythonHouseKeeper -{ - std::map u_object_py_mapping; - - struct FPythonDelegateTracker - { - FWeakObjectPtr owner; - UPythonDelegate *delegate; - - FPythonDelegateTracker(UPythonDelegate *delegate_to_track, UObject *delegate_owner) : owner(delegate_owner), delegate(delegate_to_track) - { - } - - ~FPythonDelegateTracker() - { - } - }; - std::list py_delegates_tracker; -}; - -static void ue_py_delegates_gc() -{ - auto &lst = UnrealEnginePythonHouseKeeper::py_delegates_tracker; - for (auto itr = lst.begin(); itr != lst.end(); /*noop*/) - //for (auto itr = lst.begin(); itr != lst.end(); ++itr) - { - /*if (itr->owner.IsValid()) - { - UE_LOG(LogPython, Error, TEXT("Delegate for %s"), *itr->owner.Get()->GetName()); - } - else - { - UE_LOG(LogPython, Error, TEXT("Found bad delegate")); - } - */ - if (!itr->owner.IsValid()) - { - itr->delegate->RemoveFromRoot(); - itr = lst.erase(itr); - } - else - ++itr; - } -} -UPythonDelegate *ue_py_new_delegate(UObject *owner, PyObject *py_callable, UFunction *signature) +void init_unreal_engine_builtin() { - // TODO: do a round of gc only if enough time passed - ue_py_delegates_gc(); - - UPythonDelegate *py_delegate = NewObject(); - if (!py_delegate) - return nullptr; - - py_delegate->AddToRoot(); - py_delegate->SetPyCallable(py_callable); - py_delegate->SetSignature(signature); - - UnrealEnginePythonHouseKeeper::FPythonDelegateTracker tracker(py_delegate, owner); - UnrealEnginePythonHouseKeeper::py_delegates_tracker.push_back(tracker); - - return py_delegate; + PyImport_AppendInittab("unreal_engine", &init_unreal_engine); } +#endif static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) { - std::list broken_list; - for (auto it : UnrealEnginePythonHouseKeeper::u_object_py_mapping) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), it.first); -#endif - UObject *u_obj = it.first; - if (!u_obj || !u_obj->IsValidLowLevel() || u_obj->IsPendingKillOrUnreachable()) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), it.first, it.second->ob_base.ob_refcnt); -#endif - broken_list.push_back(u_obj); - } - else - { - UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), u_obj, *u_obj->GetName()); - } - } - /* - for (UObject *u_obj : broken_list) - { - ue_PyUObject *py_obj = UnrealEnginePythonHouseKeeper::u_object_py_mapping.at(u_obj); - Py_DECREF(py_obj); - }*/ - - return PyLong_FromLong(broken_list.size()); + int32 Garbaged = FUnrealEnginePythonHouseKeeper::Get()->RunGC(); + return PyLong_FromLong(Garbaged); } @@ -201,8 +113,7 @@ static PyObject *py_unreal_engine_sandbox_exec(PyObject * self, PyObject * args) } FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); PythonModule.RunFileSandboxed(filename, nullptr, nullptr); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) @@ -213,6 +124,7 @@ static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) if (self->py_proxy) { Py_INCREF(self->py_proxy); + UE_LOG(LogPython, Error, TEXT("PROXY %d"), self->py_proxy->ob_refcnt); return (PyObject *)self->py_proxy; } @@ -1047,7 +959,6 @@ static PyMethodDef ue_PyUObject_methods[] = { // destructor static void ue_pyobject_dealloc(ue_PyUObject *self) { - PyObject_GC_UnTrack(self); #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); #endif @@ -1057,12 +968,14 @@ static void ue_pyobject_dealloc(ue_PyUObject *self) } Py_XDECREF(self->py_dict); - UnrealEnginePythonHouseKeeper::u_object_py_mapping.erase(self->ue_object); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) { + ue_py_check(self); + PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); if (!ret) { @@ -1162,6 +1075,8 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObject *value) { + ue_py_check_int(self); + // first of all check for UProperty if (PyUnicodeOrString_Check(attr_name)) { @@ -1199,6 +1114,8 @@ static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObje static PyObject *ue_PyUObject_str(ue_PyUObject *self) { + ue_py_check(self); + #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromFormat("", TCHAR_TO_UTF8(*self->ue_object->GetName()), self->ue_object, TCHAR_TO_UTF8(*self->ue_object->GetClass()->GetName()), self->ob_base.ob_refcnt); @@ -1208,14 +1125,9 @@ static PyObject *ue_PyUObject_str(ue_PyUObject *self) #endif } -static int ue_PyUObject_traverse(ue_PyUObject *self, visitproc visit, void *arg) -{ - UE_LOG(LogPython, Error, TEXT("TRAVERSING %p"), self); - return 0; -} - static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject *kw) { + ue_py_check(self); // if it is a class, create a new object if (self->ue_object->IsA()) { @@ -1329,9 +1241,9 @@ static PyTypeObject ue_PyUObjectType = { (getattrofunc)ue_PyUObject_getattro, /* tp_getattro */ (setattrofunc)ue_PyUObject_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Unreal Engine generic UObject", /* tp_doc */ - (traverseproc)ue_PyUObject_traverse, /* tp_traverse */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine UObject wrapper", /* tp_doc */ + 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -1340,6 +1252,11 @@ static PyTypeObject ue_PyUObjectType = { ue_PyUObject_methods, /* tp_methods */ }; + + + + + UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) { bool is_overwriting = false; @@ -1779,7 +1696,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = ue_py_new_delegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -1930,13 +1847,10 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k void unreal_engine_init_py_module() { #if PY_MAJOR_VERSION >= 3 - PyImport_AppendInittab("unreal_engine", init_unreal_engine); PyObject *new_unreal_engine_module = PyImport_AddModule("unreal_engine"); #else PyObject *new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); #endif - - PyObject *unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); PyMethodDef *unreal_engine_function; @@ -1947,16 +1861,6 @@ void unreal_engine_init_py_module() Py_DECREF(func); } - - ue_PyUObjectType.tp_new = PyType_GenericNew; - ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; - ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); - if (PyType_Ready(&ue_PyUObjectType) < 0) - return; - - Py_INCREF(&ue_PyUObjectType); - PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); - ue_python_init_fvector(new_unreal_engine_module); ue_python_init_frotator(new_unreal_engine_module); ue_python_init_ftransform(new_unreal_engine_module); @@ -1965,6 +1869,7 @@ void unreal_engine_init_py_module() ue_python_init_flinearcolor(new_unreal_engine_module); ue_python_init_fquat(new_unreal_engine_module); + ue_python_init_frandomstream(new_unreal_engine_module); ue_python_init_fraw_anim_sequence_track(new_unreal_engine_module); @@ -2141,35 +2046,39 @@ void unreal_engine_init_py_module() } + // utility functions ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) { - if (!ue_obj || !ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) + if (!ue_obj) return nullptr; - std::map::iterator it = UnrealEnginePythonHouseKeeper::u_object_py_mapping.find(ue_obj); - // not found ?? - if (it == UnrealEnginePythonHouseKeeper::u_object_py_mapping.end()) + + ue_PyUObject *ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); + if (!ret) { + if (!ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) + return nullptr; - //ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); - ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_GC_New(ue_PyUObject, &ue_PyUObjectType); + ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); if (!ue_py_object) { return nullptr; } ue_py_object->ue_object = ue_obj; + ue_py_object->py_proxy = nullptr; + ue_py_object->auto_rooted = 0; ue_py_object->py_dict = PyDict_New(); - UnrealEnginePythonHouseKeeper::u_object_py_mapping[ue_obj] = ue_py_object; + FUnrealEnginePythonHouseKeeper::Get()->RegisterPyUObject(ue_obj, ue_py_object); #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("CREATED UPyObject at %p for %p %s"), ue_py_object, ue_obj, *ue_obj->GetName()); #endif - //Py_INCREF(ue_py_object); return ue_py_object; } - return it->second; + return ret; + } ue_PyUObject *ue_get_python_uobject_inc(UObject *ue_obj) @@ -2258,7 +2167,7 @@ void unreal_engine_py_log_error() } PyErr_Clear(); - } +} // retrieve a UWorld from a generic UObject (if possible) UWorld *ue_get_uworld(ue_PyUObject *py_obj) @@ -3282,7 +3191,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = ue_py_new_delegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3674,3 +3583,23 @@ bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct return ue_py_struct->u_struct->IsChildOf(parent_u_struct); } + +static PyObject *init_unreal_engine() +{ + + ue_PyUObjectType.tp_new = PyType_GenericNew; + ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; + ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); + + if (PyType_Ready(&ue_PyUObjectType) < 0) + return nullptr; + + PyObject *new_unreal_engine_module = PyModule_Create(&unreal_engine_module); + if (!new_unreal_engine_module) + return nullptr; + + Py_INCREF(&ue_PyUObjectType); + PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); + + return new_unreal_engine_module; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 5a8d1019b..375245a1c 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -89,4 +89,3 @@ template bool ue_py_check_childstruct(PyObject *py_obj) FGuid *ue_py_check_fguid(PyObject *); -UPythonDelegate *ue_py_new_delegate(UObject *, PyObject *p_callable, UFunction *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index 5887067cb..f705074ef 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -132,13 +132,12 @@ PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) ue_py_check(self); - if (!self->ue_object->IsA()) + AActor *actor = ue_py_check_type(self); + if (!actor) { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } - AActor *actor = (AActor *)self->ue_object; - actor->Destroy(); Py_RETURN_NONE; @@ -739,7 +738,7 @@ PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwar { ue_py_check(self); - + PyObject *py_class; PyObject *py_obj_location = nullptr; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp index 677a528ce..47578a83b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp @@ -405,7 +405,7 @@ PyObject *py_ue_bind_action(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputActionBinding input_action_binding(FName(UTF8_TO_TCHAR(action_name)), (const EInputEvent)key); input_action_binding.ActionDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); @@ -452,7 +452,7 @@ PyObject *py_ue_bind_axis(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputAxisBinding input_axis_binding(FName(UTF8_TO_TCHAR(axis_name))); input_axis_binding.AxisDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputAxisHandler); @@ -503,7 +503,7 @@ PyObject *py_ue_bind_key(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = ue_py_new_delegate(input, py_callable, nullptr); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputKeyBinding input_key_binding(FKey(UTF8_TO_TCHAR(key_name)), (const EInputEvent)key); input_key_binding.KeyDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index be68ba904..3fefa2edf 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -2,7 +2,6 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PythonBlueprintFunctionLibrary.h" -#include "PythonCanary.h" #include "HAL/IConsoleManager.h" #if ENGINE_MINOR_VERSION < 13 #include "ClassIconFinder.h" @@ -15,16 +14,20 @@ #endif void unreal_engine_init_py_module(); +void init_unreal_engine_builtin(); #if defined(UNREAL_ENGINE_PYTHON_ON_LINUX) const char *ue4_module_options = "linux_global_symbols"; #endif #if PY_MAJOR_VERSION < 3 -char *PyUnicode_AsUTF8(PyObject *py_str) { - if (PyUnicode_Check(py_str)) { +char *PyUnicode_AsUTF8(PyObject *py_str) +{ + if (PyUnicode_Check(py_str)) + { PyObject *unicode = PyUnicode_AsUTF8String(py_str); - if (unicode) { + if (unicode) + { return PyString_AsString(unicode); } // just a hack to avoid crashes @@ -33,18 +36,22 @@ char *PyUnicode_AsUTF8(PyObject *py_str) { return PyString_AsString(py_str); } -int PyGILState_Check() { +int PyGILState_Check() +{ PyThreadState * tstate = _PyThreadState_Current; return tstate && (tstate == PyGILState_GetThisThreadState()); } #endif -bool PyUnicodeOrString_Check(PyObject *py_obj) { - if (PyUnicode_Check(py_obj)) { +bool PyUnicodeOrString_Check(PyObject *py_obj) +{ + if (PyUnicode_Check(py_obj)) + { return true; } #if PY_MAJOR_VERSION < 3 - else if (PyString_Check(py_obj)) { + else if (PyString_Check(py_obj)) + { return true; } #endif @@ -54,26 +61,31 @@ bool PyUnicodeOrString_Check(PyObject *py_obj) { #define LOCTEXT_NAMESPACE "FUnrealEnginePythonModule" -void FUnrealEnginePythonModule::PythonGILRelease() { +void FUnrealEnginePythonModule::PythonGILRelease() +{ #if defined(UEPY_THREADING) - if (PyGILState_Check() == 1) { + if (PyGILState_Check() == 1) + { ue_python_gil = PyEval_SaveThread(); } #endif } -bool FUnrealEnginePythonModule::PythonGILAcquire() { +bool FUnrealEnginePythonModule::PythonGILAcquire() +{ #if defined(UEPY_THREADING) - if (PyGILState_Check() == 0) { + if (PyGILState_Check() == 0) + { PyEval_RestoreThread((PyThreadState *)ue_python_gil); return true; } return false; #endif return true; -} + } -void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) { +void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) +{ #if PY_MAJOR_VERSION >= 3 wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL }; @@ -101,13 +113,15 @@ void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) { PyObject *py_additional_modules_path = PyUnicode_FromString(additional_modules_path); PyList_Insert(py_path, 0, py_additional_modules_path); - if (verbose) { + if (verbose) + { UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion())); UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path)); } } -static void setup_stdout_stderr() { +static void setup_stdout_stderr() +{ // Redirecting stdout char const* code = "import sys\n" "import unreal_engine\n" @@ -134,7 +148,8 @@ static void setup_stdout_stderr() { PyRun_SimpleString(code); } -namespace { +namespace +{ static void consoleExecScript(const TArray& Args) { if (Args.Num() != 1) @@ -148,24 +163,24 @@ namespace { } } - static void consoleExecString(const TArray& Args) - { - if (Args.Num() == 0) - { - UE_LOG(LogPython, Warning, TEXT("Usage: 'py.cmd '.")); - UE_LOG(LogPython, Warning, TEXT(" scriptname: Name of script, must reside in Scripts folder. Ex: myscript.py")); - } - else - { - FString cmdString; - for (const FString& argStr : Args) - { - cmdString += argStr.TrimQuotes() + '\n'; - } - - UPythonBlueprintFunctionLibrary::ExecutePythonString(cmdString); - } - } + static void consoleExecString(const TArray& Args) + { + if (Args.Num() == 0) + { + UE_LOG(LogPython, Warning, TEXT("Usage: 'py.cmd '.")); + UE_LOG(LogPython, Warning, TEXT(" scriptname: Name of script, must reside in Scripts folder. Ex: myscript.py")); + } + else + { + FString cmdString; + for (const FString& argStr : Args) + { + cmdString += argStr.TrimQuotes() + '\n'; + } + + UPythonBlueprintFunctionLibrary::ExecutePythonString(cmdString); + } + } } FAutoConsoleCommand ExecPythonScriptCommand( @@ -174,9 +189,9 @@ FAutoConsoleCommand ExecPythonScriptCommand( FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecScript)); FAutoConsoleCommand ExecPythonStringCommand( - TEXT("py.cmd"), - *NSLOCTEXT("UnrealEnginePython", "CommandText_Cmd", "Execute python string").ToString(), - FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecString)); + TEXT("py.cmd"), + *NSLOCTEXT("UnrealEnginePython", "CommandText_Cmd", "Execute python string").ToString(), + FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecString)); void FUnrealEnginePythonModule::StartupModule() { @@ -185,7 +200,8 @@ void FUnrealEnginePythonModule::StartupModule() // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module FString PythonHome; - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) + { #if PY_MAJOR_VERSION >= 3 wchar_t *home = (wchar_t *)*PythonHome; #else @@ -193,9 +209,10 @@ void FUnrealEnginePythonModule::StartupModule() #endif FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); Py_SetPythonHome(home); - } +} - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), PythonHome, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), PythonHome, GEngineIni)) + { PythonHome = FPaths::Combine(*PROJECT_CONTENT_DIR, *PythonHome); FPaths::NormalizeFilename(PythonHome); PythonHome = FPaths::ConvertRelativePathToFull(PythonHome); @@ -206,10 +223,11 @@ void FUnrealEnginePythonModule::StartupModule() #endif Py_SetPythonHome(home); - } +} FString IniValue; - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) + { #if PY_MAJOR_VERSION >= 3 wchar_t *program_name = (wchar_t *)*IniValue; #else @@ -218,7 +236,8 @@ void FUnrealEnginePythonModule::StartupModule() Py_SetProgramName(program_name); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeProgramName"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeProgramName"), IniValue, GEngineIni)) + { IniValue = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); FPaths::NormalizeFilename(IniValue); IniValue = FPaths::ConvertRelativePathToFull(IniValue); @@ -230,39 +249,48 @@ void FUnrealEnginePythonModule::StartupModule() Py_SetProgramName(program_name); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ScriptsPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ScriptsPath"), IniValue, GEngineIni)) + { ScriptsPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeScriptsPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeScriptsPath"), IniValue, GEngineIni)) + { ScriptsPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("AdditionalModulesPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("AdditionalModulesPath"), IniValue, GEngineIni)) + { AdditionalModulesPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeAdditionalModulesPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeAdditionalModulesPath"), IniValue, GEngineIni)) + { AdditionalModulesPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ZipPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ZipPath"), IniValue, GEngineIni)) + { ZipPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeZipPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeZipPath"), IniValue, GEngineIni)) + { ZipPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (ScriptsPath.IsEmpty()) { + if (ScriptsPath.IsEmpty()) + { ScriptsPath = FPaths::Combine(*PROJECT_CONTENT_DIR, UTF8_TO_TCHAR("Scripts")); } - if (ZipPath.IsEmpty()) { + if (ZipPath.IsEmpty()) + { ZipPath = FPaths::Combine(*PROJECT_CONTENT_DIR, UTF8_TO_TCHAR("ue_python.zip")); } - if (!FPaths::DirectoryExists(ScriptsPath)) { + if (!FPaths::DirectoryExists(ScriptsPath)) + { IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); PlatformFile.CreateDirectory(*ScriptsPath); } @@ -304,7 +332,9 @@ void FUnrealEnginePythonModule::StartupModule() PathVars.Append(OurPythonPaths); FString ModifiedPath = FString::Join(PathVars, PathDelimiter); FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); - } + } + + init_unreal_engine_builtin(); Py_Initialize(); @@ -328,10 +358,12 @@ void FUnrealEnginePythonModule::StartupModule() setup_stdout_stderr(); - if (PyImport_ImportModule("ue_site")) { + if (PyImport_ImportModule("ue_site")) + { UE_LOG(LogPython, Log, TEXT("ue_site Python module successfully imported")); } - else { + else + { // TODO gracefully manage the error unreal_engine_py_log_error(); } @@ -339,8 +371,7 @@ void FUnrealEnginePythonModule::StartupModule() // release the GIL PythonGILRelease(); - NewObject(); -} + } void FUnrealEnginePythonModule::ShutdownModule() { @@ -348,25 +379,30 @@ void FUnrealEnginePythonModule::ShutdownModule() // we call this function before unloading the module. UE_LOG(LogPython, Log, TEXT("Goodbye Python")); - if (!BrutalFinalize) { + if (!BrutalFinalize) + { PythonGILAcquire(); Py_Finalize(); } } -void FUnrealEnginePythonModule::RunString(char *str) { +void FUnrealEnginePythonModule::RunString(char *str) +{ FScopePythonGIL gil; PyObject *eval_ret = PyRun_String(str, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(eval_ret); } -FString FUnrealEnginePythonModule::Pep8ize(FString Code) { +FString FUnrealEnginePythonModule::Pep8ize(FString Code) +{ PyObject *pep8izer_module = PyImport_ImportModule("autopep8"); - if (!pep8izer_module) { + if (!pep8izer_module) + { unreal_engine_py_log_error(); UE_LOG(LogPython, Error, TEXT("unable to load autopep8 module, please install it")); // return the original string to avoid losing data @@ -374,7 +410,8 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } PyObject *pep8izer_func = PyObject_GetAttrString(pep8izer_module, (char*)"fix_code"); - if (!pep8izer_func) { + if (!pep8izer_func) + { unreal_engine_py_log_error(); UE_LOG(LogPython, Error, TEXT("unable to get autopep8.fix_code function")); // return the original string to avoid losing data @@ -382,13 +419,15 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } PyObject *ret = PyObject_CallFunction(pep8izer_func, (char *)"s", TCHAR_TO_UTF8(*Code)); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); // return the original string to avoid losing data return Code; } - if (!PyUnicode_Check(ret)) { + if (!PyUnicode_Check(ret)) + { UE_LOG(LogPython, Error, TEXT("returned value is not a string")); // return the original string to avoid losing data return Code; @@ -402,13 +441,15 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } // run a python string in a new sub interpreter -void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { +void FUnrealEnginePythonModule::RunStringSandboxed(char *str) +{ FScopePythonGIL gil; PyThreadState *_main = PyThreadState_Get(); PyThreadState *py_new_state = Py_NewInterpreter(); - if (!py_new_state) { + if (!py_new_state) + { UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter")); return; } @@ -420,16 +461,18 @@ void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { setup_stdout_stderr(); PyObject *m = PyImport_AddModule("__main__"); - if (m == NULL) { + if (m == NULL) + { UE_LOG(LogPython, Error, TEXT("Unable to create new global dict")); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); return; - } +} PyObject *global_dict = PyModule_GetDict(m); PyObject *eval_ret = PyRun_String(str, Py_file_input, global_dict, global_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -440,7 +483,8 @@ void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { PyThreadState_Swap(_main); } -void FUnrealEnginePythonModule::RunFile(char *filename) { +void FUnrealEnginePythonModule::RunFile(char *filename) +{ FScopePythonGIL gil; char *full_path = filename; if (!FPaths::FileExists(filename)) @@ -451,21 +495,24 @@ void FUnrealEnginePythonModule::RunFile(char *filename) { FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) { + if (fopen_s(&fd, full_path, "r") != 0) + { UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); return; } #else fd = fopen(full_path, "r"); - if (!fd) { + if (!fd) + { UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); return; -} + } #endif PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); fclose(fd); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } @@ -474,7 +521,8 @@ void FUnrealEnginePythonModule::RunFile(char *filename) { // damn, this is horrible, but it is the only way i found to avoid the CRT error :( FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } @@ -483,7 +531,8 @@ void FUnrealEnginePythonModule::RunFile(char *filename) { } // run a python script in a new sub interpreter (useful for unit tests) -void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback)(void *arg), void *arg) { +void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback)(void *arg), void *arg) +{ FScopePythonGIL gil; char *full_path = filename; if (!FPaths::FileExists(filename)) @@ -494,7 +543,8 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) PyThreadState *_main = PyThreadState_Get(); PyThreadState *py_new_state = Py_NewInterpreter(); - if (!py_new_state) { + if (!py_new_state) + { UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter")); return; } @@ -506,25 +556,28 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) setup_stdout_stderr(); PyObject *m = PyImport_AddModule("__main__"); - if (m == NULL) { + if (m == NULL) + { UE_LOG(LogPython, Error, TEXT("Unable to create new global dict")); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); return; -} + } PyObject *global_dict = PyModule_GetDict(m); #if PY_MAJOR_VERSION >= 3 FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) { + if (fopen_s(&fd, full_path, "r") != 0) + { UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); return; } #else fd = fopen(full_path, "r"); - if (!fd) { + if (!fd) + { UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); return; } @@ -532,7 +585,8 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict); fclose(fd); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -543,7 +597,8 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) // damn, this is horrible, but it is the only way i found to avoid the CRT error :( FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -556,7 +611,7 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); - } +} #undef LOCTEXT_NAMESPACE diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index ff5a52252..ca39635cc 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -80,9 +80,17 @@ #include "SlateApplication/UEPyFSlateApplication.h" #include "Voice/UEPyIVoiceCapture.h" +#include "PythonHouseKeeper.h" -#define ue_py_check(py_u) if (!py_u->ue_object || !py_u->ue_object->IsValidLowLevel() || py_u->ue_object->IsPendingKillOrUnreachable())\ - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") + +#define ue_py_check(py_u) if (!FUnrealEnginePythonHouseKeeper::Get()->IsValidPyUObject(py_u))\ + return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") + +#define ue_py_check_int(py_u) if (!FUnrealEnginePythonHouseKeeper::Get()->IsValidPyUObject(py_u))\ + {\ + PyErr_SetString(PyExc_Exception, "PyUObject is in invalid state");\ + return -1;\ + } #if PY_MAJOR_VERSION < 3 char *PyUnicode_AsUTF8(PyObject *py_str); diff --git a/Source/UnrealEnginePython/Public/PythonCanary.h b/Source/UnrealEnginePython/Public/PythonCanary.h deleted file mode 100644 index 1f811fbfa..000000000 --- a/Source/UnrealEnginePython/Public/PythonCanary.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "UnrealEnginePython.h" -#include "UObject/Object.h" -#include "PythonCanary.generated.h" - -UCLASS(MinimalAPI) -class UPythonCanary : public UObject -{ - GENERATED_BODY() - -public: - UPythonCanary() - { - UE_LOG(LogPython, Error, TEXT("Canary Spawned")); - } - - ~UPythonCanary() - { - UE_LOG(LogPython, Error, TEXT("Canary IS DEAD !!!")); - - NewObject(); - } -}; - diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h new file mode 100644 index 000000000..84096f74b --- /dev/null +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -0,0 +1,187 @@ +#pragma once + +#include "UnrealEnginePythonPrivatePCH.h" + +class FUnrealEnginePythonHouseKeeper +{ + + struct FPythonUOjectTracker + { + FWeakObjectPtr Owner; + ue_PyUObject *PyUObject; + + FPythonUOjectTracker(UObject *Object, ue_PyUObject *InPyUObject) + { + Owner = FWeakObjectPtr(Object); + PyUObject = InPyUObject; + } + }; + + struct FPythonDelegateTracker + { + FWeakObjectPtr Owner; + UPythonDelegate *Delegate; + + FPythonDelegateTracker(UPythonDelegate *DelegateToTrack, UObject *DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonDelegateTracker() + { + } + }; + +public: + + static FUnrealEnginePythonHouseKeeper *Get() + { + static FUnrealEnginePythonHouseKeeper *Singleton; + if (!Singleton) + { + Singleton = new FUnrealEnginePythonHouseKeeper(); + // register a new delegate for the GC + FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); + } + return Singleton; + } + + void RunGCDelegate() + { + FScopePythonGIL gil; + RunGC(); + } + + int32 RunGC() + { + int32 Garbaged = PyUObjectsGC(); + Garbaged += DelegatesGC(); + return Garbaged; + } + + bool IsValidPyUObject(ue_PyUObject *PyUObject) + { + if (!PyUObject) + return false; + + UObject *Object = PyUObject->ue_object; + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return false; + } + + if (!Tracker->Owner.IsValid()) + return false; + + return true; + + } + + void RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject) + { + UObjectPyMapping.Add(Object, FPythonUOjectTracker(Object, InPyUObject)); + } + + void UnregisterPyUObject(UObject *Object) + { + UObjectPyMapping.Remove(Object); + } + + ue_PyUObject *GetPyUObject(UObject *Object) + { + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return nullptr; + } + + if (!Tracker->Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("DEFREF'ing UObject at %p (refcnt: %d)"), Object, Tracker->PyUObject->ob_base.ob_refcnt); +#endif + Py_DECREF((PyObject *)Tracker->PyUObject); + UnregisterPyUObject(Object); + return nullptr; + } + + return Tracker->PyUObject; + } + + uint32 PyUObjectsGC() + { + uint32 Garbaged = 0; + TArray BrokenList; + for (auto &UObjectPyItem : UObjectPyMapping) + { + UObject *Object = UObjectPyItem.Key; + FPythonUOjectTracker &Tracker = UObjectPyItem.Value; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), Object); +#endif + if (!Tracker.Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), Object, Tracker.PyUObject->ob_base.ob_refcnt); +#endif + BrokenList.Add(Object); + Garbaged++; + } + else + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), Object, *Object->GetName()); +#endif + } + } + + for (UObject *Object : BrokenList) + { + FPythonUOjectTracker &Tracker = UObjectPyMapping[Object]; + Py_DECREF((PyObject *)Tracker.PyUObject); + UnregisterPyUObject(Object); + } + + return Garbaged; + + } + + + int32 DelegatesGC() + { + int32 Garbaged = 0; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Error, TEXT("Checking %d delegates"), PyDelegatesTracker.Num()); +#endif + for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; + if (!Tracker.Owner.IsValid(true)) + { + Tracker.Delegate->RemoveFromRoot(); + PyDelegatesTracker.RemoveAt(i); + Garbaged++; + } + + } + return Garbaged; + } + + UPythonDelegate *NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature) + { + UPythonDelegate *Delegate = NewObject(); + + Delegate->AddToRoot(); + Delegate->SetPyCallable(PyCallable); + Delegate->SetSignature(Signature); + + FPythonDelegateTracker Tracker(Delegate, Owner); + PyDelegatesTracker.Add(Tracker); + + return Delegate; + } + +private: + TMap UObjectPyMapping; + TArray PyDelegatesTracker; +}; diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index 4a7035e8d..35ef5031b 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -13,9 +13,6 @@ #include "Runtime/SlateCore/Public/Styling/ISlateStyle.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" -#include -#include - // We need to make sure reference structs do not mistaken for callable #define PyCalllable_Check_Extended(value) PyCallable_Check(value) && py_ue_is_uscriptstruct(value) == nullptr From 90cbe9b02634a7a6b66bac2348cbe7977e3e2069 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Feb 2018 18:18:26 +0100 Subject: [PATCH 55/94] completed first part of UObject GC, Timer and Ticker refactoring --- Source/UnrealEnginePython/Private/PyActor.cpp | 7 --- .../Private/PyCharacter.cpp | 7 +-- Source/UnrealEnginePython/Private/PyHUD.cpp | 8 +-- Source/UnrealEnginePython/Private/PyPawn.cpp | 10 +--- .../Private/PyUserWidget.cpp | 6 +-- .../Private/PythonComponent.cpp | 7 +-- .../Private/PythonDelegate.cpp | 7 +-- .../Private/PythonFunction.cpp | 3 +- .../UnrealEnginePython/Private/UEPyTicker.h | 2 +- .../UnrealEnginePython/Private/UEPyTimer.cpp | 54 +++++++++++-------- Source/UnrealEnginePython/Private/UEPyTimer.h | 5 +- 11 files changed, 46 insertions(+), 70 deletions(-) diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 9e1304c90..a7fa60170 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -289,19 +289,12 @@ APyActor::~APyActor() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_actor_instance && py_actor_instance->ob_refcnt != 1) - { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python AActor wrapper refcnt = %d"), py_actor_instance->ob_refcnt); - } -#endif Py_XDECREF(py_actor_instance); #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif - Py_XDECREF(py_uobject); FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index cf3c45788..ce5954922 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -443,11 +443,7 @@ APyCharacter::~APyCharacter() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_character_instance && py_character_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python ACharacter wrapper refcnt = %d"), py_character_instance->ob_refcnt); - } -#endif + Py_XDECREF(py_character_instance); #if defined(UEPY_MEMORY_DEBUG) @@ -456,4 +452,5 @@ APyCharacter::~APyCharacter() // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyHUD.cpp b/Source/UnrealEnginePython/Private/PyHUD.cpp index bdf59af35..dbedc3027 100644 --- a/Source/UnrealEnginePython/Private/PyHUD.cpp +++ b/Source/UnrealEnginePython/Private/PyHUD.cpp @@ -242,12 +242,7 @@ APyHUD::~APyHUD() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_hud_instance && py_hud_instance->ob_refcnt != 1) - { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python AHUD wrapper refcnt = %d"), py_hud_instance->ob_refcnt); - } -#endif + Py_XDECREF(py_hud_instance); #if defined(UEPY_MEMORY_DEBUG) @@ -256,4 +251,5 @@ APyHUD::~APyHUD() // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyPawn.cpp b/Source/UnrealEnginePython/Private/PyPawn.cpp index 02a79ff6c..066716863 100644 --- a/Source/UnrealEnginePython/Private/PyPawn.cpp +++ b/Source/UnrealEnginePython/Private/PyPawn.cpp @@ -239,18 +239,12 @@ APyPawn::~APyPawn() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_pawn_instance && py_pawn_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python APawn wrapper refcnt = %d"), py_pawn_instance->ob_refcnt); - } -#endif - Py_XDECREF(py_pawn_instance); - - +# #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Python APawn (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index e07b3c7c6..a7efd3b92 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -355,11 +355,6 @@ UPyUserWidget::~UPyUserWidget() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_user_widget_instance && py_user_widget_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python UUserWidget wrapper refcnt = %d"), py_user_widget_instance->ob_refcnt); -} -#endif Py_XDECREF(py_user_widget_instance); #if defined(UEPY_MEMORY_DEBUG) @@ -368,6 +363,7 @@ UPyUserWidget::~UPyUserWidget() // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } void UPyUserWidget::CallPythonUserWidgetMethod(FString method_name, FString args) diff --git a/Source/UnrealEnginePython/Private/PythonComponent.cpp b/Source/UnrealEnginePython/Private/PythonComponent.cpp index 2a8c630ef..1d8879494 100644 --- a/Source/UnrealEnginePython/Private/PythonComponent.cpp +++ b/Source/UnrealEnginePython/Private/PythonComponent.cpp @@ -556,11 +556,7 @@ UPythonComponent::~UPythonComponent() { FScopePythonGIL gil; -#if defined(UEPY_MEMORY_DEBUG) - if (py_component_instance && py_component_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python UActorComponent wrapper refcnt = %d"), py_component_instance->ob_refcnt); - } -#endif +# Py_XDECREF(py_component_instance); #if defined(UEPY_MEMORY_DEBUG) @@ -569,4 +565,5 @@ UPythonComponent::~UPythonComponent() // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index e25881521..35955e817 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -12,7 +12,6 @@ void UPythonDelegate::SetPyCallable(PyObject *callable) // do not acquire the gil here as we set the callable in python call themselves py_callable = callable; Py_INCREF(py_callable); - UE_LOG(LogPython, Error, TEXT("CALLABLE %d"), py_callable->ob_refcnt); } void UPythonDelegate::SetSignature(UFunction *original_signature) @@ -106,12 +105,8 @@ UPythonDelegate::~UPythonDelegate() { FScopePythonGIL gil; - if (py_callable) - { - UE_LOG(LogPython, Error, TEXT("DELEGATE N %d\n"), py_callable->ob_refcnt); - } Py_XDECREF(py_callable); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("PythonDelegate callable XDECREF'ed")); + UE_LOG(LogPython, Warning, TEXT("PythonDelegate %p callable XDECREF'ed"), this); #endif } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PythonFunction.cpp b/Source/UnrealEnginePython/Private/PythonFunction.cpp index f30ada1dd..f63aeb8b9 100644 --- a/Source/UnrealEnginePython/Private/PythonFunction.cpp +++ b/Source/UnrealEnginePython/Private/PythonFunction.cpp @@ -122,7 +122,8 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) UPythonFunction::~UPythonFunction() { Py_XDECREF(py_callable); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("PythonFunction callable XDECREF'ed")); + UE_LOG(LogPython, Warning, TEXT("PythonFunction callable %p XDECREF'ed"), this); #endif } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.h b/Source/UnrealEnginePython/Private/UEPyTicker.h index 3e387277d..0972a3055 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.h +++ b/Source/UnrealEnginePython/Private/UEPyTicker.h @@ -9,7 +9,7 @@ typedef struct { /* Type-specific fields go here. */ FDelegateHandle dhandle; bool garbaged; - TSharedPtr delegate_ptr; + TSharedPtr delegate_ptr; } ue_PyFDelegateHandle; PyObject *py_unreal_engine_add_ticker(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.cpp b/Source/UnrealEnginePython/Private/UEPyTimer.cpp index 277dbc053..f00a22b3c 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTimer.cpp @@ -4,23 +4,26 @@ static PyObject *py_ue_ftimerhandle_clear(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().ClearTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().ClearTimer(self->thandle); + Py_RETURN_NONE; } static PyObject *py_ue_ftimerhandle_pause(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().PauseTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().PauseTimer(self->thandle); + Py_RETURN_NONE; } static PyObject *py_ue_ftimerhandle_unpause(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().UnPauseTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().UnPauseTimer(self->thandle); + Py_RETURN_NONE; } @@ -37,7 +40,14 @@ static PyMethodDef ue_PyFTimerHandle_methods[] = { // destructor static void ue_pyftimerhandle_dealloc(ue_PyFTimerHandle *self) { - Py_DECREF(self->py_callable); + if (self->world.IsValid()) + { + self->world.Get()->GetTimerManager().ClearTimer(self->thandle); + } + if (self->delegate_ptr.IsValid()) + { + self->delegate_ptr.Reset(); + } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -107,26 +117,24 @@ PyObject *py_ue_set_timer(ue_PyUObject *self, PyObject * args) if (!world) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - FTimerDelegate timer_delegate; - TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); - py_delegate->SetPyCallable(py_callable); - - timer_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Void); - FTimerHandle thandle; - world->GetTimerManager().SetTimer(thandle, timer_delegate, rate, loop, first_delay); ue_PyFTimerHandle *ret = (ue_PyFTimerHandle *)PyObject_New(ue_PyFTimerHandle, &ue_PyFTimerHandleType); if (!ret) { - world->GetTimerManager().ClearTimer(thandle); return PyErr_Format(PyExc_Exception, "unable to allocate FTimerHandle python object"); } - ret->thandle = thandle; - ret->py_callable = py_callable; - // will be decref'ed on clear - Py_INCREF(ret->py_callable); - ret->world = world; + + FTimerDelegate timer_delegate; + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); + py_delegate->SetPyCallable(py_callable); + + timer_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Void); + + world->GetTimerManager().SetTimer(ret->thandle, timer_delegate, rate, loop, first_delay); + + new(&ret->delegate_ptr) TSharedPtr(py_delegate); + ret->world = TWeakObjectPtr(world); return (PyObject *)ret; } diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.h b/Source/UnrealEnginePython/Private/UEPyTimer.h index 41d4fe9ad..aec770fae 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.h +++ b/Source/UnrealEnginePython/Private/UEPyTimer.h @@ -8,9 +8,8 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FTimerHandle thandle; - PyObject *py_callable; - UWorld *world; - TSharedPtr delegate_ptr; + TWeakObjectPtr world; + TSharedPtr delegate_ptr; } ue_PyFTimerHandle; PyObject *py_ue_set_timer(ue_PyUObject *, PyObject *); From 74d9f2feb346458c71582d8c0097d7ec0e9126a4 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Feb 2018 19:07:56 +0100 Subject: [PATCH 56/94] Linux and OSX fixes --- .../Private/Slate/UEPySPythonMultiColumnTableRow.h | 8 ++++---- Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp | 8 ++++---- .../UnrealEnginePython/Private/UEPyUClassesImporter.cpp | 8 ++++---- .../UnrealEnginePython/Private/UEPyUStructsImporter.cpp | 8 ++++---- Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp | 8 ++++---- .../Private/Wrappers/UEPyESlateEnums.cpp | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h index 8a8933578..21ccda269 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h @@ -19,10 +19,10 @@ class SPythonMultiColumnTableRow : public SMultiColumnTableRow& InOwnerTableView, PyObject *in_py_self) { SetPyObject(in_py_self); - SMultiColumnTableRow >::Construct(FSuperRowType::FArguments(), InOwnerTableView); + SMultiColumnTableRow>::Construct(FSuperRowType::FArguments(), InOwnerTableView); } - TSharedRef SPythonMultiColumnTableRow::GenerateWidgetForColumn(const FName& ColumnName) + TSharedRef GenerateWidgetForColumn(const FName& ColumnName) { FScopePythonGIL gil; @@ -57,7 +57,7 @@ class SPythonMultiColumnTableRow : public SMultiColumnTableRowGetComponentByClass(UDestructibleComponent::StaticClass()); #else - for (UActorComponent *component : actor->GetComponents()) + for (UActorComponent *checked_component : actor->GetComponents()) { - if (Cast(component)) + if (Cast(checked_component)) { - destructible = (IDestructibleInterface *)component; + destructible = (IDestructibleInterface *)checked_component; break; } } @@ -500,4 +500,4 @@ PyObject *py_ue_destructible_apply_damage(ue_PyUObject * self, PyObject * args) } Py_RETURN_NONE; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp index d8f7e09a8..71113bd09 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp @@ -77,7 +77,7 @@ void ue_python_init_eslate_enums(PyObject *ue_module) }; #if ENGINE_MINOR_VERSION > 15 -#define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)##EnumType::Type::##EnumVal) +#define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)EnumType::Type::EnumVal) ADD_NATIVE_ENUM(EUserInterfaceActionType, None ); ADD_NATIVE_ENUM(EUserInterfaceActionType, Button ); ADD_NATIVE_ENUM(EUserInterfaceActionType, ToggleButton ); From 003849b633a1154a45143166028cb2731b811724 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Feb 2018 20:42:28 +0100 Subject: [PATCH 57/94] removed memory debug --- Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp | 1 + .../UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 96ae7a414..9b9a9559f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -566,6 +566,7 @@ PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) for (UMovieSceneSection *section : sections) { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)section); if (!ret) { diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index ca39635cc..833830a65 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -#define UEPY_MEMORY_DEBUG 1 +//#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" From ee687914efa25c0c59e5af4e182db0b8e9aa4b28 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Feb 2018 22:24:31 +0100 Subject: [PATCH 58/94] improved sequencer api --- .../UnrealEnginePython/Private/UEPyModule.cpp | 1 + .../Private/UObject/UEPySequencer.cpp | 49 +++++++++++++++---- .../Private/UObject/UEPySequencer.h | 1 + 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 109251c4e..e9005e542 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -885,6 +885,7 @@ static PyMethodDef ue_PyUObject_methods[] = { // Sequencer { "sequencer_master_tracks", (PyCFunction)py_ue_sequencer_master_tracks, METH_VARARGS, "" }, { "sequencer_possessable_tracks", (PyCFunction)py_ue_sequencer_possessable_tracks, METH_VARARGS, "" }, + { "sequencer_get_camera_cut_track", (PyCFunction)py_ue_sequencer_get_camera_cut_track, METH_VARARGS, "" }, #if WITH_EDITOR { "sequencer_folders", (PyCFunction)py_ue_sequencer_folders, METH_VARARGS, "" }, { "sequencer_create_folder", (PyCFunction)py_ue_sequencer_create_folder, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 9b9a9559f..7dd063f76 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -121,13 +121,17 @@ PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_find_possessable", &guid)) + PyObject *py_parent = nullptr; + if (!PyArg_ParseTuple(args, "s|O:sequencer_find_possessable", &guid, &py_parent)) { - return NULL; + return nullptr; } - if (!self->ue_object->IsA()) - return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + { + return PyErr_Format(PyExc_Exception, "uobject is not a ULevelSequence"); + } FGuid f_guid; if (!FGuid::Parse(FString(guid), f_guid)) @@ -135,14 +139,21 @@ PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "invalid GUID"); } - ULevelSequence *seq = (ULevelSequence *)self->ue_object; - #if ENGINE_MINOR_VERSION < 15 UObject *u_obj = seq->FindPossessableObject(f_guid, seq); #else + UObject *parent = nullptr; + if (py_parent) + { + parent = ue_py_check_type(py_parent); + if (!parent) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + } UObject *u_obj = nullptr; TArray> u_objects; - seq->LocateBoundObjects(f_guid, nullptr, u_objects); + seq->LocateBoundObjects(f_guid, parent, u_objects); if (u_objects.Num() > 0) { u_obj = u_objects[0]; @@ -152,7 +163,7 @@ PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "unable to find uobject with GUID \"%s\"", guid); Py_RETURN_UOBJECT(u_obj); -} + } PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { @@ -273,7 +284,7 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) } return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); -} + } PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * args) { @@ -395,6 +406,26 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) return py_tracks; } +PyObject *py_ue_sequencer_get_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + + ULevelSequence *seq = (ULevelSequence *)self->ue_object; + UMovieScene *scene = seq->GetMovieScene(); + + UMovieSceneTrack *track = scene->GetCameraCutTrack(); + if (!track) + { + return PyErr_Format(PyExc_Exception, "unable to find camera cut track"); + } + + Py_RETURN_UOBJECT(track); +} + PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h index 0ab72f427..4e10c1187 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h @@ -7,6 +7,7 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_track_sections(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_get_camera_cut_track(ue_PyUObject *, PyObject *); #if WITH_EDITOR PyObject *py_ue_sequencer_folders(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_create_folder(ue_PyUObject *, PyObject *); From 14c1c139779cc433fa0c461f8ac2a17d3c364351 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 19 Feb 2018 16:55:30 +0100 Subject: [PATCH 59/94] first round of slate refactoring --- .../Private/Slate/UEPyFMenuBuilder.cpp | 18 +- .../Private/Slate/UEPyFToolBarBuilder.cpp | 63 +- .../Private/Slate/UEPySBorder.cpp | 8 +- .../Private/Slate/UEPySBox.cpp | 19 +- .../Private/Slate/UEPySBoxPanel.cpp | 13 +- .../Private/Slate/UEPySButton.cpp | 8 +- .../Private/Slate/UEPySCanvas.cpp | 42 +- .../Private/Slate/UEPySOverlay.cpp | 1 - .../Private/Slate/UEPySPythonComboBox.cpp | 5 - .../Private/Slate/UEPySPythonListView.cpp | 1 - .../Private/Slate/UEPySPythonShelf.cpp | 80 +-- .../Private/Slate/UEPySPythonTreeView.cpp | 2 - .../Private/Slate/UEPySPythonWidget.cpp | 3 - .../Private/Slate/UEPySScrollBox.cpp | 23 +- .../Private/Slate/UEPySSplitter.cpp | 1 - .../Private/Slate/UEPySVerticalBox.cpp | 1 - .../Private/Slate/UEPySWidget.cpp | 41 +- .../Private/Slate/UEPySWidget.h | 9 - .../Private/Slate/UEPySWindow.cpp | 68 +-- .../Private/Slate/UEPySlate.cpp | 547 +++++++++--------- .../Private/Slate/UEPySlate.h | 31 +- .../Public/PythonHouseKeeper.h | 41 ++ 22 files changed, 518 insertions(+), 507 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index ea71c0d3a..67fa2d172 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -41,8 +41,8 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO char *label; char *tooltip; PyObject *py_callable; - PyObject *py_obj = nullptr; - PyObject *py_uiaction_obj = nullptr; + PyObject *py_obj = nullptr; + PyObject *py_uiaction_obj = nullptr; if (!PyArg_ParseTuple(args, "ssO|OO:add_menu_entry", &label, &tooltip, &py_callable, &py_obj, &py_uiaction_obj)) return nullptr; @@ -53,23 +53,21 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO FExecuteAction handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); if (py_obj) { Py_INCREF(py_obj); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::ExecuteAction, py_obj); + handler.BindSP(py_delegate, &FPythonSlateDelegate::ExecuteAction, py_obj); } else { - handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); + handler.BindSP(py_delegate, &FPythonSlateDelegate::SimpleExecuteAction); } - ue_PyESlateEnums *py_uiaction_enum = py_uiaction_obj ? py_ue_is_eslate_enums(py_uiaction_obj) : nullptr; - self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler), NAME_None, - py_uiaction_enum ? (EUserInterfaceActionType::Type)(py_uiaction_enum->val) : EUserInterfaceActionType::Button); + ue_PyESlateEnums *py_uiaction_enum = py_uiaction_obj ? py_ue_is_eslate_enums(py_uiaction_obj) : nullptr; + self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler), NAME_None, + py_uiaction_enum ? (EUserInterfaceActionType::Type)(py_uiaction_enum->val) : EUserInterfaceActionType::Button); Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index 4465e87e1..c31e24f3b 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -4,7 +4,8 @@ #include "Runtime/Slate/Public/Framework/Commands/UIAction.h" -static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *name; if (!PyArg_ParseTuple(args, "s:begin_section", &name)) return NULL; @@ -15,14 +16,16 @@ static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *sel return Py_None; } -static PyObject *py_ue_ftool_bar_builder_end_section(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_end_section(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.EndSection(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *hook; char *label; char *tooltip; @@ -33,25 +36,27 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde return NULL; ue_PyFSlateIcon *py_slate_icon = py_ue_is_fslate_icon(py_icon); - if (!py_slate_icon) { + if (!py_slate_icon) + { return PyErr_Format(PyExc_Exception, "argument is not a FSlateIcon"); } - if (!PyCalllable_Check_Extended(py_callable)) { + if (!PyCalllable_Check_Extended(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FExecuteAction handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); - if (py_obj) { + if (py_obj) + { Py_INCREF(py_obj); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::ExecuteAction, py_obj); + handler.BindSP(py_delegate, &FPythonSlateDelegate::ExecuteAction, py_obj); } - else { - handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); + else + { + handler.BindSP(py_delegate, &FPythonSlateDelegate::SimpleExecuteAction); } self->tool_bar_builder.AddToolBarButton(FUIAction(handler), FName(hook), FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), py_slate_icon->icon); @@ -60,7 +65,8 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde return Py_None; } -static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *hook = nullptr; if (!PyArg_ParseTuple(args, "|s:add_separator", &hook)) @@ -78,25 +84,28 @@ static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *sel } -static PyObject *py_ue_ftool_bar_builder_begin_block_group(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_begin_block_group(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.BeginBlockGroup(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_end_block_group(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_end_block_group(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.EndBlockGroup(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_make_widget(ue_PyFToolBarBuilder *self, PyObject * args) { - ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType); +static PyObject *py_ue_ftool_bar_builder_make_widget(ue_PyFToolBarBuilder *self, PyObject * args) +{ + ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType); ue_py_setup_swidget(ret); - ret->s_widget = self->tool_bar_builder.MakeWidget(); - return (PyObject *)ret; + ret->s_widget = self->tool_bar_builder.MakeWidget(); + return (PyObject *)ret; } static PyMethodDef ue_PyFToolBarBuilder_methods[] = { @@ -106,7 +115,7 @@ static PyMethodDef ue_PyFToolBarBuilder_methods[] = { { "add_separator", (PyCFunction)py_ue_ftool_bar_builder_add_separator, METH_VARARGS, "" }, { "begin_block_group", (PyCFunction)py_ue_ftool_bar_builder_begin_block_group, METH_VARARGS, "" }, { "end_block_group", (PyCFunction)py_ue_ftool_bar_builder_end_block_group, METH_VARARGS, "" }, - { "make_widget", (PyCFunction)py_ue_ftool_bar_builder_make_widget, METH_VARARGS, "" }, + { "make_widget", (PyCFunction)py_ue_ftool_bar_builder_make_widget, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -117,7 +126,8 @@ static PyObject *ue_PyFToolBarBuilder_str(ue_PyFToolBarBuilder *self) &self->tool_bar_builder); } -static void ue_py_ftool_bar_builder_dealloc(ue_PyFToolBarBuilder *self) { +static void ue_py_ftool_bar_builder_dealloc(ue_PyFToolBarBuilder *self) +{ #if PY_MAJOR_VERSION < 3 self->ob_type->tp_free((PyObject*)self); #else @@ -156,13 +166,15 @@ static PyTypeObject ue_PyFToolBarBuilderType = { ue_PyFToolBarBuilder_methods, /* tp_methods */ }; -static int ue_py_ftool_bar_builder_init(ue_PyFToolBarBuilder *self, PyObject *args, PyObject *kwargs) { - new(&self->tool_bar_builder) FToolBarBuilder(TSharedPtr(), FMultiBoxCustomization::None); +static int ue_py_ftool_bar_builder_init(ue_PyFToolBarBuilder *self, PyObject *args, PyObject *kwargs) +{ + new(&self->tool_bar_builder) FToolBarBuilder(TSharedPtr(), FMultiBoxCustomization::None); return 0; } -void ue_python_init_ftool_bar_builder(PyObject *ue_module) { +void ue_python_init_ftool_bar_builder(PyObject *ue_module) +{ ue_PyFToolBarBuilderType.tp_new = PyType_GenericNew; ue_PyFToolBarBuilderType.tp_init = (initproc)ue_py_ftool_bar_builder_init; @@ -174,7 +186,8 @@ void ue_python_init_ftool_bar_builder(PyObject *ue_module) { PyModule_AddObject(ue_module, "FToolBarBuilder", (PyObject *)&ue_PyFToolBarBuilderType); } -PyObject *py_ue_new_ftool_bar_builder(FToolBarBuilder tool_bar_builder) { +PyObject *py_ue_new_ftool_bar_builder(FToolBarBuilder tool_bar_builder) +{ ue_PyFToolBarBuilder *ret = (ue_PyFToolBarBuilder *)PyObject_New(ue_PyFToolBarBuilder, &ue_PyFToolBarBuilderType); new(&ret->tool_bar_builder) FToolBarBuilder(tool_bar_builder); return (PyObject *)ret; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp index f1adb6dba..198307ed5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp @@ -10,10 +10,8 @@ static PyObject *py_ue_sborder_clear_content(ue_PySBorder *self, PyObject * args { sw_border->ClearContent(); - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject *py_ue_sborder_set_content(ue_PySBorder *self, PyObject * args) @@ -30,9 +28,9 @@ static PyObject *py_ue_sborder_set_content(ue_PySBorder *self, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); + Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_content = py_swidget; + sw_border->SetContent(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp index f9204e389..a64b6946d 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp @@ -6,20 +6,21 @@ #define sw_box StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_sbox_set_content(ue_PySBox *self, PyObject * args) { +static PyObject *py_ue_sbox_set_content(ue_PySBox *self, PyObject * args) +{ PyObject *py_content; - if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) { + if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_panel.s_widget.py_swidget_content); Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_content = py_swidget; sw_box->SetContent(py_swidget->s_widget->AsShared()); @@ -63,7 +64,8 @@ PyTypeObject ue_PySBoxType = { ue_PySBox_methods, /* tp_methods */ }; -static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) { +static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) +{ ue_py_slate_setup_farguments(SBox); @@ -78,13 +80,14 @@ static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) { ue_py_slate_farguments_optional_foptional_size("max_desired_width", MaxDesiredWidth); ue_py_slate_farguments_optional_foptional_size("min_desired_height", MinDesiredHeight); ue_py_slate_farguments_optional_foptional_size("min_desired_width", MinDesiredWidth); - + ue_py_snew(SBox, s_panel.s_widget); return 0; } -void ue_python_init_sbox(PyObject *ue_module) { +void ue_python_init_sbox(PyObject *ue_module) +{ ue_PySBoxType.tp_init = (initproc)ue_py_sbox_init; ue_PySBoxType.tp_call = (ternaryfunc)py_ue_sbox_set_content; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp index 59963667b..bd055e94f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp @@ -6,16 +6,12 @@ #define sw_box_panel StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_sbox_panel_clear_children(ue_PySGridPanel *self, PyObject * args) { +static PyObject *py_ue_sbox_panel_clear_children(ue_PySGridPanel *self, PyObject * args) +{ sw_box_panel->ClearChildren(); - for (ue_PySWidget *item : self->s_panel.s_widget.py_swidget_slots) { - Py_DECREF(item); - } - self->s_panel.s_widget.py_swidget_slots.Empty(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef ue_PySBoxPanel_methods[] = { @@ -54,7 +50,8 @@ PyTypeObject ue_PySBoxPanelType = { ue_PySBoxPanel_methods, /* tp_methods */ }; -void ue_python_init_sbox_panel(PyObject *ue_module) { +void ue_python_init_sbox_panel(PyObject *ue_module) +{ ue_PySBoxPanelType.tp_base = &ue_PySPanelType; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp index 4ebddb07e..f99bf5e9f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp @@ -33,11 +33,9 @@ static PyObject *py_ue_sbutton_bind_on_clicked(ue_PySButton *self, PyObject * ar } FOnClicked handler; - UPythonSlateDelegate *py_delegate = NewObject(); + TSharedRefpy_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_border.s_compound_widget.s_widget.s_widget, py_callable); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnClicked); - self->s_border.s_compound_widget.s_widget.delegates.Add(py_delegate); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnClicked); sw_button->SetOnClicked(handler); @@ -106,7 +104,7 @@ static int ue_py_sbutton_init(ue_PySButton *self, PyObject *args, PyObject *kwar ue_py_slate_farguments_text("text", Text); ue_py_slate_farguments_optional_enum("text_flow_direction", TextFlowDirection, ETextFlowDirection); ue_py_slate_farguments_optional_enum("text_shaping_method", TextShapingMethod, ETextShapingMethod); - ue_py_slate_farguments_optional_struct_ptr("button_style", ButtonStyle, FButtonStyle); + ue_py_slate_farguments_optional_struct_ptr("button_style", ButtonStyle, FButtonStyle); ue_py_slate_farguments_optional_struct_ptr("text_style", TextStyle, FTextBlockStyle); ue_py_slate_farguments_optional_enum("touch_method", TouchMethod, EButtonTouchMethod::Type); ue_py_slate_farguments_optional_enum("v_align", VAlign, EVerticalAlignment); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp index fd7734336..056de71d7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp @@ -6,18 +6,15 @@ #define sw_canvas StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_scanvas_clear_children(ue_PySCanvas *self, PyObject * args) { - +static PyObject *py_ue_scanvas_clear_children(ue_PySCanvas *self, PyObject * args) +{ sw_canvas->ClearChildren(); - for (ue_PySWidget *item : self->s_panel.s_widget.py_swidget_slots) { - Py_DECREF(item); - } - self->s_panel.s_widget.py_swidget_slots.Empty(); Py_RETURN_NONE; } -static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyObject *kwargs) { +static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyObject *kwargs) +{ PyObject *py_content; int h_align = 0; int v_align = 0; @@ -37,28 +34,32 @@ static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyO &h_align, &v_align, &position, - &size)) { + &size)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SCanvas::FSlot &fslot = sw_canvas->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); fslot.HAlign((EHorizontalAlignment)h_align); fslot.VAlign((EVerticalAlignment)v_align); - if (position && PyTuple_Check(position)) { - if (PyTuple_Size(position) == 2) { + if (position && PyTuple_Check(position)) + { + if (PyTuple_Size(position) == 2) + { PyObject *py_x = PyTuple_GetItem(position, 0); PyObject *py_y = PyTuple_GetItem(position, 1); - if (PyNumber_Check(py_x)) { + if (PyNumber_Check(py_x)) + { PyObject *py_x_float = PyNumber_Float(py_x); float x = PyFloat_AsDouble(py_x_float); Py_DECREF(py_x_float); @@ -70,11 +71,14 @@ static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyO } } - if (size && PyTuple_Check(size)) { - if (PyTuple_Size(size) == 2) { + if (size && PyTuple_Check(size)) + { + if (PyTuple_Size(size) == 2) + { PyObject *py_x = PyTuple_GetItem(size, 0); PyObject *py_y = PyTuple_GetItem(size, 1); - if (PyNumber_Check(py_x)) { + if (PyNumber_Check(py_x)) + { PyObject *py_x_float = PyNumber_Float(py_x); float x = PyFloat_AsDouble(py_x_float); Py_DECREF(py_x_float); @@ -128,12 +132,14 @@ PyTypeObject ue_PySCanvasType = { ue_PySCanvas_methods, /* tp_methods */ }; -static int ue_py_scanvas_init(ue_PySCanvas *self, PyObject *args, PyObject *kwargs) { +static int ue_py_scanvas_init(ue_PySCanvas *self, PyObject *args, PyObject *kwargs) +{ ue_py_snew_simple(SCanvas, s_panel.s_widget); return 0; } -void ue_python_init_scanvas(PyObject *ue_module) { +void ue_python_init_scanvas(PyObject *ue_module) +{ ue_PySCanvasType.tp_base = &ue_PySPanelType; ue_PySCanvasType.tp_call = (ternaryfunc)py_ue_scanvas_add_slot; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp index 3380a0363..fadef42f4 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp @@ -38,7 +38,6 @@ static PyObject *py_ue_soverlay_add_slot(ue_PySOverlay *self, PyObject * args, P } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SOverlay::FOverlaySlot &fslot = sw_overlay->AddSlot(z_order); fslot.AttachWidget(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp index dca4c64ca..83dedef20 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp @@ -99,8 +99,6 @@ static int ue_py_spython_combo_box_init(ue_PySPythonComboBox *self, PyObject *ar TArray> *items = new TArray>(); while (PyObject *item = PyIter_Next(values)) { Py_INCREF(item); - // keep track of items - self->s_panel.s_widget.py_refs.Add(item); items->Add(TSharedPtr(new FPythonItem(item))); } Py_DECREF(values); @@ -143,9 +141,6 @@ static int ue_py_spython_combo_box_init(ue_PySPythonComboBox *self, PyObject *ar // keep track of the list, so we can delete on destruction sw_python_combo_box->PythonOptionsSource = items; - // eventually track the content - self->s_panel.s_widget.py_swidget_content = s_widget_content; - return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp index f8c3b08ea..e8eb44747 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp @@ -175,7 +175,6 @@ static int ue_py_spython_list_view_init(ue_PySPythonListView *self, PyObject *ar if (ue_PySHeaderRow *_py_swidget = py_ue_is_sheader_row(value)) { Py_INCREF(_py_swidget); - ((ue_PySWidget *)self)->py_refs.Add(value); arguments.HeaderRow(StaticCastSharedRef(((ue_PySWidget *)_py_swidget)->s_widget)); } else { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp index e6b580c09..eea9f41d6 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp @@ -46,7 +46,8 @@ PyTypeObject ue_PySPythonShelfType = { ue_PySPythonShelf_methods, /* tp_methods */ }; -static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyObject *kwargs) { +static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyObject *kwargs) +{ PyObject *py_classes_iterable = nullptr; PyObject *py_collections_iterable = nullptr; @@ -65,37 +66,45 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO &py_collections_iterable, &py_callable_double_clicked, &py_callable_get_context_menu, - &py_callable_asset_selected)) { + &py_callable_asset_selected)) + { return -1; } - if (py_classes_iterable) { + if (py_classes_iterable) + { py_classes_iterable = PyObject_GetIter(py_classes_iterable); - if (!py_classes_iterable) { + if (!py_classes_iterable) + { PyErr_SetString(PyExc_Exception, "argument is not an iterable"); return -1; } } - if (py_collections_iterable) { + if (py_collections_iterable) + { py_collections_iterable = PyObject_GetIter(py_collections_iterable); - if (!py_collections_iterable) { + if (!py_collections_iterable) + { PyErr_SetString(PyExc_Exception, "argument is not an iterable"); return -1; } } - if (py_callable_double_clicked && !PyCalllable_Check_Extended(py_callable_double_clicked)) { + if (py_callable_double_clicked && !PyCalllable_Check_Extended(py_callable_double_clicked)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } - if (py_callable_get_context_menu && !PyCalllable_Check_Extended(py_callable_get_context_menu)) { + if (py_callable_get_context_menu && !PyCalllable_Check_Extended(py_callable_get_context_menu)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } - if (py_callable_asset_selected && !PyCalllable_Check_Extended(py_callable_asset_selected)) { + if (py_callable_asset_selected && !PyCalllable_Check_Extended(py_callable_asset_selected)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } @@ -108,9 +117,12 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO asset_picker_config.bShowBottomToolbar = false; asset_picker_config.bAutohideSearchBar = false; - if (py_classes_iterable) { - while (PyObject *item = PyIter_Next(py_classes_iterable)) { - if (PyUnicode_Check(item)) { + if (py_classes_iterable) + { + while (PyObject *item = PyIter_Next(py_classes_iterable)) + { + if (PyUnicode_Check(item)) + { FName class_name = FName(UTF8_TO_TCHAR(PyUnicode_AsUTF8(item))); asset_picker_config.Filter.ClassNames.Add(class_name); } @@ -118,9 +130,12 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO Py_DECREF(py_classes_iterable); } - if (py_collections_iterable) { - while (PyObject *item = PyIter_Next(py_collections_iterable)) { - if (PyUnicode_Check(item)) { + if (py_collections_iterable) + { + while (PyObject *item = PyIter_Next(py_collections_iterable)) + { + if (PyUnicode_Check(item)) + { FName collection_name = FName(UTF8_TO_TCHAR(PyUnicode_AsUTF8(item))); asset_picker_config.Collections.Add(FCollectionNameType(collection_name, ECollectionShareType::CST_Local)); } @@ -128,36 +143,32 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO Py_DECREF(py_collections_iterable); } - if (py_callable_double_clicked) { + if (py_callable_double_clicked) + { FOnAssetDoubleClicked handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_double_clicked); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnAssetDoubleClicked); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_double_clicked); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetDoubleClicked); asset_picker_config.OnAssetDoubleClicked = handler; } - if (py_callable_get_context_menu) { + if (py_callable_get_context_menu) + { FOnGetAssetContextMenu handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_get_context_menu); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnGetAssetContextMenu); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_get_context_menu); + + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnGetAssetContextMenu); asset_picker_config.OnGetAssetContextMenu = handler; } - if (py_callable_asset_selected) { + if (py_callable_asset_selected) + { FOnAssetSelected handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_asset_selected); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnAssetSelected); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_asset_selected); + + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetSelected); asset_picker_config.OnAssetSelected = handler; } @@ -167,7 +178,8 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO return 0; } -void ue_python_init_spython_shelf(PyObject *ue_module) { +void ue_python_init_spython_shelf(PyObject *ue_module) +{ ue_PySPythonShelfType.tp_init = (initproc)ue_py_spython_shelf_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp index c1e4deaac..17d9909e0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp @@ -91,8 +91,6 @@ static int ue_py_spython_tree_view_init(ue_PySPythonTreeView *self, PyObject *ar while (PyObject *item = PyIter_Next(values)) { Py_INCREF(item); - // keep track of items - self->s_tree_view.s_list_view.s_table_view_base.s_compound_widget.s_widget.py_refs.Add(item); items->Add(TSharedPtr(new FPythonItem(item))); } Py_DECREF(values); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp index 27beedcee..4369af222 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp @@ -45,9 +45,7 @@ static PyObject *py_ue_spython_widget_set_content(ue_PySPythonWidget *self, PyOb return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_content = py_swidget; sw_python_widget->SetContent(py_swidget->s_widget->AsShared()); @@ -58,7 +56,6 @@ static PyObject *py_ue_spython_widget_set_content(ue_PySPythonWidget *self, PyOb static PyObject *py_ue_spython_widget_clear_content(ue_PySPythonWidget *self, PyObject *args) { sw_python_widget->ClearContent(); - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); Py_INCREF(Py_None); return Py_None; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp index 350e619bc..0684c89c0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp @@ -6,7 +6,8 @@ #define sw_scroll_box StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * args, PyObject *kwargs) { +static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * args, PyObject *kwargs) +{ PyObject *py_content; int h_align = 0; int v_align = 0; @@ -19,17 +20,18 @@ static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * ar if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ii:add_slot", kwlist, &py_content, &h_align, - &v_align)) { + &v_align)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_slots.Add(py_swidget); SScrollBox::FSlot &fslot = sw_scroll_box->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); @@ -40,11 +42,8 @@ static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * ar return (PyObject *)self; } -static PyObject *py_ue_sscroll_box_clear_children(ue_PySScrollBox *self, PyObject * args) { - for (ue_PySWidget *child : self->s_compound_widget.s_widget.py_swidget_slots) { - Py_DECREF(child); - } - self->s_compound_widget.s_widget.py_swidget_slots.Empty(); +static PyObject *py_ue_sscroll_box_clear_children(ue_PySScrollBox *self, PyObject * args) +{ sw_scroll_box->ClearChildren(); Py_RETURN_NONE; @@ -88,7 +87,8 @@ PyTypeObject ue_PySScrollBoxType = { ue_PySScrollBox_methods, /* tp_methods */ }; -static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObject *kwargs) { +static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObject *kwargs) +{ ue_py_slate_setup_farguments(SScrollBox); @@ -105,7 +105,8 @@ static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObjec return 0; } -void ue_python_init_sscroll_box(PyObject *ue_module) { +void ue_python_init_sscroll_box(PyObject *ue_module) +{ ue_PySScrollBoxType.tp_init = (initproc)ue_py_sscroll_box_init; ue_PySScrollBoxType.tp_call = (ternaryfunc)py_ue_sscroll_box_add_slot; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp index 08e12f92f..d4f5e783a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp @@ -30,7 +30,6 @@ static PyObject *py_ue_ssplitter_add_slot(ue_PySSplitter *self, PyObject * args, } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SSplitter::FSlot &fslot = sw_splitter->AddSlot(index); if (size_value > -1) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp index 949995e44..dd1a48896 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp @@ -41,7 +41,6 @@ static PyObject *py_ue_svertical_box_add_slot(ue_PySVerticalBox *self, PyObject } Py_INCREF(py_swidget); - self->s_box_panel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); SVerticalBox::FSlot &fslot = sw_vertical_box->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 091dc665f..8b2b40421 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -128,10 +128,8 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_down(ue_PySWidget *self, PyO } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseButtonDown(handler); @@ -144,7 +142,7 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObj PyObject *py_callable; if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_up", &py_callable)) { - return NULL; + return nullptr; } if (!PyCalllable_Check_Extended(py_callable)) @@ -153,10 +151,8 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObj } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseButtonUp(handler); @@ -178,10 +174,8 @@ static PyObject *py_ue_swidget_bind_on_mouse_double_click(ue_PySWidget *self, Py } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseDoubleClick(handler); @@ -203,10 +197,8 @@ static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseMove(handler); @@ -354,21 +346,6 @@ static void ue_PySWidgett_dealloc(ue_PySWidget *self) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PySWidget %p mapped to %s %p (slate refcount: %d)"), self, *self->s_widget->GetTypeAsString(), &self->s_widget.Get(), self->s_widget.GetSharedReferenceCount()); #endif Py_DECREF(self->py_dict); - for (UPythonSlateDelegate *item : self->delegates) - { - if (item->IsValidLowLevel() && item->IsRooted()) - item->RemoveFromRoot(); - } - for (ue_PySWidget *item : self->py_swidget_slots) - { - Py_DECREF(item); - } - // decref content (if any) - Py_XDECREF(self->py_swidget_content); - for (PyObject *item : self->py_refs) - { - Py_DECREF(item); - } ue_py_unregister_swidget(&self->s_widget.Get()); // decrement widget reference count // but only if python vm is still fully active (hack to avoid crashes on editor shutdown) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h index 9433fba00..ac3ca3645 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h @@ -13,16 +13,7 @@ struct ue_PySWidget /* Type-specific fields go here. */ TSharedRef s_widget; - PyObject *py_dict; - - TArray delegates; - - ue_PySWidget *py_swidget_content; - - TArray py_swidget_slots; - - TArray py_refs; }; void ue_python_init_swidget(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp index b6e597096..054e8c485 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp @@ -41,10 +41,10 @@ static PyObject *py_ue_swindow_resize(ue_PySWindow *self, PyObject * args) static PyObject *py_ue_swindow_minimize(ue_PySWindow *self, PyObject * args) { - sw_window->Minimize(); + sw_window->Minimize(); - Py_INCREF(self); - return (PyObject *)self; + Py_INCREF(self); + return (PyObject *)self; } static PyObject *py_ue_swindow_set_content(ue_PySWindow *self, PyObject * args) @@ -61,9 +61,7 @@ static PyObject *py_ue_swindow_set_content(ue_PySWindow *self, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_content = py_swidget; sw_window->SetContent(py_swidget->s_widget->AsShared()); @@ -118,29 +116,29 @@ static PyObject *py_ue_swindow_add_modal(ue_PySWindow *self, PyObject * args) static PyObject *py_ue_swindow_add_child(ue_PySWindow *self, PyObject * args) { - PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:add_child", &py_obj)) - { - return NULL; - } - - ue_PySWindow *py_swindow_child = py_ue_is_swindow(py_obj); - if (!py_swindow_child) - { - return PyErr_Format(PyExc_Exception, "argument is not a SWindow"); - } - - FSlateApplication::Get().AddWindowAsNativeChild( - StaticCastSharedRef(py_swindow_child->s_compound_widget.s_widget.s_widget), - sw_window); - - Py_RETURN_NONE; + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:add_child", &py_obj)) + { + return NULL; + } + + ue_PySWindow *py_swindow_child = py_ue_is_swindow(py_obj); + if (!py_swindow_child) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWindow"); + } + + FSlateApplication::Get().AddWindowAsNativeChild( + StaticCastSharedRef(py_swindow_child->s_compound_widget.s_widget.s_widget), + sw_window); + + Py_RETURN_NONE; } static PyMethodDef ue_PySWindow_methods[] = { - { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, - { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, - { "minimize", (PyCFunction)py_ue_swindow_minimize, METH_VARARGS, "" }, + { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, + { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, + { "minimize", (PyCFunction)py_ue_swindow_minimize, METH_VARARGS, "" }, { "resize", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_client_size", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_content", (PyCFunction)py_ue_swindow_set_content, METH_VARARGS, "" }, @@ -149,7 +147,7 @@ static PyMethodDef ue_PySWindow_methods[] = { #if WITH_EDITOR { "add_modal", (PyCFunction)py_ue_swindow_add_modal, METH_VARARGS, "" }, #endif - { "add_child", (PyCFunction)py_ue_swindow_add_child, METH_VARARGS, "" }, + { "add_child", (PyCFunction)py_ue_swindow_add_child, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -245,20 +243,18 @@ static int ue_py_swindow_init(ue_PySWindow *self, PyObject *args, PyObject *kwar if (on_closed && PyCalllable_Check_Extended(on_closed)) { FOnWindowClosed handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(on_closed); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnWindowClosed); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, on_closed); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnWindowClosed); sw_window->SetOnWindowClosed(handler); } - // is it a child ? - PyObject *is_child = ue_py_dict_get_item(kwargs, "child"); - if (!(is_child && PyObject_IsTrue(is_child))) - { - FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); - } + // is it a child ? + PyObject *is_child = ue_py_dict_get_item(kwargs, "child"); + if (!(is_child && PyObject_IsTrue(is_child))) + { + FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); + } return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 100de470d..536223cf5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -26,7 +26,7 @@ #include "UEPySlate.h" #include "PyNativeWidgetHost.h" -FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) +FReply FPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) { FScopePythonGIL gil; @@ -46,7 +46,7 @@ FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPoin return FReply::Handled(); } -FReply UPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event) +FReply FPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event) { FScopePythonGIL gil; @@ -66,7 +66,7 @@ FReply UPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEven return FReply::Handled(); } -FReply UPythonSlateDelegate::OnClicked() +FReply FPythonSlateDelegate::OnClicked() { FScopePythonGIL gil; @@ -86,7 +86,7 @@ FReply UPythonSlateDelegate::OnClicked() return FReply::Handled(); } -void UPythonSlateDelegate::OnTextChanged(const FText& text) +void FPythonSlateDelegate::OnTextChanged(const FText& text) { FScopePythonGIL gil; @@ -99,7 +99,7 @@ void UPythonSlateDelegate::OnTextChanged(const FText& text) Py_DECREF(ret); } -void UPythonSlateDelegate::OnStringChanged(const FString& text) +void FPythonSlateDelegate::OnStringChanged(const FString& text) { FScopePythonGIL gil; @@ -112,7 +112,7 @@ void UPythonSlateDelegate::OnStringChanged(const FString& text) Py_DECREF(ret); } -void UPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type commit_type) +void FPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type commit_type) { FScopePythonGIL gil; @@ -125,33 +125,33 @@ void UPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type Py_DECREF(ret); } -void UPythonSlateDelegate::OnInt32Changed(int32 value) +void FPythonSlateDelegate::OnInt32Changed(int32 value) { - FScopePythonGIL gil; - - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"i", value); - if (!ret) - { - unreal_engine_py_log_error(); - return; - } - Py_DECREF(ret); + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"i", value); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); } -void UPythonSlateDelegate::OnInt32Committed(int32 value, ETextCommit::Type commit_type) +void FPythonSlateDelegate::OnInt32Committed(int32 value, ETextCommit::Type commit_type) { - FScopePythonGIL gil; - - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"ii", value, (int)commit_type); - if (!ret) - { - unreal_engine_py_log_error(); - return; - } - Py_DECREF(ret); + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"ii", value, (int)commit_type); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); } -void UPythonSlateDelegate::OnFloatChanged(float value) +void FPythonSlateDelegate::OnFloatChanged(float value) { FScopePythonGIL gil; @@ -164,7 +164,7 @@ void UPythonSlateDelegate::OnFloatChanged(float value) Py_DECREF(ret); } -void UPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) +void FPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) { FScopePythonGIL gil; @@ -177,7 +177,7 @@ void UPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) Py_DECREF(ret); } -void UPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) +void FPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) { FScopePythonGIL gil; @@ -190,7 +190,7 @@ void UPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) Py_DECREF(ret); } -void UPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commit_type) +void FPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commit_type) { FScopePythonGIL gil; @@ -203,20 +203,20 @@ void UPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commi Py_DECREF(ret); } -void UPythonSlateDelegate::OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode) +void FPythonSlateDelegate::OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode) { - FScopePythonGIL gil; - - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"isi", (int)SortPriority, TCHAR_TO_UTF8(*ColumnName.ToString()), (int)NewSortMode); - if (!ret) - { - unreal_engine_py_log_error(); - return; - } - Py_DECREF(ret); + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"isi", (int)SortPriority, TCHAR_TO_UTF8(*ColumnName.ToString()), (int)NewSortMode); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); } -void UPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) +void FPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) { FScopePythonGIL gil; @@ -230,7 +230,7 @@ void UPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) } #if WITH_EDITOR -void UPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -242,7 +242,7 @@ void UPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) Py_XDECREF(ret); } -void UPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -254,7 +254,7 @@ void UPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) Py_XDECREF(ret); } -void UPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -266,7 +266,7 @@ void UPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) Py_XDECREF(ret); } -bool UPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) +bool FPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -281,7 +281,7 @@ bool UPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) return bValue; } -TSharedPtr UPythonSlateDelegate::OnGetAssetContextMenu(const TArray& SelectedAssets) +TSharedPtr FPythonSlateDelegate::OnGetAssetContextMenu(const TArray& SelectedAssets) { FScopePythonGIL gil; @@ -311,7 +311,7 @@ TSharedPtr UPythonSlateDelegate::OnGetAssetContextMenu(const TArray SelectedAssets) +void FPythonSlateDelegate::MenuPyAssetBuilder(FMenuBuilder &Builder, TArray SelectedAssets) { FScopePythonGIL gil; @@ -330,18 +330,18 @@ void UPythonSlateDelegate::MenuPyAssetBuilder(FMenuBuilder &Builder, TArray UPythonSlateDelegate::OnExtendContentBrowserMenu(const TArray& SelectedAssets) +TSharedRef FPythonSlateDelegate::OnExtendContentBrowserMenu(const TArray& SelectedAssets) { TSharedRef Extender(new FExtender()); - Extender->AddMenuExtension((char *)"GetAssetActions", EExtensionHook::After, nullptr, FMenuExtensionDelegate::CreateUObject(this, &UPythonSlateDelegate::MenuPyAssetBuilder, SelectedAssets)); + Extender->AddMenuExtension((char *)"GetAssetActions", EExtensionHook::After, nullptr, FMenuExtensionDelegate::CreateSP(this, &FPythonSlateDelegate::MenuPyAssetBuilder, SelectedAssets)); return Extender; } #endif -TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item) +TSharedRef FPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item) { FScopePythonGIL gil; @@ -365,35 +365,35 @@ TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr UPythonSlateDelegate::OnGetMenuContent() +TSharedRef FPythonSlateDelegate::OnGetMenuContent() { - FScopePythonGIL gil; - - PyObject *ret = PyObject_CallFunction(py_callable, (char *)""); - if (!ret) - { - unreal_engine_py_log_error(); - return SNullWidget::NullWidget; - } - - ue_PySWidget *s_widget = py_ue_is_swidget(ret); - if (!s_widget) - { - Py_DECREF(ret); - UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget")); - return SNullWidget::NullWidget; - } - TSharedRef value = s_widget->s_widget; - Py_DECREF(ret); - return value; + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)""); + if (!ret) + { + unreal_engine_py_log_error(); + return SNullWidget::NullWidget; + } + + ue_PySWidget *s_widget = py_ue_is_swidget(ret); + if (!s_widget) + { + Py_DECREF(ret); + UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget")); + return SNullWidget::NullWidget; + } + TSharedRef value = s_widget->s_widget; + Py_DECREF(ret); + return value; } -void UPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type) +void FPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type) { - if (!py_item.IsValid()) - { - return; - } + if (!py_item.IsValid()) + { + return; + } FScopePythonGIL gil; @@ -406,7 +406,7 @@ void UPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, E Py_DECREF(ret); } -TSharedPtr UPythonSlateDelegate::OnContextMenuOpening() +TSharedPtr FPythonSlateDelegate::OnContextMenuOpening() { FScopePythonGIL gil; @@ -429,7 +429,7 @@ TSharedPtr UPythonSlateDelegate::OnContextMenuOpening() return value; } -void UPythonSlateDelegate::SimpleExecuteAction() +void FPythonSlateDelegate::SimpleExecuteAction() { FScopePythonGIL gil; @@ -441,7 +441,7 @@ void UPythonSlateDelegate::SimpleExecuteAction() Py_XDECREF(ret); } -void UPythonSlateDelegate::ExecuteAction(PyObject *py_obj) +void FPythonSlateDelegate::ExecuteAction(PyObject *py_obj) { FScopePythonGIL gil; @@ -453,7 +453,7 @@ void UPythonSlateDelegate::ExecuteAction(PyObject *py_obj) Py_XDECREF(ret); } -FText UPythonSlateDelegate::GetterFText() const +FText FPythonSlateDelegate::GetterFText() const { FScopePythonGIL gil; @@ -477,7 +477,7 @@ FText UPythonSlateDelegate::GetterFText() const return text; } -FString UPythonSlateDelegate::GetterFString() const +FString FPythonSlateDelegate::GetterFString() const { FScopePythonGIL gil; @@ -501,7 +501,7 @@ FString UPythonSlateDelegate::GetterFString() const return fstr; } -float UPythonSlateDelegate::GetterFloat() const +float FPythonSlateDelegate::GetterFloat() const { FScopePythonGIL gil; @@ -525,7 +525,7 @@ float UPythonSlateDelegate::GetterFloat() const return n; } -TOptional UPythonSlateDelegate::GetterTFloat() const +TOptional FPythonSlateDelegate::GetterTFloat() const { FScopePythonGIL gil; @@ -549,7 +549,7 @@ TOptional UPythonSlateDelegate::GetterTFloat() const return n; } -int UPythonSlateDelegate::GetterInt() const +int FPythonSlateDelegate::GetterInt() const { FScopePythonGIL gil; @@ -573,7 +573,7 @@ int UPythonSlateDelegate::GetterInt() const return n; } -bool UPythonSlateDelegate::GetterBool() const +bool FPythonSlateDelegate::GetterBool() const { FScopePythonGIL gil; @@ -593,7 +593,7 @@ bool UPythonSlateDelegate::GetterBool() const return false; } -FVector2D UPythonSlateDelegate::GetterFVector2D() const +FVector2D FPythonSlateDelegate::GetterFVector2D() const { FScopePythonGIL gil; @@ -640,7 +640,7 @@ FVector2D UPythonSlateDelegate::GetterFVector2D() const return FVector2D(x, y); } -FLinearColor UPythonSlateDelegate::GetterFLinearColor() const +FLinearColor FPythonSlateDelegate::GetterFLinearColor() const { FScopePythonGIL gil; @@ -666,7 +666,7 @@ FLinearColor UPythonSlateDelegate::GetterFLinearColor() const return color; } -TSharedRef UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &args) +TSharedRef FPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &args) { TSharedRef dock_tab = SNew(SDockTab).TabRole(ETabRole::NomadTab); PyObject *py_dock = (PyObject *)ue_py_get_swidget(dock_tab); @@ -682,9 +682,9 @@ TSharedRef UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &a return dock_tab; } -TSharedRef UPythonSlateDelegate::GenerateRow(TSharedPtr InItem, const TSharedRef& OwnerTable) +TSharedRef FPythonSlateDelegate::GenerateRow(TSharedPtr InItem, const TSharedRef& OwnerTable) { - FScopePythonGIL gil; + FScopePythonGIL gil; PyObject *ret = PyObject_CallFunction(py_callable, (char*)"O", InItem.Get()->py_object); if (!ret) @@ -692,24 +692,24 @@ TSharedRef UPythonSlateDelegate::GenerateRow(TSharedPtr unreal_engine_py_log_error(); return SNew(STableRow>, OwnerTable); } - - if (ue_PySPythonMultiColumnTableRow *spython_multicolumn_table_row = py_ue_is_spython_multicolumn_table_row(ret)) - { - Py_INCREF(spython_multicolumn_table_row); - TSharedRef value = StaticCastSharedRef(spython_multicolumn_table_row->s_compound_widget.s_widget.s_widget->AsShared()); - return value; - } + + if (ue_PySPythonMultiColumnTableRow *spython_multicolumn_table_row = py_ue_is_spython_multicolumn_table_row(ret)) + { + Py_INCREF(spython_multicolumn_table_row); + TSharedRef value = StaticCastSharedRef(spython_multicolumn_table_row->s_compound_widget.s_widget.s_widget->AsShared()); + return value; + } else if (ue_PySWidget *s_widget = py_ue_is_swidget(ret)) { - return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; + return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; } - UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); - return SNew(STableRow>, OwnerTable); - + UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); + return SNew(STableRow>, OwnerTable); + } -void UPythonSlateDelegate::GetChildren(TSharedPtr InItem, TArray>& OutChildren) +void FPythonSlateDelegate::GetChildren(TSharedPtr InItem, TArray>& OutChildren) { PyObject *ret = PyObject_CallFunction(py_callable, (char*)"O", InItem.Get()->py_object); if (!ret) @@ -772,10 +772,6 @@ void ue_py_setup_swidget(ue_PySWidget *self) #endif self->py_dict = PyDict_New(); new(&self->s_widget) TSharedRef(SNullWidget::NullWidget); - new(&self->delegates) TArray(); - new(&self->py_swidget_slots) TArray(); - new(&self->py_refs) TArray(); - self->py_swidget_content = nullptr; } void ue_py_register_swidget(SWidget *s_widget, ue_PySWidget *py_s_widget) @@ -813,7 +809,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_stable_view_base(module); ue_python_init_slist_view(module); ue_python_init_spython_list_view(module); - ue_python_init_spython_multicolumn_table_row(module); + ue_python_init_spython_multicolumn_table_row(module); ue_python_init_stree_view(module); ue_python_init_spython_tree_view(module); ue_python_init_ssplitter(module); @@ -835,12 +831,12 @@ void ue_python_init_slate(PyObject *module) #if WITH_EDITOR - ue_python_init_snode_panel(module); + ue_python_init_snode_panel(module); #if ENGINE_MINOR_VERSION > 15 - ue_python_init_sgraph_panel(module); + ue_python_init_sgraph_panel(module); #endif - ue_python_init_idetails_view(module); - ue_python_init_istructure_details_view(module); + ue_python_init_idetails_view(module); + ue_python_init_istructure_details_view(module); ue_python_init_seditor_viewport(module); ue_python_init_slevel_viewport(module); ue_python_init_spython_editor_viewport(module); @@ -871,7 +867,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_fkey_event(module); ue_python_init_fcharacter_event(module); ue_python_init_fmodifier_keys_state(module); - ue_python_init_eslate_enums(module); + ue_python_init_eslate_enums(module); } PyObject *ue_py_dict_get_item(PyObject *dict, const char *key) @@ -894,35 +890,35 @@ PyObject *py_unreal_engine_get_editor_window(PyObject *self, PyObject *args) PyObject *py_unreal_engine_find_slate_style(PyObject *self, PyObject *args) { - char *name = nullptr; - if (!PyArg_ParseTuple(args, "s:find_slate_style", &name)) - return nullptr; - - ISlateStyle const* const foundStyleSet = FSlateStyleRegistry::FindSlateStyle(FName(name)); - if (!foundStyleSet) - { - UE_LOG(LogPython, Warning, TEXT("Could not find SlateStyle")); - Py_RETURN_NONE; - } - - ue_PyFSlateStyleSet *ret = py_ue_new_fslate_style_set(static_cast(const_cast(foundStyleSet))); - return (PyObject *)ret; + char *name = nullptr; + if (!PyArg_ParseTuple(args, "s:find_slate_style", &name)) + return nullptr; + + ISlateStyle const* const foundStyleSet = FSlateStyleRegistry::FindSlateStyle(FName(name)); + if (!foundStyleSet) + { + UE_LOG(LogPython, Warning, TEXT("Could not find SlateStyle")); + Py_RETURN_NONE; + } + + ue_PyFSlateStyleSet *ret = py_ue_new_fslate_style_set(static_cast(const_cast(foundStyleSet))); + return (PyObject *)ret; } PyObject *py_unreal_engine_find_icon_for_class(PyObject *self, PyObject *args) { - PyObject* py_class = nullptr; - char *name = nullptr; - if (!PyArg_ParseTuple(args, "O|s:find_icon_for_class", &py_class, &name)) - return nullptr; - - UClass *u_class = ue_py_check_type(py_class); - if (!u_class) - return PyErr_Format(PyExc_Exception, "argument is not a UClass object"); - - FSlateIcon foundIcon = FSlateIconFinder::FindIconForClass(u_class, FName(UTF8_TO_TCHAR(name))); - ue_PyFSlateIcon *ret = py_ue_new_fslate_icon(foundIcon); - return (PyObject *)ret; + PyObject* py_class = nullptr; + char *name = nullptr; + if (!PyArg_ParseTuple(args, "O|s:find_icon_for_class", &py_class, &name)) + return nullptr; + + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + return PyErr_Format(PyExc_Exception, "argument is not a UClass object"); + + FSlateIcon foundIcon = FSlateIconFinder::FindIconForClass(u_class, FName(UTF8_TO_TCHAR(name))); + ue_PyFSlateIcon *ret = py_ue_new_fslate_icon(foundIcon); + return (PyObject *)ret; } @@ -1019,21 +1015,21 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P PyObject *py_object; PyObject *py_allow_search = nullptr; - PyObject *py_update_from_selection = nullptr; - PyObject *py_lockable = nullptr; - char *py_name_area_settings = nullptr; - PyObject *py_hide_selection_tip = nullptr; - PyObject *py_search_initial_key_focus = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; char *kwlist[] = { (char *)"uobject", (char *)"allow_search", - (char *)"update_from_selection", - (char *)"lockable", - (char *)"name_area_settings", - (char *)"hide_selection_tip", - (char *)"search_initial_key_focus", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", nullptr }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_detail_view", kwlist, @@ -1045,103 +1041,103 @@ PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, P FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); FDetailsViewArgs view_args; - view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; - view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; - view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; - view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; - view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; - FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); - view_args.NameAreaSettings = [&name_area_string]() { - if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } - else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - }(); + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); TSharedPtr view = PropertyEditorModule.CreateDetailView(view_args); - - if (UObject *u_object = ue_py_check_type(py_object)) - { - view->SetObject(u_object); - } - extern PyTypeObject ue_PyIDetailsViewType; + if (UObject *u_object = ue_py_check_type(py_object)) + { + view->SetObject(u_object); + } + + extern PyTypeObject ue_PyIDetailsViewType; return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PyIDetailsViewType); } PyObject *py_unreal_engine_create_structure_detail_view(PyObject *self, PyObject * args, PyObject *kwargs) { - PyObject *py_object = nullptr; - - PyObject *py_allow_search = nullptr; - PyObject *py_update_from_selection = nullptr; - PyObject *py_lockable = nullptr; - char *py_name_area_settings = nullptr; - PyObject *py_hide_selection_tip = nullptr; - PyObject *py_search_initial_key_focus = nullptr; - - char *kwlist[] = { - (char*)"struct_data", - (char *)"allow_search", - (char *)"update_from_selection", - (char *)"lockable", - (char *)"name_area_settings", - (char *)"hide_selection_tip", - (char *)"search_initial_key_focus", - nullptr }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_structure_detail_view", kwlist, - &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) - { - return nullptr; - } - - if (py_object && !py_ue_is_uscriptstruct(py_object)) - { - return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); - } - - FDetailsViewArgs view_args; - view_args.bAllowSearch = (py_allow_search ) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; - view_args.bUpdatesFromSelection = (py_update_from_selection ) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; - view_args.bLockable = (py_lockable ) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; - view_args.bHideSelectionTip = (py_hide_selection_tip ) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; - view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; - - FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); - view_args.NameAreaSettings = [&name_area_string]() { - if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } - else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } - }(); - FStructureDetailsViewArgs struct_view_args; - { - struct_view_args.bShowObjects = true; - struct_view_args.bShowAssets = true; - struct_view_args.bShowClasses = true; - struct_view_args.bShowInterfaces = true; - } - - extern PyTypeObject ue_PyIStructureDetailsViewType; - ue_PyIStructureDetailsView *ret = (ue_PyIStructureDetailsView *)PyObject_New(ue_PyIStructureDetailsView, &ue_PyIStructureDetailsViewType); - new(&ret->istructure_details_view) TSharedPtr(nullptr); - ret->ue_py_struct = nullptr; - TSharedPtr struct_scope; - - if (ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object)) - { - Py_INCREF(ue_py_struct); - ret->ue_py_struct = ue_py_struct; - struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); - } - - FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); - ret->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); - - return (PyObject *)ret; + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_structure_detail_view", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return nullptr; + } + + if (py_object && !py_ue_is_uscriptstruct(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + + extern PyTypeObject ue_PyIStructureDetailsViewType; + ue_PyIStructureDetailsView *ret = (ue_PyIStructureDetailsView *)PyObject_New(ue_PyIStructureDetailsView, &ue_PyIStructureDetailsViewType); + new(&ret->istructure_details_view) TSharedPtr(nullptr); + ret->ue_py_struct = nullptr; + TSharedPtr struct_scope; + + if (ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object)) + { + Py_INCREF(ue_py_struct); + ret->ue_py_struct = ue_py_struct; + struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + } + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + ret->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return (PyObject *)ret; } PyObject *py_unreal_engine_create_property_view(PyObject *self, PyObject * args, PyObject *kwargs) @@ -1330,10 +1326,8 @@ PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self TArray &Extenders = ContentBrowser.GetAllAssetViewContextMenuExtenders(); FContentBrowserMenuExtender_SelectedAssets handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnExtendContentBrowserMenu); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnExtendContentBrowserMenu); Extenders.Add(handler); @@ -1355,10 +1349,8 @@ PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject * self, PyObject return PyErr_Format(PyExc_Exception, "argument is not callable"); FOnSpawnTab spawn_tab; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - spawn_tab.BindUObject(py_delegate, &UPythonSlateDelegate::SpawnPythonTab); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + spawn_tab.BindSP(py_delegate, &FPythonSlateDelegate::SpawnPythonTab); FTabSpawnerEntry *spawner_entry = &FGlobalTabmanager::Get()->RegisterNomadTabSpawner(UTF8_TO_TCHAR(name), spawn_tab) // TODO: more generic way to set the group @@ -1405,69 +1397,66 @@ PyObject *py_unreal_engine_invoke_tab(PyObject * self, PyObject * args) PyObject * py_unreal_engine_get_swidget_from_wrapper(PyObject *self, PyObject *args) { - PyObject *py_object; + PyObject *py_object; - if (!PyArg_ParseTuple(args, "O:get_swidget_from_wrapper", &py_object)) - { - return NULL; - } + if (!PyArg_ParseTuple(args, "O:get_swidget_from_wrapper", &py_object)) + { + return NULL; + } - FPythonSWidgetWrapper *py_swidget_wrapper = ue_py_check_struct(py_object); - if (!py_swidget_wrapper) - return PyErr_Format(PyExc_Exception, "argument is not a FPythonSWidgetWrapper"); + FPythonSWidgetWrapper *py_swidget_wrapper = ue_py_check_struct(py_object); + if (!py_swidget_wrapper) + return PyErr_Format(PyExc_Exception, "argument is not a FPythonSWidgetWrapper"); - if (!py_swidget_wrapper->Widget.IsValid()) - return PyErr_Format(PyExc_Exception, "wrapper contained invalid SWidget!"); + if (!py_swidget_wrapper->Widget.IsValid()) + return PyErr_Format(PyExc_Exception, "wrapper contained invalid SWidget!"); - return (PyObject *)py_ue_new_swidget(py_swidget_wrapper->Widget->AsShared(), &ue_PySWidgetType); + return (PyObject *)py_ue_new_swidget(py_swidget_wrapper->Widget->AsShared(), &ue_PySWidgetType); } PyObject * py_unreal_engine_create_wrapper_from_pyswidget(PyObject *self, PyObject *args) { - PyObject *py_object; - - if (!PyArg_ParseTuple(args, "O:create_wrapper_from_pyswidget", &py_object)) - { - return NULL; - } - - ue_PySWidget *py_swidget = py_ue_is_swidget(py_object); - if (!py_swidget) { - return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - } - - Py_INCREF(py_swidget); - FPythonSWidgetWrapper py_swidget_wrapper; - py_swidget_wrapper.Widget = py_swidget->s_widget; - return py_ue_new_uscriptstruct(FPythonSWidgetWrapper::StaticStruct(), (uint8 *)&py_swidget_wrapper); + PyObject *py_object; + + if (!PyArg_ParseTuple(args, "O:create_wrapper_from_pyswidget", &py_object)) + { + return NULL; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_object); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + FPythonSWidgetWrapper py_swidget_wrapper; + py_swidget_wrapper.Widget = py_swidget->s_widget; + return py_ue_new_uscriptstruct(FPythonSWidgetWrapper::StaticStruct(), (uint8 *)&py_swidget_wrapper); } PyObject *py_unreal_engine_open_color_picker(PyObject *self, PyObject *args, PyObject *kwargs) { - PyObject *py_callable_on_color_committed = nullptr; + PyObject *py_callable = nullptr; char *kwlist[] = { (char *)"on_color_committed", nullptr }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:open_color_picker", kwlist, - &py_callable_on_color_committed)) + &py_callable)) { return nullptr; } - if (!PyCalllable_Check_Extended(py_callable_on_color_committed)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "on_color_committed must be a callable"); } - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_on_color_committed); - // TODO: this memory should be freed... - py_delegate->AddToRoot(); - + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); FColorPickerArgs color_args; - color_args.OnColorCommitted.BindUObject(py_delegate, &UPythonSlateDelegate::OnLinearColorChanged); + color_args.OnColorCommitted.BindSP(py_delegate, &FPythonSlateDelegate::OnLinearColorChanged); if (OpenColorPicker(color_args)) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 481b453d1..99a8ffc7b 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -97,8 +97,6 @@ #include "PythonDelegate.h" -#include "UEPySlate.generated.h" - PyObject *py_unreal_engine_get_editor_window(PyObject *, PyObject *); PyObject *py_unreal_engine_find_slate_style(PyObject *, PyObject *); @@ -166,11 +164,21 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); if (value) {\ if (PyCalllable_Check_Extended(value)) {\ _base handler;\ - UPythonSlateDelegate *py_delegate = NewObject();\ - py_delegate->SetPyCallable(value);\ - py_delegate->AddToRoot();\ - handler.BindUObject(py_delegate, &UPythonSlateDelegate::_func);\ - ((ue_PySWidget *)self)->delegates.Add(py_delegate);\ + ue_PySWidget *py_swidget = (ue_PySWidget *)self;\ + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(py_swidget->s_widget, value);\ + handler.Bind(py_delegate, &FPythonSlateDelegate::_func);\ + arguments._attribute(handler);\ + } + +#define ue_py_slate_base_event_up(_base, _func, _param, _attribute) \ +{\ + PyObject *value = ue_py_dict_get_item(kwargs, _param);\ + if (value) {\ + if (PyCalllable_Check_Extended(value)) {\ + _base handler;\ + ue_PySWidget *py_swidget = (ue_PySWidget *)self;\ + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(py_swidget->s_widget, value);\ + handler.BindSP(py_delegate, &FPythonSlateDelegate::_func);\ arguments._attribute(handler);\ } @@ -296,7 +304,6 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); if (value) {\ if (_type *u_struct = ue_py_check_struct<_type>(value)) {\ Py_INCREF(value);\ - ((ue_PySWidget *)self)->py_refs.Add(value);\ arguments.attribute((_type *)u_struct);\ }\ else {\ @@ -449,7 +456,7 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); -#define ue_py_slate_farguments_event(param, attribute, type, method) ue_py_slate_base_up(type, method, param, attribute)\ +#define ue_py_slate_farguments_event(param, attribute, type, method) ue_py_slate_base_event_up(type, method, param, attribute)\ ue_py_slate_down(param) @@ -464,7 +471,6 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); if (ue_PySWidget *py_swidget = value ? py_ue_is_swidget(value) : nullptr) {\ Py_INCREF(py_swidget);\ ue_PySWidget *self_py_swidget = py_ue_is_swidget((PyObject*)self);\ - self_py_swidget->py_swidget_slots.Add(py_swidget);\ arguments.AttachWidget(py_swidget->s_widget->AsShared());\ }\ else {\ @@ -488,10 +494,9 @@ struct FPythonItem } }; -UCLASS() -class UPythonSlateDelegate : public UPythonDelegate + +class FPythonSlateDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: FReply OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event); diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index 84096f74b..9ca37a8ec 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -31,6 +31,32 @@ class FUnrealEnginePythonHouseKeeper } }; + struct FPythonSWidgetTracker + { + TWeakPtr Owner; + ue_PySWidget *PySWidget; + + FPythonSWidgetTracker(TSharedRef InOwner, ue_PySWidget *InPySWidget) + { + Owner = InOwner; + PySWidget = InPySWidget; + } + }; + + struct FPythonSWidgetDelegateTracker + { + TWeakPtr Owner; + TSharedPtr Delegate; + + FPythonSWidgetDelegateTracker(TSharedRef DelegateToTrack, TSharedRef DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonSWidgetDelegateTracker() + { + } + }; + public: static FUnrealEnginePythonHouseKeeper *Get() @@ -181,7 +207,22 @@ class FUnrealEnginePythonHouseKeeper return Delegate; } + TSharedRef NewSlateDelegate(TSharedRef Owner, PyObject *PyCallable) + { + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); + PySlateDelegatesTracker.Add(Tracker); + + return Delegate; + } + private: TMap UObjectPyMapping; TArray PyDelegatesTracker; + + + TArray PySlateTracker; + TArray PySlateDelegatesTracker; }; From e71d4052bb160d290c68490a5d30f9e2be7da3a6 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 20 Feb 2018 08:48:50 +0100 Subject: [PATCH 60/94] refactored slate delegates [2] --- .../Private/Slate/UEPyFMenuBuilder.cpp | 2 +- .../Private/Slate/UEPyFToolBarBuilder.cpp | 2 +- .../Private/Slate/UEPySPythonShelf.cpp | 6 +++--- .../UnrealEnginePython/Private/Slate/UEPySWindow.cpp | 2 +- Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp | 6 +++--- Source/UnrealEnginePython/Public/PythonHouseKeeper.h | 11 +++++++++++ 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 67fa2d172..1b128f24f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -53,7 +53,7 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO FExecuteAction handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); if (py_obj) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index c31e24f3b..8a5fe212f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -47,7 +47,7 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde } FExecuteAction handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); if (py_obj) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp index eea9f41d6..267af55c3 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp @@ -146,7 +146,7 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO if (py_callable_double_clicked) { FOnAssetDoubleClicked handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_double_clicked); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_double_clicked); handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetDoubleClicked); @@ -156,7 +156,7 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO if (py_callable_get_context_menu) { FOnGetAssetContextMenu handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_get_context_menu); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_get_context_menu); handler.BindSP(py_delegate, &FPythonSlateDelegate::OnGetAssetContextMenu); @@ -166,7 +166,7 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO if (py_callable_asset_selected) { FOnAssetSelected handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable_asset_selected); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_asset_selected); handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetSelected); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp index 054e8c485..7d22b4671 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp @@ -243,7 +243,7 @@ static int ue_py_swindow_init(ue_PySWindow *self, PyObject *args, PyObject *kwar if (on_closed && PyCalllable_Check_Extended(on_closed)) { FOnWindowClosed handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, on_closed); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_compound_widget.s_widget.s_widget, on_closed); handler.BindSP(py_delegate, &FPythonSlateDelegate::OnWindowClosed); sw_window->SetOnWindowClosed(handler); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 536223cf5..3064f7997 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -1326,7 +1326,7 @@ PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self TArray &Extenders = ContentBrowser.GetAllAssetViewContextMenuExtenders(); FContentBrowserMenuExtender_SelectedAssets handler; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); handler.BindSP(py_delegate, &FPythonSlateDelegate::OnExtendContentBrowserMenu); Extenders.Add(handler); @@ -1349,7 +1349,7 @@ PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject * self, PyObject return PyErr_Format(PyExc_Exception, "argument is not callable"); FOnSpawnTab spawn_tab; - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); spawn_tab.BindSP(py_delegate, &FPythonSlateDelegate::SpawnPythonTab); FTabSpawnerEntry *spawner_entry = &FGlobalTabmanager::Get()->RegisterNomadTabSpawner(UTF8_TO_TCHAR(name), spawn_tab) @@ -1454,7 +1454,7 @@ PyObject *py_unreal_engine_open_color_picker(PyObject *self, PyObject *args, PyO return PyErr_Format(PyExc_Exception, "on_color_committed must be a callable"); } - TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); FColorPickerArgs color_args; color_args.OnColorCommitted.BindSP(py_delegate, &FPythonSlateDelegate::OnLinearColorChanged); diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index 9ca37a8ec..4df841808 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -218,6 +218,16 @@ class FUnrealEnginePythonHouseKeeper return Delegate; } + TSharedRef NewStaticSlateDelegate(PyObject *PyCallable) + { + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + PyStaticSlateDelegatesTracker.Add(Delegate); + + return Delegate; + } + private: TMap UObjectPyMapping; TArray PyDelegatesTracker; @@ -225,4 +235,5 @@ class FUnrealEnginePythonHouseKeeper TArray PySlateTracker; TArray PySlateDelegatesTracker; + TArray> PyStaticSlateDelegatesTracker; }; From 5376debf6961cbfc4fc1ef0a291ecb1dbac6c381 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Wed, 21 Feb 2018 19:37:09 +0100 Subject: [PATCH 61/94] reintroduced memory debug --- .../Private/UEPyUScriptStruct.cpp | 31 ++++++++++++------- .../Private/UnrealEnginePythonPrivatePCH.h | 2 +- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index f481dd784..7b230baf8 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -105,6 +105,10 @@ PyObject *py_ue_uscriptstruct_as_dict(ue_PyUScriptStruct * self, PyObject * args return py_struct_dict; } +static PyObject *py_ue_uscriptstruct_ref(ue_PyUScriptStruct *, PyObject *); + + + static PyMethodDef ue_PyUScriptStruct_methods[] = { { "get_field", (PyCFunction)py_ue_uscriptstruct_get_field, METH_VARARGS, "" }, @@ -113,6 +117,7 @@ static PyMethodDef ue_PyUScriptStruct_methods[] = { { "get_struct", (PyCFunction)py_ue_uscriptstruct_get_struct, METH_VARARGS, "" }, { "clone", (PyCFunction)py_ue_uscriptstruct_clone, METH_VARARGS, "" }, { "as_dict", (PyCFunction)py_ue_uscriptstruct_as_dict, METH_VARARGS, "" }, + { "ref", (PyCFunction)py_ue_uscriptstruct_ref, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -198,6 +203,8 @@ static int ue_PyUScriptStruct_setattro(ue_PyUScriptStruct *self, PyObject *attr_ return PyObject_GenericSetAttr((PyObject *)self, attr_name, value); } + + // destructor static void ue_PyUScriptStruct_dealloc(ue_PyUScriptStruct *self) { @@ -274,6 +281,17 @@ static int ue_py_uscriptstruct_init(ue_PyUScriptStruct *self, PyObject *args, Py return 0; } +// get the original pointer of a struct +static PyObject *py_ue_uscriptstruct_ref(ue_PyUScriptStruct *self, PyObject * args) +{ + ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); + ret->u_struct = self->u_struct; + ret->data = self->original_data; + ret->original_data = ret->data; + ret->is_ptr = 1; + return (PyObject *)ret; +} + static PyObject *ue_py_uscriptstruct_richcompare(ue_PyUScriptStruct *u_struct1, PyObject *py_obj, int op) { ue_PyUScriptStruct *u_struct2 = py_ue_is_uscriptstruct(py_obj); @@ -300,16 +318,7 @@ static PyObject *ue_py_uscriptstruct_richcompare(ue_PyUScriptStruct *u_struct1, Py_RETURN_TRUE; } -// get the original pointer of a struct -static PyObject *ue_py_uscriptstruct_get_ptr(ue_PyUScriptStruct *self, PyObject * args) -{ - ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); - ret->u_struct = self->u_struct; - ret->data = self->original_data; - ret->original_data = ret->data; - ret->is_ptr = 1; - return (PyObject *)ret; -} + void ue_python_init_uscriptstruct(PyObject *ue_module) @@ -320,8 +329,6 @@ void ue_python_init_uscriptstruct(PyObject *ue_module) ue_PyUScriptStructType.tp_init = (initproc)ue_py_uscriptstruct_init; - ue_PyUScriptStructType.tp_call = (ternaryfunc)ue_py_uscriptstruct_get_ptr; - if (PyType_Ready(&ue_PyUScriptStructType) < 0) return; diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 833830a65..ca39635cc 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -//#define UEPY_MEMORY_DEBUG 1 +#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" From b665a9c35d266deba073bab64f01bd09ce74ae63 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Wed, 21 Feb 2018 20:01:26 +0100 Subject: [PATCH 62/94] correctly expose UCLASS'es --- Source/UnrealEnginePython/Public/PyActor.h | 2 +- Source/UnrealEnginePython/Public/PyCharacter.h | 2 +- Source/UnrealEnginePython/Public/PyHUD.h | 2 +- Source/UnrealEnginePython/Public/PyPawn.h | 2 +- Source/UnrealEnginePython/Public/PyUserWidget.h | 2 +- Source/UnrealEnginePython/Public/PythonComponent.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/UnrealEnginePython/Public/PyActor.h b/Source/UnrealEnginePython/Public/PyActor.h index 1670fc201..dcc03fbde 100644 --- a/Source/UnrealEnginePython/Public/PyActor.h +++ b/Source/UnrealEnginePython/Public/PyActor.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyActor : public AActor +class UNREALENGINEPYTHON_API APyActor : public AActor { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyCharacter.h b/Source/UnrealEnginePython/Public/PyCharacter.h index 1eea75bb0..f1136c9cf 100644 --- a/Source/UnrealEnginePython/Public/PyCharacter.h +++ b/Source/UnrealEnginePython/Public/PyCharacter.h @@ -7,7 +7,7 @@ UCLASS() -class APyCharacter : public ACharacter +class UNREALENGINEPYTHON_API APyCharacter : public ACharacter { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyHUD.h b/Source/UnrealEnginePython/Public/PyHUD.h index 107cb7ff5..de5ffdd40 100644 --- a/Source/UnrealEnginePython/Public/PyHUD.h +++ b/Source/UnrealEnginePython/Public/PyHUD.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyHUD : public AHUD +class UNREALENGINEPYTHON_API APyHUD : public AHUD { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyPawn.h b/Source/UnrealEnginePython/Public/PyPawn.h index 9e7fa632b..974c930a2 100644 --- a/Source/UnrealEnginePython/Public/PyPawn.h +++ b/Source/UnrealEnginePython/Public/PyPawn.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyPawn : public APawn +class UNREALENGINEPYTHON_API APyPawn : public APawn { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyUserWidget.h b/Source/UnrealEnginePython/Public/PyUserWidget.h index ac715fa11..20b856f32 100644 --- a/Source/UnrealEnginePython/Public/PyUserWidget.h +++ b/Source/UnrealEnginePython/Public/PyUserWidget.h @@ -7,7 +7,7 @@ UCLASS(BlueprintType, Blueprintable) -class UPyUserWidget : public UUserWidget +class UNREALENGINEPYTHON_API UPyUserWidget : public UUserWidget { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PythonComponent.h b/Source/UnrealEnginePython/Public/PythonComponent.h index 4b9856efa..aa08c70af 100644 --- a/Source/UnrealEnginePython/Public/PythonComponent.h +++ b/Source/UnrealEnginePython/Public/PythonComponent.h @@ -7,7 +7,7 @@ UCLASS(BlueprintType, Blueprintable, ClassGroup = (Python), meta = (BlueprintSpawnableComponent)) -class UPythonComponent : public UActorComponent +class UNREALENGINEPYTHON_API UPythonComponent : public UActorComponent { GENERATED_BODY() From 9920aacb18526e84747f243a091493bbac9c7360 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 07:52:52 +0100 Subject: [PATCH 63/94] added bytes serialization --- .../UnrealEnginePython/Private/UEPyEditor.cpp | 22 +++++- .../UnrealEnginePython/Private/UEPyEditor.h | 2 + .../UnrealEnginePython/Private/UEPyModule.cpp | 7 ++ .../Private/UObject/UEPyCapture.cpp | 71 ++++++++++++++----- .../Private/UObject/UEPyCapture.h | 1 + .../Private/UObject/UEPyObject.cpp | 41 +++++++++++ .../Private/UObject/UEPyObject.h | 6 +- 7 files changed, 132 insertions(+), 18 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index db643f084..9d06aecce 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -70,6 +70,26 @@ PyObject *py_unreal_engine_editor_play_in_viewport(PyObject * self, PyObject * a } +PyObject *py_unreal_engine_request_play_session(PyObject * self, PyObject * args) +{ + + PyObject *py_at_player_start = nullptr; + PyObject *py_simulate_in_editor = nullptr; + + if (!PyArg_ParseTuple(args, "|OO:request_play_session", &py_at_player_start, &py_simulate_in_editor)) + { + return nullptr; + } + + bool bAtPlayerStart = py_at_player_start && PyObject_IsTrue(py_at_player_start); + bool bSimulate = py_simulate_in_editor && PyObject_IsTrue(py_simulate_in_editor); + + GEditor->RequestPlaySession(bAtPlayerStart, nullptr, bSimulate); + + Py_RETURN_NONE; + +} + PyObject *py_unreal_engine_get_editor_world(PyObject * self, PyObject * args) { @@ -396,7 +416,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) char * filename = PyString_AsString(PyObject_Str(assetsObject)); #endif files.Add(UTF8_TO_TCHAR(filename)); - } +} else { return PyErr_Format(PyExc_Exception, "Not a string nor valid list of string"); diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index 700fe68d5..35346a4ca 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -118,4 +118,6 @@ PyObject *py_unreal_engine_play_preview_sound(PyObject *, PyObject *); PyObject *py_unreal_engine_register_settings(PyObject *, PyObject *); PyObject *py_unreal_engine_show_viewer(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_settings(PyObject *, PyObject *); + +PyObject *py_unreal_engine_request_play_session(PyObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index e9005e542..d0a79e8ab 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -360,6 +360,7 @@ static PyMethodDef unreal_engine_methods[] = { { "play_sound", py_unreal_engine_play_sound, METH_VARARGS, "" }, #if WITH_EDITOR { "editor_play_in_viewport", py_unreal_engine_editor_play_in_viewport, METH_VARARGS, "" }, + { "request_play_session", py_unreal_engine_request_play_session, METH_VARARGS, "" }, { "get_editor_pie_game_viewport_client", py_unreal_engine_get_editor_pie_game_viewport_client, METH_VARARGS, "" }, { "editor_get_active_viewport_screenshot", py_unreal_engine_editor_get_active_viewport_screenshot, METH_VARARGS, "" }, { "editor_get_pie_viewport_screenshot", py_unreal_engine_editor_get_pie_viewport_screenshot, METH_VARARGS, "" }, @@ -799,6 +800,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "capture_initialize", (PyCFunction)py_ue_capture_initialize, METH_VARARGS, "" }, { "capture_start", (PyCFunction)py_ue_capture_start, METH_VARARGS, "" }, { "capture_stop", (PyCFunction)py_ue_capture_stop, METH_VARARGS, "" }, + { "capture_load_from_config", (PyCFunction)py_ue_capture_load_from_config, METH_VARARGS, "" }, // Pawn { "get_controller", (PyCFunction)py_ue_pawn_get_controller, METH_VARARGS, "" }, @@ -953,6 +955,11 @@ static PyMethodDef ue_PyUObject_methods[] = { #if ENGINE_MINOR_VERSION >= 15 { "enum_user_defined_names", (PyCFunction)py_ue_enum_user_defined_names, METH_VARARGS, "" }, #endif + + // serialization + { "to_bytes", (PyCFunction)py_ue_to_bytes, METH_VARARGS, "" }, + { "to_bytearray", (PyCFunction)py_ue_to_bytearray, METH_VARARGS, "" }, + { "from_bytes", (PyCFunction)py_ue_from_bytes, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 48b5c40c8..f8ace8dbf 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -3,13 +3,15 @@ #include "Runtime/MovieSceneCapture/Public/MovieSceneCapture.h" -PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - PyObject *py_widget; + PyObject *py_widget = nullptr; - if (!PyArg_ParseTuple(args, "O", &py_widget)) { + if (!PyArg_ParseTuple(args, "|O:capture_initialize", &py_widget)) + { return nullptr; } @@ -17,26 +19,62 @@ PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) { if (!capture) return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); - ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); - if (!s_widget) - return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - #if WITH_EDITOR - if (s_widget->s_widget->GetType().Compare(FName("SPythonEditorViewport")) == 0) { - TSharedRef s_viewport = StaticCastSharedRef(s_widget->s_widget); - capture->Initialize(s_viewport->GetSceneViewport()); - capture->StartWarmup(); + if (py_widget) + { + ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); + if (!s_widget) + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + + + if (s_widget->s_widget->GetType().Compare(FName("SPythonEditorViewport")) == 0) + { + TSharedRef s_viewport = StaticCastSharedRef(s_widget->s_widget); + capture->Initialize(s_viewport->GetSceneViewport()); + capture->StartWarmup(); + } + else + { + return PyErr_Format(PyExc_Exception, "argument is not a supported Viewport-based SWidget"); + } + } - else { - return PyErr_Format(PyExc_Exception, "argument is not a supported Viewport-based SWidget"); + else + { + for (const FWorldContext &Context : GEngine->GetWorldContexts()) + { + if (Context.WorldType == EWorldType::PIE) + { + if (Context.GameViewport) + { + UE_LOG(LogPython, Error, TEXT("Found Viewport at %p"), Context.GameViewport); + FSlatePlayInEditorInfo *SlatePIEInfo = GEditor->SlatePlayInEditorMap.Find(Context.ContextHandle); + + capture->Initialize(SlatePIEInfo->SlatePlayInEditorWindowViewport); + } + } + } } #endif + Py_RETURN_NONE; +} +PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + UMovieSceneCapture *capture = ue_py_check_type(self); + if (!capture) + return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); + + capture->StartCapture(); Py_RETURN_NONE; } -PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_capture_load_from_config(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -44,12 +82,13 @@ PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) { if (!capture) return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); - capture->StartCapture(); + capture->LoadFromConfig(); Py_RETURN_NONE; } -PyObject *py_ue_capture_stop(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_capture_stop(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h index 55de8ddcf..23e7d8e12 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h @@ -6,4 +6,5 @@ PyObject *py_ue_capture_initialize(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_start(ue_PyUObject *, PyObject *); +PyObject *py_ue_capture_load_from_config(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 249d2dd4c..61f7ebe8c 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1834,3 +1834,44 @@ PyObject *py_ue_duplicate(ue_PyUObject * self, PyObject * args) } #endif + +PyObject *py_ue_to_bytes(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + TArray Bytes; + + FObjectWriter(self->ue_object, Bytes); + + return PyBytes_FromStringAndSize((const char *)Bytes.GetData(), Bytes.Num()); +} + +PyObject *py_ue_to_bytearray(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + TArray Bytes; + + FObjectWriter(self->ue_object, Bytes); + + return PyByteArray_FromStringAndSize((const char *)Bytes.GetData(), Bytes.Num()); +} + +PyObject *py_ue_from_bytes(ue_PyUObject * self, PyObject * args) +{ + + Py_buffer py_buf; + + if (!PyArg_ParseTuple(args, "z*:from_bytes", &py_buf)) + return nullptr; + + ue_py_check(self); + + TArray Bytes((const uint8 *)py_buf.buf, py_buf.len); + + FObjectReader(self->ue_object, Bytes); + + Py_RETURN_NONE; +} diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index 347470bd2..a941f2ce9 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -89,4 +89,8 @@ PyObject *py_ue_import_custom_properties(ue_PyUObject *, PyObject *); #endif PyObject *py_ue_get_thumbnail(ue_PyUObject *, PyObject *); -PyObject *py_ue_render_thumbnail(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_render_thumbnail(ue_PyUObject *, PyObject *); + +PyObject *py_ue_to_bytes(ue_PyUObject *, PyObject *); +PyObject *py_ue_to_bytearray(ue_PyUObject *, PyObject *); +PyObject *py_ue_from_bytes(ue_PyUObject *, PyObject *); \ No newline at end of file From 1ad6f81f13a5d70d7870ac40be9b6be15995ba54 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 09:56:35 +0100 Subject: [PATCH 64/94] Update FixingMixamoRootMotionWithPython.md --- tutorials/FixingMixamoRootMotionWithPython.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/FixingMixamoRootMotionWithPython.md b/tutorials/FixingMixamoRootMotionWithPython.md index 4eabad28d..3abb1266d 100644 --- a/tutorials/FixingMixamoRootMotionWithPython.md +++ b/tutorials/FixingMixamoRootMotionWithPython.md @@ -169,7 +169,7 @@ bone_map = mesh.skeletal_mesh_get_bone_map() and to assign a new one: ```python -mesh.skeletal_mesh_get_bone_map([17, 22, 26, 30, ...]) +mesh.skeletal_mesh_set_bone_map([17, 22, 26, 30, ...]) ``` In the same way you can retrieve soft skin vertices: From 120e16da720818c052271cf13d3d33a60e9159ce Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 10:12:48 +0100 Subject: [PATCH 65/94] added automation for level sequence exporter --- .../UnrealEnginePython/Private/UEPyModule.cpp | 5 + .../Private/UObject/UEPyCapture.cpp | 304 +++++++++++++++++- .../Private/UObject/UEPyCapture.h | 5 +- 3 files changed, 297 insertions(+), 17 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index d0a79e8ab..235e91fb8 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -802,6 +802,11 @@ static PyMethodDef ue_PyUObject_methods[] = { { "capture_stop", (PyCFunction)py_ue_capture_stop, METH_VARARGS, "" }, { "capture_load_from_config", (PyCFunction)py_ue_capture_load_from_config, METH_VARARGS, "" }, +#if WITH_EDITOR + { "in_editor_capture", (PyCFunction)py_ue_in_editor_capture, METH_VARARGS, "" }, + { "set_level_sequence_asset", (PyCFunction)py_ue_set_level_sequence_asset, METH_VARARGS, "" }, +#endif + // Pawn { "get_controller", (PyCFunction)py_ue_pawn_get_controller, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index f8ace8dbf..a532753ad 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -2,6 +2,294 @@ #include "Runtime/MovieSceneCapture/Public/MovieSceneCapture.h" +#if WITH_EDITOR + +/* + +This is taken as-is (more or less) from MovieSceneCaptureDialogModule.cpp +to automate sequencer capturing + +*/ + +#include "AudioDevice.h" +#include "Editor/EditorEngine.h" +#include "Slate/SceneViewport.h" +#include "AutomatedLevelSequenceCapture.h" + +struct FInEditorCapture : TSharedFromThis +{ + + static TWeakPtr CreateInEditorCapture(UMovieSceneCapture* InCaptureObject) + { + // FInEditorCapture owns itself, so should only be kept alive by itself, or a pinned (=> temporary) weakptr + FInEditorCapture* Capture = new FInEditorCapture; + Capture->Start(InCaptureObject); + return Capture->AsShared(); + } + + UWorld* GetWorld() const + { + return CapturingFromWorld; + } + +private: + FInEditorCapture() + { + CapturingFromWorld = nullptr; + CaptureObject = nullptr; + } + + void Start(UMovieSceneCapture* InCaptureObject) + { + check(InCaptureObject); + + CapturingFromWorld = nullptr; + OnlyStrongReference = MakeShareable(this); + + CaptureObject = InCaptureObject; + + ULevelEditorPlaySettings* PlayInEditorSettings = GetMutableDefault(); + + bScreenMessagesWereEnabled = GAreScreenMessagesEnabled; + GAreScreenMessagesEnabled = false; + + if (!InCaptureObject->Settings.bEnableTextureStreaming) + { + const int32 UndefinedTexturePoolSize = -1; + IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); + if (CVarStreamingPoolSize) + { + BackedUpStreamingPoolSize = CVarStreamingPoolSize->GetInt(); + CVarStreamingPoolSize->Set(UndefinedTexturePoolSize, ECVF_SetByConsole); + } + + IConsoleVariable* CVarUseFixedPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.UseFixedPoolSize")); + if (CVarUseFixedPoolSize) + { + BackedUpUseFixedPoolSize = CVarUseFixedPoolSize->GetInt(); + CVarUseFixedPoolSize->Set(0, ECVF_SetByConsole); + } + } + + FObjectWriter(PlayInEditorSettings, BackedUpPlaySettings); + OverridePlaySettings(PlayInEditorSettings); + + CaptureObject->AddToRoot(); + CaptureObject->OnCaptureFinished().AddRaw(this, &FInEditorCapture::OnEnd); + + UGameViewportClient::OnViewportCreated().AddRaw(this, &FInEditorCapture::OnStart); + FEditorDelegates::EndPIE.AddRaw(this, &FInEditorCapture::OnEndPIE); + + FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); + if (AudioDevice != nullptr) + { + TransientMasterVolume = AudioDevice->GetTransientMasterVolume(); + AudioDevice->SetTransientMasterVolume(0.0f); + } + + GEditor->RequestPlaySession(true, nullptr, false); + } + + void OverridePlaySettings(ULevelEditorPlaySettings* PlayInEditorSettings) + { + const FMovieSceneCaptureSettings& Settings = CaptureObject->GetSettings(); + + PlayInEditorSettings->NewWindowWidth = Settings.Resolution.ResX; + PlayInEditorSettings->NewWindowHeight = Settings.Resolution.ResY; + PlayInEditorSettings->CenterNewWindow = true; + PlayInEditorSettings->LastExecutedPlayModeType = EPlayModeType::PlayMode_InEditorFloating; + + TSharedRef CustomWindow = SNew(SWindow) + .Title(FText::FromString("Movie Render - Preview")) + .AutoCenter(EAutoCenter::PrimaryWorkArea) + .UseOSWindowBorder(true) + .FocusWhenFirstShown(false) + .ActivationPolicy(EWindowActivationPolicy::Never) + .HasCloseButton(true) + .SupportsMaximize(false) + .SupportsMinimize(true) + .MaxWidth(Settings.Resolution.ResX) + .MaxHeight(Settings.Resolution.ResY) + .SizingRule(ESizingRule::FixedSize); + + FSlateApplication::Get().AddWindow(CustomWindow); + + PlayInEditorSettings->CustomPIEWindow = CustomWindow; + + // Reset everything else + PlayInEditorSettings->GameGetsMouseControl = false; + PlayInEditorSettings->ShowMouseControlLabel = false; + PlayInEditorSettings->ViewportGetsHMDControl = false; + PlayInEditorSettings->ShouldMinimizeEditorOnVRPIE = true; + PlayInEditorSettings->EnableGameSound = false; + PlayInEditorSettings->bOnlyLoadVisibleLevelsInPIE = false; + PlayInEditorSettings->bPreferToStreamLevelsInPIE = false; + PlayInEditorSettings->PIEAlwaysOnTop = false; + PlayInEditorSettings->DisableStandaloneSound = true; + PlayInEditorSettings->AdditionalLaunchParameters = TEXT(""); + PlayInEditorSettings->BuildGameBeforeLaunch = EPlayOnBuildMode::PlayOnBuild_Never; + PlayInEditorSettings->LaunchConfiguration = EPlayOnLaunchConfiguration::LaunchConfig_Default; + PlayInEditorSettings->SetPlayNetMode(EPlayNetMode::PIE_Standalone); + PlayInEditorSettings->SetRunUnderOneProcess(true); + PlayInEditorSettings->SetPlayNetDedicated(false); + PlayInEditorSettings->SetPlayNumberOfClients(1); + } + + void OnStart() + { + for (const FWorldContext& Context : GEngine->GetWorldContexts()) + { + if (Context.WorldType == EWorldType::PIE) + { + FSlatePlayInEditorInfo* SlatePlayInEditorSession = GEditor->SlatePlayInEditorMap.Find(Context.ContextHandle); + if (SlatePlayInEditorSession) + { + CapturingFromWorld = Context.World(); + + TSharedPtr Window = SlatePlayInEditorSession->SlatePlayInEditorWindow.Pin(); + + const FMovieSceneCaptureSettings& Settings = CaptureObject->GetSettings(); + + SlatePlayInEditorSession->SlatePlayInEditorWindowViewport->SetViewportSize(Settings.Resolution.ResX, Settings.Resolution.ResY); + + FVector2D PreviewWindowSize(Settings.Resolution.ResX, Settings.Resolution.ResY); + + // Keep scaling down the window size while we're bigger than half the destop width/height + { + FDisplayMetrics DisplayMetrics; + FSlateApplication::Get().GetDisplayMetrics(DisplayMetrics); + + while (PreviewWindowSize.X >= DisplayMetrics.PrimaryDisplayWidth*.5f || PreviewWindowSize.Y >= DisplayMetrics.PrimaryDisplayHeight*.5f) + { + PreviewWindowSize *= .5f; + } + } + + // Resize and move the window into the desktop a bit + FVector2D PreviewWindowPosition(50, 50); + Window->ReshapeWindow(PreviewWindowPosition, PreviewWindowSize); + + if (CaptureObject->Settings.GameModeOverride != nullptr) + { + CachedGameMode = CapturingFromWorld->GetWorldSettings()->DefaultGameMode; + CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CaptureObject->Settings.GameModeOverride; + } + + CaptureObject->Initialize(SlatePlayInEditorSession->SlatePlayInEditorWindowViewport, Context.PIEInstance); + } + return; + } + } + + // todo: error? + } + + void Shutdown() + { + FEditorDelegates::EndPIE.RemoveAll(this); + UGameViewportClient::OnViewportCreated().RemoveAll(this); + CaptureObject->OnCaptureFinished().RemoveAll(this); + + GAreScreenMessagesEnabled = bScreenMessagesWereEnabled; + + if (!CaptureObject->Settings.bEnableTextureStreaming) + { + IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); + if (CVarStreamingPoolSize) + { + CVarStreamingPoolSize->Set(BackedUpStreamingPoolSize, ECVF_SetByConsole); + } + + IConsoleVariable* CVarUseFixedPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.UseFixedPoolSize")); + if (CVarUseFixedPoolSize) + { + CVarUseFixedPoolSize->Set(BackedUpUseFixedPoolSize, ECVF_SetByConsole); + } + } + + if (CaptureObject->Settings.GameModeOverride != nullptr) + { + CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CachedGameMode; + } + + FObjectReader(GetMutableDefault(), BackedUpPlaySettings); + + FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); + if (AudioDevice != nullptr) + { + AudioDevice->SetTransientMasterVolume(TransientMasterVolume); + } + + CaptureObject->Close(); + CaptureObject->RemoveFromRoot(); + + } + void OnEndPIE(bool bIsSimulating) + { + Shutdown(); + OnlyStrongReference = nullptr; + } + + void OnEnd() + { + Shutdown(); + OnlyStrongReference = nullptr; + + GEditor->RequestEndPlayMap(); + } + + TSharedPtr OnlyStrongReference; + UWorld* CapturingFromWorld; + + bool bScreenMessagesWereEnabled; + float TransientMasterVolume; + int32 BackedUpStreamingPoolSize; + int32 BackedUpUseFixedPoolSize; + TArray BackedUpPlaySettings; + UMovieSceneCapture* CaptureObject; + + TSubclassOf CachedGameMode; +}; + +PyObject *py_ue_in_editor_capture(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + UMovieSceneCapture *capture = ue_py_check_type(self); + if (!capture) + return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); + + FInEditorCapture::CreateInEditorCapture(capture); + + Py_RETURN_NONE; +} + +PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *self, PyObject *args) +{ + ue_py_check(self); + + PyObject *py_sequence = nullptr; + + if (!PyArg_ParseTuple(args, "O:set_level_sequence_asset", &py_sequence)) + { + return nullptr; + } + + ULevelSequence *sequence = ue_py_check_type(py_sequence); + if (!sequence) + { + return PyErr_Format(PyExc_Exception, "uobject is not a ULevelSequence"); + } + + UAutomatedLevelSequenceCapture *capture = ue_py_check_type(self); + if (!capture) + return PyErr_Format(PyExc_Exception, "uobject is not a UAutomatedLevelSequenceCapture"); + + capture->SetLevelSequenceAsset(sequence->GetPathName()); + + Py_RETURN_NONE; +} +#endif PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) { @@ -39,22 +327,6 @@ PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) } } - else - { - for (const FWorldContext &Context : GEngine->GetWorldContexts()) - { - if (Context.WorldType == EWorldType::PIE) - { - if (Context.GameViewport) - { - UE_LOG(LogPython, Error, TEXT("Found Viewport at %p"), Context.GameViewport); - FSlatePlayInEditorInfo *SlatePIEInfo = GEditor->SlatePlayInEditorMap.Find(Context.ContextHandle); - - capture->Initialize(SlatePIEInfo->SlatePlayInEditorWindowViewport); - } - } - } - } #endif Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h index 23e7d8e12..d9f1e5e46 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h @@ -7,4 +7,7 @@ PyObject *py_ue_capture_initialize(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_start(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_load_from_config(ue_PyUObject *, PyObject *); -PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); + +PyObject *py_ue_in_editor_capture(ue_PyUObject *, PyObject *); +PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *, PyObject *); \ No newline at end of file From 3308e81e16076d33505da41bc22302427eb5e323 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 10:34:13 +0100 Subject: [PATCH 66/94] ported to 4.16 --- Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp | 4 ++++ Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp | 2 ++ Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp | 4 +++- Source/UnrealEnginePython/Public/PythonHouseKeeper.h | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp index 81bf1bf47..503bc028f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp @@ -10,7 +10,11 @@ static PyObject *py_ue_fgeometry_get_local_size(ue_PyFGeometry *self, PyObject * static PyObject *py_ue_fgeometry_get_absolute_position(ue_PyFGeometry *self, PyObject * args) { +#if ENGINE_MINOR_VERSION < 17 + FVector2D size = self->geometry.AbsolutePosition; +#else FVector2D size = self->geometry.GetAbsolutePosition(); +#endif return Py_BuildValue("(ff)", size.X, size.Y); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index a532753ad..f6a13540c 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -120,7 +120,9 @@ struct FInEditorCapture : TSharedFromThis PlayInEditorSettings->GameGetsMouseControl = false; PlayInEditorSettings->ShowMouseControlLabel = false; PlayInEditorSettings->ViewportGetsHMDControl = false; +#if ENGINE_MINOR_VERSION >= 17 PlayInEditorSettings->ShouldMinimizeEditorOnVRPIE = true; +#endif PlayInEditorSettings->EnableGameSound = false; PlayInEditorSettings->bOnlyLoadVisibleLevelsInPIE = false; PlayInEditorSettings->bPreferToStreamLevelsInPIE = false; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 61f7ebe8c..5420dd74a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1869,7 +1869,9 @@ PyObject *py_ue_from_bytes(ue_PyUObject * self, PyObject * args) ue_py_check(self); - TArray Bytes((const uint8 *)py_buf.buf, py_buf.len); + TArray Bytes; + Bytes.AddUninitialized(py_buf.len); + FMemory::Memcpy(Bytes.GetData(), py_buf.buf, py_buf.len); FObjectReader(self->ue_object, Bytes); diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index 4df841808..3b1c1ad1a 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -66,7 +66,11 @@ class FUnrealEnginePythonHouseKeeper { Singleton = new FUnrealEnginePythonHouseKeeper(); // register a new delegate for the GC +#if ENGINE_MINOR_VERSION >= 18 FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#else + FCoreUObjectDelegates::PostGarbageCollect.AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#endif } return Singleton; } From 8681a48f1d412819f5850a52808eec66fc164451 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 14:53:21 +0100 Subject: [PATCH 67/94] improved capture and asset export --- .../UnrealEnginePython/Private/UEPyEditor.cpp | 43 ++++- .../UnrealEnginePython/Private/UEPyEditor.h | 1 + .../UnrealEnginePython/Private/UEPyModule.cpp | 7 +- .../Private/UObject/UEPyCapture.cpp | 155 +++++++++++++----- .../Private/UObject/UEPyCapture.h | 4 +- .../Private/UObject/UEPyExporter.cpp | 39 +++++ .../Private/UObject/UEPyExporter.h | 8 + .../Private/UnrealEnginePythonPrivatePCH.h | 2 +- 8 files changed, 214 insertions(+), 45 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp create mode 100644 Source/UnrealEnginePython/Private/UObject/UEPyExporter.h diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 9d06aecce..15beb4936 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -416,7 +416,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) char * filename = PyString_AsString(PyObject_Str(assetsObject)); #endif files.Add(UTF8_TO_TCHAR(filename)); -} + } else { return PyErr_Format(PyExc_Exception, "Not a string nor valid list of string"); @@ -2334,5 +2334,46 @@ PyObject *py_unreal_engine_editor_sync_browser_to_assets(PyObject * self, PyObje Py_RETURN_NONE; } + +PyObject *py_unreal_engine_export_assets(PyObject * self, PyObject * args) +{ + + if (!GEditor) + return PyErr_Format(PyExc_Exception, "no GEditor found"); + + PyObject * py_assets = nullptr; + char *filename; + + if (!PyArg_ParseTuple(args, "Os:export_assets", &py_assets, &filename)) + { + return nullptr; + } + + TArray UObjects; + PyObject *py_iter = PyObject_GetIter(py_assets); + + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UObject"); + } + + while (PyObject *py_item = PyIter_Next(py_iter)) + { + UObject *Object = ue_py_check_type(py_item); + if (!Object) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UObject"); + } + UObjects.Add(Object); + } + + Py_DECREF(py_iter); + + FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + AssetToolsModule.Get().ExportAssets(UObjects, FString(UTF8_TO_TCHAR(filename))); + + Py_RETURN_NONE; +} #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index 35346a4ca..36e24c6ad 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -120,4 +120,5 @@ PyObject *py_unreal_engine_show_viewer(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_settings(PyObject *, PyObject *); PyObject *py_unreal_engine_request_play_session(PyObject *, PyObject *); +PyObject *py_unreal_engine_export_assets(PyObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 235e91fb8..8e69d9f70 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -35,6 +35,7 @@ #include "UObject/UEPyLandscape.h" #include "UObject/UEPyUserDefinedStruct.h" #include "UObject/UEPyDataTable.h" +#include "UObject/UEPyExporter.h" #include "UEPyAssetUserData.h" @@ -225,6 +226,7 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_select_actor", py_unreal_engine_editor_select_actor, METH_VARARGS, "" }, { "editor_deselect_actors", py_unreal_engine_editor_deselect_actors, METH_VARARGS, "" }, { "import_asset", py_unreal_engine_import_asset, METH_VARARGS, "" }, + { "export_assets", py_unreal_engine_export_assets, METH_VARARGS, "" }, { "get_asset", py_unreal_engine_get_asset, METH_VARARGS, "" }, { "find_asset", py_unreal_engine_find_asset, METH_VARARGS, "" }, { "delete_object", py_unreal_engine_delete_object, METH_VARARGS, "" }, @@ -378,6 +380,8 @@ static PyMethodDef unreal_engine_methods[] = { { "register_settings", py_unreal_engine_register_settings, METH_VARARGS, "" }, { "show_viewer", py_unreal_engine_show_viewer, METH_VARARGS, "" }, { "unregister_settings", py_unreal_engine_unregister_settings, METH_VARARGS, "" }, + + { "in_editor_capture", py_unreal_engine_in_editor_capture, METH_VARARGS, "" }, #endif { "clipboard_copy", py_unreal_engine_clipboard_copy, METH_VARARGS, "" }, @@ -546,6 +550,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "data_table_get_all_rows", (PyCFunction)py_ue_data_table_get_all_rows, METH_VARARGS, "" }, #endif + { "export_to_file", (PyCFunction)py_ue_export_to_file, METH_VARARGS, "" }, + { "is_rooted", (PyCFunction)py_ue_is_rooted, METH_VARARGS, "" }, { "add_to_root", (PyCFunction)py_ue_add_to_root, METH_VARARGS, "" }, { "auto_root", (PyCFunction)py_ue_auto_root, METH_VARARGS, "" }, @@ -803,7 +809,6 @@ static PyMethodDef ue_PyUObject_methods[] = { { "capture_load_from_config", (PyCFunction)py_ue_capture_load_from_config, METH_VARARGS, "" }, #if WITH_EDITOR - { "in_editor_capture", (PyCFunction)py_ue_in_editor_capture, METH_VARARGS, "" }, { "set_level_sequence_asset", (PyCFunction)py_ue_set_level_sequence_asset, METH_VARARGS, "" }, #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index f6a13540c..597510e9a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -7,7 +7,8 @@ /* This is taken as-is (more or less) from MovieSceneCaptureDialogModule.cpp -to automate sequencer capturing +to automate sequencer capturing. The only relevant implementation is the support +for a queue of UMovieSceneCapture objects */ @@ -16,44 +17,61 @@ to automate sequencer capturing #include "Slate/SceneViewport.h" #include "AutomatedLevelSequenceCapture.h" -struct FInEditorCapture : TSharedFromThis +struct FInEditorMultiCapture : TSharedFromThis { - static TWeakPtr CreateInEditorCapture(UMovieSceneCapture* InCaptureObject) + static TWeakPtr CreateInEditorMultiCapture(TArray InCaptureObjects) { // FInEditorCapture owns itself, so should only be kept alive by itself, or a pinned (=> temporary) weakptr - FInEditorCapture* Capture = new FInEditorCapture; - Capture->Start(InCaptureObject); + FInEditorMultiCapture* Capture = new FInEditorMultiCapture; + Capture->CaptureObjects = InCaptureObjects; + for (UMovieSceneCapture *SceneCapture : Capture->CaptureObjects) + { + SceneCapture->AddToRoot(); + } + Capture->Dequeue(); return Capture->AsShared(); } - UWorld* GetWorld() const +private: + FInEditorMultiCapture() { - return CapturingFromWorld; + CapturingFromWorld = nullptr; } -private: - FInEditorCapture() + void Die() { - CapturingFromWorld = nullptr; - CaptureObject = nullptr; + for (UMovieSceneCapture *SceneCapture : CaptureObjects) + { + SceneCapture->RemoveFromRoot(); + } + OnlyStrongReference = nullptr; } - void Start(UMovieSceneCapture* InCaptureObject) + void Dequeue() { - check(InCaptureObject); + + if (CaptureObjects.Num() < 1) + { + Die(); + return; + } + + CurrentCaptureObject = CaptureObjects[0]; + + check(CurrentCaptureObject); CapturingFromWorld = nullptr; - OnlyStrongReference = MakeShareable(this); - CaptureObject = InCaptureObject; + if (!OnlyStrongReference.IsValid()) + OnlyStrongReference = MakeShareable(this); ULevelEditorPlaySettings* PlayInEditorSettings = GetMutableDefault(); bScreenMessagesWereEnabled = GAreScreenMessagesEnabled; GAreScreenMessagesEnabled = false; - if (!InCaptureObject->Settings.bEnableTextureStreaming) + if (!CurrentCaptureObject->Settings.bEnableTextureStreaming) { const int32 UndefinedTexturePoolSize = -1; IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); @@ -71,14 +89,18 @@ struct FInEditorCapture : TSharedFromThis } } + // cleanup from previous run + BackedUpPlaySettings.Empty(); + FObjectWriter(PlayInEditorSettings, BackedUpPlaySettings); + OverridePlaySettings(PlayInEditorSettings); - CaptureObject->AddToRoot(); - CaptureObject->OnCaptureFinished().AddRaw(this, &FInEditorCapture::OnEnd); + //CurrentCaptureObject->AddToRoot(); + CurrentCaptureObject->OnCaptureFinished().AddRaw(this, &FInEditorMultiCapture::OnEnd); - UGameViewportClient::OnViewportCreated().AddRaw(this, &FInEditorCapture::OnStart); - FEditorDelegates::EndPIE.AddRaw(this, &FInEditorCapture::OnEndPIE); + UGameViewportClient::OnViewportCreated().AddRaw(this, &FInEditorMultiCapture::OnStart); + FEditorDelegates::EndPIE.AddRaw(this, &FInEditorMultiCapture::OnEndPIE); FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); if (AudioDevice != nullptr) @@ -87,12 +109,19 @@ struct FInEditorCapture : TSharedFromThis AudioDevice->SetTransientMasterVolume(0.0f); } + // play at the next tick + FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FInEditorMultiCapture::PlaySession), 0); + } + + bool PlaySession(float DeltaTime) + { GEditor->RequestPlaySession(true, nullptr, false); + return false; } void OverridePlaySettings(ULevelEditorPlaySettings* PlayInEditorSettings) { - const FMovieSceneCaptureSettings& Settings = CaptureObject->GetSettings(); + const FMovieSceneCaptureSettings& Settings = CurrentCaptureObject->GetSettings(); PlayInEditorSettings->NewWindowWidth = Settings.Resolution.ResX; PlayInEditorSettings->NewWindowHeight = Settings.Resolution.ResY; @@ -150,7 +179,7 @@ struct FInEditorCapture : TSharedFromThis TSharedPtr Window = SlatePlayInEditorSession->SlatePlayInEditorWindow.Pin(); - const FMovieSceneCaptureSettings& Settings = CaptureObject->GetSettings(); + const FMovieSceneCaptureSettings& Settings = CurrentCaptureObject->GetSettings(); SlatePlayInEditorSession->SlatePlayInEditorWindowViewport->SetViewportSize(Settings.Resolution.ResX, Settings.Resolution.ResY); @@ -171,30 +200,29 @@ struct FInEditorCapture : TSharedFromThis FVector2D PreviewWindowPosition(50, 50); Window->ReshapeWindow(PreviewWindowPosition, PreviewWindowSize); - if (CaptureObject->Settings.GameModeOverride != nullptr) + if (CurrentCaptureObject->Settings.GameModeOverride != nullptr) { CachedGameMode = CapturingFromWorld->GetWorldSettings()->DefaultGameMode; - CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CaptureObject->Settings.GameModeOverride; + CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CurrentCaptureObject->Settings.GameModeOverride; } - CaptureObject->Initialize(SlatePlayInEditorSession->SlatePlayInEditorWindowViewport, Context.PIEInstance); + CurrentCaptureObject->Initialize(SlatePlayInEditorSession->SlatePlayInEditorWindowViewport, Context.PIEInstance); } return; } } - // todo: error? } void Shutdown() { FEditorDelegates::EndPIE.RemoveAll(this); UGameViewportClient::OnViewportCreated().RemoveAll(this); - CaptureObject->OnCaptureFinished().RemoveAll(this); + CurrentCaptureObject->OnCaptureFinished().RemoveAll(this); GAreScreenMessagesEnabled = bScreenMessagesWereEnabled; - if (!CaptureObject->Settings.bEnableTextureStreaming) + if (!CurrentCaptureObject->Settings.bEnableTextureStreaming) { IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); if (CVarStreamingPoolSize) @@ -209,7 +237,7 @@ struct FInEditorCapture : TSharedFromThis } } - if (CaptureObject->Settings.GameModeOverride != nullptr) + if (CurrentCaptureObject->Settings.GameModeOverride != nullptr) { CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CachedGameMode; } @@ -222,25 +250,43 @@ struct FInEditorCapture : TSharedFromThis AudioDevice->SetTransientMasterVolume(TransientMasterVolume); } - CaptureObject->Close(); - CaptureObject->RemoveFromRoot(); + CurrentCaptureObject->Close(); + //CurrentCaptureObject->RemoveFromRoot(); } void OnEndPIE(bool bIsSimulating) { Shutdown(); - OnlyStrongReference = nullptr; + + Die(); + } + + void NextCapture(bool bIsSimulating) + { + + FEditorDelegates::EndPIE.RemoveAll(this); + // remove item from the TArray; + CaptureObjects.RemoveAt(0); + + if (CaptureObjects.Num() > 0) + { + Dequeue(); + } + else + { + Die(); + } } void OnEnd() { Shutdown(); - OnlyStrongReference = nullptr; + FEditorDelegates::EndPIE.AddRaw(this, &FInEditorMultiCapture::NextCapture); GEditor->RequestEndPlayMap(); } - TSharedPtr OnlyStrongReference; + TSharedPtr OnlyStrongReference; UWorld* CapturingFromWorld; bool bScreenMessagesWereEnabled; @@ -248,20 +294,49 @@ struct FInEditorCapture : TSharedFromThis int32 BackedUpStreamingPoolSize; int32 BackedUpUseFixedPoolSize; TArray BackedUpPlaySettings; - UMovieSceneCapture* CaptureObject; + UMovieSceneCapture* CurrentCaptureObject; TSubclassOf CachedGameMode; + TArray CaptureObjects; }; -PyObject *py_ue_in_editor_capture(ue_PyUObject * self, PyObject * args) +PyObject *py_unreal_engine_in_editor_capture(PyObject * self, PyObject * args) { - ue_py_check(self); + PyObject *py_scene_captures; - UMovieSceneCapture *capture = ue_py_check_type(self); + if (!PyArg_ParseTuple(args, "O:in_editor_capture", &py_scene_captures)) + { + return nullptr; + } + + TArray Captures; + + UMovieSceneCapture *capture = ue_py_check_type(py_scene_captures); if (!capture) - return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); + { + PyObject *py_iter = PyObject_GetIter(py_scene_captures); + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneCapture or an iterable of UMovieSceneCapture"); + } + while (PyObject *py_item = PyIter_Next(py_iter)) + { + capture = ue_py_check_type(py_item); + if (!capture) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UMovieSceneCapture"); + } + Captures.Add(capture); + } + Py_DECREF(py_iter); + } + else + { + Captures.Add(capture); + } - FInEditorCapture::CreateInEditorCapture(capture); + FInEditorMultiCapture::CreateInEditorMultiCapture(Captures); Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h index d9f1e5e46..1ef58340d 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h @@ -9,5 +9,5 @@ PyObject *py_ue_capture_start(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_load_from_config(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); -PyObject *py_ue_in_editor_capture(ue_PyUObject *, PyObject *); -PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *, PyObject *); +PyObject *py_unreal_engine_in_editor_capture(PyObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp new file mode 100644 index 000000000..3edbc509d --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp @@ -0,0 +1,39 @@ +#include "UnrealEnginePythonPrivatePCH.h" + + +#include "Exporters/Exporter.h" + + +PyObject *py_ue_export_to_file(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_object; + char *filename; + + if (!PyArg_ParseTuple(args, "Os:export_to_file", &py_object, &filename)) + { + return nullptr; + } + + UExporter *Exporter = ue_py_check_type(self); + if (!Exporter) + { + return PyErr_Format(PyExc_Exception, "uobject is not a UExporter"); + } + + UObject *Object = ue_py_check_type(py_object); + if (!Object) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + + if (UExporter::ExportToFile(Object, Exporter, UTF8_TO_TCHAR(filename), false, false, false) > 0) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h new file mode 100644 index 000000000..72eda9b35 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h @@ -0,0 +1,8 @@ +#pragma once + + + +#include "UnrealEnginePython.h" + + +PyObject *py_ue_export_to_file(ue_PyUObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index ca39635cc..833830a65 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -2,7 +2,7 @@ #pragma once -#define UEPY_MEMORY_DEBUG 1 +//#define UEPY_MEMORY_DEBUG 1 //#define UEPY_THREADING 1 #include "UnrealEnginePython.h" From ff3d43b5fa738dba0cf0b1722d380e2c89799d45 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 15:09:38 +0100 Subject: [PATCH 68/94] backported to 4.15 --- Source/UnrealEnginePython/Private/UEPyEditor.cpp | 2 ++ Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 15beb4936..18d32916b 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -2371,7 +2371,9 @@ PyObject *py_unreal_engine_export_assets(PyObject * self, PyObject * args) Py_DECREF(py_iter); FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); +#if ENGINE_MINOR_VERSION > 15 AssetToolsModule.Get().ExportAssets(UObjects, FString(UTF8_TO_TCHAR(filename))); +#endif Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 597510e9a..02aec9493 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -133,7 +133,9 @@ struct FInEditorMultiCapture : TSharedFromThis .AutoCenter(EAutoCenter::PrimaryWorkArea) .UseOSWindowBorder(true) .FocusWhenFirstShown(false) +#if ENGINE_MINOR_VERSION > 15 .ActivationPolicy(EWindowActivationPolicy::Never) +#endif .HasCloseButton(true) .SupportsMaximize(false) .SupportsMinimize(true) @@ -151,8 +153,8 @@ struct FInEditorMultiCapture : TSharedFromThis PlayInEditorSettings->ViewportGetsHMDControl = false; #if ENGINE_MINOR_VERSION >= 17 PlayInEditorSettings->ShouldMinimizeEditorOnVRPIE = true; -#endif PlayInEditorSettings->EnableGameSound = false; +#endif PlayInEditorSettings->bOnlyLoadVisibleLevelsInPIE = false; PlayInEditorSettings->bPreferToStreamLevelsInPIE = false; PlayInEditorSettings->PIEAlwaysOnTop = false; From a601633f3be7c395f8cc09568f24e628d06e4495 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 16:46:03 +0100 Subject: [PATCH 69/94] added FMaterialEditorUtilities --- .../UEPyFMaterialEditorUtilities.cpp | 88 +++++++++++++++++++ .../UEPyFMaterialEditorUtilities.h | 16 ++++ .../Private/Slate/UEPyFModifierKeysState.cpp | 2 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 1 + .../Private/UObject/UEPyMaterial.cpp | 12 +-- .../Private/UnrealEnginePythonPrivatePCH.h | 1 + .../UnrealEnginePython.Build.cs | 3 +- 7 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp create mode 100644 Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp new file mode 100644 index 000000000..e86b94236 --- /dev/null +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -0,0 +1,88 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR + +#include "UEPyFMaterialEditorUtilities.h" + + + + +static PyObject *py_ue_paste_nodes_here(PyObject *cls, PyObject * args) +{ + PyObject *py_graph; + float x; + float y; + + if (!PyArg_ParseTuple(args, "O(ff):paste_nodes_here", &py_graph, &x, &y)) + return nullptr; + + UEdGraph *Graph = ue_py_check_type(py_graph); + if (!Graph) + { + return PyErr_Format(PyExc_Exception, "argument is not a UEdGraph"); + } + + FMaterialEditorUtilities::PasteNodesHere(Graph, FVector2D(x, y)); + Py_RETURN_NONE; +} + + +static PyMethodDef ue_PyFMaterialEditorUtilities_methods[] = { + { "paste_nodes_here", (PyCFunction)py_ue_paste_nodes_here, METH_VARARGS | METH_CLASS, "" }, + { NULL } /* Sentinel */ +}; + + +static PyTypeObject ue_PyFMaterialEditorUtilitiesType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FMaterialEditorUtilities", /* tp_name */ + sizeof(ue_PyFMaterialEditorUtilities), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine MaterialEditorUtilities", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFMaterialEditorUtilities_methods, /* tp_methods */ + 0, + 0, +}; + +static int py_ue_fmaterial_editor_utilities_init(ue_PyFMaterialEditorUtilities *self, PyObject * args) +{ + PyErr_SetString(PyExc_Exception, "FMaterialEditorUtilities is a singleton"); + return -1; +} + +void ue_python_init_fmaterial_editor_utilities(PyObject *ue_module) +{ + ue_PyFMaterialEditorUtilitiesType.tp_new = PyType_GenericNew; + ue_PyFMaterialEditorUtilitiesType.tp_init = (initproc)py_ue_fmaterial_editor_utilities_init; + + if (PyType_Ready(&ue_PyFMaterialEditorUtilitiesType) < 0) + return; + + Py_INCREF(&ue_PyFMaterialEditorUtilitiesType); + PyModule_AddObject(ue_module, "FMaterialEditorUtilities", (PyObject *)&ue_PyFMaterialEditorUtilitiesType); +} + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h new file mode 100644 index 000000000..db36d4131 --- /dev/null +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h @@ -0,0 +1,16 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR + +#include "Editor/MaterialEditor/Public/MaterialEditorUtilities.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ +} ue_PyFMaterialEditorUtilities; + +void ue_python_init_fmaterial_editor_utilities(PyObject *); +#endif diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp index 1a4ea4bdd..fa998a860 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp @@ -116,7 +116,7 @@ static int ue_py_fmodifier_keys_state_init(ue_PyFModifierKeysState *self, PyObje void ue_python_init_fmodifier_keys_state(PyObject *ue_module) { - + ue_PyFModifierKeysStateType.tp_new = PyType_GenericNew; ue_PyFModifierKeysStateType.tp_init = (initproc)ue_py_fmodifier_keys_state_init; if (PyType_Ready(&ue_PyFModifierKeysStateType) < 0) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 8e69d9f70..d77004b41 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1947,6 +1947,7 @@ void unreal_engine_init_py_module() ue_python_init_fslate_application(new_unreal_engine_module); #if WITH_EDITOR + ue_python_init_fmaterial_editor_utilities(new_unreal_engine_module); ue_python_init_icollection_manager(new_unreal_engine_module); #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index 3b23d0189..381e3ee6a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -455,16 +455,16 @@ PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) ue_py_check(self); - if (!self->ue_object->IsA()) - { + UMaterial *material = ue_py_check_type(self); + if (!material) return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInterface"); - } - - UMaterial *material = (UMaterial *)self->ue_object; UMaterialGraph *graph = material->MaterialGraph; if (!graph) - material->MaterialGraph = (UMaterialGraph *)FBlueprintEditorUtils::CreateNewGraph(material, NAME_None, UMaterialGraph::StaticClass(), UMaterialGraphSchema::StaticClass()); + { + graph = (UMaterialGraph *)FBlueprintEditorUtils::CreateNewGraph(material, NAME_None, UMaterialGraph::StaticClass(), UMaterialGraphSchema::StaticClass()); + material->MaterialGraph = graph; + } if (!graph) return PyErr_Format(PyExc_Exception, "Unable to retrieve/allocate MaterialGraph"); diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index 833830a65..6bd720b0a 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -72,6 +72,7 @@ #include "Blueprint/UEPyEdGraphPin.h" #include "UEPyIPlugin.h" #include "CollectionManager/UEPyICollectionManager.h" +#include "MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h" #endif #include "Slate/UEPySlate.h" diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 623768cd6..09ec39aad 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -166,7 +166,8 @@ public UnrealEnginePython(TargetInfo Target) "FBX", "Persona", "PropertyEditor", - "LandscapeEditor" + "LandscapeEditor", + "MaterialEditor" }); } From da126b9fad52a78c6f4fb9f402dc5296e98dbc06 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 18:36:51 +0100 Subject: [PATCH 70/94] added command_apply for materials --- .../UEPyFMaterialEditorUtilities.cpp | 46 +++++++++++++++++++ .../UEPyFMaterialEditorUtilities.h | 3 ++ 2 files changed, 49 insertions(+) diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp index e86b94236..661105f14 100644 --- a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -27,9 +27,55 @@ static PyObject *py_ue_paste_nodes_here(PyObject *cls, PyObject * args) Py_RETURN_NONE; } +static PyObject *py_ue_update_material_after_graph_change(PyObject *cls, PyObject * args) +{ + PyObject *py_graph; + + if (!PyArg_ParseTuple(args, "O:update_material_after_graph_change", &py_graph)) + return nullptr; + + UEdGraph *Graph = ue_py_check_type(py_graph); + if (!Graph) + { + return PyErr_Format(PyExc_Exception, "argument is not a UEdGraph"); + } + + FMaterialEditorUtilities::UpdateMaterialAfterGraphChange(Graph); + Py_RETURN_NONE; +} + +static PyObject *py_ue_command_apply(PyObject *cls, PyObject * args) +{ + PyObject *py_material; + + if (!PyArg_ParseTuple(args, "O:command_apply", &py_material)) + return nullptr; + + UMaterial *Material = ue_py_check_type(py_material); + if (!Material) + { + return PyErr_Format(PyExc_Exception, "argument is not a UMaterial"); + } + + IAssetEditorInstance *Instance = FAssetEditorManager::Get().FindEditorForAsset(Material, false); + if (!Instance) + { + return PyErr_Format(PyExc_Exception, "unable to retrieve editor for UMaterial"); + } + + IMaterialEditor *MaterialEditor = (IMaterialEditor *)Instance; + + MaterialEditor->GetToolkitCommands()->ExecuteAction(FMaterialEditorCommands::Get().Apply.ToSharedRef()); + + Py_RETURN_NONE; + +} + static PyMethodDef ue_PyFMaterialEditorUtilities_methods[] = { { "paste_nodes_here", (PyCFunction)py_ue_paste_nodes_here, METH_VARARGS | METH_CLASS, "" }, + { "update_material_after_graph_change", (PyCFunction)py_ue_update_material_after_graph_change, METH_VARARGS | METH_CLASS, "" }, + { "command_apply", (PyCFunction)py_ue_command_apply, METH_VARARGS | METH_CLASS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h index db36d4131..7e6b7d5d3 100644 --- a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h @@ -5,6 +5,9 @@ #if WITH_EDITOR #include "Editor/MaterialEditor/Public/MaterialEditorUtilities.h" +#include "Editor/MaterialEditor/Public/MaterialEditorActions.h" +#include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" +#include "Editor/MaterialEditor/Public/IMaterialEditor.h" typedef struct { From ded42b2c14e8b462ecaa00d0cd0f5ff249dfcb9e Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 18:53:28 +0100 Subject: [PATCH 71/94] fixed python2 support --- Source/UnrealEnginePython/Private/UEPyModule.cpp | 13 ++++++++++++- .../Private/UnrealEnginePython.cpp | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index d77004b41..a356275f4 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1868,6 +1868,15 @@ void unreal_engine_init_py_module() PyObject *new_unreal_engine_module = PyImport_AddModule("unreal_engine"); #else PyObject *new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); + ue_PyUObjectType.tp_new = PyType_GenericNew; + ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; + ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); + + if (PyType_Ready(&ue_PyUObjectType) < 0) + return; + + Py_INCREF(&ue_PyUObjectType); + PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); #endif PyObject *unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); @@ -3603,6 +3612,7 @@ bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct return ue_py_struct->u_struct->IsChildOf(parent_u_struct); } +#if PY_MAJOR_VERSION >= 3 static PyObject *init_unreal_engine() { @@ -3621,4 +3631,5 @@ static PyObject *init_unreal_engine() PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); return new_unreal_engine_module; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 3fefa2edf..7357b1504 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -334,7 +334,9 @@ void FUnrealEnginePythonModule::StartupModule() FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); } +#if PY_MAJOR_VERSION >= 3 init_unreal_engine_builtin(); +#endif Py_Initialize(); From 8f877947260f63e6fac743675e183f61222c2075 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 19:06:09 +0100 Subject: [PATCH 72/94] fixed struct test --- tests/test_structs.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_structs.py b/tests/test_structs.py index 451a10924..9b6bb351f 100644 --- a/tests/test_structs.py +++ b/tests/test_structs.py @@ -40,11 +40,11 @@ def test_cmp(self): def test_ptr(self): source_model = StaticMeshSourceModel() - source_model().BuildSettings().bRecomputeNormals=False - source_model().BuildSettings().bRecomputeTangents=True - source_model().BuildSettings().bUseMikkTSpace=True - source_model().BuildSettings().bBuildAdjacencyBuffer=True - source_model().BuildSettings().bRemoveDegenerates=True + source_model.ref().BuildSettings.ref().bRecomputeNormals=False + source_model.ref().BuildSettings.ref().bRecomputeTangents=True + source_model.ref().BuildSettings.ref().bUseMikkTSpace=True + source_model.ref().BuildSettings.ref().bBuildAdjacencyBuffer=True + source_model.ref().BuildSettings.ref().bRemoveDegenerates=True source_model2 = source_model.clone() self.assertEqual(source_model2.BuildSettings.bRecomputeNormals, False) From b33e0670f1cef628b341ff2c5ded2337a507dd32 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 26 Feb 2018 19:44:26 +0100 Subject: [PATCH 73/94] fixed 4.16 support --- Source/UnrealEnginePython/Private/UEPyEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 18d32916b..50ff5b6d0 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -2371,7 +2371,7 @@ PyObject *py_unreal_engine_export_assets(PyObject * self, PyObject * args) Py_DECREF(py_iter); FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); -#if ENGINE_MINOR_VERSION > 15 +#if ENGINE_MINOR_VERSION > 16 AssetToolsModule.Get().ExportAssets(UObjects, FString(UTF8_TO_TCHAR(filename))); #endif From 3c33d0de0b33dfbe74008256d54747129e968f16 Mon Sep 17 00:00:00 2001 From: efeng Date: Tue, 27 Feb 2018 17:13:48 +1300 Subject: [PATCH 74/94] Add new getters for fbx curve key: left/right tangents, tangent mode and interpolation mode --- .../Private/Fbx/UEPyFbxObject.cpp | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp index 414d0a545..b9d902a32 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp @@ -138,6 +138,108 @@ static PyObject *py_ue_fbx_object_key_get_seconds(ue_PyFbxObject *self, PyObject return PyFloat_FromDouble(fbx_anim_curve->KeyGetTime(index).GetSecondDouble()); } +static PyObject *py_ue_fbx_object_key_get_left_tangent(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + return PyFloat_FromDouble(fbx_anim_curve->KeyGetLeftDerivative(index)); +} + +static PyObject *py_ue_fbx_object_key_get_right_tangent(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + return PyFloat_FromDouble(fbx_anim_curve->KeyGetRightDerivative(index)); +} + +static PyObject *py_ue_fbx_object_key_get_interp_mode(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + + ERichCurveInterpMode Mode = RCIM_Linear; + // Convert the interpolation type from FBX to Unreal. + switch (fbx_anim_curve->KeyGetInterpolation(index)) + { + case FbxAnimCurveDef::eInterpolationCubic: + Mode = RCIM_Cubic; + break; + + case FbxAnimCurveDef::eInterpolationConstant: + if (fbx_anim_curve->KeyGetTangentMode(index) != (FbxAnimCurveDef::ETangentMode)FbxAnimCurveDef::eConstantStandard) + { + // warning not support + ; + } + Mode = RCIM_Constant; + break; + + case FbxAnimCurveDef::eInterpolationLinear: + Mode = RCIM_Linear; + break; + } + + return PyLong_FromUnsignedLong(uint64(Mode)); +} + +static PyObject *py_ue_fbx_object_key_get_tangent_mode(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + + ERichCurveTangentMode Mode = RCTM_Auto; + // Convert the interpolation type from FBX to Unreal. + if ( fbx_anim_curve->KeyGetInterpolation(index) == + FbxAnimCurveDef::eInterpolationCubic ) + { + switch (fbx_anim_curve->KeyGetTangentMode(index)) + { + // Auto tangents will now be imported as user tangents to allow the + // user to modify them without inadvertently resetting other tangents + // case KFbxAnimCurveDef::eTANGENT_AUTO: + // if ((KFbxAnimCurveDef::eTANGENT_GENERIC_CLAMP & FbxKey.GetTangentMode(true))) + // { + // Mode = CIM_CurveAutoClamped; + // } + // else + // { + // Mode = CIM_CurveAuto; + // } + // break; + case FbxAnimCurveDef::eTangentBreak: + Mode = RCTM_Break; + break; + case FbxAnimCurveDef::eTangentAuto: + Mode = RCTM_Auto; + break; + case FbxAnimCurveDef::eTangentUser: + case FbxAnimCurveDef::eTangentTCB: + Mode = RCTM_User; + break; + default: + break; + } + } + + return PyLong_FromUnsignedLong(uint64(Mode)); +} + static PyMethodDef ue_PyFbxObject_methods[] = { { "get_member_count", (PyCFunction)py_ue_fbx_object_get_member_count, METH_VARARGS, "" }, { "get_member", (PyCFunction)py_ue_fbx_object_get_member, METH_VARARGS, "" }, @@ -153,6 +255,10 @@ static PyMethodDef ue_PyFbxObject_methods[] = { { "key_get_count", (PyCFunction)py_ue_fbx_object_key_get_count, METH_VARARGS, "" }, { "key_get_value", (PyCFunction)py_ue_fbx_object_key_get_value, METH_VARARGS, "" }, { "key_get_seconds", (PyCFunction)py_ue_fbx_object_key_get_seconds, METH_VARARGS, "" }, + { "key_get_left_tangent", (PyCFunction)py_ue_fbx_object_key_get_left_tangent, METH_VARARGS, "" }, + { "key_get_right_tangent", (PyCFunction)py_ue_fbx_object_key_get_right_tangent, METH_VARARGS, "" }, + { "key_get_interp_mode", (PyCFunction)py_ue_fbx_object_key_get_interp_mode, METH_VARARGS, "" }, + { "key_get_tangent_mode", (PyCFunction)py_ue_fbx_object_key_get_tangent_mode, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; From 1c7386b6a0edbeeafa4ffe9b2dadf8f96948da2b Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 27 Feb 2018 10:03:57 +0100 Subject: [PATCH 75/94] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83dd95636..75f54c8ef 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Once the plugin is installed and enabled, you get access to the 'PythonConsole' All of the exposed engine features are under the 'unreal_engine' virtual module (it is completely coded in c into the plugin, so do not expect to run 'import unreal_engine' from a standard python shell) -The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17 and 4.18. +The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18 and 4.19 We support official python.org releases as well as IntelPython and Anaconda distributions. From 979d83243bc468446f5e1dd32ff9b368891d2d76 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 27 Feb 2018 10:10:22 +0100 Subject: [PATCH 76/94] Update README.md --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 75f54c8ef..b949c1e3c 100644 --- a/README.md +++ b/README.md @@ -808,15 +808,9 @@ Memory management Dealing with 2 different GC's is really challenging. -PyActor, PyPawn and PythonComponent automatically DECREF's the mapped classes. This should be enough unless you hold references -in the python modules themselves. As this would be a bad programming practice, the current approach should be safe enough. +Starting from release 20180226 a new memory management system has been added (FUnrealEnginePythonHouseKeeper, available here https://github.com/20tab/UnrealEnginePython/blob/master/Source/UnrealEnginePython/Public/PythonHouseKeeper.h). This new system is completely integrated with the Unreal Engine reflection-based GC and will hold track of each ue_PyUObject abd the related UObject to understand when a python object can be safely destroyed. -In addition to this, every time a uobject has to return its UObject mapping, it checks for its validity and safety: - -```c -#define ue_py_check(py_u) if (!py_u->ue_object || !py_u->ue_object->IsValidLowLevel() || py_u->ue_object->IsPendingKillOrUnreachable())\ - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") -``` +The same system works for delegates, as well as Slate. Unit Testing From b6058c952dd86c35349ae96371548f36fa672d63 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 27 Feb 2018 10:14:47 +0100 Subject: [PATCH 77/94] Update README.md --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index b949c1e3c..f18f3d6b9 100644 --- a/README.md +++ b/README.md @@ -487,6 +487,30 @@ To access the fields of a struct just call the fields() method. A good example of struct usage is available here: https://github.com/20tab/UnrealEnginePython/blob/master/docs/Settings.md +As structs are passed by value, you need to pay attention when manipulating structs fields that are structs by themselves: + +```python +from unreal_engine.structs import TerrificStruct, DumbStruct + +ts = TerrificStruct() +ts.dumb = DumbStruct(Foo=17, Bar=22) + +# will not modify the original DumbStruct but a copy of it !!! +ts.dumb.Foo = 22 +``` + +You can eventually force structs to be passed by ref (extremely dangerous as the internal C pointer could be a dangling one) using the ref() function: + +```python +from unreal_engine.structs import TerrificStruct, DumbStruct + +ts = TerrificStruct() +ts.dumb = DumbStruct(Foo=17, Bar=22) + +# ref() will return a pointer to a struct +ts.ref().dumb.foo().Foo = 22 +``` + The ue_site.py file ------------------- From 9cacca413baf27582bd1e1051446a36dbf3fe849 Mon Sep 17 00:00:00 2001 From: Hippid Date: Tue, 27 Feb 2018 11:35:34 +0100 Subject: [PATCH 78/94] Fixed logging message for garbage collect delegates. --- Source/UnrealEnginePython/Public/PythonHouseKeeper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index 3b1c1ad1a..0063db4df 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -181,7 +181,7 @@ class FUnrealEnginePythonHouseKeeper { int32 Garbaged = 0; #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Error, TEXT("Checking %d delegates"), PyDelegatesTracker.Num()); + UE_LOG(LogPython, Display, TEXT("Garbage collecting %d delegates"), PyDelegatesTracker.Num()); #endif for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) { From de9cafa510df43053bb019c15691569ccb61b0fa Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 2 Mar 2018 13:25:12 +0100 Subject: [PATCH 79/94] added get_matrix() to FTransform --- .../Private/Wrappers/UEPyFTransform.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp index b76be90a8..e91f2dc49 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp @@ -26,10 +26,22 @@ static PyObject *py_ue_ftransform_get_relative_transform(ue_PyFTransform *self, return py_ue_new_ftransform(self->transform.GetRelativeTransform(py_transform->transform)); } +static PyObject *py_ue_ftransform_get_matrix(ue_PyFTransform *self, PyObject * args) +{ + FMatrix matrix = self->transform.ToMatrixWithScale(); + UScriptStruct *u_struct = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR("Matrix")); + if (!u_struct) + { + return PyErr_Format(PyExc_Exception, "unable to get Matrix struct"); + } + return py_ue_new_uscriptstruct(u_struct, (uint8 *)&matrix); +} + static PyMethodDef ue_PyFTransform_methods[] = { { "inverse", (PyCFunction)py_ue_ftransform_inverse, METH_VARARGS, "" }, { "get_relative_transform", (PyCFunction)py_ue_ftransform_get_relative_transform, METH_VARARGS, "" }, { "normalize_rotation", (PyCFunction)py_ue_ftransform_normalize_rotation, METH_VARARGS, "" }, + { "get_matrix", (PyCFunction)py_ue_ftransform_get_matrix, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; From 7dccc39c1df82372c518d6e6ca30d19948511e91 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 5 Mar 2018 09:32:28 +0100 Subject: [PATCH 80/94] added game_viewport_client_set_rendering_flag() --- .../UnrealEnginePython/Private/UEPyModule.cpp | 2 ++ .../Private/UObject/UEPyViewport.cpp | 25 +++++++++++++++++++ .../Private/UObject/UEPyViewport.h | 1 + .../Private/UObject/UEPyWorld.cpp | 3 +++ 4 files changed, 31 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index a356275f4..6764f9177 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -755,6 +755,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "has_world", (PyCFunction)py_ue_has_world, METH_VARARGS, "" }, { "get_game_viewport", (PyCFunction)py_ue_get_game_viewport, METH_VARARGS, "" }, + + { "game_viewport_client_set_rendering_flag", (PyCFunction)py_ue_game_viewport_client_set_rendering_flag, METH_VARARGS, "" }, { "get_world_location_at_distance_along_spline", (PyCFunction)py_ue_get_world_location_at_distance_along_spline, METH_VARARGS, "" }, { "get_spline_length", (PyCFunction)py_ue_get_spline_length, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 364947cc3..0ac579604 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp @@ -208,3 +208,28 @@ PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; } +PyObject *py_ue_game_viewport_client_set_rendering_flag(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_bool; + + if (!PyArg_ParseTuple(args, "O:game_viewport_client_set_rendering_flag", &py_bool)) + { + return nullptr; + } + + bool bEnabled = PyObject_IsTrue(py_bool) ? true : false; + + UGameViewportClient *ViewportClient = ue_py_check_type(self); + if (!ViewportClient) + { + return PyErr_Format(PyExc_Exception, "object is not a UGameViewportClient"); + } + + ViewportClient->EngineShowFlags.Rendering = bEnabled; + + Py_RETURN_NONE; +} + diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h index 2f6a8dc2b..f5a31a9b7 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h @@ -16,3 +16,4 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject *, PyObject *); PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *, PyObject *); PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *, PyObject *); PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *, PyObject *); +PyObject *py_ue_game_viewport_client_set_rendering_flag(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp index ce33397d0..fa58027fa 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp @@ -195,6 +195,9 @@ PyObject *py_ue_get_game_viewport(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(viewport_client); } + + + PyObject *py_ue_has_world(ue_PyUObject *self, PyObject * args) { From a639bd4e530903b895396c9fcafc83c95fab2236 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 5 Mar 2018 11:14:46 +0100 Subject: [PATCH 81/94] fixed py_exec() #336 --- .../Private/UnrealEnginePython.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 7357b1504..75c50e3c1 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -488,30 +488,30 @@ void FUnrealEnginePythonModule::RunStringSandboxed(char *str) void FUnrealEnginePythonModule::RunFile(char *filename) { FScopePythonGIL gil; - char *full_path = filename; + FString full_path = UTF8_TO_TCHAR(filename); if (!FPaths::FileExists(filename)) { - full_path = TCHAR_TO_UTF8(*FPaths::Combine(*ScriptsPath, UTF8_TO_TCHAR(filename))); + full_path = FPaths::Combine(*ScriptsPath, full_path); } #if PY_MAJOR_VERSION >= 3 FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) + if (fopen_s(&fd, TCHAR_TO_UTF8(*full_path), "r") != 0) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #else - fd = fopen(full_path, "r"); + fd = fopen(TCHAR_TO_UTF8(*full_path), "r"); if (!fd) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #endif - PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); + PyObject *eval_ret = PyRun_File(fd, TCHAR_TO_UTF8(*full_path), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); fclose(fd); if (!eval_ret) { @@ -521,7 +521,7 @@ void FUnrealEnginePythonModule::RunFile(char *filename) Py_DECREF(eval_ret); #else // damn, this is horrible, but it is the only way i found to avoid the CRT error :( - FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); + FString command = FString::Printf(TEXT("execfile(\"%s\")"), *full_path); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); if (!eval_ret) { @@ -536,10 +536,10 @@ void FUnrealEnginePythonModule::RunFile(char *filename) void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback)(void *arg), void *arg) { FScopePythonGIL gil; - char *full_path = filename; + FString full_path = filename; if (!FPaths::FileExists(filename)) { - full_path = TCHAR_TO_UTF8(*FPaths::Combine(*ScriptsPath, UTF8_TO_TCHAR(filename))); + full_path = FPaths::Combine(*ScriptsPath, full_path); } PyThreadState *_main = PyThreadState_Get(); @@ -571,21 +571,21 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) + if (fopen_s(&fd, TCHAR_TO_UTF8(*full_path), "r") != 0) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #else - fd = fopen(full_path, "r"); + fd = fopen(TCHAR_TO_UTF8(*full_path), "r"); if (!fd) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #endif - PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict); + PyObject *eval_ret = PyRun_File(fd, TCHAR_TO_UTF8(*full_path), Py_file_input, global_dict, global_dict); fclose(fd); if (!eval_ret) { @@ -597,7 +597,7 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) Py_DECREF(eval_ret); #else // damn, this is horrible, but it is the only way i found to avoid the CRT error :( - FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); + FString command = FString::Printf(TEXT("execfile(\"%s\")"), *full_path); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict); if (!eval_ret) { From 6dcbffe19ea88654a187e8620ae52d3686818a5f Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 5 Mar 2018 12:11:12 +0100 Subject: [PATCH 82/94] fixed subclassing api --- .../Private/PythonConsoleModule.cpp | 67 --- .../Public/PythonConsoleModule.h | 8 +- .../UnrealEnginePython/Private/UEPyModule.cpp | 492 +----------------- .../Private/UEPySubclassing.cpp | 484 +++++++++++++++++ 4 files changed, 490 insertions(+), 561 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/UEPySubclassing.cpp diff --git a/Source/PythonConsole/Private/PythonConsoleModule.cpp b/Source/PythonConsole/Private/PythonConsoleModule.cpp index 0569edec5..d9800bd36 100644 --- a/Source/PythonConsole/Private/PythonConsoleModule.cpp +++ b/Source/PythonConsole/Private/PythonConsoleModule.cpp @@ -93,71 +93,4 @@ TSharedRef< SWidget > FPythonConsoleModule::MakeConsoleInputBox( TSharedPtr< SEd TSharedRef< SPythonConsoleInputBox > NewConsoleInputBox = SNew( SPythonConsoleInputBox ); OutExposedEditableTextBox = NewConsoleInputBox->GetEditableTextBox(); return NewConsoleInputBox; -} - - -void FPythonConsoleModule::TogglePythonConsoleForWindow( const TSharedRef< SWindow >& Window, const EPythonConsoleStyle::Type InStyle, const FPythonConsoleDelegates& PythonConsoleDelegates ) -{ - bool bShouldOpen = true; - // Close an existing console box, if there is one - TSharedPtr< SWidget > PinnedPythonConsole( PythonConsole.Pin() ); - if( PinnedPythonConsole.IsValid() ) - { - // If the console is already open close it unless it is in a different window. In that case reopen it on that window - bShouldOpen = false; - TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedPythonConsole.ToSharedRef()); - if (WindowForExistingConsole.IsValid()) - { - WindowForExistingConsole->RemoveOverlaySlot(PinnedPythonConsole.ToSharedRef()); - PythonConsole.Reset(); - } - - if( WindowForExistingConsole != Window ) - { - // Console is being opened on another window - bShouldOpen = true; - } - } - - TSharedPtr ActiveTab = FGlobalTabmanager::Get()->GetActiveTab(); - if (ActiveTab.IsValid() && ActiveTab->GetLayoutIdentifier() == FTabId(PythonConsoleModule::PythonLogTabName)) - { - FGlobalTabmanager::Get()->DrawAttention(ActiveTab.ToSharedRef()); - bShouldOpen = false; - } - - if( bShouldOpen ) - { - const EPythonConsoleStyle::Type PythonConsoleStyle = InStyle; - TSharedRef< SPythonConsole > PythonConsoleRef = SNew( SPythonConsole, PythonConsoleStyle, this, &PythonConsoleDelegates ); - PythonConsole = PythonConsoleRef; - - const int32 MaximumZOrder = MAX_int32; - Window->AddOverlaySlot( MaximumZOrder ) - .VAlign(VAlign_Bottom) - .HAlign(HAlign_Center) - .Padding( 10.0f ) - [ - PythonConsoleRef - ]; - - // Force keyboard focus - PythonConsoleRef->SetFocusToEditableText(); - } -} - - -void FPythonConsoleModule::ClosePythonConsole() -{ - TSharedPtr< SWidget > PinnedPythonConsole( PythonConsole.Pin() ); - - if( PinnedPythonConsole.IsValid() ) - { - TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedPythonConsole.ToSharedRef()); - if (WindowForExistingConsole.IsValid()) - { - WindowForExistingConsole->RemoveOverlaySlot( PinnedPythonConsole.ToSharedRef() ); - PythonConsole.Reset(); - } - } } \ No newline at end of file diff --git a/Source/PythonConsole/Public/PythonConsoleModule.h b/Source/PythonConsole/Public/PythonConsoleModule.h index 7d5189b48..607fd61b2 100644 --- a/Source/PythonConsole/Public/PythonConsoleModule.h +++ b/Source/PythonConsole/Public/PythonConsoleModule.h @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2018 20Tab S.r.l. All Rights Reserved. #pragma once @@ -33,12 +33,6 @@ class FPythonConsoleModule : public IModuleInterface output log DLL is unloaded on the fly. */ virtual TSharedRef< SWidget > MakeConsoleInputBox( TSharedPtr< SEditableTextBox >& OutExposedEditableTextBox ) const; - /** Opens a debug console in the specified window, if not already open */ - virtual void TogglePythonConsoleForWindow( const TSharedRef< SWindow >& Window, const EPythonConsoleStyle::Type InStyle, const FPythonConsoleDelegates& PythonConsoleDelegates ); - - /** Closes the debug console for the specified window */ - virtual void ClosePythonConsole(); - private: diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 6764f9177..64b381c3c 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1387,482 +1387,7 @@ static void UEPyClassConstructor(UClass *u_class, const FObjectInitializer &Obje ue_py_class_constructor_placeholder = nullptr; } -static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) -{ - // is it subclassing ? - if (PyTuple_Size(args) == 3) - { - // TODO make it smarter on error checking - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0))))); - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 1))))); - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 2))))); - - PyObject *parents = PyTuple_GetItem(args, 1); - ue_PyUObject *parent = (ue_PyUObject *)PyTuple_GetItem(parents, 0); - - PyObject *class_attributes = PyTuple_GetItem(args, 2); - - PyObject *class_name = PyDict_GetItemString(class_attributes, (char *)"__qualname__"); - char *name = PyUnicode_AsUTF8(class_name); - // check if parent is a uclass - UClass *new_class = unreal_engine_new_uclass(name, (UClass *)parent->ue_object); - if (!new_class) - return -1; - - // map the class to the python object - self->ue_object = new_class; - - PyObject *py_additional_properties = PyDict_New(); - - PyObject *class_attributes_keys = PyObject_GetIter(class_attributes); - for (;;) - { - PyObject *key = PyIter_Next(class_attributes_keys); - if (!key) - { - if (PyErr_Occurred()) - return -1; - break; - } - if (!PyUnicodeOrString_Check(key)) - continue; - char *class_key = PyUnicode_AsUTF8(key); - - PyObject *value = PyDict_GetItem(class_attributes, key); - - if (strlen(class_key) > 2 && class_key[0] == '_' && class_key[1] == '_') - { - continue; - } - - bool prop_added = false; - - if (UProperty *u_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) - { - UE_LOG(LogPython, Warning, TEXT("Found UProperty %s"), UTF8_TO_TCHAR(class_key)); - PyDict_SetItem(py_additional_properties, key, value); - prop_added = true; - } - // add simple property - else if (ue_is_pyuobject(value)) - { - ue_PyUObject *py_obj = (ue_PyUObject *)value; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - else - { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - else if (py_obj->ue_object->IsA()) - { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - - // add array property - else if (PyList_Check(value)) - { - if (PyList_Size(value) == 1) - { - PyObject *first_item = PyList_GetItem(value, 0); - if (ue_is_pyuobject(first_item)) - { - ue_PyUObject *py_obj = (ue_PyUObject *)first_item; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - - else - { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, first_item))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - else if (py_obj->ue_object->IsA()) - { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, first_item))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - - } - } -#if ENGINE_MINOR_VERSION >= 15 - else if (PyDict_Check(value)) - { - if (PyDict_Size(value) == 1) - { - PyObject *py_key = nullptr; - PyObject *py_value = nullptr; - Py_ssize_t pos = 0; - PyDict_Next(value, &pos, &py_key, &py_value); - if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) - { - PyObject *first_item = nullptr; - PyObject *second_item = nullptr; - - ue_PyUObject *py_obj = (ue_PyUObject *)py_key; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - first_item = py_key; - } - else - { - first_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); - } - } - else if (py_obj->ue_object->IsA()) - { - first_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); - } - - ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; - if (py_obj2->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj2->ue_object; - if (p_class->IsChildOf()) - { - second_item = py_value; - } - else - { - second_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); - } - } - else if (py_obj2->ue_object->IsA()) - { - second_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); - } - - if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - } -#endif - // function ? - else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') - { - uint32 func_flags = FUNC_Native | FUNC_BlueprintCallable | FUNC_Public; - PyObject *is_event = PyObject_GetAttrString(value, (char *)"event"); - if (is_event && PyObject_IsTrue(is_event)) - { - func_flags |= FUNC_Event | FUNC_BlueprintEvent; - } - else if (!is_event) - PyErr_Clear(); - - PyObject *is_multicast = PyObject_GetAttrString(value, (char *)"multicast"); - if (is_multicast && PyObject_IsTrue(is_multicast)) - { - func_flags |= FUNC_NetMulticast; - } - else if (!is_multicast) - PyErr_Clear(); - PyObject *is_server = PyObject_GetAttrString(value, (char *)"server"); - if (is_server && PyObject_IsTrue(is_server)) - { - func_flags |= FUNC_NetServer; - } - else if (!is_server) - PyErr_Clear(); - PyObject *is_client = PyObject_GetAttrString(value, (char *)"client"); - if (is_client && PyObject_IsTrue(is_client)) - { - func_flags |= FUNC_NetClient; - } - else if (!is_client) - PyErr_Clear(); - PyObject *is_reliable = PyObject_GetAttrString(value, (char *)"reliable"); - if (is_reliable && PyObject_IsTrue(is_reliable)) - { - func_flags |= FUNC_NetReliable; - } - else if (!is_reliable) - PyErr_Clear(); - - - PyObject *is_pure = PyObject_GetAttrString(value, (char *)"pure"); - if (is_pure && PyObject_IsTrue(is_pure)) - { - func_flags |= FUNC_BlueprintPure; - } - else if (!is_pure) - PyErr_Clear(); - PyObject *is_static = PyObject_GetAttrString(value, (char *)"static"); - if (is_static && PyObject_IsTrue(is_static)) - { - func_flags |= FUNC_Static; - } - else if (!is_static) - PyErr_Clear(); - PyObject *override_name = PyObject_GetAttrString(value, (char *)"override"); - if (override_name && PyUnicodeOrString_Check(override_name)) - { - class_key = PyUnicode_AsUTF8(override_name); - } - else if (override_name && PyUnicodeOrString_Check(override_name)) - { - class_key = PyUnicode_AsUTF8(override_name); - } - else if (!override_name) - PyErr_Clear(); - if (!unreal_engine_add_function(new_class, class_key, value, func_flags)) - { - UE_LOG(LogPython, Error, TEXT("unable to add function %s"), UTF8_TO_TCHAR(class_key)); - return -1; - } - prop_added = true; - } - - - if (!prop_added) - { - UE_LOG(LogPython, Warning, TEXT("Adding %s as attr"), UTF8_TO_TCHAR(class_key)); - PyObject_SetAttr((PyObject *)self, key, value); - } - } - - if (PyDict_Size(py_additional_properties) > 0) - { - PyObject_SetAttrString((PyObject *)self, (char*)"__additional_uproperties__", py_additional_properties); - } - - UPythonClass *new_u_py_class = (UPythonClass *)new_class; - // TODO: check if we can use this to decref the ue_PyUbject mapped to the class - new_u_py_class->py_uobject = self; - new_u_py_class->ClassConstructor = [](const FObjectInitializer &ObjectInitializer) - { - FScopePythonGIL gil; - UClass *u_class = ue_py_class_constructor_placeholder ? ue_py_class_constructor_placeholder : ObjectInitializer.GetClass(); - ue_py_class_constructor_placeholder = nullptr; - - UEPyClassConstructor(u_class->GetSuperClass(), ObjectInitializer); - - if (UPythonClass *u_py_class_casted = Cast(u_class)) - { - ue_PyUObject *new_self = ue_get_python_uobject(ObjectInitializer.GetObj()); - if (!new_self) - { - unreal_engine_py_log_error(); - return; - } - - // fill __dict__ from class - if (u_py_class_casted->py_uobject && u_py_class_casted->py_uobject->py_dict) - { - PyObject *found_additional_props = PyDict_GetItemString(u_py_class_casted->py_uobject->py_dict, (char *)"__additional_uproperties__"); - // manage UProperties (and automatically maps multicast properties) - if (found_additional_props) - { - PyObject *keys = PyDict_Keys(found_additional_props); - Py_ssize_t items_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < items_len; i++) - { - PyObject *mc_key = PyList_GetItem(keys, i); - PyObject *mc_value = PyDict_GetItem(found_additional_props, mc_key); - - char *mc_name = PyUnicode_AsUTF8(mc_key); - UProperty *u_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); - if (u_property) - { - if (auto casted_prop = Cast(u_property)) - { - FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); - - FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); - // fake UFUNCTION for bypassing checks - script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); - - // add the new delegate - multiscript_delegate.Add(script_delegate); - - // re-assign multicast delegate - casted_prop->SetPropertyValue_InContainer(ObjectInitializer.GetObj(), multiscript_delegate); - } - else - { - PyObject_SetAttr((PyObject *)new_self, mc_key, mc_value); - } - } - - } - Py_DECREF(keys); - } - else - { - PyErr_Clear(); - } - PyObject *keys = PyDict_Keys(u_py_class_casted->py_uobject->py_dict); - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(u_py_class_casted->py_uobject->py_dict, key); - if (PyUnicode_Check(key)) - { - char *key_name = PyUnicode_AsUTF8(key); - if (!strcmp(key_name, (char *)"__additional_uproperties__")) - continue; - } - // special case to bound function to method - if (PyFunction_Check(value)) - { - PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_self); - if (bound_function) - { - PyObject_SetAttr((PyObject *)new_self, key, bound_function); - Py_DECREF(bound_function); - } - else - { - unreal_engine_py_log_error(); - } - } - else - { - PyObject_SetAttr((PyObject *)new_self, key, value); - } - } - Py_DECREF(keys); - } - // call __init__ - u_py_class_casted->CallPyConstructor(new_self); - } - }; - - - - if (self->py_dict) - { - ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); - - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to set dict on new ClassDefaultObject")); - return -1; - } - PyObject *keys = PyDict_Keys(self->py_dict); - - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(self->py_dict, key); - // special case to bound function to method - if (PyFunction_Check(value)) - { - PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_default_self); - if (bound_function) - { - PyObject_SetAttr((PyObject *)new_default_self, key, bound_function); - Py_DECREF(bound_function); - } - else - { - unreal_engine_py_log_error(); - } - } - else - { - PyObject_SetAttr((PyObject *)new_default_self, key, value); - } - } - Py_DECREF(keys); - } - - // add default uproperties values - if (py_additional_properties) - { - ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to set properties on new ClassDefaultObject")); - return -1; - } - PyObject *keys = PyDict_Keys(py_additional_properties); - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(py_additional_properties, key); - - PyObject_SetAttr((PyObject *)new_default_self, key, value); - } - Py_DECREF(keys); - } - - // add custom constructor (__init__) - PyObject *py_init = PyDict_GetItemString(class_attributes, (char *)"__init__"); - if (py_init && PyCallable_Check(py_init)) - { - // fake initializer - FObjectInitializer initializer(new_u_py_class->ClassDefaultObject, nullptr, false, true); - new_u_py_class->SetPyConstructor(py_init); - ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); - - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to call __init__ on new ClassDefaultObject")); - return -1; - } - - new_u_py_class->CallPyConstructor(new_default_self); - } - - } - - return 0; -} +int unreal_engine_py_init(ue_PyUObject *, PyObject *, PyObject *); void unreal_engine_init_py_module() { @@ -1870,6 +1395,7 @@ void unreal_engine_init_py_module() PyObject *new_unreal_engine_module = PyImport_AddModule("unreal_engine"); #else PyObject *new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); +#endif ue_PyUObjectType.tp_new = PyType_GenericNew; ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); @@ -1879,7 +1405,7 @@ void unreal_engine_init_py_module() Py_INCREF(&ue_PyUObjectType); PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); -#endif + PyObject *unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); PyMethodDef *unreal_engine_function; @@ -3614,24 +3140,16 @@ bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct return ue_py_struct->u_struct->IsChildOf(parent_u_struct); } + + #if PY_MAJOR_VERSION >= 3 static PyObject *init_unreal_engine() { - ue_PyUObjectType.tp_new = PyType_GenericNew; - ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; - ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); - - if (PyType_Ready(&ue_PyUObjectType) < 0) - return nullptr; - PyObject *new_unreal_engine_module = PyModule_Create(&unreal_engine_module); if (!new_unreal_engine_module) return nullptr; - Py_INCREF(&ue_PyUObjectType); - PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); - return new_unreal_engine_module; } #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp new file mode 100644 index 000000000..a7e7f19ab --- /dev/null +++ b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp @@ -0,0 +1,484 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) +{ + + // is it subclassing ? + if (PyTuple_Size(args) == 3) + { + // TODO make it smarter on error checking + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0))))); + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 1))))); + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 2))))); + + PyObject *parents = PyTuple_GetItem(args, 1); + ue_PyUObject *parent = (ue_PyUObject *)PyTuple_GetItem(parents, 0); + + PyObject *class_attributes = PyTuple_GetItem(args, 2); + + PyObject *class_name = PyDict_GetItemString(class_attributes, (char *)"__qualname__"); + char *name = PyUnicode_AsUTF8(class_name); + // check if parent is a uclass + UClass *new_class = unreal_engine_new_uclass(name, (UClass *)parent->ue_object); + if (!new_class) + return -1; + + // map the class to the python object + self->ue_object = new_class; + self->py_proxy = nullptr; + self->auto_rooted = 0; + self->py_dict = PyDict_New(); + + FUnrealEnginePythonHouseKeeper::Get()->RegisterPyUObject(new_class, self); + + PyObject *py_additional_properties = PyDict_New(); + + PyObject *class_attributes_keys = PyObject_GetIter(class_attributes); + for (;;) + { + PyObject *key = PyIter_Next(class_attributes_keys); + if (!key) + { + if (PyErr_Occurred()) + return -1; + break; + } + if (!PyUnicodeOrString_Check(key)) + continue; + char *class_key = PyUnicode_AsUTF8(key); + + PyObject *value = PyDict_GetItem(class_attributes, key); + + if (strlen(class_key) > 2 && class_key[0] == '_' && class_key[1] == '_') + { + continue; + } + + bool prop_added = false; + + if (UProperty *u_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) + { + UE_LOG(LogPython, Warning, TEXT("Found UProperty %s"), UTF8_TO_TCHAR(class_key)); + PyDict_SetItem(py_additional_properties, key, value); + prop_added = true; + } + // add simple property + else if (ue_is_pyuobject(value)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)value; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + else + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + + // add array property + else if (PyList_Check(value)) + { + if (PyList_Size(value) == 1) + { + PyObject *first_item = PyList_GetItem(value, 0); + if (ue_is_pyuobject(first_item)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)first_item; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + + else + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + + } + } +#if ENGINE_MINOR_VERSION >= 15 + else if (PyDict_Check(value)) + { + if (PyDict_Size(value) == 1) + { + PyObject *py_key = nullptr; + PyObject *py_value = nullptr; + Py_ssize_t pos = 0; + PyDict_Next(value, &pos, &py_key, &py_value); + if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) + { + PyObject *first_item = nullptr; + PyObject *second_item = nullptr; + + ue_PyUObject *py_obj = (ue_PyUObject *)py_key; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + first_item = py_key; + } + else + { + first_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); + } + } + else if (py_obj->ue_object->IsA()) + { + first_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); + } + + ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; + if (py_obj2->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj2->ue_object; + if (p_class->IsChildOf()) + { + second_item = py_value; + } + else + { + second_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); + } + } + else if (py_obj2->ue_object->IsA()) + { + second_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); + } + + if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + } +#endif + // function ? + else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') + { + uint32 func_flags = FUNC_Native | FUNC_BlueprintCallable | FUNC_Public; + PyObject *is_event = PyObject_GetAttrString(value, (char *)"event"); + if (is_event && PyObject_IsTrue(is_event)) + { + func_flags |= FUNC_Event | FUNC_BlueprintEvent; + } + else if (!is_event) + PyErr_Clear(); + + PyObject *is_multicast = PyObject_GetAttrString(value, (char *)"multicast"); + if (is_multicast && PyObject_IsTrue(is_multicast)) + { + func_flags |= FUNC_NetMulticast; + } + else if (!is_multicast) + PyErr_Clear(); + PyObject *is_server = PyObject_GetAttrString(value, (char *)"server"); + if (is_server && PyObject_IsTrue(is_server)) + { + func_flags |= FUNC_NetServer; + } + else if (!is_server) + PyErr_Clear(); + PyObject *is_client = PyObject_GetAttrString(value, (char *)"client"); + if (is_client && PyObject_IsTrue(is_client)) + { + func_flags |= FUNC_NetClient; + } + else if (!is_client) + PyErr_Clear(); + PyObject *is_reliable = PyObject_GetAttrString(value, (char *)"reliable"); + if (is_reliable && PyObject_IsTrue(is_reliable)) + { + func_flags |= FUNC_NetReliable; + } + else if (!is_reliable) + PyErr_Clear(); + + + PyObject *is_pure = PyObject_GetAttrString(value, (char *)"pure"); + if (is_pure && PyObject_IsTrue(is_pure)) + { + func_flags |= FUNC_BlueprintPure; + } + else if (!is_pure) + PyErr_Clear(); + PyObject *is_static = PyObject_GetAttrString(value, (char *)"static"); + if (is_static && PyObject_IsTrue(is_static)) + { + func_flags |= FUNC_Static; + } + else if (!is_static) + PyErr_Clear(); + PyObject *override_name = PyObject_GetAttrString(value, (char *)"override"); + if (override_name && PyUnicodeOrString_Check(override_name)) + { + class_key = PyUnicode_AsUTF8(override_name); + } + else if (override_name && PyUnicodeOrString_Check(override_name)) + { + class_key = PyUnicode_AsUTF8(override_name); + } + else if (!override_name) + PyErr_Clear(); + if (!unreal_engine_add_function(new_class, class_key, value, func_flags)) + { + UE_LOG(LogPython, Error, TEXT("unable to add function %s"), UTF8_TO_TCHAR(class_key)); + return -1; + } + prop_added = true; + } + + + if (!prop_added) + { + UE_LOG(LogPython, Warning, TEXT("Adding %s as attr"), UTF8_TO_TCHAR(class_key)); + PyObject_SetAttr((PyObject *)self, key, value); + } + } + + if (PyDict_Size(py_additional_properties) > 0) + { + PyObject_SetAttrString((PyObject *)self, (char*)"__additional_uproperties__", py_additional_properties); + } + + UPythonClass *new_u_py_class = (UPythonClass *)new_class; + // TODO: check if we can use this to decref the ue_PyUbject mapped to the class + new_u_py_class->py_uobject = self; + new_u_py_class->ClassConstructor = [](const FObjectInitializer &ObjectInitializer) + { + FScopePythonGIL gil; + UClass *u_class = ue_py_class_constructor_placeholder ? ue_py_class_constructor_placeholder : ObjectInitializer.GetClass(); + ue_py_class_constructor_placeholder = nullptr; + + UEPyClassConstructor(u_class->GetSuperClass(), ObjectInitializer); + + if (UPythonClass *u_py_class_casted = Cast(u_class)) + { + ue_PyUObject *new_self = ue_get_python_uobject(ObjectInitializer.GetObj()); + if (!new_self) + { + unreal_engine_py_log_error(); + return; + } + + // fill __dict__ from class + if (u_py_class_casted->py_uobject && u_py_class_casted->py_uobject->py_dict) + { + PyObject *found_additional_props = PyDict_GetItemString(u_py_class_casted->py_uobject->py_dict, (char *)"__additional_uproperties__"); + // manage UProperties (and automatically maps multicast properties) + if (found_additional_props) + { + PyObject *keys = PyDict_Keys(found_additional_props); + Py_ssize_t items_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < items_len; i++) + { + PyObject *mc_key = PyList_GetItem(keys, i); + PyObject *mc_value = PyDict_GetItem(found_additional_props, mc_key); + + char *mc_name = PyUnicode_AsUTF8(mc_key); + UProperty *u_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); + if (u_property) + { + if (auto casted_prop = Cast(u_property)) + { + FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); + + FScriptDelegate script_delegate; + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); + // fake UFUNCTION for bypassing checks + script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); + + // add the new delegate + multiscript_delegate.Add(script_delegate); + + // re-assign multicast delegate + casted_prop->SetPropertyValue_InContainer(ObjectInitializer.GetObj(), multiscript_delegate); + } + else + { + PyObject_SetAttr((PyObject *)new_self, mc_key, mc_value); + } + } + + } + Py_DECREF(keys); + } + else + { + PyErr_Clear(); + } + PyObject *keys = PyDict_Keys(u_py_class_casted->py_uobject->py_dict); + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(u_py_class_casted->py_uobject->py_dict, key); + if (PyUnicode_Check(key)) + { + char *key_name = PyUnicode_AsUTF8(key); + if (!strcmp(key_name, (char *)"__additional_uproperties__")) + continue; + } + // special case to bound function to method + if (PyFunction_Check(value)) + { + PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_self); + if (bound_function) + { + PyObject_SetAttr((PyObject *)new_self, key, bound_function); + Py_DECREF(bound_function); + } + else + { + unreal_engine_py_log_error(); + } + } + else + { + PyObject_SetAttr((PyObject *)new_self, key, value); + } + } + Py_DECREF(keys); + } + // call __init__ + u_py_class_casted->CallPyConstructor(new_self); + } + }; + + + + if (self->py_dict) + { + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to set dict on new ClassDefaultObject")); + return -1; + } + PyObject *keys = PyDict_Keys(self->py_dict); + + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(self->py_dict, key); + // special case to bound function to method + if (PyFunction_Check(value)) + { + PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_default_self); + if (bound_function) + { + PyObject_SetAttr((PyObject *)new_default_self, key, bound_function); + Py_DECREF(bound_function); + } + else + { + unreal_engine_py_log_error(); + } + } + else + { + PyObject_SetAttr((PyObject *)new_default_self, key, value); + } + } + Py_DECREF(keys); + } + + // add default uproperties values + if (py_additional_properties) + { + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to set properties on new ClassDefaultObject")); + return -1; + } + PyObject *keys = PyDict_Keys(py_additional_properties); + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(py_additional_properties, key); + + PyObject_SetAttr((PyObject *)new_default_self, key, value); + } + Py_DECREF(keys); + } + + // add custom constructor (__init__) + PyObject *py_init = PyDict_GetItemString(class_attributes, (char *)"__init__"); + if (py_init && PyCallable_Check(py_init)) + { + // fake initializer + FObjectInitializer initializer(new_u_py_class->ClassDefaultObject, nullptr, false, true); + new_u_py_class->SetPyConstructor(py_init); + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to call __init__ on new ClassDefaultObject")); + return -1; + } + + new_u_py_class->CallPyConstructor(new_default_self); + } + + } + + return 0; +} \ No newline at end of file From b06d66661597b0ea8ba81904c8f9294d6c242dbc Mon Sep 17 00:00:00 2001 From: Steven Shan Date: Thu, 8 Mar 2018 10:04:51 -0500 Subject: [PATCH 83/94] Update Material_API.md --- docs/Material_API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Material_API.md b/docs/Material_API.md index ec5909798..c802beca0 100644 --- a/docs/Material_API.md +++ b/docs/Material_API.md @@ -54,9 +54,9 @@ You have two ways to create a instanced material: (new_material is a reference to a previously created/loaded material) ```python -from unreal_engine.classes import MaterialInstancedConstant +from unreal_engine.classes import MaterialInstanceConstant -material_instance = MaterialInstancedConstant() +material_instance = MaterialInstanceConstant() material_instance.set_name('New Funny Material Instance') material_instance.set_material_parent(new_material) material_instance.save_package('/Game/Materials/instanced') From 7699c88334686ef80bcf097d78392d37f17aa083 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 10 Mar 2018 12:14:41 +0100 Subject: [PATCH 84/94] fixed Transform keys in sequencer --- Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 7dd063f76..ed9330355 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -751,9 +751,9 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) section_transform->AddKey(time, ty, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, tz, (EMovieSceneKeyInterpolation)interpolation); - FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().X, unwind); - FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Y, unwind); - FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Z, unwind); + FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.Rotator().Roll, unwind); + FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.Rotator().Pitch, unwind); + FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.Rotator().Yaw, unwind); section_transform->AddKey(time, rx, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, ry, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, rz, (EMovieSceneKeyInterpolation)interpolation); From 45717d1d06f0ee77ff0f76d1248289407f28a6c8 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 13 Mar 2018 15:02:32 +0100 Subject: [PATCH 85/94] Create MaterialExpressionStaticSwitch.md --- examples/MaterialExpressionStaticSwitch.md | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 examples/MaterialExpressionStaticSwitch.md diff --git a/examples/MaterialExpressionStaticSwitch.md b/examples/MaterialExpressionStaticSwitch.md new file mode 100644 index 000000000..0969e3242 --- /dev/null +++ b/examples/MaterialExpressionStaticSwitch.md @@ -0,0 +1,72 @@ +```python +import unreal_engine as ue +from unreal_engine.classes import MaterialFactoryNew +from unreal_engine.classes import MaterialExpressionStaticSwitch, MaterialExpressionStaticSwitchParameter, MaterialExpressionVectorParameter +from unreal_engine.structs import ColorMaterialInput, ExpressionInput +import time + +material_factory = MaterialFactoryNew() +material = material_factory.factory_create_new('/Game/Materials/Test' + str(int(time.time()))) + +material.modify() + +static_switch_node = MaterialExpressionStaticSwitch('', material) +static_switch_node.MaterialExpressionEditorX = -200 +static_switch_node.MaterialExpressionEditorY = 50 + +static_switch_parameter_node_true = MaterialExpressionStaticSwitchParameter('', material) +static_switch_parameter_node_true.MaterialExpressionEditorX = -400 +static_switch_parameter_node_true.MaterialExpressionEditorY = 0 +static_switch_parameter_node_true.ParameterName = 'True' + +static_switch_parameter_node_false = MaterialExpressionStaticSwitchParameter('', material) +static_switch_parameter_node_false.MaterialExpressionEditorX = -400 +static_switch_parameter_node_false.MaterialExpressionEditorY = 200 +static_switch_parameter_node_false.ParameterName = 'False' + +static_switch_node.A = ExpressionInput(Expression=static_switch_parameter_node_true) +static_switch_node.B = ExpressionInput(Expression=static_switch_parameter_node_false) + +vector_parameter_one = MaterialExpressionVectorParameter('', material) +vector_parameter_one.MaterialExpressionEditorX = -600 +vector_parameter_one.MaterialExpressionEditorY = -150 +vector_parameter_one.ParameterName = 'One' + +vector_parameter_two = MaterialExpressionVectorParameter('', material) +vector_parameter_two.MaterialExpressionEditorX = -800 +vector_parameter_two.MaterialExpressionEditorY = -50 +vector_parameter_two.ParameterName = 'Two' + +vector_parameter_three = MaterialExpressionVectorParameter('', material) +vector_parameter_three.MaterialExpressionEditorX = -600 +vector_parameter_three.MaterialExpressionEditorY = 150 +vector_parameter_three.ParameterName = 'Three' + +vector_parameter_four = MaterialExpressionVectorParameter('', material) +vector_parameter_four.MaterialExpressionEditorX = -800 +vector_parameter_four.MaterialExpressionEditorY = 250 +vector_parameter_four.ParameterName = 'Four' + + +static_switch_parameter_node_true.A = ExpressionInput(Expression=vector_parameter_one) +static_switch_parameter_node_true.B = ExpressionInput(Expression=vector_parameter_two) + +static_switch_parameter_node_false.A = ExpressionInput(Expression=vector_parameter_three) +static_switch_parameter_node_false.B = ExpressionInput(Expression=vector_parameter_four) + +material.BaseColor = ColorMaterialInput(Expression=static_switch_node) + +material.Expressions = [ + static_switch_node, + static_switch_parameter_node_true, + static_switch_parameter_node_false, + vector_parameter_one, + vector_parameter_two, + vector_parameter_three, + vector_parameter_four + ] + +material.post_edit_change() + +ue.open_editor_for_asset(material) +``` From 63d14478d906e30661d02e9e2526e2647ed038ac Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 13 Mar 2018 15:09:57 +0100 Subject: [PATCH 86/94] added screenshot for material expression static switch example --- examples/MaterialExpressionStaticSwitch.png | Bin 0 -> 216428 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/MaterialExpressionStaticSwitch.png diff --git a/examples/MaterialExpressionStaticSwitch.png b/examples/MaterialExpressionStaticSwitch.png new file mode 100644 index 0000000000000000000000000000000000000000..59ddc5b061c2ecbfdef5d307decf997064862545 GIT binary patch literal 216428 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%0De==~HQio*3}R|VV&;Ko=3MvVUi+{N15r-Mw` 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�B}Z61OhknfbbQLUb zQ36qTTqhNxavqp`_gkoT3-yfh^_6}v6k;=+KAw+k@c%P{I<5yW_o2>q-BLZ)6yWdt zkqB6`+{ZuD&px-VZ0XQu)`p*vx60vAJS@f{#cu8hKlgcgxCEB@?AOn&U&2d9ezoMA z_{R}&1UOb*$qF5E`v0}gKxV@8>;Ehsz-&nTPbSzOCGrR7BQ~d-T#KC;qtqZ|LLl^A z^V4oIDuX@~6*rja3Dw>cevpHTYNqauckgOYS4xi>+KxV-jP`jcqdt6$+i!~<5Jmh# zV{#G!Tz!R*EOWN+&z2fyQ=EV+1W44 z(W>SK5USlP3pckH$L?-J4h}rC%^Vt4j_|nZufnrxpEkiaEHyNXJ)pW){B{duw2v!r zkveyFqWXrnUNCvkbAp%RrSv!?<})R-vvc^t)Tr|iE&&sZs&a8uBb@qoedfmz`VMxH z-H2%+hX?u%V)yJ*dM#9yQS|tkU*`j#YIL~%cxseYo#N99MJWqi-W|3CP3`Qc!5ShC zPDM!r7@HENwx)hu$aU+(_02*$W@go4i=^zdeRA?`eLRr v8CxsDP55YVNyP#2- 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 Date: Tue, 13 Mar 2018 15:11:26 +0100 Subject: [PATCH 87/94] Update MaterialExpressionStaticSwitch.md --- examples/MaterialExpressionStaticSwitch.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/MaterialExpressionStaticSwitch.md b/examples/MaterialExpressionStaticSwitch.md index 0969e3242..91bd959af 100644 --- a/examples/MaterialExpressionStaticSwitch.md +++ b/examples/MaterialExpressionStaticSwitch.md @@ -1,3 +1,6 @@ + +![MaterialExpressionStaticSwitch Screenshot](https://github.com/20tab/UnrealEnginePython/blob/master/examples/MaterialExpressionStaticSwitch.png) + ```python import unreal_engine as ue from unreal_engine.classes import MaterialFactoryNew From a98cd6e0368ae7089d881c7173c8db46b13e1be0 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 13 Mar 2018 15:12:44 +0100 Subject: [PATCH 88/94] added screenshot for material expression static switch example [2] --- examples/MaterialExpressionStaticSwitch.png | Bin 216428 -> 215637 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/MaterialExpressionStaticSwitch.png b/examples/MaterialExpressionStaticSwitch.png index 59ddc5b061c2ecbfdef5d307decf997064862545..247a19eef30b08bb0f9e667f3cebb638f05d87ea 100644 GIT binary patch literal 215637 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�c0w|6fO4aFQqmbB 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


H0DK1$pSyi>B z4J|U(e)QPZ&b8i$_1_h#Jt`PC8b9h(W5*dCul>k*Z?;-%AtiuvL(SaYdS_}iF4dOy z7G+I)Y_n5!X=m%$9RAOEY>w5l@F&H)em;Ifb8nwvH+6MhSK9ug%KI)9$r(*omXuKnV>{9tGAQFb)m_ zEkVt=RJPfEBXX@our*+GMQEFpdu~wrVt)sQbCV#N7cXp(X__=$z{$-9D=O)6;V#wuDr}&lPj#>oG z%T&Tlw3c-&{`i4}|BUs|b2Y3}AtV8-t`zM|wVmD@0(+&>sB>W0x)Dw5nosD6pF^(* zb;^Yo%p~vPt;wQeQv#q#t97-B&BK`1b=ytD2g3pad54pr vt;$xu0t|S#g+u-xQm)A>4rVzX_@;J<8Sy~+rx#7@;E(g_I%jiFTYCRL61;)| literal 216428 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%0De==~HQio*3}R|VV&;Ko=3MvVUi+{N15r-Mw` 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�B}Z61OhknfbbQLUb zQ36qTTqhNxavqp`_gkoT3-yfh^_6}v6k;=+KAw+k@c%P{I<5yW_o2>q-BLZ)6yWdt zkqB6`+{ZuD&px-VZ0XQu)`p*vx60vAJS@f{#cu8hKlgcgxCEB@?AOn&U&2d9ezoMA z_{R}&1UOb*$qF5E`v0}gKxV@8>;Ehsz-&nTPbSzOCGrR7BQ~d-T#KC;qtqZ|LLl^A z^V4oIDuX@~6*rja3Dw>cevpHTYNqauckgOYS4xi>+KxV-jP`jcqdt6$+i!~<5Jmh# zV{#G!Tz!R*EOWN+&z2fyQ=EV+1W44 z(W>SK5USlP3pckH$L?-J4h}rC%^Vt4j_|nZufnrxpEkiaEHyNXJ)pW){B{duw2v!r zkveyFqWXrnUNCvkbAp%RrSv!?<})R-vvc^t)Tr|iE&&sZs&a8uBb@qoedfmz`VMxH z-H2%+hX?u%V)yJ*dM#9yQS|tkU*`j#YIL~%cxseYo#N99MJWqi-W|3CP3`Qc!5ShC zPDM!r7@HENwx)hu$aU+(_02*$W@go4i=^zdeRA?`eLRr v8CxsDP55YVNyP#2- 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 Date: Sat, 17 Mar 2018 07:41:58 +0100 Subject: [PATCH 89/94] fixed sequencer rotations --- .../UnrealEnginePython/Private/UObject/UEPySequencer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index ed9330355..3bd6669d3 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -751,9 +751,12 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) section_transform->AddKey(time, ty, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, tz, (EMovieSceneKeyInterpolation)interpolation); - FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.Rotator().Roll, unwind); - FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.Rotator().Pitch, unwind); - FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.Rotator().Yaw, unwind); + /*FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().Rotator().Roll, unwind); + FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Rotator().Pitch, unwind); + FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Rotator().Yaw, unwind);*/ + FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().Euler().X, unwind); + FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Euler().Y, unwind); + FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Euler().Z, unwind); section_transform->AddKey(time, rx, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, ry, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, rz, (EMovieSceneKeyInterpolation)interpolation); From 51631743596eeabfd5547f9f1d58020a2363c14f Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Mar 2018 08:18:44 +0100 Subject: [PATCH 90/94] fixed warnings in 4.18 --- ...tSyntaxHighlighterTextLayoutMarshaller.cpp | 4 ++++ .../Private/PyCommandlet.cpp | 4 ++++ .../UnrealEnginePython/Private/UEPyEngine.cpp | 20 ++++++++++++++++++ .../Private/UObject/UEPyPhysics.cpp | 21 +++++++++++++------ .../UnrealEnginePython.Build.cs | 3 ++- 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp index b6303bf18..57b63d6a2 100644 --- a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp +++ b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp @@ -311,7 +311,11 @@ void FPYRichTextSyntaxHighlighterTextLayoutMarshaller::ParseTokens(const FString FRunInfo RunInfo(TEXT("SyntaxHighlight.PY.Normal")); FTextBlockStyle TextBlockStyle = SyntaxTextStyle.NormalTextStyle; +#if ENGINE_MINOR_VERSION + const bool bIsWhitespace = FString(TokenText).TrimEnd().IsEmpty(); +#else const bool bIsWhitespace = FString(TokenText).TrimTrailing().IsEmpty(); +#endif if(!bIsWhitespace) { bool bHasMatchedSyntax = false; diff --git a/Source/UnrealEnginePython/Private/PyCommandlet.cpp b/Source/UnrealEnginePython/Private/PyCommandlet.cpp index 33c243450..42d80c422 100644 --- a/Source/UnrealEnginePython/Private/PyCommandlet.cpp +++ b/Source/UnrealEnginePython/Private/PyCommandlet.cpp @@ -30,7 +30,11 @@ int32 UPyCommandlet::Main(const FString& CommandLine) const FRegexPattern myPattern(RegexString); FRegexMatcher myMatcher(myPattern, *CommandLine); myMatcher.FindNext(); +#if ENGINE_MINOR_VERSION >= 18 + FString PyCommandLine = myMatcher.GetCaptureGroup(0).TrimStart().TrimEnd(); +#else FString PyCommandLine = myMatcher.GetCaptureGroup(0).Trim().TrimTrailing(); +#endif TArray PyArgv; PyArgv.Add(FString()); diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index e4f5b1b50..9a9333a86 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -11,6 +11,10 @@ #include "PackageTools.h" #endif +#if ENGINE_MINOR_VERSION >= 18 +#include "HAL/PlatformApplicationMisc.h" +#endif + PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) { @@ -165,12 +169,20 @@ PyObject *py_unreal_engine_get_up_vector(PyObject * self, PyObject * args) PyObject *py_unreal_engine_get_content_dir(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION >= 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::ProjectContentDir())); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameContentDir())); +#endif } PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION >= 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::ProjectSavedDir())); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); +#endif } PyObject * py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *) @@ -1237,13 +1249,21 @@ PyObject *py_unreal_engine_clipboard_copy(PyObject * self, PyObject * args) return nullptr; } +#if ENGINE_MINOR_VERSION >= 18 + FPlatformApplicationMisc::ClipboardCopy(UTF8_TO_TCHAR(text)); +#else FGenericPlatformMisc::ClipboardCopy(UTF8_TO_TCHAR(text)); +#endif Py_RETURN_NONE; } PyObject *py_unreal_engine_clipboard_paste(PyObject * self, PyObject * args) { FString clipboard; +#if ENGINE_MINOR_VERSION >= 18 + FPlatformApplicationMisc::ClipboardPaste(clipboard); +#else FGenericPlatformMisc::ClipboardPaste(clipboard); +#endif return PyUnicode_FromString(TCHAR_TO_UTF8(*clipboard)); } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp index 95d278def..4abfde503 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp @@ -137,10 +137,13 @@ PyObject *py_ue_add_angular_impulse(ue_PyUObject * self, PyObject * args) if (py_obj_b_vel_change && PyObject_IsTrue(py_obj_b_vel_change)) b_vel_change = true; +#if ENGINE_MINOR_VERSION >= 18 + primitive->AddAngularImpulseInRadians(impulse, f_bone_name, b_vel_change); +#else primitive->AddAngularImpulse(impulse, f_bone_name, b_vel_change); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -237,10 +240,13 @@ PyObject *py_ue_add_torque(ue_PyUObject * self, PyObject * args) if (py_obj_b_accel_change && PyObject_IsTrue(py_obj_b_accel_change)) b_accel_change = true; +#if ENGINE_MINOR_VERSION >= 18 + primitive->AddTorqueInRadians(torque, f_bone_name, b_accel_change); +#else primitive->AddTorque(torque, f_bone_name, b_accel_change); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -369,10 +375,13 @@ PyObject *py_ue_set_physics_angular_velocity(ue_PyUObject * self, PyObject * arg f_bone_name = FName(UTF8_TO_TCHAR(bone_name)); } +#if ENGINE_MINOR_VERSION >= 18 + primitive->SetPhysicsAngularVelocityInDegrees(new_ang_vel, add_to_current, f_bone_name); +#else primitive->SetPhysicsAngularVelocity(new_ang_vel, add_to_current, f_bone_name); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_ue_get_physics_angular_velocity(ue_PyUObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 09ec39aad..9021467dd 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -125,7 +125,8 @@ public UnrealEnginePython(TargetInfo Target) "RenderCore", "MovieSceneCapture", "Landscape", - "Foliage" + "Foliage", + "ApplicationCore" // ... add private dependencies that you statically link with here ... } ); From 63dcf3cafeecc5556187f1f80d855cfdd506e13b Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Mar 2018 08:21:22 +0100 Subject: [PATCH 91/94] fixed 4.15 support --- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 9021467dd..a35f3f615 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -126,7 +126,9 @@ public UnrealEnginePython(TargetInfo Target) "MovieSceneCapture", "Landscape", "Foliage", +#if WITH_FORWARDED_MODULE_RULES_CTOR "ApplicationCore" +#endif // ... add private dependencies that you statically link with here ... } ); From f81ebcd79bfafb5506cf82ad9b897535aea683a6 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Mar 2018 08:26:12 +0100 Subject: [PATCH 92/94] fixed typo --- .../Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp index 57b63d6a2..53417bf6b 100644 --- a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp +++ b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp @@ -311,7 +311,7 @@ void FPYRichTextSyntaxHighlighterTextLayoutMarshaller::ParseTokens(const FString FRunInfo RunInfo(TEXT("SyntaxHighlight.PY.Normal")); FTextBlockStyle TextBlockStyle = SyntaxTextStyle.NormalTextStyle; -#if ENGINE_MINOR_VERSION +#if ENGINE_MINOR_VERSION >= 18 const bool bIsWhitespace = FString(TokenText).TrimEnd().IsEmpty(); #else const bool bIsWhitespace = FString(TokenText).TrimTrailing().IsEmpty(); From 7bfcc878809a1a9f298dcb33b7c93944197f0f99 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Mar 2018 08:33:59 +0100 Subject: [PATCH 93/94] more robust build system --- .../UnrealEnginePython/UnrealEnginePython.Build.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index a35f3f615..7f82c936c 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -126,13 +126,21 @@ public UnrealEnginePython(TargetInfo Target) "MovieSceneCapture", "Landscape", "Foliage", -#if WITH_FORWARDED_MODULE_RULES_CTOR - "ApplicationCore" -#endif // ... add private dependencies that you statically link with here ... } ); +#if WITH_FORWARDED_MODULE_RULES_CTOR + BuildVersion Version; + if (BuildVersion.TryRead(out Version)) + { + if (Version.MinorVersion >= 18) + { + PrivateDependencyModuleNames.Add("ApplicationCore"); + } + } +#endif + DynamicallyLoadedModuleNames.AddRange( new string[] From 91438909f9ca0e80d2469f39d0b76b39e4acdebf Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 17 Mar 2018 09:11:41 +0100 Subject: [PATCH 94/94] fixed 4.17 --- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 7f82c936c..2b28c6916 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -130,9 +130,10 @@ public UnrealEnginePython(TargetInfo Target) } ); + #if WITH_FORWARDED_MODULE_RULES_CTOR BuildVersion Version; - if (BuildVersion.TryRead(out Version)) + if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) { if (Version.MinorVersion >= 18) {