Skip to content

Commit be308d2

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 9403e24 + 8de7445 commit be308d2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1446
-381
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Once the plugin is installed and enabled, you get access to the 'PythonConsole'
3636

3737
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)
3838

39-
The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19 and 4.20
39+
The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19, 4.20 and 4.21
4040

4141
We support official python.org releases as well as IntelPython and Anaconda distributions.
4242

Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,49 @@ static PyObject *py_ue_edgraphpin_break_link_to(ue_PyEdGraphPin *self, PyObject
8080
Py_RETURN_NONE;
8181
}
8282

83+
static PyObject *py_ue_edgraphpin_break_all_pin_links(ue_PyEdGraphPin *self, PyObject * args)
84+
{
85+
PyObject *py_notify_nodes = nullptr;
86+
if (!PyArg_ParseTuple(args, "O:break_all_pin_links", &py_notify_nodes))
87+
{
88+
return NULL;
89+
}
90+
91+
bool notify_nodes = true;
92+
if (py_notify_nodes && !PyObject_IsTrue(py_notify_nodes))
93+
notify_nodes = false;
94+
95+
self->pin->BreakAllPinLinks(notify_nodes);
96+
97+
if (UBlueprint *bp = Cast<UBlueprint>(self->pin->GetOwningNode()->GetGraph()->GetOuter()))
98+
{
99+
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp);
100+
}
101+
102+
Py_RETURN_NONE;
103+
}
104+
105+
static PyObject *py_ue_edgraphpin_get_linked_to(ue_PyEdGraphPin * self, PyObject * args)
106+
{
107+
PyObject *pins = PyList_New(0);
108+
109+
TArray<UEdGraphPin*> Links = self->pin->LinkedTo;
110+
111+
for (int32 i = 0; i < Links.Num(); i++)
112+
{
113+
UEdGraphPin *pin = Links[i];
114+
ue_PyUObject *item = (ue_PyUObject *)py_ue_new_edgraphpin(pin);
115+
if (item)
116+
PyList_Append(pins, (PyObject *)item);
117+
}
118+
return pins;
119+
}
120+
83121
static PyMethodDef ue_PyEdGraphPin_methods[] = {
84122
{ "make_link_to", (PyCFunction)py_ue_edgraphpin_make_link_to, METH_VARARGS, "" },
85123
{ "break_link_to", (PyCFunction)py_ue_edgraphpin_break_link_to, METH_VARARGS, "" },
124+
{ "break_all_pin_links", (PyCFunction)py_ue_edgraphpin_break_all_pin_links, METH_VARARGS, "" },
125+
{ "get_linked_to", (PyCFunction)py_ue_edgraphpin_get_linked_to, METH_VARARGS, "" },
86126
{ "connect", (PyCFunction)py_ue_edgraphpin_connect, METH_VARARGS, "" },
87127
{ NULL } /* Sentinel */
88128
};

Source/UnrealEnginePython/Private/PyCommandlet.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ int32 UPyCommandlet::Main(const FString& CommandLine)
9191
#if PY_MAJOR_VERSION >= 3
9292
argv[i] = (wchar_t*)malloc(PyArgv[i].Len() + 1);
9393
#if PLATFORM_MAC || PLATFORM_LINUX
94+
#if ENGINE_MINOR_VERSION >= 20
95+
wcsncpy(argv[i], (const wchar_t *) TCHAR_TO_WCHAR(*PyArgv[i].ReplaceEscapedCharWithChar()), PyArgv[i].Len() + 1);
96+
#else
9497
wcsncpy(argv[i], *PyArgv[i].ReplaceEscapedCharWithChar(), PyArgv[i].Len() + 1);
98+
#endif
9599
#elif PLATFORM_ANDROID
96100
wcsncpy(argv[i], (const wchar_t *)*PyArgv[i].ReplaceEscapedCharWithChar(), PyArgv[i].Len() + 1);
97101
#else

Source/UnrealEnginePython/Private/PythonDelegate.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
#include "PythonDelegate.h"
33
#include "UEPyModule.h"
4+
#include "UEPyCallable.h"
45

56
UPythonDelegate::UPythonDelegate()
67
{
@@ -101,6 +102,12 @@ void UPythonDelegate::PyInputAxisHandler(float value)
101102
Py_DECREF(ret);
102103
}
103104

105+
bool UPythonDelegate::UsesPyCallable(PyObject *other)
106+
{
107+
ue_PyCallable *other_callable = (ue_PyCallable*)other;
108+
ue_PyCallable *this_callable = (ue_PyCallable*)py_callable;
109+
return other_callable->u_function == this_callable->u_function && other_callable->u_target == this_callable->u_target;
110+
}
104111

105112
UPythonDelegate::~UPythonDelegate()
106113
{
@@ -110,4 +117,4 @@ UPythonDelegate::~UPythonDelegate()
110117
#if defined(UEPY_MEMORY_DEBUG)
111118
UE_LOG(LogPython, Warning, TEXT("PythonDelegate %p callable XDECREF'ed"), this);
112119
#endif
113-
}
120+
}
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
#pragma once
2+
3+
#include "PythonHouseKeeper.h"
4+
5+
void FUnrealEnginePythonHouseKeeper::AddReferencedObjects(FReferenceCollector& InCollector)
6+
{
7+
InCollector.AddReferencedObjects(PythonTrackedObjects);
8+
}
9+
10+
FUnrealEnginePythonHouseKeeper *FUnrealEnginePythonHouseKeeper::Get()
11+
{
12+
static FUnrealEnginePythonHouseKeeper *Singleton;
13+
if (!Singleton)
14+
{
15+
Singleton = new FUnrealEnginePythonHouseKeeper();
16+
// register a new delegate for the GC
17+
#if ENGINE_MINOR_VERSION >= 18
18+
FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate);
19+
#else
20+
FCoreUObjectDelegates::PostGarbageCollect.AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate);
21+
#endif
22+
}
23+
return Singleton;
24+
}
25+
26+
void FUnrealEnginePythonHouseKeeper::RunGCDelegate()
27+
{
28+
FScopePythonGIL gil;
29+
RunGC();
30+
}
31+
32+
int32 FUnrealEnginePythonHouseKeeper::RunGC()
33+
{
34+
int32 Garbaged = PyUObjectsGC();
35+
Garbaged += DelegatesGC();
36+
return Garbaged;
37+
}
38+
39+
bool FUnrealEnginePythonHouseKeeper::IsValidPyUObject(ue_PyUObject *PyUObject)
40+
{
41+
if (!PyUObject)
42+
return false;
43+
44+
UObject *Object = PyUObject->ue_object;
45+
FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object);
46+
if (!Tracker)
47+
{
48+
return false;
49+
}
50+
51+
if (!Tracker->Owner.IsValid())
52+
return false;
53+
54+
return true;
55+
56+
}
57+
58+
void FUnrealEnginePythonHouseKeeper::TrackUObject(UObject *Object)
59+
{
60+
FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object);
61+
if (!Tracker)
62+
{
63+
return;
64+
}
65+
if (Tracker->bPythonOwned)
66+
return;
67+
Tracker->bPythonOwned = true;
68+
// when a new ue_PyUObject spawns, it has a reference counting of two
69+
Py_DECREF(Tracker->PyUObject);
70+
Tracker->PyUObject->owned = 1;
71+
PythonTrackedObjects.Add(Object);
72+
}
73+
74+
void FUnrealEnginePythonHouseKeeper::UntrackUObject(UObject *Object)
75+
{
76+
PythonTrackedObjects.Remove(Object);
77+
}
78+
79+
void FUnrealEnginePythonHouseKeeper::RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject)
80+
{
81+
UObjectPyMapping.Add(Object, FPythonUOjectTracker(Object, InPyUObject));
82+
}
83+
84+
void FUnrealEnginePythonHouseKeeper::UnregisterPyUObject(UObject *Object)
85+
{
86+
UObjectPyMapping.Remove(Object);
87+
}
88+
89+
ue_PyUObject *FUnrealEnginePythonHouseKeeper::GetPyUObject(UObject *Object)
90+
{
91+
FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object);
92+
if (!Tracker)
93+
{
94+
return nullptr;
95+
}
96+
97+
if (!Tracker->Owner.IsValid(true))
98+
{
99+
#if defined(UEPY_MEMORY_DEBUG)
100+
UE_LOG(LogPython, Warning, TEXT("DEFREF'ing UObject at %p (refcnt: %d)"), Object, Tracker->PyUObject->ob_base.ob_refcnt);
101+
#endif
102+
if (!Tracker->bPythonOwned)
103+
Py_DECREF((PyObject *)Tracker->PyUObject);
104+
UnregisterPyUObject(Object);
105+
return nullptr;
106+
}
107+
108+
return Tracker->PyUObject;
109+
}
110+
111+
uint32 FUnrealEnginePythonHouseKeeper::PyUObjectsGC()
112+
{
113+
uint32 Garbaged = 0;
114+
TArray<UObject *> BrokenList;
115+
for (auto &UObjectPyItem : UObjectPyMapping)
116+
{
117+
UObject *Object = UObjectPyItem.Key;
118+
FPythonUOjectTracker &Tracker = UObjectPyItem.Value;
119+
#if defined(UEPY_MEMORY_DEBUG)
120+
UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), Object);
121+
#endif
122+
if (!Tracker.Owner.IsValid(true))
123+
{
124+
#if defined(UEPY_MEMORY_DEBUG)
125+
UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), Object, Tracker.PyUObject->ob_base.ob_refcnt);
126+
#endif
127+
BrokenList.Add(Object);
128+
Garbaged++;
129+
}
130+
else
131+
{
132+
#if defined(UEPY_MEMORY_DEBUG)
133+
UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), Object, *Object->GetName());
134+
#endif
135+
}
136+
}
137+
138+
for (UObject *Object : BrokenList)
139+
{
140+
FPythonUOjectTracker &Tracker = UObjectPyMapping[Object];
141+
if (!Tracker.bPythonOwned)
142+
Py_DECREF((PyObject *)Tracker.PyUObject);
143+
UnregisterPyUObject(Object);
144+
}
145+
146+
return Garbaged;
147+
148+
}
149+
150+
151+
int32 FUnrealEnginePythonHouseKeeper::DelegatesGC()
152+
{
153+
int32 Garbaged = 0;
154+
#if defined(UEPY_MEMORY_DEBUG)
155+
UE_LOG(LogPython, Display, TEXT("Garbage collecting %d UObject delegates"), PyDelegatesTracker.Num());
156+
#endif
157+
for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i)
158+
{
159+
FPythonDelegateTracker &Tracker = PyDelegatesTracker[i];
160+
if (!Tracker.Owner.IsValid(true))
161+
{
162+
Tracker.Delegate->RemoveFromRoot();
163+
PyDelegatesTracker.RemoveAt(i);
164+
Garbaged++;
165+
}
166+
167+
}
168+
169+
#if defined(UEPY_MEMORY_DEBUG)
170+
UE_LOG(LogPython, Display, TEXT("Garbage collecting %d Slate delegates"), PySlateDelegatesTracker.Num());
171+
#endif
172+
173+
for (int32 i = PySlateDelegatesTracker.Num() - 1; i >= 0; --i)
174+
{
175+
FPythonSWidgetDelegateTracker &Tracker = PySlateDelegatesTracker[i];
176+
if (!Tracker.Owner.IsValid())
177+
{
178+
PySlateDelegatesTracker.RemoveAt(i);
179+
Garbaged++;
180+
}
181+
182+
}
183+
return Garbaged;
184+
}
185+
186+
UPythonDelegate *FUnrealEnginePythonHouseKeeper::FindDelegate(UObject *Owner, PyObject *PyCallable)
187+
{
188+
for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i)
189+
{
190+
FPythonDelegateTracker &Tracker = PyDelegatesTracker[i];
191+
if (Tracker.Owner.Get() == Owner && Tracker.Delegate->UsesPyCallable(PyCallable))
192+
return Tracker.Delegate;
193+
}
194+
return nullptr;
195+
}
196+
197+
UPythonDelegate *FUnrealEnginePythonHouseKeeper::NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature)
198+
{
199+
UPythonDelegate *Delegate = NewObject<UPythonDelegate>();
200+
201+
Delegate->AddToRoot();
202+
Delegate->SetPyCallable(PyCallable);
203+
Delegate->SetSignature(Signature);
204+
205+
FPythonDelegateTracker Tracker(Delegate, Owner);
206+
PyDelegatesTracker.Add(Tracker);
207+
208+
return Delegate;
209+
}
210+
211+
TSharedRef<FPythonSlateDelegate> FUnrealEnginePythonHouseKeeper::NewSlateDelegate(TSharedRef<SWidget> Owner, PyObject *PyCallable)
212+
{
213+
TSharedRef<FPythonSlateDelegate> Delegate = MakeShareable(new FPythonSlateDelegate());
214+
Delegate->SetPyCallable(PyCallable);
215+
216+
FPythonSWidgetDelegateTracker Tracker(Delegate, Owner);
217+
PySlateDelegatesTracker.Add(Tracker);
218+
219+
return Delegate;
220+
}
221+
222+
TSharedRef<FPythonSlateDelegate> FUnrealEnginePythonHouseKeeper::NewDeferredSlateDelegate(PyObject *PyCallable)
223+
{
224+
TSharedRef<FPythonSlateDelegate> Delegate = MakeShareable(new FPythonSlateDelegate());
225+
Delegate->SetPyCallable(PyCallable);
226+
227+
return Delegate;
228+
}
229+
230+
TSharedRef<FPythonSmartDelegate> FUnrealEnginePythonHouseKeeper::NewPythonSmartDelegate(PyObject *PyCallable)
231+
{
232+
TSharedRef<FPythonSmartDelegate> Delegate = MakeShareable(new FPythonSmartDelegate());
233+
Delegate->SetPyCallable(PyCallable);
234+
235+
PyStaticSmartDelegatesTracker.Add(Delegate);
236+
237+
return Delegate;
238+
}
239+
240+
void FUnrealEnginePythonHouseKeeper::TrackDeferredSlateDelegate(TSharedRef<FPythonSlateDelegate> Delegate, TSharedRef<SWidget> Owner)
241+
{
242+
FPythonSWidgetDelegateTracker Tracker(Delegate, Owner);
243+
PySlateDelegatesTracker.Add(Tracker);
244+
}
245+
246+
TSharedRef<FPythonSlateDelegate> FUnrealEnginePythonHouseKeeper::NewStaticSlateDelegate(PyObject *PyCallable)
247+
{
248+
TSharedRef<FPythonSlateDelegate> Delegate = MakeShareable(new FPythonSlateDelegate());
249+
Delegate->SetPyCallable(PyCallable);
250+
251+
PyStaticSlateDelegatesTracker.Add(Delegate);
252+
253+
return Delegate;
254+
}
255+

Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO
6363
Py_RETURN_NONE;
6464
}
6565

66+
static PyObject *py_ue_fmenu_builder_add_sub_menu(ue_PyFMenuBuilder *self, PyObject * args)
67+
{
68+
char *label;
69+
char *tooltip;
70+
PyObject *py_callable;
71+
PyObject *py_bool = nullptr;
72+
if (!PyArg_ParseTuple(args, "ssO|O:add_sub_menu", &label, &tooltip, &py_callable, &py_bool))
73+
return nullptr;
74+
75+
if (!PyCallable_Check(py_callable))
76+
{
77+
return PyErr_Format(PyExc_Exception, "argument is not callable");
78+
}
79+
80+
81+
TSharedRef<FPythonSlateDelegate> py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable);
82+
83+
FNewMenuDelegate menu_delegate;
84+
menu_delegate.BindSP(py_delegate, &FPythonSlateDelegate::SubMenuPyBuilder);
85+
86+
87+
self->menu_builder.AddSubMenu(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), menu_delegate, (py_bool && PyObject_IsTrue(py_bool)) ? true : false);
88+
89+
Py_RETURN_NONE;
90+
}
91+
6692
static PyObject *py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder *self, PyObject * args)
6793
{
6894
char *name = nullptr;
@@ -130,6 +156,7 @@ static PyMethodDef ue_PyFMenuBuilder_methods[] = {
130156
{ "add_menu_entry", (PyCFunction)py_ue_fmenu_builder_add_menu_entry, METH_VARARGS, "" },
131157
{ "add_menu_separator", (PyCFunction)py_ue_fmenu_builder_add_menu_separator, METH_VARARGS, "" },
132158
{ "add_search_widget", (PyCFunction)py_ue_fmenu_builder_add_search_widget, METH_VARARGS, "" },
159+
{ "add_sub_menu", (PyCFunction)py_ue_fmenu_builder_add_sub_menu, METH_VARARGS, "" },
133160
#if WITH_EDITOR
134161
{ "add_asset_actions", (PyCFunction)py_ue_fmenu_builder_add_asset_actions, METH_VARARGS, "" },
135162
#endif

0 commit comments

Comments
 (0)