Skip to content

Commit 028f5a5

Browse files
author
Roberto De Ioris
committed
added preliminary support for PyUserWidget, ad-hoc wrappers for FGeometry and FPaintContext will follow
1 parent cef4f0c commit 028f5a5

3 files changed

Lines changed: 191 additions & 0 deletions

File tree

Source/UnrealEnginePython/Private/PyActor.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ void APyActor::PostInitializeComponents()
9191
if (!py_actor_instance)
9292
return;
9393

94+
FScopePythonGIL gil;
95+
9496
if (!PyObject_HasAttrString(py_actor_instance, (char *)"post_initialize_components"))
9597
return;
9698

@@ -110,6 +112,8 @@ void APyActor::BeginPlay()
110112
if (!py_actor_instance)
111113
return;
112114

115+
FScopePythonGIL gil;
116+
113117
if (!PyObject_HasAttrString(py_actor_instance, (char *)"begin_play"))
114118
return;
115119

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include "UnrealEnginePythonPrivatePCH.h"
2+
#include "PyUserWidget.h"
3+
4+
#include "PythonDelegate.h"
5+
6+
bool UPyUserWidget::Initialize()
7+
{
8+
if (!Super::Initialize())
9+
return false;
10+
11+
if (PythonModule.IsEmpty())
12+
return false;
13+
14+
FScopePythonGIL gil;
15+
16+
py_uobject = ue_get_python_wrapper(this);
17+
if (!py_uobject) {
18+
unreal_engine_py_log_error();
19+
return false;
20+
}
21+
22+
PyObject *py_user_widget_module = PyImport_ImportModule(TCHAR_TO_UTF8(*PythonModule));
23+
if (!py_user_widget_module) {
24+
unreal_engine_py_log_error();
25+
return false;
26+
}
27+
28+
#if WITH_EDITOR
29+
// todo implement autoreload with a dictionary of module timestamps
30+
py_user_widget_module = PyImport_ReloadModule(py_user_widget_module);
31+
if (!py_user_widget_module) {
32+
unreal_engine_py_log_error();
33+
return false;
34+
}
35+
#endif
36+
37+
if (PythonClass.IsEmpty())
38+
return false;
39+
40+
PyObject *py_user_widget_module_dict = PyModule_GetDict(py_user_widget_module);
41+
PyObject *py_user_widget_class = PyDict_GetItemString(py_user_widget_module_dict, TCHAR_TO_UTF8(*PythonClass));
42+
43+
if (!py_user_widget_class) {
44+
UE_LOG(LogPython, Error, TEXT("Unable to find class %s in module %s"), *PythonClass, *PythonModule);
45+
return false;
46+
}
47+
48+
py_user_widget_instance = PyObject_CallObject(py_user_widget_class, NULL);
49+
if (!py_user_widget_instance) {
50+
unreal_engine_py_log_error();
51+
return false;
52+
}
53+
54+
py_uobject->py_proxy = py_user_widget_instance;
55+
56+
PyObject_SetAttrString(py_user_widget_instance, (char*)"uobject", (PyObject *)py_uobject);
57+
58+
return true;
59+
}
60+
61+
62+
void UPyUserWidget::NativeConstruct()
63+
{
64+
Super::NativeConstruct();
65+
66+
if (!py_user_widget_instance)
67+
return;
68+
69+
FScopePythonGIL gil;
70+
71+
if (!PyObject_HasAttrString(py_user_widget_instance, (char *)"construct"))
72+
return;
73+
74+
PyObject *bp_ret = PyObject_CallMethod(py_user_widget_instance, (char *)"construct", NULL);
75+
if (!bp_ret) {
76+
unreal_engine_py_log_error();
77+
return;
78+
}
79+
Py_DECREF(bp_ret);
80+
}
81+
82+
void UPyUserWidget::NativeDestruct()
83+
{
84+
Super::NativeDestruct();
85+
86+
if (!py_user_widget_instance)
87+
return;
88+
89+
FScopePythonGIL gil;
90+
91+
if (!PyObject_HasAttrString(py_user_widget_instance, (char *)"destruct"))
92+
return;
93+
94+
PyObject *bp_ret = PyObject_CallMethod(py_user_widget_instance, (char *)"destruct", NULL);
95+
if (!bp_ret) {
96+
unreal_engine_py_log_error();
97+
return;
98+
}
99+
Py_DECREF(bp_ret);
100+
}
101+
102+
// Called every frame
103+
void UPyUserWidget::NativeTick(const FGeometry & MyGeometry, float InDeltaTime)
104+
{
105+
Super::NativeTick(MyGeometry, InDeltaTime);
106+
107+
if (!py_user_widget_instance)
108+
return;
109+
110+
FScopePythonGIL gil;
111+
112+
if (!PyObject_HasAttrString(py_user_widget_instance, (char *)"tick"))
113+
return;
114+
115+
PyObject *ret = PyObject_CallMethod(py_user_widget_instance, (char *)"tick", (char *)"Of", py_ue_new_uscriptstruct(FGeometry::StaticStruct(), (uint8 *) &MyGeometry), InDeltaTime);
116+
if (!ret) {
117+
unreal_engine_py_log_error();
118+
return;
119+
}
120+
Py_DECREF(ret);
121+
122+
}
123+
124+
UPyUserWidget::~UPyUserWidget()
125+
{
126+
FScopePythonGIL gil;
127+
128+
ue_pydelegates_cleanup(py_uobject);
129+
130+
#if defined(UEPY_MEMORY_DEBUG)
131+
if (py_user_widget_instance && py_user_widget_instance->ob_refcnt != 1) {
132+
UE_LOG(LogPython, Error, TEXT("Inconsistent Python UUserWidget wrapper refcnt = %d"), py_user_widget_instance->ob_refcnt);
133+
}
134+
#endif
135+
Py_XDECREF(py_user_widget_instance);
136+
137+
#if defined(UEPY_MEMORY_DEBUG)
138+
UE_LOG(LogPython, Warning, TEXT("Python UUserWidget %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr);
139+
#endif
140+
141+
// this could trigger the distruction of the python/uobject mapper
142+
Py_XDECREF(py_uobject);
143+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
4+
5+
#include "PyUserWidget.generated.h"
6+
7+
8+
9+
UCLASS(BlueprintType, Blueprintable)
10+
class UPyUserWidget : public UUserWidget
11+
{
12+
GENERATED_BODY()
13+
14+
public:
15+
16+
~UPyUserWidget();
17+
18+
virtual bool Initialize() override;
19+
20+
virtual void NativeConstruct() override;
21+
virtual void NativeDestruct() override;
22+
23+
// Called every frame
24+
virtual void NativeTick(const FGeometry & MyGeometry, float InDeltaTime) override;
25+
26+
//virtual void NativePaint(FPaintContext & InContext) const override;
27+
28+
//virtual bool NativeIsInteractable() const override;
29+
30+
UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite, meta = (ExposeOnSpawn = true))
31+
FString PythonModule;
32+
33+
UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite, meta = (ExposeOnSpawn = true))
34+
FString PythonClass;
35+
36+
UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite, meta = (ExposeOnSpawn = true))
37+
bool PythonTickForceDisabled;
38+
39+
private:
40+
PyObject *py_user_widget_instance;
41+
// mapped uobject, required for debug and advanced reflection
42+
ue_PyUObject *py_uobject;
43+
};
44+

0 commit comments

Comments
 (0)