1 /***************************************************************************
3 * This file is part of the KDE project
4 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 ***************************************************************************/
20 #include "pythonextension.h"
21 #include "pythoninterpreter.h"
22 #include "pythonvariant.h"
23 #include "pythonfunction.h"
26 #include <QMetaMethod>
28 #include <QVarLengthArray>
32 /// \internal d-pointer class.
33 class PythonExtension::Private
36 /// The QObject this PythonExtension wraps.
37 QPointer
<QObject
> object
;
38 /// Defines if this PythonExtension the owner of the QObject.
41 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
42 /// \internal string for debugging.
46 /// The cached list of methods.
47 QHash
<QByteArray
, Py::Int
> methods
;
48 /// The cached list of properties.
49 QHash
<QByteArray
, QMetaProperty
> properties
;
50 /// The cached list of enumerations.
51 QHash
<QByteArray
, int> enumerations
;
53 /// The \a PythonFunction instances.
54 QHash
<QByteArray
, PythonFunction
*> functions
;
56 /// The cached list of methodnames.
58 /// The cached list of membernames.
61 /// The proxymethod which will handle all calls to our \a PythonExtension instance.
62 Py::MethodDefExt
<PythonExtension
>* proxymethod
;
68 using namespace Kross
;
70 PythonExtension::PythonExtension(QObject
* object
, bool owner
)
71 : Py::PythonExtension
<PythonExtension
>()
77 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
78 d
->debuginfo
= object
? QString("%1 (%2)").arg(object
->objectName()).arg(object
->metaObject()->className()) : "NULL";
79 krossdebug( QString("PythonExtension::Constructor object=%1").arg(d
->debuginfo
) );
82 behaviors().name("KrossPythonExtension");
83 behaviors().doc("The KrossPythonExtension object wraps a QObject into the world of python.");
84 //behaviors().supportPrint();
85 behaviors().supportGetattr();
86 behaviors().supportSetattr();
87 //behaviors().supportGetattro();
88 //behaviors().supportSetattro();
89 behaviors().supportCompare();
90 //behaviors().supportRepr();
91 //behaviors().supportStr();
92 behaviors().supportHash();
93 //behaviors().supportIter();
94 //behaviors().supportCall();
95 behaviors().supportSequenceType();
96 behaviors().supportMappingType();
97 behaviors().supportNumberType();
98 //behaviors().supportBufferType();
100 add_varargs_method("className", &PythonExtension::getClassName
, "Return the name of the QObject class.");
101 //add_varargs_method("classInfo", &PythonExtension::getClassInfo, "Return a list of key,value-tuples of class information.");
102 add_varargs_method("signalNames", &PythonExtension::getSignalNames
, "Return list of signal names the QObject provides.");
103 add_varargs_method("slotNames", &PythonExtension::getSlotNames
, "Return list of slot names the QObject provides.");
104 add_varargs_method("propertyNames", &PythonExtension::getPropertyNames
, "Return list of property names the QObject provides.");
105 //add_varargs_method("dynamicPropertyNames", &PythonExtension::getDynamicPropertyNames, "");
106 add_varargs_method("property", &PythonExtension::getProperty
, "Return a property value.");
107 add_varargs_method("setProperty", &PythonExtension::setProperty
, "Set a property value.");
108 add_varargs_method("__toPointer__", &PythonExtension::toPointer
, "Return the void* pointer of the QObject.");
109 //add_varargs_method("__fromPointer__", &PythonExtension::fromPointer, "Set the QObject* to the passed void* pointer.");
110 add_varargs_method("connect", &PythonExtension::doConnect
, "Connect signal, slots or python functions together.");
111 add_varargs_method("disconnect", &PythonExtension::doDisconnect
, "Disconnect signal, slots or python functions that are connected together.");
113 d
->proxymethod
= new Py::MethodDefExt
<PythonExtension
>(
114 "", // methodname, not needed cause we use the method only internaly.
115 0, // method that should handle the callback, not needed cause proxyhandler will handle it.
116 Py::method_varargs_call_handler_t( proxyhandler
), // callback handler
121 const QMetaObject
* metaobject
= d
->object
->metaObject();
123 { // initialize methods.
124 const int count
= metaobject
->methodCount();
125 for(int i
= 0; i
< count
; ++i
) {
126 QMetaMethod member
= metaobject
->method(i
);
127 const QString signature
= member
.signature();
128 const QByteArray name
= signature
.left(signature
.indexOf('(')).toLatin1();
129 if(! d
->methods
.contains(name
)) {
130 d
->methods
.insert(name
, Py::Int(i
));
131 d
->methodnames
.append(Py::String(name
));
136 { // initialize properties
137 const int count
= metaobject
->propertyCount();
138 for(int i
= 0; i
< count
; ++i
) {
139 QMetaProperty prop
= metaobject
->property(i
);
140 d
->properties
.insert(prop
.name(), prop
);
141 d
->membernames
.append( Py::String(prop
.name()) );
145 { // initialize enumerations
146 const int count
= metaobject
->enumeratorCount();
147 for(int i
= 0; i
< count
; ++i
) {
148 QMetaEnum e
= metaobject
->enumerator(i
);
149 const int kc
= e
.keyCount();
150 for(int k
= 0; k
< kc
; ++k
) {
151 const QByteArray name
= /*e.name() +*/ e
.key(k
);
152 d
->enumerations
.insert(name
, e
.value(k
));
153 d
->membernames
.append( Py::String(name
) );
160 PythonExtension::~PythonExtension()
162 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
163 krossdebug( QString("PythonExtension::Destructor object=%1").arg(d
->debuginfo
) );
168 qDeleteAll(d
->functions
);
170 delete d
->proxymethod
;
174 QObject
* PythonExtension::object() const
179 Py::Object
PythonExtension::getattr(const char* n
)
181 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
182 krossdebug( QString("PythonExtension::getattr name='%1'").arg(n
) );
185 // handle internal methods
187 if(strcmp(n
,"__methods__") == 0)
188 return d
->methodnames
;
189 if(strcmp(n
,"__members__") == 0)
190 return d
->membernames
;
191 //if(strcmp(n,"__dict__") == 0)
192 // return PythonType<QStringList>::toPyObject( QStringList() );
193 if(strcmp(n
,"__all__") == 0) //this hack is needed to prevent "from-import-* object has no __dict__ and no __all__" exceptions
194 return d
->methodnames
;
197 // look if the attribute is a method
198 if(d
->methods
.contains(n
)) {
199 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
200 krossdebug( QString("PythonExtension::getattr name='%1' is a method.").arg(n
) );
203 // Python is more dynamic then someone thinks of what provides us bigger problems
204 // here since we are not able to cache the whole lookup and need to create and return
205 // a new function object at each request since 1) they may used internaly by python
206 // to redirect this method/attr to somebody else and 2) those returned function object
207 // may prevent this object instance from beeing removed by the garbage collector.
209 //FIXME use callcache here to improve the performance by some factors!
211 t
[0] = Py::Object(this); // reference to this instance, set at getattr()
212 t
[1] = d
->methods
[n
]; // the first index used for faster access
213 t
[2] = Py::String(n
); // the name of the method
214 t
.increment_reference_count(); // the PyCFunction_New shoukd take care of removing our ref...
215 return Py::Object(PyCFunction_New( &d
->proxymethod
->ext_meth_def
, t
.ptr() ), true);
218 // look if the attribute is a property
219 if(d
->properties
.contains(n
) && d
->object
) {
220 QMetaProperty property
= d
->properties
[n
];
222 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
223 krossdebug( QString("PythonExtension::getattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 user=%7 usertype=%8")
224 .arg(n
).arg(property
.typeName()).arg(property
.isValid())
225 .arg(property
.isReadable()).arg(property
.isScriptable(d
->object
)).arg(property
.isWritable())
226 .arg(property
.isUser(d
->object
)).arg(property
.userType())
230 if(! property
.isReadable()) {
231 Py::AttributeError( ::QString("Attribute \"%1\" is not readable.").arg(n
).toLatin1().constData() );
235 return PythonType
<QVariant
>::toPyObject( property
.read(d
->object
) );
238 // look if the attribute is an enumerator
239 if(d
->enumerations
.contains(n
)) {
240 return Py::Int(d
->enumerations
[n
]);
243 // finally redirect the unhandled attribute-request...
244 //return Py::PythonExtension<PythonExtension>::getattr_methods(n);
245 return Py::PythonExtension
<PythonExtension
>::getattr(n
);
248 int PythonExtension::setattr(const char* n
, const Py::Object
& value
)
250 // look if the attribute is a property
251 if(d
->properties
.contains(n
) && d
->object
) {
252 QMetaProperty property
= d
->properties
[n
];
254 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
255 krossdebug( QString("PythonExtension::setattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 usertype=%7")
256 .arg(n
).arg(property
.typeName()).arg(property
.isValid())
257 .arg(property
.isReadable()).arg(property
.isScriptable(d
->object
)).arg(property
.isWritable())
258 .arg(property
.isUser(d
->object
)).arg(property
.userType())
262 if(! property
.isWritable()) {
263 Py::AttributeError( ::QString("Attribute \"%1\" is not writable.").arg(n
).toLatin1().constData() );
264 return -1; // indicate error
267 QVariant v
= PythonType
<QVariant
>::toVariant(value
);
268 if(! property
.write(d
->object
, v
)) {
269 Py::AttributeError( ::QString("Setting attribute \"%1\" failed.").arg(n
).toLatin1().constData() );
270 return -1; // indicate error
272 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
273 krossdebug( QString("PythonExtension::setattr name='%1' value='%2'").arg(n
).arg(v
.toString()) );
275 return 0; // indicate success
278 // finally redirect the unhandled attribute-request...
279 return Py::PythonExtension
<PythonExtension
>::setattr(n
, value
);
282 int PythonExtension::compare(const Py::Object
& other
)
284 if(Py::PythonExtension
<PythonExtension
>::check( other
)) {
285 Py::ExtensionObject
<PythonExtension
> extobj( other
);
286 PythonExtension
* extension
= extobj
.extensionObject();
287 QObject
* obj
= extension
->object();
288 //krossdebug( QString("PythonExtension::compare this.name='%1' other.name='%2' other.pyobject='%3'").arg(d->object ? d->object->objectName() : "NULL").arg(obj ? obj->objectName() : "NULL").arg(other.as_string().c_str()) );
290 //Q_ASSERT( object() );
291 return obj
== object() ? 0 : ( obj
> object() ? -1 : 1 );
293 PyErr_SetObject(PyExc_TypeError
, other
.ptr());
297 long PythonExtension::hash()
299 return long( (QObject
*) d
->object
);
302 /* objectName is a property anyway and therefore already accessible
303 Py::Object PythonExtension::getObjectName(const Py::Tuple&)
305 return PythonType<QString>::toPyObject( d->object->objectName() );
309 Py::Object
PythonExtension::getClassName(const Py::Tuple
&)
311 return PythonType
<QString
>::toPyObject( d
->object
->metaObject()->className() );
314 Py::Object
PythonExtension::getSignalNames(const Py::Tuple
&)
317 const QMetaObject
* metaobject
= d
->object
->metaObject();
318 const int count
= metaobject
->methodCount();
319 for(int i
= 0; i
< count
; ++i
) {
320 QMetaMethod m
= metaobject
->method(i
);
321 if( m
.methodType() == QMetaMethod::Signal
)
322 list
.append( Py::String(m
.signature()) );
327 Py::Object
PythonExtension::getSlotNames(const Py::Tuple
&)
330 const QMetaObject
* metaobject
= d
->object
->metaObject();
331 const int count
= metaobject
->methodCount();
332 for(int i
= 0; i
< count
; ++i
) {
333 QMetaMethod m
= metaobject
->method(i
);
334 if( m
.methodType() == QMetaMethod::Slot
)
335 list
.append( Py::String(m
.signature()) );
340 Py::Object
PythonExtension::getPropertyNames(const Py::Tuple
&)
343 const QMetaObject
* metaobject
= d
->object
->metaObject();
344 const int count
= metaobject
->propertyCount();
345 for(int i
= 0; i
< count
; ++i
)
346 list
.append( Py::String(metaobject
->property(i
).name()) );
350 Py::Object
PythonExtension::getProperty(const Py::Tuple
& args
)
352 if( args
.size() != 1 ) {
353 Py::TypeError("Expected the propertyname as argument.");
356 return PythonType
<QVariant
>::toPyObject( d
->object
->property(
357 PythonType
<QByteArray
>::toVariant(args
[0]).constData()
361 Py::Object
PythonExtension::setProperty(const Py::Tuple
& args
)
363 if( args
.size() != 2 ) {
364 Py::TypeError("Expected the propertyname and the value as arguments.");
367 return PythonType
<bool>::toPyObject( d
->object
->setProperty(
368 PythonType
<QByteArray
>::toVariant(args
[0]).constData(),
369 PythonType
<QVariant
>::toVariant(args
[1])
373 Py::Object
PythonExtension::toPointer(const Py::Tuple
&)
375 QObject
* obj
= d
->object
;
376 PyObject
* qobjectptr
= PyLong_FromVoidPtr( (void*) obj
);
377 //PyObject* o = Py_BuildValue ("N", mw);
378 return Py::asObject( qobjectptr
);
379 //PythonPyQtExtension* pyqtextension = new PythonPyQtExtension(self, args);
380 //return pyqtextension;
384 Py::Object PythonExtension::fromPointer(fromPointer(const Py::Tuple&)
386 QObject* object = dynamic_cast< QObject* >(PyLong_AsVoidPtr( args[0] ));
390 Py::Object
PythonExtension::doConnect(const Py::Tuple
& args
)
392 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
393 krossdebug( QString("PythonExtension::doConnect" ) );
394 for(uint i
= 0; i
< args
.size(); ++i
)
396 QVariant v
= PythonType
<QVariant
>::toVariant( args
[i
] );
397 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i
).arg(v
.toString()).arg(v
.typeName()) );
399 catch(Py::Exception
& e
) { // may happen e.g. on "function" types
400 PyTypeObject
*type
= (PyTypeObject
*) args
[i
].type().ptr();
401 krossdebug( QString(" Argument index=%1 tp_name=%2").arg(i
).arg(type
->tp_name
) );
406 if( args
.size() < 2 ) {
407 Py::TypeError("Expected at least 2 arguments.");
408 return PythonType
<bool>::toPyObject(false);
411 uint idx
; // next argument to check
412 QObject
* sender
; // the sender object
413 QByteArray sendersignal
; // the sender signal
414 if( args
[0].isString() ) { // connect(signal, ...)
415 //krossdebug( QString("PythonExtension::doConnect connect(signal, ...)" ) );
417 sendersignal
= PythonType
<QByteArray
>::toVariant( args
[0] );
420 else { // connect(sender, signal, ...)
421 //krossdebug( QString("PythonExtension::doConnect connect(sender, signal, ...)" ) );
422 Py::ExtensionObject
<PythonExtension
> extobj(args
[0]);
423 PythonExtension
* extension
= extobj
.extensionObject();
425 Py::TypeError( ::QString("First argument needs to be a signalname or a sender-object.").toLatin1().constData() );
426 return PythonType
<bool>::toPyObject(false);
428 sender
= extension
->object();
429 if( ! args
[1].isString() ) {
430 Py::TypeError( ::QString("Second argument needs to be a signalname.").toLatin1().constData() );
431 return PythonType
<bool>::toPyObject(false);
433 sendersignal
= PythonType
<QByteArray
>::toVariant( args
[1] );
435 if( args
.size() <= idx
) {
436 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx
+1).toLatin1().constData() );
437 return PythonType
<bool>::toPyObject(false);
441 QObject
* receiver
; // the receiver object
442 QByteArray receiverslot
; // the receiver slot
443 if( args
[idx
].isCallable() ) { // connect with python function
444 //krossdebug( QString("PythonExtension::doConnect connect with python function" ) );
445 Py::Callable
func(args
[idx
]); // the callable python function
446 PythonFunction
* function
= new PythonFunction(sender
, sendersignal
, func
);
447 d
->functions
.insertMulti(sendersignal
, function
);
449 receiverslot
= sendersignal
;
451 else { // connect with receiver and slot
452 //krossdebug( QString("PythonExtension::doConnect connect with receiver and slot" ) );
453 if( args
[idx
].isString() ) { // connect(..., slot)
454 receiver
= d
->object
;
455 receiverslot
= PythonType
<QByteArray
>::toVariant( args
[idx
] );
457 else { // connect(..., receiver, slot)
458 Py::ExtensionObject
<PythonExtension
> extobj(args
[idx
]);
459 PythonExtension
* extension
= extobj
.extensionObject();
461 Py::TypeError( ::QString("Receiver argument needs to be a slotname or a receiver-object.").toLatin1().constData() );
462 return PythonType
<bool>::toPyObject(false);
464 receiver
= extension
->object();
466 if( args
.size() < idx
) {
467 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx
+1).toLatin1().constData() );
468 return PythonType
<bool>::toPyObject(false);
470 if( ! args
[idx
].isString() ) {
471 Py::TypeError( ::QString("Expected receiver slotname as argument %1.").arg(idx
+1).toLatin1().constData() );
472 return PythonType
<bool>::toPyObject(false);
474 receiverslot
= PythonType
<QByteArray
>::toVariant( args
[idx
] );
478 // Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
479 // defined them explicit, we assume it's wanted to connect from a signal to
480 // a slot. This seems to be the most flexible solution so far...
481 if( ! sendersignal
.startsWith('1') && ! sendersignal
.startsWith('2') )
482 sendersignal
.prepend('2'); // prepending 2 means SIGNAL(...)
483 if( ! receiverslot
.startsWith('1') && ! receiverslot
.startsWith('2') )
484 receiverslot
.prepend('1'); // prepending 1 means SLOT(...)
486 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
487 krossdebug( QString("PythonExtension::doConnect sender=%1 signal=%2 receiver=%3 slot=%4").arg(sender
->objectName()).arg(sendersignal
.constData()).arg(receiver
->objectName()).arg(receiverslot
.constData()).toLatin1().constData() );
490 if(! QObject::connect(sender
, sendersignal
, receiver
, receiverslot
) ) {
491 krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
492 return PythonType
<bool>::toPyObject(false);
494 return PythonType
<bool>::toPyObject(true);
497 Py::Object
PythonExtension::doDisconnect(const Py::Tuple
&)
500 return PythonType
<bool>::toPyObject(false);
503 PyObject
* PythonExtension::proxyhandler(PyObject
*_self_and_name_tuple
, PyObject
*args
)
508 GilState() { m_gilstate = PyGILState_Ensure(); } // Acquire interpreter lock
509 ~GilState() { PyGILState_Release( m_gilstate ); } // Free interpreter lock
511 PyGILState_STATE m_gilstate;
517 Py::Tuple
selftuple(_self_and_name_tuple
);
519 int methodindex
= Py::Int(selftuple
[1]);
521 QByteArray ba
= Py::String(selftuple
[2]).as_string().c_str();
522 const char* methodname
= ba
.constData();
524 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
525 krossdebug( QString("PythonExtension::proxyhandler methodname=%1 methodindex=%2").arg(methodname
).arg(methodindex
) );
528 PythonExtension
*self
= static_cast<PythonExtension
*>( selftuple
[0].ptr() );
529 if( ! self
->d
->object
) {
530 Py::RuntimeError( QString("Underlying QObject instance of method '%1' was removed.").arg(methodname
).toLatin1().constData() );
534 Py::Tuple
argstuple(args
);
535 const int argssize
= int( argstuple
.size() );
536 QMetaMethod metamethod
= self
->d
->object
->metaObject()->method( methodindex
);
537 if(metamethod
.parameterTypes().size() != argssize
) {
539 const int count
= self
->d
->object
->metaObject()->methodCount();
540 for(++methodindex
; methodindex
< count
; ++methodindex
) {
541 metamethod
= self
->d
->object
->metaObject()->method( methodindex
);
542 const QString signature
= metamethod
.signature();
543 const QByteArray name
= signature
.left(signature
.indexOf('(')).toLatin1();
544 if(name
== methodname
) {
545 if(metamethod
.parameterTypes().size() == argssize
) {
552 krosswarning( QString("PythonExtension::proxyhandler The method '%1' does not expect %2 arguments.").arg(methodname
).arg(argssize
) );
553 throw Py::TypeError( QString("Invalid number of arguments for the method %1").arg(methodname
).toLatin1().constData() );
557 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
558 krossdebug( QString("PythonExtension::proxyhandler QMetaMethod idx=%1 sig=%2 tag=%3 type=%4").arg(methodindex
).arg(metamethod
.signature()).arg(metamethod
.tag()).arg(metamethod
.typeName()) );
559 for(int i
= 0; i
< argssize
; ++i
) {
562 v
= PythonType
<QVariant
>::toVariant( argstuple
[i
] );
564 catch(Py::Exception
& e
) {
565 v
= Py::value(e
).as_string().c_str();
568 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i
).arg(v
.toString()).arg(v
.typeName()) );
574 QList
<QByteArray
> typelist
= metamethod
.parameterTypes();
575 const int typelistcount
= typelist
.count();
576 bool hasreturnvalue
= strcmp(metamethod
.typeName(),"") != 0;
578 // exact 1 returnvalue + 0..9 arguments
579 Q_ASSERT(typelistcount
<= 10);
580 QVarLengthArray
<MetaType
*> variantargs( typelistcount
+ 1 );
581 QVarLengthArray
<void*> voidstarargs( typelistcount
+ 1 );
583 // set the return value
585 MetaType
* returntype
= PythonMetaTypeFactory::create( metamethod
.typeName(), Py::Object(), false /*owner*/ );
586 variantargs
[0] = returntype
;
587 voidstarargs
[0] = returntype
->toVoidStar();
588 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
589 krossdebug( QString("PythonExtension::proxyhandler typeName=%1").arg(metamethod
.typeName()) );
594 voidstarargs
[0] = (void*)0;
600 for(; idx
<= typelistcount
; ++idx
) {
601 variantargs
[idx
] = PythonMetaTypeFactory::create( typelist
[idx
-1].constData(), argstuple
[idx
- 1], false /*owner*/ );
602 voidstarargs
[idx
] = variantargs
[idx
]->toVoidStar();
605 catch(Py::Exception
& e
) {
606 // Seems PythonMetaTypeFactory::create raised an exception
607 // up. Clean all already allocated MetaType instances.
608 for(int i
= 0; i
< idx
; ++i
)
609 delete variantargs
[i
];
610 throw e
; // re-throw exception
613 // call the method now
614 int r
= self
->d
->object
->qt_metacall(QMetaObject::InvokeMetaMethod
, methodindex
, &voidstarargs
[0]);
615 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
616 krossdebug( QString("RESULT nr=%1").arg(r
) );
621 // eval the return-value
624 if( Kross::MetaTypeHandler
* handler
= Kross::Manager::self().metaTypeHandler(metamethod
.typeName()) ) {
625 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
626 krossdebug( QString("Returnvalue of type '%2' has a handler").arg(metamethod
.typeName()) );
628 void *ptr
= (*reinterpret_cast<void*(*)>( variantargs
[0]->toVoidStar() ));
629 v
= handler
->callHandler(ptr
);
632 v
= QVariant(variantargs
[0]->typeId(), variantargs
[0]->toVoidStar());
634 if( ! Kross::Manager::self().strictTypesEnabled() ) {
635 if( v
.type() == QVariant::Invalid
&& QByteArray(metamethod
.typeName()).endsWith("*") ) {
636 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
637 krossdebug( QString("Returnvalue of type '%2' will be reinterpret_cast<QObject*>").arg(d
->metamethod
.typeName()) );
639 QObject
* obj
= (*reinterpret_cast<QObject
*(*)>( variantargs
[0]->toVoidStar() ));
640 v
.setValue( (QObject
*) obj
);
644 pyresult
= PythonType
<QVariant
>::toPyObject(v
);
645 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
646 krossdebug( QString("Returnvalue typeId=%1 metamethod.typename=%2 variant.toString=%3 variant.typeName=%4 pyobject=%5").arg(variantargs
[0]->typeId()).arg(metamethod
.typeName()).arg(v
.toString()).arg(v
.typeName()).arg(pyresult
.as_string().c_str()) );
650 // finally free the PythonVariable instances
651 for(int i
= 0; i
<= typelistcount
; ++i
)
652 delete variantargs
[i
];
655 pyresult
.increment_reference_count(); // don't destroy PyObject* if pyresult got destroyed.
656 return pyresult
.ptr();
658 catch(Py::Exception
& e
) {
661 PythonInterpreter::extractException(trace
, lineno
);
662 krosswarning( QString("PythonExtension::proxyhandler Had exception on line %1:\n%2 \n%3").arg(lineno
).arg(Py::value(e
).as_string().c_str()).arg(trace
.join("\n")) );
663 PyErr_Print(); //e.clear();
669 int PythonExtension::sequence_length()
671 return d
->object
->children().count();
674 Py::Object
PythonExtension::sequence_concat(const Py::Object
& obj
)
676 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_concat %1").arg(obj
.as_string().c_str()).toLatin1().constData() );
679 Py::Object
PythonExtension::sequence_repeat(Py_ssize_t index
)
681 // what we do here is rather evil and does not make sense at all. We assume that something
682 // like "myobjinstance * 2" means, that the pointer-address should be multiplied by 2. This
683 // is the case to keep backward-compatibility with SuperKaramba. In normal cases you wan't
684 // use such kind of logic within your python scripts anyway + we can't guarantee that the
685 // resulting number may not overflow or something like this.
686 return Py::Long( long( (QObject
*) d
->object
) * index
);
689 Py::Object
PythonExtension::sequence_item(Py_ssize_t index
)
691 if(index
< d
->object
->children().count())
692 return Py::asObject(new PythonExtension( d
->object
->children().at(index
) ));
693 return Py::asObject( Py::new_reference_to( NULL
) );
696 Py::Object
PythonExtension::sequence_slice(Py_ssize_t from
, Py_ssize_t to
)
700 const int count
= d
->object
->children().count();
701 for(int i
= from
; i
<= to
&& i
< count
; ++i
)
702 list
.append( Py::asObject(new PythonExtension( d
->object
->children().at(i
) )) );
707 int PythonExtension::sequence_ass_item(Py_ssize_t index
, const Py::Object
& obj
)
709 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_item %1 %2").arg(index
).arg(obj
.as_string().c_str()).toLatin1().constData() );
712 int PythonExtension::sequence_ass_slice(Py_ssize_t from
, Py_ssize_t to
, const Py::Object
& obj
)
714 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_slice %1 %2 %3").arg(from
).arg(to
).arg(obj
.as_string().c_str()).toLatin1().constData() );
717 int PythonExtension::mapping_length()
719 return d
->object
->children().count();
722 Py::Object
PythonExtension::mapping_subscript(const Py::Object
& obj
)
724 QString name
= Py::String(obj
).as_string().c_str();
725 QObject
* object
= d
->object
->findChild
< QObject
* >( name
);
727 foreach(QObject
* o
, d
->object
->children()) {
728 if(name
== o
->metaObject()->className()) {
735 return Py::asObject(new PythonExtension(object
));
736 return Py::asObject( Py::new_reference_to( NULL
) );
739 int PythonExtension::mapping_ass_subscript(const Py::Object
& obj1
, const Py::Object
& obj2
)
741 throw Py::RuntimeError( QString("Unsupported: PythonExtension::mapping_ass_subscript %1 %2").arg(obj1
.as_string().c_str()).arg(obj2
.as_string().c_str()).toLatin1().constData() );
744 int PythonExtension::number_nonzero()
746 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
747 krossdebug("PythonExtension::number_nonzero");
749 return d
->object
? 1 : 0;
752 Py::Object
PythonExtension::number_int()
754 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
755 krossdebug("PythonExtension::number_int");
757 return Py::Int( hash() );
760 Py::Object
PythonExtension::number_long()
762 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
763 krossdebug("PythonExtension::number_long");
765 return Py::Long( hash() );
768 Py::Object
PythonExtension::number_hex()
770 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
771 krossdebug("PythonExtension::number_hex");
773 void* ptr
= (QObject
*) d
->object
;
774 return Py::Object(PyString_FromFormat("%p",ptr
),true);