static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) { const char *attributeName; PythonQtInstanceWrapper *wrapper = (PythonQtInstanceWrapper *)obj; if ((attributeName = PyString_AsString(name)) == NULL) { return NULL; } if (qstrcmp(attributeName, "__dict__")==0) { PyObject* dict = PyBaseObject_Type.tp_getattro(obj, name); dict = PyDict_Copy(dict); if (wrapper->_obj) { // we need to replace the properties with their real values... QStringList l = wrapper->classInfo()->propertyList(); Q_FOREACH (QString name, l) { PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); if (o) { PyDict_SetItemString(dict, name.toLatin1().data(), o); Py_DECREF(o); } else { std::cerr << "PythonQtInstanceWrapper: something is wrong, could not get attribute " << name.toLatin1().data(); } }
static PyObject* PythonQtInstanceWrapper_binaryfunc(PyObject* self, PyObject* other, const QByteArray& opName, const QByteArray& fallbackOpName = QByteArray()) { // since we disabled type checking, we can receive any object as self, but we currently only support // different objects on the right. Otherwise we would need to generate __radd__ etc. methods. if (!PyObject_TypeCheck(self, &PythonQtInstanceWrapper_Type)) { QString error = "Unsupported operation " + opName + "(" + self->ob_type->tp_name + ", " + other->ob_type->tp_name + ")"; PyErr_SetString(PyExc_ArithmeticError, error.toLatin1().data()); return NULL; } PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)self; PyObject* result = NULL; PythonQtMemberInfo opSlot = wrapper->classInfo()->member(opName); if (opSlot._type == PythonQtMemberInfo::Slot) { // TODO get rid of tuple PyObject* args = PyTuple_New(1); Py_INCREF(other); PyTuple_SET_ITEM(args, 0, other); result = PythonQtSlotFunction_CallImpl(wrapper->classInfo(), wrapper->_obj, opSlot._slot, args, NULL, wrapper->_wrappedPtr); Py_DECREF(args); if (!result && !fallbackOpName.isEmpty()) { // try fallback if we did not get a result result = PythonQtInstanceWrapper_binaryfunc(self, other, fallbackOpName); } } return result; }
static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) { const char *attributeName; PythonQtInstanceWrapper *wrapper = (PythonQtInstanceWrapper *)obj; if ((attributeName = PyString_AsString(name)) == NULL) { return NULL; } if (qstrcmp(attributeName, "__dict__")==0) { PyObject* dict = PyBaseObject_Type.tp_getattro(obj, name); dict = PyDict_Copy(dict); if (wrapper->_obj) { // only the properties are missing, the rest is already available from // PythonQtClassWrapper... QStringList l = wrapper->classInfo()->propertyList(); foreach (QString name, l) { PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); if (o) { PyDict_SetItemString(dict, name.toLatin1().data(), o); Py_DECREF(o); } else { std::cerr << "PythonQtInstanceWrapper: something is wrong, could not get attribute " << name.toLatin1().data(); } }
bool PythonQtDebugAPI::passOwnershipToPython( PyObject* object ) { if (PyObject_TypeCheck(object, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)object; wrapper->passOwnershipToPython(); return true; } return false; }
PyObject *PythonQtClassWrapper_inherits(PythonQtClassWrapper *type, PyObject *args) { Q_UNUSED(type); PythonQtInstanceWrapper* wrapper = NULL; char *name = NULL; if (!PyArg_ParseTuple(args, "O!s:PythonQtClassWrapper.inherits",&PythonQtInstanceWrapper_Type, &wrapper, &name)) { return NULL; } return PythonQtConv::GetPyBool(wrapper->classInfo()->inherits(name)); }
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 { return PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, args, kw, self->_wrappedPtr); } } 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); PyObject* result = PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, newargs, kw, self->_wrappedPtr); 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; }
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); }