##// END OF EJS Templates
added support for return by value of classes that are NOT registered with QMetaType but which have a default constructor decorator...
florianlink -
r46:1db88b846b4a
parent child
Show More
@@ -1,498 +1,515
1 1 /*
2 2 *
3 3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQtSlot.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQt.h"
43 43 #include "PythonQtSlot.h"
44 44 #include "PythonQtInstanceWrapper.h"
45 45 #include "PythonQtClassInfo.h"
46 46 #include "PythonQtMisc.h"
47 47 #include "PythonQtConversion.h"
48 48 #include <iostream>
49 49
50 50 #define PYTHONQT_MAX_ARGS 32
51 51
52 52
53 53 bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer)
54 54 {
55 55 static unsigned int recursiveEntry = 0;
56 56
57 57 if (directReturnValuePointer) {
58 58 *directReturnValuePointer = NULL;
59 59 }
60 60 // store the current storage position, so that we can get back to this state after a slot is called
61 61 // (do this locally, so that we have all positions on the stack
62 62 PythonQtValueStoragePosition globalValueStoragePos;
63 63 PythonQtValueStoragePosition globalPtrStoragePos;
64 64 PythonQtValueStoragePosition globalVariantStoragePos;
65 65 PythonQtConv::global_valueStorage.getPos(globalValueStoragePos);
66 66 PythonQtConv::global_ptrStorage.getPos(globalPtrStoragePos);
67 67 PythonQtConv::global_variantStorage.getPos(globalVariantStoragePos);
68 68
69 69 recursiveEntry++;
70 70
71 71 // the arguments that are passed to qt_metacall
72 72 void* argList[PYTHONQT_MAX_ARGS];
73 73 PyObject* result = NULL;
74 74 int argc = info->parameterCount();
75 75 const QList<PythonQtSlotInfo::ParameterInfo>& params = info->parameters();
76 76
77 77 bool returnValueIsEnum = false;
78 78 const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0);
79 79 // set return argument to NULL
80 80 argList[0] = NULL;
81 81
82 82 bool ok = true;
83 83 bool skipFirst = false;
84 84 if (info->isInstanceDecorator()) {
85 85 skipFirst = true;
86 86
87 87 // for decorators on CPP objects, we take the cpp ptr, for QObjects we take the QObject pointer
88 88 void* arg1 = firstArgument;
89 89 if (!arg1) {
90 90 arg1 = objectToCall;
91 91 }
92 92 if (arg1) {
93 93 // upcast to correct parent class
94 94 arg1 = ((char*)arg1)+info->upcastingOffset();
95 95 }
96 96
97 97 argList[1] = &arg1;
98 98 if (ok) {
99 99 for (int i = 2; i<argc && ok; i++) {
100 100 const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
101 101 //std::cout << param.name.data() << " " << param.typeId << (param.isPointer?"*":"") << (param.isConst?" const":"") << std::endl;
102 102 argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-2), strict, classInfo);
103 103 if (argList[i]==NULL) {
104 104 ok = false;
105 105 break;
106 106 }
107 107 }
108 108 }
109 109 } else {
110 110 for (int i = 1; i<argc && ok; i++) {
111 111 const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
112 112 //std::cout << param.name.data() << " " << param.typeId << (param.isPointer?"*":"") << (param.isConst?" const":"") << std::endl;
113 113 argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-1), strict, classInfo);
114 114 if (argList[i]==NULL) {
115 115 ok = false;
116 116 break;
117 117 }
118 118 }
119 119 }
120 120
121 121 if (ok) {
122 122 // parameters are ok, now create the qt return value which is assigned to by metacall
123 123 if (returnValueParam.typeId != QMetaType::Void) {
124 124 // extra handling of enum return value
125 125 if (!returnValueParam.isPointer && returnValueParam.typeId == PythonQtMethodInfo::Unknown) {
126 126 returnValueIsEnum = PythonQtClassInfo::hasEnum(returnValueParam.name, classInfo);
127 127 if (returnValueIsEnum) {
128 128 // create enum return value
129 129 PythonQtValueStorage_ADD_VALUE(PythonQtConv::global_valueStorage, long, 0, argList[0]);
130 130 }
131 131 }
132 132 if (argList[0]==NULL) {
133 133 // create empty default value for the return value
134 134 if (!directReturnValuePointer) {
135 135 // create empty default value for the return value
136 136 argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam);
137 if (argList[0]==NULL) {
138 // 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
139 // pass its internal pointer
140 PythonQtClassInfo* info = PythonQt::priv()->getClassInfo(returnValueParam.name);
141 if (info && info->pythonQtClassWrapper()) {
142 PyObject* emptyTuple = PyTuple_New(0);
143 // 1) default construct an empty object as python object (owned by PythonQt), by calling the meta class with empty arguments
144 result = PyObject_Call((PyObject*)info->pythonQtClassWrapper(), emptyTuple, NULL);
145 if (result) {
146 argList[0] = ((PythonQtInstanceWrapper*)result)->_wrappedPtr;
147 }
148 Py_DECREF(emptyTuple);
149 }
150 }
137 151 } else {
138 152 // we can use our pointer directly!
139 153 argList[0] = directReturnValuePointer;
140 154 }
141 155 }
142 156 }
143 157
144 158 // invoke the slot via metacall
145 159 (info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList);
146 160
147 161 // handle the return value (which in most cases still needs to be converted to a Python object)
148 162 if (argList[0] || returnValueParam.typeId == QMetaType::Void) {
149 163 if (directReturnValuePointer) {
150 164 result = NULL;
151 165 } else {
152 166 if (!returnValueIsEnum) {
153 result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]);
167 // the resulting object maybe present already, because we created it above at 1)...
168 if (!result) {
169 result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]);
170 }
154 171 } else {
155 172 result = PyInt_FromLong(*((unsigned int*)argList[0]));
156 173 }
157 174 }
158 175 } else {
159 QString e = QString("Called ") + info->fullSignature() + ", return type '" + returnValueParam.name + "' is ignored because it is unknown to PythonQt. Probaby you should register it using qRegisterMetaType().";
176 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.";
160 177 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
161 178 result = NULL;
162 179 }
163 180 }
164 181 recursiveEntry--;
165 182
166 183 // reset the parameter storage position to the stored pos to "pop" the parameter stack
167 184 PythonQtConv::global_valueStorage.setPos(globalValueStoragePos);
168 185 PythonQtConv::global_ptrStorage.setPos(globalPtrStoragePos);
169 186 PythonQtConv::global_variantStorage.setPos(globalVariantStoragePos);
170 187
171 188 *pythonReturnValue = result;
172 189 // NOTE: it is important to only return here, otherwise the stack will not be popped!!!
173 190 return result || (directReturnValuePointer && *directReturnValuePointer);
174 191 }
175 192
176 193 //-----------------------------------------------------------------------------------
177 194
178 195 static PythonQtSlotFunctionObject *pythonqtslot_free_list = NULL;
179 196
180 197 PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw)
181 198 {
182 199 PythonQtSlotFunctionObject* f = (PythonQtSlotFunctionObject*)func;
183 200 PythonQtSlotInfo* info = f->m_ml;
184 201 if (PyObject_TypeCheck(f->m_self, &PythonQtInstanceWrapper_Type)) {
185 202 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) f->m_self;
186 203 return PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, args, kw, self->_wrappedPtr);
187 204 } else if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
188 205 PythonQtClassWrapper* type = (PythonQtClassWrapper*) f->m_self;
189 206 if (info->isClassDecorator()) {
190 207 return PythonQtSlotFunction_CallImpl(type->classInfo(), NULL, info, args, kw);
191 208 } else {
192 209 // otherwise, it is an unbound call and we have an instanceDecorator or normal slot...
193 210 Py_ssize_t argc = PyTuple_Size(args);
194 211 if (argc>0) {
195 212 PyObject* firstArg = PyTuple_GET_ITEM(args, 0);
196 213 if (PyObject_TypeCheck(firstArg, (PyTypeObject*)&PythonQtInstanceWrapper_Type)
197 214 && ((PythonQtInstanceWrapper*)firstArg)->classInfo()->inherits(type->classInfo())) {
198 215 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)firstArg;
199 216 // strip the first argument...
200 217 PyObject* newargs = PyTuple_GetSlice(args, 1, argc);
201 218 PyObject* result = PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, newargs, kw, self->_wrappedPtr);
202 219 Py_DECREF(newargs);
203 220 return result;
204 221 } else {
205 222 // first arg is not of correct type!
206 223 QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument, got " + firstArg->ob_type->tp_name;
207 224 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
208 225 return NULL;
209 226 }
210 227 } else {
211 228 // wrong number of args
212 229 QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument.";
213 230 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
214 231 return NULL;
215 232 }
216 233 }
217 234 }
218 235 return NULL;
219 236 }
220 237
221 238 PyObject *PythonQtSlotFunction_CallImpl(PythonQtClassInfo* classInfo, QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * /*kw*/, void* firstArg, void** directReturnValuePointer)
222 239 {
223 240 int argc = PyTuple_Size(args);
224 241
225 242 #ifdef PYTHONQT_DEBUG
226 243 std::cout << "called " << info->metaMethod()->typeName() << " " << info->metaMethod()->signature() << std::endl;
227 244 #endif
228 245
229 246 PyObject* r = NULL;
230 247 bool ok = false;
231 248 if (directReturnValuePointer) {
232 249 *directReturnValuePointer = NULL;
233 250 }
234 251 if (info->nextInfo()) {
235 252 // overloaded slot call, try on all slots with strict conversion first
236 253 bool strict = true;
237 254 PythonQtSlotInfo* i = info;
238 255 while (i) {
239 256 bool skipFirst = i->isInstanceDecorator();
240 257 if (i->parameterCount()-1-(skipFirst?1:0) == argc) {
241 258 PyErr_Clear();
242 259 ok = PythonQtCallSlot(classInfo, objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer);
243 260 if (PyErr_Occurred() || ok) break;
244 261 }
245 262 i = i->nextInfo();
246 263 if (!i) {
247 264 if (strict) {
248 265 // one more run without being strict
249 266 strict = false;
250 267 i = info;
251 268 }
252 269 }
253 270 }
254 271 if (!ok && !PyErr_Occurred()) {
255 272 QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n");
256 273 PythonQtSlotInfo* i = info;
257 274 while (i) {
258 275 e += QString(i->fullSignature()) + "\n";
259 276 i = i->nextInfo();
260 277 }
261 278 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
262 279 }
263 280 } else {
264 281 // simple (non-overloaded) slot call
265 282 bool skipFirst = info->isInstanceDecorator();
266 283 if (info->parameterCount()-1-(skipFirst?1:0) == argc) {
267 284 PyErr_Clear();
268 285 ok = PythonQtCallSlot(classInfo, objectToCall, args, false, info, firstArg, &r, directReturnValuePointer);
269 286 if (!ok && !PyErr_Occurred()) {
270 287 QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
271 288 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
272 289 }
273 290 } else {
274 291 QString e = QString("Called ") + info->fullSignature() + " with wrong number of arguments: " + PythonQtConv::PyObjGetString(args);
275 292 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
276 293 }
277 294 }
278 295
279 296 return r;
280 297 }
281 298
282 299 PyObject *
283 300 PythonQtSlotFunction_New(PythonQtSlotInfo *ml, PyObject *self, PyObject *module)
284 301 {
285 302 PythonQtSlotFunctionObject *op;
286 303 op = pythonqtslot_free_list;
287 304 if (op != NULL) {
288 305 pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(op->m_self);
289 306 PyObject_INIT(op, &PythonQtSlotFunction_Type);
290 307 }
291 308 else {
292 309 op = PyObject_GC_New(PythonQtSlotFunctionObject, &PythonQtSlotFunction_Type);
293 310 if (op == NULL)
294 311 return NULL;
295 312 }
296 313 op->m_ml = ml;
297 314 Py_XINCREF(self);
298 315 op->m_self = self;
299 316 Py_XINCREF(module);
300 317 op->m_module = module;
301 318 PyObject_GC_Track(op);
302 319 return (PyObject *)op;
303 320 }
304 321
305 322 PythonQtSlotInfo*
306 323 PythonQtSlotFunction_GetSlotInfo(PyObject *op)
307 324 {
308 325 if (!PythonQtSlotFunction_Check(op)) {
309 326 PyErr_BadInternalCall();
310 327 return NULL;
311 328 }
312 329 return ((PythonQtSlotFunctionObject *)op) -> m_ml;
313 330 }
314 331
315 332 PyObject *
316 333 PythonQtSlotFunction_GetSelf(PyObject *op)
317 334 {
318 335 if (!PythonQtSlotFunction_Check(op)) {
319 336 PyErr_BadInternalCall();
320 337 return NULL;
321 338 }
322 339 return ((PythonQtSlotFunctionObject *)op) -> m_self;
323 340 }
324 341
325 342 /* Methods (the standard built-in methods, that is) */
326 343
327 344 static void
328 345 meth_dealloc(PythonQtSlotFunctionObject *m)
329 346 {
330 347 PyObject_GC_UnTrack(m);
331 348 Py_XDECREF(m->m_self);
332 349 Py_XDECREF(m->m_module);
333 350 m->m_self = (PyObject *)pythonqtslot_free_list;
334 351 pythonqtslot_free_list = m;
335 352 }
336 353
337 354 static PyObject *
338 355 meth_get__doc__(PythonQtSlotFunctionObject * /*m*/, void * /*closure*/)
339 356 {
340 357 Py_INCREF(Py_None);
341 358 return Py_None;
342 359 }
343 360
344 361 static PyObject *
345 362 meth_get__name__(PythonQtSlotFunctionObject *m, void * /*closure*/)
346 363 {
347 364 return PyString_FromString(m->m_ml->metaMethod()->signature());
348 365 }
349 366
350 367 static int
351 368 meth_traverse(PythonQtSlotFunctionObject *m, visitproc visit, void *arg)
352 369 {
353 370 int err;
354 371 if (m->m_self != NULL) {
355 372 err = visit(m->m_self, arg);
356 373 if (err)
357 374 return err;
358 375 }
359 376 if (m->m_module != NULL) {
360 377 err = visit(m->m_module, arg);
361 378 if (err)
362 379 return err;
363 380 }
364 381 return 0;
365 382 }
366 383
367 384 static PyObject *
368 385 meth_get__self__(PythonQtSlotFunctionObject *m, void * /*closure*/)
369 386 {
370 387 PyObject *self;
371 388 if (PyEval_GetRestricted()) {
372 389 PyErr_SetString(PyExc_RuntimeError,
373 390 "method.__self__ not accessible in restricted mode");
374 391 return NULL;
375 392 }
376 393 self = m->m_self;
377 394 if (self == NULL)
378 395 self = Py_None;
379 396 Py_INCREF(self);
380 397 return self;
381 398 }
382 399
383 400 static PyGetSetDef meth_getsets [] = {
384 401 {"__doc__", (getter)meth_get__doc__, NULL, NULL},
385 402 {"__name__", (getter)meth_get__name__, NULL, NULL},
386 403 {"__self__", (getter)meth_get__self__, NULL, NULL},
387 404 {NULL, NULL, NULL,NULL},
388 405 };
389 406
390 407 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 6
391 408 #define PY_WRITE_RESTRICTED WRITE_RESTRICTED
392 409 #endif
393 410
394 411 #define OFF(x) offsetof(PythonQtSlotFunctionObject, x)
395 412
396 413 static PyMemberDef meth_members[] = {
397 414 {"__module__", T_OBJECT, OFF(m_module), PY_WRITE_RESTRICTED},
398 415 {NULL}
399 416 };
400 417
401 418 static PyObject *
402 419 meth_repr(PythonQtSlotFunctionObject *f)
403 420 {
404 421 if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
405 422 PythonQtClassWrapper* self = (PythonQtClassWrapper*) f->m_self;
406 423 return PyString_FromFormat("<unbound qt slot %s of %s type>",
407 424 f->m_ml->slotName().data(),
408 425 self->classInfo()->className());
409 426 } else {
410 427 return PyString_FromFormat("<qt slot %s of %s instance at %p>",
411 428 f->m_ml->slotName().data(),
412 429 f->m_self->ob_type->tp_name,
413 430 f->m_self);
414 431 }
415 432 }
416 433
417 434 static int
418 435 meth_compare(PythonQtSlotFunctionObject *a, PythonQtSlotFunctionObject *b)
419 436 {
420 437 if (a->m_self != b->m_self)
421 438 return (a->m_self < b->m_self) ? -1 : 1;
422 439 if (a->m_ml == b->m_ml)
423 440 return 0;
424 441 if (strcmp(a->m_ml->metaMethod()->signature(), b->m_ml->metaMethod()->signature()) < 0)
425 442 return -1;
426 443 else
427 444 return 1;
428 445 }
429 446
430 447 static long
431 448 meth_hash(PythonQtSlotFunctionObject *a)
432 449 {
433 450 long x,y;
434 451 if (a->m_self == NULL)
435 452 x = 0;
436 453 else {
437 454 x = PyObject_Hash(a->m_self);
438 455 if (x == -1)
439 456 return -1;
440 457 }
441 458 y = _Py_HashPointer((void*)(a->m_ml));
442 459 if (y == -1)
443 460 return -1;
444 461 x ^= y;
445 462 if (x == -1)
446 463 x = -2;
447 464 return x;
448 465 }
449 466
450 467
451 468 PyTypeObject PythonQtSlotFunction_Type = {
452 469 PyObject_HEAD_INIT(&PyType_Type)
453 470 0,
454 471 "builtin_qt_slot",
455 472 sizeof(PythonQtSlotFunctionObject),
456 473 0,
457 474 (destructor)meth_dealloc, /* tp_dealloc */
458 475 0, /* tp_print */
459 476 0, /* tp_getattr */
460 477 0, /* tp_setattr */
461 478 (cmpfunc)meth_compare, /* tp_compare */
462 479 (reprfunc)meth_repr, /* tp_repr */
463 480 0, /* tp_as_number */
464 481 0, /* tp_as_sequence */
465 482 0, /* tp_as_mapping */
466 483 (hashfunc)meth_hash, /* tp_hash */
467 484 PythonQtSlotFunction_Call, /* tp_call */
468 485 0, /* tp_str */
469 486 PyObject_GenericGetAttr, /* tp_getattro */
470 487 0, /* tp_setattro */
471 488 0, /* tp_as_buffer */
472 489 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
473 490 0, /* tp_doc */
474 491 (traverseproc)meth_traverse, /* tp_traverse */
475 492 0, /* tp_clear */
476 493 0, /* tp_richcompare */
477 494 0, /* tp_weaklistoffset */
478 495 0, /* tp_iter */
479 496 0, /* tp_iternext */
480 497 0, /* tp_methods */
481 498 meth_members, /* tp_members */
482 499 meth_getsets, /* tp_getset */
483 500 0, /* tp_base */
484 501 0, /* tp_dict */
485 502 };
486 503
487 504 /* Clear out the free list */
488 505
489 506 void
490 507 PythonQtSlotFunction_Fini(void)
491 508 {
492 509 while (pythonqtslot_free_list) {
493 510 PythonQtSlotFunctionObject *v = pythonqtslot_free_list;
494 511 pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(v->m_self);
495 512 PyObject_GC_Del(v);
496 513 }
497 514 }
498 515
General Comments 0
You need to be logged in to leave comments. Login now