bool PythonQtDebugAPI::passOwnershipToPython( PyObject* object ) { if (PyObject_TypeCheck(object, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)object; wrapper->passOwnershipToPython(); return true; } return false; }
bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer, PythonQtPassThisOwnershipType* passThisOwnershipToCPP) { static unsigned int recursiveEntry = 0; if (directReturnValuePointer) { *directReturnValuePointer = NULL; } // store the current storage position, so that we can get back to this state after a slot is called // (do this locally, so that we have all positions on the stack PythonQtValueStoragePosition globalValueStoragePos; PythonQtValueStoragePosition globalPtrStoragePos; PythonQtValueStoragePosition globalVariantStoragePos; PythonQtConv::global_valueStorage.getPos(globalValueStoragePos); PythonQtConv::global_ptrStorage.getPos(globalPtrStoragePos); PythonQtConv::global_variantStorage.getPos(globalVariantStoragePos); recursiveEntry++; // the arguments that are passed to qt_metacall void* argList[PYTHONQT_MAX_ARGS]; PyObject* result = NULL; int argc = info->parameterCount(); const QList<PythonQtSlotInfo::ParameterInfo>& params = info->parameters(); const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0); // set return argument to NULL argList[0] = NULL; bool ok = true; bool skipFirst = false; PythonQtPassThisOwnershipType passThisOwnership = IgnoreOwnership; int instanceDecoOffset = 0; // it is important to keep arg1 on this scope, because it is stored in argList[1] and // would go away if it is moved into the if scope void* arg1 = NULL; if (info->isInstanceDecorator()) { skipFirst = true; instanceDecoOffset = 1; // for decorators on CPP objects, we take the cpp ptr, for QObjects we take the QObject pointer arg1 = firstArgument; if (!arg1) { arg1 = objectToCall; } if (arg1) { // upcast to correct parent class arg1 = ((char*)arg1)+info->upcastingOffset(); } argList[1] = &arg1; } for (int i = 1 + instanceDecoOffset; i<argc && ok; i++) { const PythonQtSlotInfo::ParameterInfo& param = params.at(i); argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i - 1 - instanceDecoOffset), strict, classInfo); if (argList[i]==NULL) { ok = false; break; } if (param.newOwnerOfThis) { // (typical use case: setParent(someObject) -> pass ownership of this to someObject) if (argList[i] && (*(void**)argList[i])==NULL) { // if the object to which the ownership should be passed is NULL, // we need to pass the ownership to Python passThisOwnership = PassOwnershipToPython; } else { // if the object is given, pass the ownership to CPP passThisOwnership = PassOwnershipToCPP; } } } if (ok) { if (passThisOwnershipToCPP) { *passThisOwnershipToCPP = passThisOwnership; } // parameters are ok, now create the qt return value which is assigned to by metacall if (returnValueParam.typeId != QMetaType::Void) { // create empty default value for the return value if (!directReturnValuePointer) { // create empty default value for the return value argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam); if (argList[0]==NULL) { // return value could not be created, maybe we have a registered class with a default constructor, so that we can construct the pythonqt wrapper object and // pass its internal pointer PythonQtClassInfo* info = PythonQt::priv()->getClassInfo(returnValueParam.name); if (info && info->pythonQtClassWrapper()) { PyObject* emptyTuple = PyTuple_New(0); // 1) default construct an empty object as python object (owned by PythonQt), by calling the meta class with empty arguments result = PyObject_Call((PyObject*)info->pythonQtClassWrapper(), emptyTuple, NULL); if (result) { argList[0] = ((PythonQtInstanceWrapper*)result)->_wrappedPtr; } Py_DECREF(emptyTuple); } } } else { // we can use our pointer directly! argList[0] = directReturnValuePointer; } } PythonQt::ProfilingCB* profilingCB = PythonQt::priv()->profilingCB(); if (profilingCB) { const char* className = NULL; if (info->decorator()) { className = info->decorator()->metaObject()->className(); } else { className = objectToCall->metaObject()->className(); } profilingCB(PythonQt::Enter, className, info->signature(), args); } // invoke the slot via metacall bool hadException = false; QObject* obj = info->decorator()?info->decorator():objectToCall; if (!obj) { hadException = true; PyErr_SetString(PyExc_RuntimeError, "Trying to call a slot on a deleted QObject!"); } else { try { obj->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList); } catch (std::out_of_range & e) { hadException = true; QByteArray what("std::out_of_range: "); what += e.what(); PyErr_SetString(PyExc_IndexError, what.constData()); } catch (std::bad_alloc & e) { hadException = true; QByteArray what("std::bad_alloc: "); what += e.what(); PyErr_SetString(PyExc_MemoryError, what.constData()); } catch (std::runtime_error & e) { hadException = true; QByteArray what("std::runtime_error: "); what += e.what(); PyErr_SetString(PyExc_RuntimeError, what.constData()); } catch (std::logic_error & e) { hadException = true; QByteArray what("std::logic_error: "); what += e.what(); PyErr_SetString(PyExc_RuntimeError, what.constData()); } catch (std::exception& e) { hadException = true; QByteArray what("std::exception: "); what += e.what(); PyErr_SetString(PyExc_RuntimeError, what.constData()); } } if (profilingCB) { profilingCB(PythonQt::Leave, NULL, NULL, NULL); } // handle the return value (which in most cases still needs to be converted to a Python object) if (!hadException) { if (argList[0] || returnValueParam.typeId == QMetaType::Void) { if (directReturnValuePointer) { result = NULL; } else { // the resulting object maybe present already, because we created it above at 1)... if (!result) { result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]); } } } else { QString e = QString("Called ") + info->fullSignature() + ", return type '" + returnValueParam.name + "' is ignored because it is unknown to PythonQt. Probably you should register it using qRegisterMetaType() or add a default constructor decorator to the class."; PyErr_SetString(PyExc_ValueError, e.toLatin1().data()); result = NULL; } } else { result = NULL; } } recursiveEntry--; // reset the parameter storage position to the stored pos to "pop" the parameter stack PythonQtConv::global_valueStorage.setPos(globalValueStoragePos); PythonQtConv::global_ptrStorage.setPos(globalPtrStoragePos); PythonQtConv::global_variantStorage.setPos(globalVariantStoragePos); *pythonReturnValue = result; if (result && returnValueParam.passOwnershipToPython) { // if the ownership should be passed to PythonQt, it has to be a PythonQtInstanceWrapper, // cast it and pass the ownership if (PyObject_TypeCheck(result, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)result; wrapper->passOwnershipToPython(); } // NOTE: a return value can not pass the ownership to CPP, it would not make sense... } // NOTE: it is important to only return here, otherwise the stack will not be popped!!! return result || (directReturnValuePointer && *directReturnValuePointer); }
PyObject *PythonQtMemberFunction_Call(PythonQtSlotInfo* info, PyObject* m_self, PyObject *args, PyObject *kw) { if (PyObject_TypeCheck(m_self, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) m_self; if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) { QString error = QString("Trying to call '") + info->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } else { PythonQtPassThisOwnershipType ownership; PyObject* result = PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, args, kw, self->_wrappedPtr, NULL, &ownership); if (ownership == PassOwnershipToCPP) { self->passOwnershipToCPP(); } else if (ownership == PassOwnershipToPython) { self->passOwnershipToPython(); } return result; } } else if (m_self->ob_type == &PythonQtClassWrapper_Type) { PythonQtClassWrapper* type = (PythonQtClassWrapper*) m_self; if (info->isClassDecorator()) { return PythonQtSlotFunction_CallImpl(type->classInfo(), NULL, info, args, kw); } else { // otherwise, it is an unbound call and we have an instanceDecorator or normal slot... Py_ssize_t argc = PyTuple_Size(args); if (argc>0) { PyObject* firstArg = PyTuple_GET_ITEM(args, 0); if (PyObject_TypeCheck(firstArg, (PyTypeObject*)&PythonQtInstanceWrapper_Type) && ((PythonQtInstanceWrapper*)firstArg)->classInfo()->inherits(type->classInfo())) { PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)firstArg; if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) { QString error = QString("Trying to call '") + info->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } // strip the first argument... PyObject* newargs = PyTuple_GetSlice(args, 1, argc); PythonQtPassThisOwnershipType ownership; PyObject* result = PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, newargs, kw, self->_wrappedPtr, NULL, &ownership); if (ownership == PassOwnershipToCPP) { self->passOwnershipToCPP(); } else if (ownership == PassOwnershipToPython) { self->passOwnershipToPython(); } Py_DECREF(newargs); return result; } else { // first arg is not of correct type! QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument, got " + firstArg->ob_type->tp_name; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } } else { // wrong number of args QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument."; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } } } return NULL; }