예제 #1
0
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();
        }
      }
예제 #4
0
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));
}
예제 #6
0
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;
}
예제 #7
0
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);
}