##// END OF EJS Templates
- added support for deriving CPP classes in Python and to override all public and protected virtual functions from PythonQt...
- added support for deriving CPP classes in Python and to override all public and protected virtual functions from PythonQt - changed signal receiver code so that it can be reused for virtual method dispatch - moved decorators completely into PythonQtClassInfo - changed python to qt conversion so that it can work with an already allocated object (needed for return values of virtual functions - added support for multiple inheritance and upcasting (e.g. a QWidget is automatically casted to a QPaintDevice when passed to QPainter.begin()) - removed default constructors of std decorators because they are now codegenerated - regenerated bindings - improved the code generator to suppot git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@60 ea8d5007-eb21-0410-b261-ccb3ea6e24a9

File last commit:

r24:f543ed746063
r24:f543ed746063
Show More
PythonQtSlot.cpp
496 lines | 16.2 KiB | text/x-c | CppLexer
/*
*
* Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: MeVis Research GmbH, Universitaetsallee 29,
* 28359 Bremen, Germany or:
*
* http://www.mevis.de
*
*/
//----------------------------------------------------------------------------------
/*!
// \file PythonQtSlot.cpp
// \author Florian Link
// \author Last changed by $Author: florian $
// \date 2006-05
*/
//----------------------------------------------------------------------------------
#include "PythonQt.h"
#include "PythonQtSlot.h"
#include "PythonQtInstanceWrapper.h"
#include "PythonQtClassInfo.h"
#include "PythonQtMisc.h"
#include "PythonQtConversion.h"
#include <iostream>
#define PYTHONQT_MAX_ARGS 32
bool PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer)
{
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();
bool returnValueIsEnum = false;
const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0);
// set return argument to NULL
argList[0] = NULL;
if (returnValueParam.typeId != QMetaType::Void) {
// extra handling of enum return value
if (!returnValueParam.isPointer && returnValueParam.typeId == PythonQtMethodInfo::Unknown) {
returnValueIsEnum = PythonQt::priv()->isEnumType(objectToCall->metaObject(), returnValueParam.name);
if (returnValueIsEnum) {
// create enum return value
PythonQtValueStorage_ADD_VALUE(PythonQtConv::global_valueStorage, long, 0, argList[0]);
}
}
if (argList[0]==NULL) {
// create empty default value for the return value
if (!directReturnValuePointer) {
// create empty default value for the return value
argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam);
} else {
// we can use our pointer directly!
argList[0] = directReturnValuePointer;
}
}
}
const QMetaObject* meta = objectToCall?objectToCall->metaObject():NULL;
bool ok = true;
bool skipFirst = false;
if (info->isInstanceDecorator()) {
skipFirst = true;
// for decorators on CPP objects, we take the cpp ptr, for QObjects we take the QObject pointer
void* arg1 = firstArgument;
if (!arg1) {
arg1 = objectToCall;
}
if (arg1) {
// upcast to correct parent class
arg1 = ((char*)arg1)+info->upcastingOffset();
}
argList[1] = &arg1;
if (ok) {
for (int i = 2; i<argc && ok; i++) {
const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
//std::cout << param.name.data() << " " << param.typeId << (param.isPointer?"*":"") << (param.isConst?" const":"") << std::endl;
argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-2), strict, meta);
if (argList[i]==NULL) {
ok = false;
break;
}
}
}
} else {
for (int i = 1; i<argc && ok; i++) {
const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
//std::cout << param.name.data() << " " << param.typeId << (param.isPointer?"*":"") << (param.isConst?" const":"") << std::endl;
argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-1), strict, meta);
if (argList[i]==NULL) {
ok = false;
break;
}
}
}
if (ok) {
(info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList);
if (argList[0] || returnValueParam.typeId == QMetaType::Void) {
if (directReturnValuePointer) {
result = NULL;
} else {
if (!returnValueIsEnum) {
result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]);
} else {
result = PyInt_FromLong(*((unsigned int*)argList[0]));
}
}
} else {
QString e = QString("Called ") + info->fullSignature() + ", return type is ignored because it is unknown to PythonQt.";
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
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;
// NOTE: it is important to only return here, otherwise the stack will not be popped!!!
return result || (directReturnValuePointer && *directReturnValuePointer);
}
//-----------------------------------------------------------------------------------
static PythonQtSlotFunctionObject *pythonqtslot_free_list = NULL;
PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw)
{
PythonQtSlotFunctionObject* f = (PythonQtSlotFunctionObject*)func;
PythonQtSlotInfo* info = f->m_ml;
if (PyObject_TypeCheck(f->m_self, &PythonQtInstanceWrapper_Type)) {
PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) f->m_self;
return PythonQtSlotFunction_CallImpl(self->_obj, info, args, kw, self->_wrappedPtr);
} else if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
if (info->isClassDecorator()) {
return PythonQtSlotFunction_CallImpl(NULL, info, args, kw);
} else {
// otherwise, it is an unbound call and we have an instanceDecorator or normal slot...
PythonQtClassWrapper* type = (PythonQtClassWrapper*) f->m_self;
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;
// strip the first argument...
PyObject* newargs = PyTuple_GetSlice(args, 1, argc);
PyObject* result = PythonQtSlotFunction_CallImpl(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;
}
PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * /*kw*/, void* firstArg, void** directReturnValuePointer)
{
int argc = PyTuple_Size(args);
#ifdef PYTHONQT_DEBUG
std::cout << "called " << info->metaMethod()->typeName() << " " << info->metaMethod()->signature() << std::endl;
#endif
PyObject* r = NULL;
bool ok = false;
if (directReturnValuePointer) {
*directReturnValuePointer = NULL;
}
if (info->nextInfo()) {
// overloaded slot call, try on all slots with strict conversion first
bool strict = true;
PythonQtSlotInfo* i = info;
while (i) {
bool skipFirst = i->isInstanceDecorator();
if (i->parameterCount()-1-(skipFirst?1:0) == argc) {
PyErr_Clear();
ok = PythonQtCallSlot(objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer);
if (PyErr_Occurred() || ok) break;
}
i = i->nextInfo();
if (!i) {
if (strict) {
// one more run without being strict
strict = false;
i = info;
}
}
}
if (!ok && !PyErr_Occurred()) {
QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n");
PythonQtSlotInfo* i = info;
while (i) {
e += QString(i->fullSignature()) + "\n";
i = i->nextInfo();
}
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
}
} else {
// simple (non-overloaded) slot call
bool skipFirst = info->isInstanceDecorator();
if (info->parameterCount()-1-(skipFirst?1:0) == argc) {
PyErr_Clear();
ok = PythonQtCallSlot(objectToCall, args, false, info, firstArg, &r, directReturnValuePointer);
if (!ok && !PyErr_Occurred()) {
QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
}
} else {
QString e = QString("Called ") + info->fullSignature() + " with wrong number of arguments: " + PythonQtConv::PyObjGetString(args);
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
}
}
return r;
}
PyObject *
PythonQtSlotFunction_New(PythonQtSlotInfo *ml, PyObject *self, PyObject *module)
{
PythonQtSlotFunctionObject *op;
op = pythonqtslot_free_list;
if (op != NULL) {
pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(op->m_self);
PyObject_INIT(op, &PythonQtSlotFunction_Type);
}
else {
op = PyObject_GC_New(PythonQtSlotFunctionObject, &PythonQtSlotFunction_Type);
if (op == NULL)
return NULL;
}
op->m_ml = ml;
Py_XINCREF(self);
op->m_self = self;
Py_XINCREF(module);
op->m_module = module;
PyObject_GC_Track(op);
return (PyObject *)op;
}
PythonQtSlotInfo*
PythonQtSlotFunction_GetSlotInfo(PyObject *op)
{
if (!PythonQtSlotFunction_Check(op)) {
PyErr_BadInternalCall();
return NULL;
}
return ((PythonQtSlotFunctionObject *)op) -> m_ml;
}
PyObject *
PythonQtSlotFunction_GetSelf(PyObject *op)
{
if (!PythonQtSlotFunction_Check(op)) {
PyErr_BadInternalCall();
return NULL;
}
return ((PythonQtSlotFunctionObject *)op) -> m_self;
}
/* Methods (the standard built-in methods, that is) */
static void
meth_dealloc(PythonQtSlotFunctionObject *m)
{
PyObject_GC_UnTrack(m);
Py_XDECREF(m->m_self);
Py_XDECREF(m->m_module);
m->m_self = (PyObject *)pythonqtslot_free_list;
pythonqtslot_free_list = m;
}
static PyObject *
meth_get__doc__(PythonQtSlotFunctionObject * /*m*/, void * /*closure*/)
{
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
meth_get__name__(PythonQtSlotFunctionObject *m, void * /*closure*/)
{
return PyString_FromString(m->m_ml->metaMethod()->signature());
}
static int
meth_traverse(PythonQtSlotFunctionObject *m, visitproc visit, void *arg)
{
int err;
if (m->m_self != NULL) {
err = visit(m->m_self, arg);
if (err)
return err;
}
if (m->m_module != NULL) {
err = visit(m->m_module, arg);
if (err)
return err;
}
return 0;
}
static PyObject *
meth_get__self__(PythonQtSlotFunctionObject *m, void * /*closure*/)
{
PyObject *self;
if (PyEval_GetRestricted()) {
PyErr_SetString(PyExc_RuntimeError,
"method.__self__ not accessible in restricted mode");
return NULL;
}
self = m->m_self;
if (self == NULL)
self = Py_None;
Py_INCREF(self);
return self;
}
static PyGetSetDef meth_getsets [] = {
{"__doc__", (getter)meth_get__doc__, NULL, NULL},
{"__name__", (getter)meth_get__name__, NULL, NULL},
{"__self__", (getter)meth_get__self__, NULL, NULL},
{NULL, NULL, NULL,NULL},
};
#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 6
#define PY_WRITE_RESTRICTED WRITE_RESTRICTED
#endif
#define OFF(x) offsetof(PythonQtSlotFunctionObject, x)
static PyMemberDef meth_members[] = {
{"__module__", T_OBJECT, OFF(m_module), PY_WRITE_RESTRICTED},
{NULL}
};
static PyObject *
meth_repr(PythonQtSlotFunctionObject *f)
{
if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
PythonQtClassWrapper* self = (PythonQtClassWrapper*) f->m_self;
return PyString_FromFormat("<unbound qt slot %s of %s type>",
f->m_ml->slotName().data(),
self->classInfo()->className());
} else {
return PyString_FromFormat("<qt slot %s of %s instance at %p>",
f->m_ml->slotName().data(),
f->m_self->ob_type->tp_name,
f->m_self);
}
}
static int
meth_compare(PythonQtSlotFunctionObject *a, PythonQtSlotFunctionObject *b)
{
if (a->m_self != b->m_self)
return (a->m_self < b->m_self) ? -1 : 1;
if (a->m_ml == b->m_ml)
return 0;
if (strcmp(a->m_ml->metaMethod()->signature(), b->m_ml->metaMethod()->signature()) < 0)
return -1;
else
return 1;
}
static long
meth_hash(PythonQtSlotFunctionObject *a)
{
long x,y;
if (a->m_self == NULL)
x = 0;
else {
x = PyObject_Hash(a->m_self);
if (x == -1)
return -1;
}
y = _Py_HashPointer((void*)(a->m_ml));
if (y == -1)
return -1;
x ^= y;
if (x == -1)
x = -2;
return x;
}
PyTypeObject PythonQtSlotFunction_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"builtin_qt_slot",
sizeof(PythonQtSlotFunctionObject),
0,
(destructor)meth_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
(cmpfunc)meth_compare, /* tp_compare */
(reprfunc)meth_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)meth_hash, /* tp_hash */
PythonQtSlotFunction_Call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)meth_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
meth_members, /* tp_members */
meth_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
};
/* Clear out the free list */
void
PythonQtSlotFunction_Fini(void)
{
while (pythonqtslot_free_list) {
PythonQtSlotFunctionObject *v = pythonqtslot_free_list;
pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(v->m_self);
PyObject_GC_Del(v);
}
}