From dc6e93cd2a87a0d77e03c3a9d423f2a32c2f776d Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 18 Feb 2019 21:55:08 -0700 Subject: [PATCH] added support for uobj.unbind_event --- .../Private/PythonDelegate.cpp | 9 ++++- .../UnrealEnginePython/Private/UEPyModule.cpp | 40 +++++++++++++++++++ .../UnrealEnginePython/Private/UEPyModule.h | 1 + .../Private/UObject/UEPyObject.cpp | 19 +++++++++ .../Private/UObject/UEPyObject.h | 3 +- .../Public/PythonDelegate.h | 1 + .../Public/PythonHouseKeeper.h | 11 +++++ 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index 0ed596d4b..e7e4c6699 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -1,6 +1,7 @@ #include "PythonDelegate.h" #include "UEPyModule.h" +#include "UEPyCallable.h" UPythonDelegate::UPythonDelegate() { @@ -101,6 +102,12 @@ void UPythonDelegate::PyInputAxisHandler(float value) Py_DECREF(ret); } +bool UPythonDelegate::UsesPyCallable(PyObject *other) +{ + ue_PyCallable *other_callable = (ue_PyCallable*)other; + ue_PyCallable *this_callable = (ue_PyCallable*)py_callable; + return other_callable->u_function == this_callable->u_function && other_callable->u_target == this_callable->u_target; +} UPythonDelegate::~UPythonDelegate() { @@ -110,4 +117,4 @@ UPythonDelegate::~UPythonDelegate() #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("PythonDelegate %p callable XDECREF'ed"), this); #endif -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 704593c8d..993828a86 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -607,6 +607,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "set_name", (PyCFunction)py_ue_set_name, METH_VARARGS, "" }, { "bind_event", (PyCFunction)py_ue_bind_event, METH_VARARGS, "" }, + { "unbind_event", (PyCFunction)py_ue_unbind_event, METH_VARARGS, "" }, { "delegate_bind_ufunction", (PyCFunction)py_ue_delegate_bind_ufunction, METH_VARARGS, "" }, { "get_py_proxy", (PyCFunction)py_ue_get_py_proxy, METH_VARARGS, "" }, @@ -3044,6 +3045,45 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * Py_RETURN_NONE; } +PyObject *ue_unbind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_callable, bool fail_on_wrong_property) +{ + UProperty *u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); + if (!u_property) + { + if (fail_on_wrong_property) + return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); + Py_RETURN_NONE; + } + + if (auto casted_prop = Cast(u_property)) + { + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->FindDelegate(u_obj->ue_object, py_callable); + if (py_delegate != nullptr) + { + FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); + multiscript_delegate.Remove(py_delegate, FName("PyFakeCallable")); + + // re-assign multicast delegate + casted_prop->SetPropertyValue_InContainer(u_obj->ue_object, multiscript_delegate); + } + } + else if (auto casted_prop_delegate = Cast(u_property)) + { + FScriptDelegate script_delegate = casted_prop_delegate->GetPropertyValue_InContainer(u_obj->ue_object); + script_delegate.Unbind(); + + // re-assign multicast delegate + casted_prop_delegate->SetPropertyValue_InContainer(u_obj->ue_object, script_delegate); + } + else + { + if (fail_on_wrong_property) + return PyErr_Format(PyExc_Exception, "property %s is not an event", TCHAR_TO_UTF8(*event_name)); + } + + Py_RETURN_NONE; +} + PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_callable, bool fail_on_wrong_property) { diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 08e599d52..9d865cb16 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -35,6 +35,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *, PyObject *); void ue_autobind_events_for_pyclass(ue_PyUObject *, PyObject *); PyObject *ue_bind_pyevent(ue_PyUObject *, FString, PyObject *, bool); +PyObject *ue_unbind_pyevent(ue_PyUObject *, FString, PyObject *, bool); PyObject *py_ue_ufunction_call(UFunction *, UObject *, PyObject *, int, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index e7fcb7de5..d11031775 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1517,6 +1517,25 @@ PyObject *py_ue_bind_event(ue_PyUObject * self, PyObject * args) return ue_bind_pyevent(self, FString(event_name), py_callable, true); } +PyObject *py_ue_unbind_event(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + char *event_name; + PyObject *py_callable; + if (!PyArg_ParseTuple(args, "sO:bind_event", &event_name, &py_callable)) + { + return NULL; + } + + if (!PyCallable_Check(py_callable)) + { + return PyErr_Format(PyExc_Exception, "object is not callable"); + } + + return ue_unbind_pyevent(self, FString(event_name), py_callable, true); +} + PyObject *py_ue_delegate_bind_ufunction(ue_PyUObject * self, PyObject * args) { ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index 4f66b78f7..6fb4678fe 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -51,6 +51,7 @@ PyObject *py_ue_enum_user_defined_names(ue_PyUObject *, PyObject *); PyObject *py_ue_bind_event(ue_PyUObject *, PyObject *); +PyObject *py_ue_unbind_event(ue_PyUObject *, PyObject *); PyObject *py_ue_add_function(ue_PyUObject *, PyObject *); PyObject *py_ue_add_property(ue_PyUObject *, PyObject *); @@ -111,4 +112,4 @@ 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 +PyObject *py_ue_from_bytes(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Public/PythonDelegate.h b/Source/UnrealEnginePython/Public/PythonDelegate.h index 01d0f70b7..be46aa511 100644 --- a/Source/UnrealEnginePython/Public/PythonDelegate.h +++ b/Source/UnrealEnginePython/Public/PythonDelegate.h @@ -13,6 +13,7 @@ class UPythonDelegate : public UObject ~UPythonDelegate(); virtual void ProcessEvent(UFunction *function, void *Parms) override; void SetPyCallable(PyObject *callable); + bool UsesPyCallable(PyObject *callable); void SetSignature(UFunction *original_signature); void PyInputHandler(); diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index ed36ecc63..dcde4d3a7 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -238,6 +238,17 @@ class FUnrealEnginePythonHouseKeeper : public FGCObject return Garbaged; } + UPythonDelegate *FindDelegate(UObject *Owner, PyObject *PyCallable) + { + for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; + if (Tracker.Owner.Get() == Owner && Tracker.Delegate->UsesPyCallable(PyCallable)) + return Tracker.Delegate; + } + return nullptr; + } + UPythonDelegate *NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature) { UPythonDelegate *Delegate = NewObject();