Repository navigation
How to receive a return value from python correctly? #259
Copy link
Copy link
Closed
Description
OS: Windows11 24H2
Python Version: 3.13
Qt Version: 6.8
PythonQt Version: 3.6
Compiler: msvc
I do two tests about receiving return value from python.
The first one is running well:
/// 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:
Test 2: C++ Class
TestCPPObj created 2258935037504
TestCppObjShell created 2258935037504
TestCppObjShell destroyed 2258935037504
TestCPPObj destroyed 2258935037504
QVariant(void*, 0x20df3053e40) /// 0x20df3053e40 ==2258935037504
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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
usiems commentedon Apr 30, 2025
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.