How to receive a return value from python correctly? #318
-
|
OS: Windows11 24H2 I do two tests about receiving return value from python. /// Test 1: Python Class
qDebug() << "Test 1: Python Class";
{
QVariant pyobj = context.evalScript("TestPyObj()", Py_eval_input); /// The TestPyObj Class is defined by Python Code
qDebug() << pyobj;
if(pyobj.isValid()) {
PythonQtObjectPtr pyobj_ptr(pyobj);
qDebug() << "PyObj RefCount:" << pyobj_ptr->ob_refcnt; ///2 pyobj & pyobj_ptr
pyobj.clear();
qDebug() << "PyObj RefCount:" << pyobj_ptr->ob_refcnt; ///1 pyobj
pyobj_ptr.call("print");
}
/// destroyed
}But if I change the TestPyObj Class to TestCppObj which is defined in Cpp code and registered to Python by PythonQt, the return value will be destroyed directly before the cpp code receive it. The Test Code is below ///... init code
PythonQt::self()->registerCPPClass("TestCppObj", "", "TestUtils", PythonQtCreateObject<TestCppObjWrapper>, PythonQtSetInstanceWrapperOnShell<TestCppObjShell>);
///... other code
qDebug() << "Test 2: C++ Class";
{
QVariant pyobj = context.evalScript("pqt.TestUtils.TestCppObj()", Py_eval_input);
qDebug() << pyobj;
if(pyobj.isValid()) {
}
}The printed messages are: The class decleration is below: /// Class
class TestCppObj {
public:
TestCppObj() {
std::cout << "TestCPPObj created " << size_t(this) << std::endl;
}
virtual ~TestCppObj() {
std::cout << "TestCPPObj destroyed " << size_t(this) << std::endl;
}
virtual void print() {
std::cout << "Hello from C++" << std::endl;
}
};
/// Shell Class
class TestCppObjShell : public TestCppObj {
public:
TestCppObjShell(): _wrapper(nullptr) {
std::cout << "TestCppObjShell created " << size_t(this) << std::endl;
}
~TestCppObjShell() {
std::cout << "TestCppObjShell destroyed " << size_t(this) << std::endl;
PythonQtPrivate* priv = PythonQt::priv();
if (priv) { priv->shellClassDeleted(this); }
}
void print() override {
std::cout << "Shell Print" << std::endl;
if (_wrapper) {
PYTHONQT_GIL_SCOPE;
if(((PyObject*)_wrapper)->ob_refcnt > 0) {
static PyObject* name = PyString_FromString("print");
PyObject* obj = PyBaseObject_Type.tp_getattro((PyObject*)_wrapper, name);
if (obj) {
static const char* argumentList[] ={""};
static const PythonQtMethodInfo* methodInfo = PythonQtMethodInfo::getCachedMethodInfoFromArgumentList(1, argumentList);
void* args[1] = {nullptr};
PyObject* result = PythonQtSignalTarget::call(obj, methodInfo, args, true);
if (result) { Py_DECREF(result); }
Py_DECREF(obj);
return;
} else {
PyErr_Clear();
}
}
}
TestCppObj::print();
}
PythonQtInstanceWrapper* _wrapper { nullptr };
};
/// Wrapper Class
class TestCppObjWrapper : public QObject {
Q_OBJECT
public slots:
TestCppObj* new_TestCppObj() {
return new TestCppObjShell ();
}
void delete_TestCppObj(TestCppObj* obj) {
delete obj;
}
void print(TestCppObj* obj) {
obj->print();
}
};Thanks for any help. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
This is somewhat expected. Objects in Python are reference-counted. If the reference count goes down to zero, the object is deleted. In the case of Python objects, the reference count is increased by the PythonQtObjectPtr stored in the QVariant. For the C++ object, there is no extra reference count. After the C++ object is returned from the evalScript method, the scope of the Python code is deleted, the reference count of the wrapper goes back zero, and the wrapper and its wrapped object are deleted. There are two things you can do:
I guess the first option is easier though. |
Beta Was this translation helpful? Give feedback.
This is somewhat expected. Objects in Python are reference-counted. If the reference count goes down to zero, the object is deleted.
In the case of Python objects, the reference count is increased by the PythonQtObjectPtr stored in the QVariant.
For the C++ object, there is no extra reference count. After the C++ object is returned from the evalScript method, the scope of the Python code is deleted, the reference count of the wrapper goes back zero, and the wrapper and its wrapped object are deleted.
There are two things you can do: